Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Why does my dynamic IEqualityComparer not work?

I have a class

public class Foo
{
  public int ID { get; set; }
}

and I've implemented a LinqEqualityComparer to allow dynamic IEqualityComparer tests for the Except extenion method.

public class LinqEqualityComparer<T> : IEqualityComparer<T>
{
    protected Func<T, T, bool> Comparison { get; set; }

    public LinqEqualityComparer(Func<T, T, bool> comparison)
    {
        Comparison = comparison;
    }

    public bool Equals(T x, T y)
    {
        return Comparison(x, y);
    }


    public int GetHashCode(T obj)
    {
        return obj.GetHashCode();
    }
}

I've created the following code to test it:

IEnumerable<Foo> settings = new Foo[]
{
  new Foo{ID = 1},
  new Foo{ID = 2}
};
IEnumerable<Foo> currentSettings = new Foo[]
{
  new Foo{ID = 1},
  new Foo{ID = 2},
  new Foo{ID = 3}
};
IEqualityComparer<Foo> comparer = new LinqEqualityComparer<Foo>((x, y) => x.ID == y.ID);
IEnumerable<Foo> missing = currentSettings.Except(settings, comparer);

However Foos 1,2 and 3 are all present in the 'missing' variable.

Why does this LinqEqualityComparer not work?

like image 853
Liath Avatar asked Jan 11 '23 16:01

Liath


1 Answers

Because your equality comparer does not implement GetHashCode correctly. The GetHashCode implementation must produce the same code for elements that compare equal. This does not happen here because the equality comparison is customized while the hash codes are not generated accordingly.

To make this work you would need to do one of two things:

  1. Make the comparer accept the hash code implementation as an additional argument, i.e. x => x.ID.GetHashCode() and forward to that. This is easiest and what you should do in practice.

  2. Modify GetHashCode in such a way that it is an aggregate function of the hash codes of the properties that take part in the comparison (here that is the ID property) -- a straight xor of the individual hash codes would work (even though it might not be optiomal).

    That leaves you with the problem of how to detect which properties are compared. To be able to answer that question automatically you would need to accept an expression tree instead of a delegate for the comparison, i.e. an Expression<Func<T, T, bool>> and then visit the expression tree to determine what to do. That's bound to not be easy going.

like image 191
Jon Avatar answered Jan 23 '23 11:01

Jon