I'm struggling with implementing the IEquatable<>
interface for a class. The class has a Parameter
property that uses a generic type. Basically the class definition is like this:
public class MyClass<T> : IEquatable<MyClass<T>>
{
public T Parameter { get; }
...
}
In the Equals()
method I'm using EqualityComparer<T>.Default.Equals(Parameter, other.Parameter)
to compare the property. Generally, this works fine – as long as the property is not a collection, for example an IEnumerable<T>
. The problem is that the default equality comparer for IEnumerable<T>
is checking reference equality.
Obviously, you'd want to use SequenceEqual()
to compare the IEnumerable<T>
. But to get this running, you need to specify the generic type of the SequenceEqual()
method. This is the closest I could get:
var parameterType = typeof(T);
var enumerableType = parameterType.GetInterfaces()
.Where(type => type.IsGenericType && type.GetGenericTypeDefinition() == typeof(IEnumerable<>))
.Select(type => type.GetGenericArguments().First()).FirstOrDefault();
if (enumerableType != null)
{
var castedThis = Convert.ChangeType(Parameter, enumerableType);
var castedOther = Convert.ChangeType(other.Parameter, enumerableType);
var isEqual = castedThis.SequenceEqual(castedOther);
}
But this does not work because Convert.ChangeType()
returns an object
. And of course object
does not implement SequenceEqual()
.
How do I get this working? Thanks for any tipps!
Best regards, Oliver
Obviously, you'd want to use SequenceEqual() to compare the IEnumerable<T>
- which, in turn, will use theT
's default equality comparer for comparing the items, which again will beReferenceEquals
ifT
is a reference type... – CounterirritantIEquatable
for this object. But I cannot change the fact that Microsoft did not bother doing this forIEnumerable<T>
. – Nurslingvar castedThis = (IEnumerable<U>)Convert.ChangeType(Parameter, enumerableType);
, whereU
is dynamic (andT
isIEnumerable<U>
). I believe the only way to do that would be to declare bothT
andU
as generic arguments for your class. – CounterirritantT
itself isIEnumerable<>
,.GetInterfaces()
on it will only return the non-genericIEnumerable
(and because you havetype => type.IsGenericType
in the filter, the result will always be empty). To getenumerableType
, just useparameterType.GetGenericArguments().First()
. Not that it would help with the above. – CounterirritantParameter
is anIList<int>
, for example,enumerableType
is identified correctly as anint
:{Name = "Int32" FullName = "System.Int32"}
But as you said: not that it would help ;-) – NurslingIList<int>
, yes. But not when it's anIEnumerable<int>
directly. – Counterirritant