4

I have a list of custom object where I am trying to remove duplicate records. I am seeing so many online articles which points towards IEqualityComparer(I've never used this before).

Question is, when should I use it? I can achieve same result by LINQ in one line code.

Example:

public static void Main(string[] args)
{
    Product[] products =
    {
        new Product {Name = "apple", Code = 9},
        new Product {Name = "orange", Code = 4},
        new Product {Name = "apple", Code = 9},
        new Product {Name = "lemon", Code = 12}
    };

    // Using Custom comparer
    var usingCustomComparer = products.Distinct(new ProductComparer()).ToList();

    // Using LINQ
    var usinLinq = products.GroupBy(x => x.Name).Select(y => y.First()).ToList();
}

public class Product
{
    public string Name { get; set; }
    public int Code { get; set; }
}

// Custom comparer for the Product class
private class ProductComparer : IEqualityComparer<Product>
{
    public bool Equals(Product x, Product y)
    {
        if (ReferenceEquals(x, y)) return true;

        if (ReferenceEquals(x, null) || ReferenceEquals(y, null)) return false;

        return x.Code == y.Code && x.Name == y.Name;
    }

    public int GetHashCode(Product product)
    {
        var hashProductName = product.Name == null ? 0 : product.Name.GetHashCode();

        var hashProductCode = product.Code.GetHashCode();

        return hashProductName ^ hashProductCode;
    }
}

Result from both:

{Name = "apple", Code = 9},
{Name = "orange", Code = 4},
{Name = "lemon", Code = 12}
GThree
  • 2,708
  • 7
  • 34
  • 67
  • 2
    Can you guarantee that Linq statement is written the same way every place you use it? Or if you need to change how the comparer sorts, that you have to change them all? Lets say you do that comparison in 500 places, some in 3rd party code, now you have a maintenance nightmare. – Ron Beyer Oct 23 '18 at 19:10
  • Interfaces are a good idea if you need a default comparer. LINQ is good if there is no default comparer or other prewritten comparer you can use. Note that the interface is a bit older then LINQ. It was introduced in .NET 2.0 (January 2006). LINQ was only added with 3.5 (November 2007). So if you ever have prewrittecode that requires a comparer, you can asume it will look for a `IEqualityComparer` being implemented. Dictionary Add or Find on any collection might use a IEqualityComparer if present. – Christopher Oct 23 '18 at 19:14

3 Answers3

1

Going with your example, say that you wanted to compare the objects ignoring case, so that "Apple" and "apple" would be considered the same object. Then your ProductComparer may look like:

class ProductComparer : IEqualityComparer<Product>
{
    public bool Equals(Product x, Product y)
    {
        if (ReferenceEquals(x, y)) return true;

        if (ReferenceEquals(x, null) || ReferenceEquals(y, null)) return false;

        return x.Code == y.Code && (x.Name.Equals(y.Name, StringComparison.InvariantCultureIgnoreCase));
    }

    public int GetHashCode(Product product)
    {
        var hashProductName = product.Name == null ? 0 : product.Name.ToLower().GetHashCode();

        var hashProductCode = product.Code.GetHashCode();

        return hashProductName ^ hashProductCode;
    }
}

And it would yield different results than Linq.

mnistic
  • 10,866
  • 2
  • 19
  • 33
1

when should I use it?

Some possibilities:

  • When your definition of "equality" is more complicated than just comparing one property
  • When you want to pre-define "equality" for use in many queries
  • When you want to define "equality" outside of Linq, e.g. when using the class as the key to a hash table
  • When you want to tweak your definition of equality slightly without repeating code (i.e. turn on/off case sensitivity)
D Stanley
  • 149,601
  • 11
  • 178
  • 240
0

Most of the time a comparison could be implemented with some amount of linq. If it's one-off, the choice is probably just down to personal preference.

If the comparison logic is re-used a fair amount, creating a IEqualityComparer is a standard and well-known way accepted by most APIs for comparing two objects. You can re-use the implementation in linq methods, collections, etc.

Eric Damtoft
  • 1,353
  • 7
  • 13