Iequalitycomparer for Sequenceequal

IEqualityComparer for SequenceEqual

There is no such comparer in .NET Framework, but you can create one:

public class IEnumerableComparer<T> : IEqualityComparer<IEnumerable<T>>
{
public bool Equals(IEnumerable<T> x, IEnumerable<T> y)
{
return Object.ReferenceEquals(x, y) || (x != null && y != null && x.SequenceEqual(y));
}

public int GetHashCode(IEnumerable<T> obj)
{
// Will not throw an OverflowException
unchecked
{
return obj.Where(e => e != null).Select(e => e.GetHashCode()).Aggregate(17, (a, b) => 23 * a + b);
}
}
}

In the above code, I iterate over all items of the collection in the GetHashCode. I don't know if it's the wisest solution but this what is done in the internal HashSetEqualityComparer.

Why does SequenceEqual take an IEqualityComparer rather than a predicate?

Now that .NET Core is open source, I posted this question as an issue on GitHub. Hopefully, that will translate into an answer or an improvement.


Update - details on the possible rationale for the existing design and its issues, taken from the GitHub issue:

IEqualityComparer, despite its name, really does two things: hashes
and checks for equality. The only behavioral difference between a
Func<TSource, TSource, bool> and IEqualityComparer is that
IEqualityComparer has a GetHashCode method. If all you want to do
is check for sequence equality, with IEqualityComparer you have to
write the hashing code (which can be tricky to do well), even though
it will likely never be used (but you can't count on it never being
used because SequenceEqual doesn't document that it won't use it).

Often, types that you compare with SequenceEqual will happen to have
one or moreIEqualityComparer companion types so that they can be
stored in hashed containers. Probably, that is why IEqualityComparer
was chosen as the parameter. However, there are also plenty of times
when there isn't an IEqualityComparer and there is no hashing
requirement. In those cases, having to create a class and implement
GetHashCode is wasteful.

What are the roles of IEqualityComparer and IEquatable in the Enumerable.SequenceEqual method

IEquatable is used by the generic collections like Dictionary to determine if two objects are equal. If the object doesn't implement IEquatable, Object.Equals method is used.

Why should you implement IEquatable? It has better performance than Object.Equals because the object doesn't need to be casted.

When to not implement IEquatable? Some developers believe that you should only implement it on sealed classes.

If IEqualityComparer is specified in SequenceEquals, it is the one used to check the equality of two objects instead of Object.Equal and it's IEquatable implementation. The example for using it in SequenceEqual is in here http://msdn.microsoft.com/en-us/library/bb342073%28v=vs.110%29.aspx. Note that the method signature accepts an IEqualityComparer.

Many collections like Dictionary also accepts IEqualityComparer in it's constructor

Answering your question

If you didn't provide an IEqualityComparer to SequenceEquals, it will use EqualityComparer.Default.

Decompiled code:

public static bool SequenceEqual<TSource>(this IEnumerable<TSource> first, IEnumerable<TSource> second)
{
return Enumerable.SequenceEqual<TSource>(first, second, (IEqualityComparer<TSource>) null);
}

