1

I am a bit confused as to when I should be doing either of the following:

public class Product : IEquatable<Comparer> 
{
}

vs

public class ProductComparer : IEqualityComparer<Comparer> 
{
}

For example: If I use technique #2 and want to do a linq statement that will merge two lists of type Product, but avoid duplicates I can easily do this:

mergedProducts = products.Union(extraProducts, new ProductComparer()).ToList();

But what if I decided to go with technique #1 can I still do the same thing with linq or only things like var equal = ProductA == ProductB ?

Yuval Itzchakov
  • 146,575
  • 32
  • 257
  • 321
Blake Rivell
  • 13,105
  • 31
  • 115
  • 231
  • 2
    Do you mean `IEquatable` and `IEqualityComparer`? – Lee Mar 31 '16 at 13:36
  • @Lee No I actually mean having your model object directly implement IEqualityComparer vs having to create an additional class that implements IEqualityComparer. So for example I wanted to just have my Product class implement IEqualityComparer but when a linq function asks for a comparer this does me no good.. I actually have to pass it a class such as ProductComparer. – Blake Rivell Mar 31 '16 at 13:38
  • 1
    `IEqualityComparer` is used to provide an 'external' definition of equality for some type `T`, while `IEquatable` is used to compare equality between an item of `T` and the receiver. Implement `IEquatable` if you have a default notion of equality for your type. It would be very odd to implement `IEqualityComparer` on `Product` itself since you would need to create a new `Product` instance to use the defined equaltiy/hashcode methods. – Lee Mar 31 '16 at 13:49

1 Answers1

2

You want to implement IEquatable<T> when your type has a single, default equality comparison. If you want to implement multiple custom equality comparisons for the given type, you can have multiple classes implement IEqualityComparer<T> and pass them to various LINQ methods.

For example, you can have a person class which implements IEquatable<Person>:

public class Person : IEquatable<Person>
{
    public Person(string name, int age)
    {
        Name = name;
        Age = age;
    }

    public string Name { get; }
    public int Age { get; }

    public bool Equals(Person other)
    {
        if (ReferenceEquals(null, other)) return false;
        if (ReferenceEquals(this, other)) return true;
        return string.Equals(Name, other.Name) && Age == other.Age;
    }

    public override bool Equals(object obj)
    {
        if (ReferenceEquals(null, obj)) return false;
        if (ReferenceEquals(this, obj)) return true;
        if (obj.GetType() != this.GetType()) return false;
        return Equals((Person) obj);
    }

    public override int GetHashCode()
    {
        unchecked
        {
            return ((Name?.GetHashCode() ?? 0)*397) ^ Age;
        }
    }

    public static bool operator ==(Person left, Person right)
    {
        return Equals(left, right);
    }

    public static bool operator !=(Person left, Person right)
    {
        return !Equals(left, right);
    }
}

But now you want to implement a custom comparer, which only performs an equality check based on the persons name. You can implement a custom IEqualityComparer<Person>:

public class ByNamePersonComparer : IEqualityComparer<Person>
{
    public bool Equals(Person x, Person y)
    {
        return x.Name.Equals(y.Name, StringComparison.OrdinalIgnoreCase);
    }

    public int GetHashCode(Person obj)
    {
        return obj.GetHashCode();
    }
}
Yuval Itzchakov
  • 146,575
  • 32
  • 257
  • 321
  • I still don't get which one is right for me. Lets say there are 3 fields within the Product object that can always be used to compare products. Additionally, I want to be able to do a Union with linq like in my example above to make sure the lists are merged without duplicates: Which one should I use? – Blake Rivell Mar 31 '16 at 13:37
  • @BlakeRivell First, see my example, perhaps it'll clarify things. Second of all, if you have a single comparison for a `Product` equality, than implement `IEquatable` on your type. LINQ will use the underlying implementation of your types equality to check if the types are equal. – Yuval Itzchakov Mar 31 '16 at 13:43
  • I see what your saying so when it needs to be completely custom based on specific properties of the object then use IEquality otherwise if you just want to compare the entire object you use IEquitable? Or by single comparison do you mean just one property vs multiple? – Blake Rivell Mar 31 '16 at 13:43
  • @BlakeRivell I wouldn't say it's about one property or all. I'd say it's more about having a default equality comparison, and having a custom implementation if there are many possible equalities for the same object type. – Yuval Itzchakov Mar 31 '16 at 13:44
  • Alright, in my case I know for a fact I only have one set of comparisons. So I would use Equitable right? Then I guess my next question would be do I have my Product class directly implement it or do I create another class that implements it called ProductComparer? So lets say I do implement IEquitable on my Product class directly how can I use it in a linq union statement? Also why does it seem like IEquitable is so much more work to implement than IEqualityComparer? – Blake Rivell Mar 31 '16 at 13:45
  • @BlakeRivell Then yes, I'd say go with `IEquatable`. – Yuval Itzchakov Mar 31 '16 at 13:48
  • Let us [continue this discussion in chat](http://chat.stackoverflow.com/rooms/107864/discussion-between-blake-rivell-and-yuval-itzchakov). – Blake Rivell Mar 31 '16 at 13:49