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>
andIEqualityComparer
is that
IEqualityComparer
has aGetHashCode
method. If all you want to do
is check for sequence equality, withIEqualityComparer
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 becauseSequenceEqual
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 whyIEqualityComparer
was chosen as the parameter. However, there are also plenty of times
when there isn't anIEqualityComparer
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
How to Implement Full Row Selecting in Gridview Without Select Button
How to Enable Nullable Reference Types Feature of C# 8.0 for the Whole Project
How to Convert Percentage String to Double
Linq Query Group by and Selecting First Items
How to Add the Same Column to All Entities in Ef Core
How to Retrieve the Screen Resolution from a C# Winform App
Wpf Combobox: Different Template in Textbox and Drop-Downlist
Multiple JSONproperty Name Assigned to Single Property
Adding Elements to an Xml File in C#
How to Run External Program via a C# Program
What's the Max Items in a List<T>
Create Object Instance of a Class Having Its Name in String Variable
Can You Prevent Your ASP.NET Application from Shutting Down
Asynchronous Iterator Task<Ienumerable<T>>
Should I Transform Entity (Persistent) Objects to Dto Objects