tags:

views:

33

answers:

2

Hi,

Why is var excludes = users.Except(matches); not excluding the matches?

What is the proper way if I want the equality comparer to use only the ID? Examples would be appreciated.

public class User
{
    public int ID { get; set; }
    public string Name { get; set; }

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

private static void LinqTest2()
{
    IEnumerable<User> users = new List<User>
    {
        new User {ID = 1, Name = "Jack"},
        new User {ID = 2, Name = "Tom"},
        new User {ID = 3, Name = "Jim"},
        new User {ID = 4, Name = "Joe"},
        new User {ID = 5, Name = "James"},
        new User {ID = 6, Name = "Matt"},
        new User {ID = 7, Name = "Jon"},
        new User {ID = 8, Name = "Jill"}
    }.OfType<User>();

    IEnumerable<User> localList = new List<User>
    {
        new User {ID = 4, Name = "Joe"},
        new User {ID = 5, Name = "James"}
    }.OfType<User>();


    //After creating the two list
    var matches = from u in users
                  join lu in localList
                    on u.ID equals lu.ID
                  select lu;
    Console.WriteLine("--------------MATCHES----------------");
    foreach (var item in matches)
    {
        Console.WriteLine(item.ToString());
    }

    Console.WriteLine("--------------EXCLUDES----------------");
    var excludes = users.Except(matches);
    foreach (var item in excludes)
    {
        Console.WriteLine(item.ToString());
    }            
}
+3  A: 

Your User class doesn't override Equals and GetHashCode so the default implementation of Equals is used. In this case this means it compares the references. You have created two user objects with the same values, but because these are differenct objects they compare unequal.

An alternative to overriding Equals is to use the overload of Except that takes an IEqualityComparer.

Mark Byers
"An alternative to overriding Equals is to use the overload of Except that takes an IEqualityComparer" - Is this what max's answer showed, or something different?
VoodooChild
Yes, it's exactly this case. Overriding `Equals` should be considered with caution because this logic will apply anywhere `User` object is used (but you might actually need this behavior); supplying `IEqualityComparer` limits comparison rules to just a single call. Btw, I wonder why they did'n make an `Except` overload which takes a more lightweight `Comparison` delegate, which eliminates the need to declare a whole new class since a delegate can be anonymous.
max
+4  A: 
    sealed class CompareUsersById : IEqualityComparer<User>
    {
        public bool Equals(User x, User y)
        {
            if(x == null)
                return y == null;
            else if(y == null)
                return false;
            else
                return x.ID == y.ID;
        }

        public int GetHashCode(User obj)
        {
            return obj.ID;
        }
    }

and then

var excludes = users.Except(matches, new CompareUsersById());
max
+1: This works. Why did you make this class `sealed`?
VoodooChild
Just a habit - if I'm pretty much sure that class won't be inherited or there is no reason to inherit it, I make it sealed.
max