public static bool SequenceEqual<TSource>(this IEnumerable<TSource> first, IEnumerable<TSource> second, IEqualityComparer<TSource> comparer)
{
if (comparer == null)
comparer = (IEqualityComparer<TSource>) EqualityComparer<TSource>.Default;
...

EqualityComparer.Default checks whether type T implements the System.IEquatable interface and, if so, returns an EqualityComparer that uses that implementation. Otherwise, it returns an EqualityComparer that uses the overrides of Object.Equals and Object.GetHashCode provided by T. This is why your Object.Equals is called.

SequenceEqual does not work below in comparing class

SequenceEqual compares the two sequences to see if the contain the same objects. In your case they don't. They contain different Product references.

If you change your code to use the same Product in each list then SequenceEqual will return `true'

var product = new Product
{
ProductId = 1,
ProductName = "TV",
ProductDescription = "Television Set"
};

product1.Add(p);
product2.Add(p);

if (product1.SequenceEqual(product2))
{
Console.WriteLine("equal data");
}
else
{
Console.WriteLine("Not equal data");
}

Why? Because in this example the lists contain the same object.

Read this for more information on how SequenceEqual compares the two sequences.

If you want SequenceEqual to return true if the properties of the Product class are the same then you need to override the Equals method on the Product class.

EDIT

As others have mentioned, it is a better practice to implement the IEqualityComparer<T> interface instead of directly overriding the Equals method.

Enumerable.SequenceEqualTSource and EqualityComparerT

Not TSource should implement IEqualityComparer

But EqualityComparer<T>.Default implements IEqualityComparer

The default equality comparer, Default, is used to compare values of the types that implement the IEqualityComparer generic interface.

That's too oblivious to be mentioned.

Comparing arrays content, difference of SequenceEqual and StructuralComparisons.StructuralEqualityComparer

The implementation of SequenceEqual is kind of similar::

using (IEnumerator<TSource> enumerator1 = first.GetEnumerator())
using (IEnumerator<TSource> enumerator2 = second.GetEnumerator())
{
while (enumerator1.MoveNext())
{
if (!enumerator2.MoveNext() || !comparer.Equals(enumerator1.Current, enumerator2.Current))
{
return false;
}
}

if (enumerator2.MoveNext())
{
return false;
}
}

return true;

This default SequenceEqual method use default EqualityComparer<int>.Default for int which is value equality.

Array implement IStructuralEquatable with Equal method:

bool IStructuralEquatable.Equals(object other, IEqualityComparer comparer)
{
if (other == null) return false;

if (!object.ReferenceEquals(this, other))
{
Array array = other as Array;
if ((array == null) || (array.Length != this.Length))
{
return false;
}
for (int i = 0; i < array.Length; i++)
{
object x = this.GetValue(i);
object y = array.GetValue(i);

if (!comparer.Equals(x, y))
{
return false;
}
}
}

return true;
}

The IEqualityComparer from input parameter is used, in here you input StructruralEqualityComparer but int does not implement IStructruralEquatable, so it uses default comparer for int which is value equality.

But, needless to input StructruralEqualityComparer because int is not structural, you should just use:

(arr1 as IStructuralEquatable).Equals(arr2, EqualityComparer<int>.Default);

It still works. You should use StructruralEqualityComparer if item in array is structrural

So to sum up, the implementation for both is kind of the same, both iterate two array based on value equality of int to make comparison.

I would prefer the LINQ verson since it is more readable.

Pass generic EquaityComparer with SequenceEqual

You can create an expression that creates an object by calling Expression.New().

To get the type, you can use typeof(StringComparer<>) to get the open generic type for StringComparer<T> and then call MakeGenericType() on it to get the closed type, for example StringComparer<string>.

Put together:

Expression.New(typeof(StringComparer<>).MakeGenericType(typeOfItemInList))

Why SequenceEqual for ListT returns false?

Your problem is that one new Sentence { Text = "Hi", Order = 1 } is not equal to another new Sentence { Text = "Hi", Order = 1 }. Although the contents are the same, you have two separate objects, and unless you've designed your class otherwise they are not equal to each other unless they are literally the same objects (as in your first example).

Your Sentence class needs to override Equals and GetHashCode, at the very least, at which point your SequenceEquals should work again.

How to compare two IEnumerableT in C# if I don't know the actual object type?

Given that you have a generic container that you want to compare various generic items, you don't want to be hard coding in various specific equality checks for certain types. There are going to be lots of situations where the default equality comparison won't work for what some particular caller is trying to do. The comments have numerous different examples of problems that can come up, but also just consider the many many classes out there who's default equality is a reference comparison by for which someone might want a value comparison. You can't have this equality comparer just hard code in a solution for all of those types.

The solution of course is easy. Let the caller provide their own equality implementation, which in C#, means an IEqualityComparer<T>. Your class can become:

public class MyClass<T> : IEquatable<MyClass<T>>
{
private IEqualityComparer<T> comparer;

public MyClass(IEqualityComparer<T> innerComparer = null)
{
comparer = innerComparer ?? EqualityComparer<T>.Default;
}

public T Parameter { get; }

...
}

And now by default the default comparer will be used for any given type, but the caller can always specify a non-default comparer for any type that needs different equality semantics.



Related Topics



Leave a reply



Submit