LINQ Except operator and object equality

Posted by Abhijeet Patel on Stack Overflow See other posts from Stack Overflow or by Abhijeet Patel
Published on 2009-03-28T19:09:31Z Indexed on 2010/12/29 15:54 UTC
Read the original article Hit count: 225

Filed under:
|

Here is an interesting issue I noticed when using the Except Operator: I have list of users from which I want to exclude some users:

The list of users is coming from an XML file:

The code goes like this:

interface IUser { int ID { get; set; } string Name { get; set; } }

class User: IUser
{

    #region IUser Members

    public int ID
    {
        get;
        set;
    }

    public string Name
    {
        get;
        set;
    }

    #endregion

    public override string ToString()
    {
        return ID + ":" +Name;
    }


    public static IEnumerable<IUser> GetMatchingUsers(IEnumerable<IUser> users)
    {
         IEnumerable<IUser> localList = new List<User>
         {
            new User{ ID=4, Name="James"},
            new User{ ID=5, Name="Tom"}

         }.OfType<IUser>();
         var matches = from u in users
                       join lu in localList
                           on u.ID equals lu.ID
                       select u;
         return matches;
    }
}

class Program
{
    static void Main(string[] args)
    {
        XDocument doc = XDocument.Load("Users.xml");
        IEnumerable<IUser> users = doc.Element("Users").Elements("User").Select
            (u => new User
                { ID = (int)u.Attribute("id"),
                  Name = (string)u.Attribute("name")
                }
            ).OfType<IUser>();       //still a query, objects have not been materialized


        var matches = User.GetMatchingUsers(users);
        var excludes = users.Except(matches);    // excludes should contain 6 users but here it contains 8 users

    }
}

When I call User.GetMatchingUsers(users) I get 2 matches as expected. The issue is that when I call users.Except(matches) The matching users are not being excluded at all! I am expecting 6 users ut "excludes" contains all 8 users instead.

Since all I'm doing in GetMatchingUsers(IEnumerable users) is taking the IEnumerable and just returning the IUsers whose ID's match( 2 IUsers in this case), my understanding is that by default "Except" will use reference equality for comparing the objects to be excluded. Is this not how "Except" behaves?

What is even more interesting is that if I materialize the objects using .ToList() and then get the matching users, and call "Except", everything works as expected!

Like so:

IEnumerable users = doc.Element("Users").Elements("User").Select (u => new User { ID = (int)u.Attribute("id"), Name = (string)u.Attribute("name") } ).OfType().ToList(); //explicity materializing all objects by calling ToList()

var matches = User.GetMatchingUsers(users); var excludes = users.Except(matches); // excludes now contains 6 users as expected

I don't see why I should need to materialize objects for calling "Except" given that its defined on IEnumerable?

Any suggesstions / insights would be much appreciated.

© Stack Overflow or respective owner

Related posts about c#

Related posts about LINQ