Inject IEquatable <T> when T can be IEnumerable <T>

I read various questions similar to mine, but none of them concerns my problem.

I have a type like this:

class MyObject<T> : IEquatable<MyObject<T>> { // no generic constraints private readonly string otherProp; private readonly T value; public MyObject(string otherProp, T value) { this.otherProp = otherProp; this.value = value; } public string OtherProp { get { return this.otherProp; } } public T Value { get { return this.value; } } // ... public bool Equals(MyObject<T> other) { if (other == null) { return false; } return this.OtherProp.Equals(other.OtherProp) && this.Value.Equals(other.Value); } } 

When T is a scalar, since the equality MyObject<int> works correctly, but when I define something as MyObject<IEnumerable<int>> fails.

The reason is that when T is IEnumerable<T> , I have to call this.Value.SequenceEqual(other.Value) .

Handling this difference bloats Equals(MyObject<T>) with too LOC check and reflection type (for me, which leads to violation of SOLID / SRP).

I could not find this particular case in the MSDN manuals, so if someone has already encountered this problem; it would be great if this knowledge could be shared.

Edit: Alternative

To KISS, I am interested in doing something like this:

 class MyObject<T> : IEquatable<MyObject<T>> { private readonly IEnumerable<T> value; // remainder omitted } 

Thus, the implementation of Equal will be very simple. And when I need only one value, I have a collection of 1 subject. Obviously, T will not be enumerable (but the data structure is private, so there is no problem).

+6
source share
4 answers

You can use MyObject<T> to match the equality for your type T and use this:

 class MyObject<T> : IEquatable<MyObject<T>> { private readonly IEqualityComparer<T> comparer; public MyObject(string otherProp, T value, IEqualityComparer<T> comparer) { this.comparer = comparer; } public MyObject(string otherProp, T value) : this(otherProp, value, EqualityComparer<T>.Default) { } public bool Equals(MyObject<T> other) { return OtherProp.Equals(other.OtherProp) && comparer.Equals(this.Value, other.Value); } } 

Then for IEnumerable<T> you can use a comparator that compares sequences instead of references. You can use factory methods to create your objects, to ensure that the same type of matching is used for the same T , to ensure that the equality remains symmetrical.

+3
source

If T is an IEnumerable<T> , your code compares two IEnumerable<T> links and, of course, these links may not be equal. In fact, you get this behavior when T is any reference type without an overridden Equals method.

If you do not want to compare links here, you should write code that will compare these sequences according to their content. However, you should note that the sequence can be infinite.

+4
source

No reflection is required. One option is to check if the value is IEnumerable or not in your Equals method:

 IEnumerable e = other.Value as IEnumerable; if(e != null){ // use SequenceEqual }else{ // use Equals } 
+1
source

This is the case when you must use a helper class, your MyObject type can use a separate EqualityChecker.

I would say that implementing a strategy template with a static factory method will simplify your design.

something like below

 class MyObject<T> : IEquatable<MyObject<T>> { // no generic constraints private readonly string otherProp; private readonly T value; public MyObject(string otherProp, T value) { this.otherProp = otherProp; this.value = value; } public string OtherProp { get { return this.otherProp; } } public T Value { get { return this.value; } } // ... public bool Equals(MyObject<T> other) { if (other == null) { return false; } var cheker = EqualityChekerCreator<T>.CreateEqualityChecker(other.Value); if (cheker != null) return cheker.CheckEquality(this.Value, other.value); return this.OtherProp.Equals(other.OtherProp) && this.Value.Equals(other.Value); } } public static class EqualityChekerCreator<T> { private static IEqualityCheker<T> checker; public static IEqualityCheker<T> CreateEqualityChecker(T type) { var currenttype = type as IEnumerable<T>; if(currenttype!=null) checker = new SequenceEqualityChecker<T>(); return checker; } } public interface IEqualityCheker<in T> { bool CheckEquality(T t1, T t2); } public class SequenceEqualityChecker <T> : IEqualityCheker<T> { #region Implementation of IEqualityCheker<in T> public bool CheckEquality(T t1, T t2) { // implement method here; } #endregion } 
+1
source

All Articles