当T可以是IEnumerable <t> </t> </t>时,实现IEquatable <t>

时间:2013-03-19 08:22:02

标签: c# implementation structural-equality

我读过与我类似的各种问题,但似乎都没有解决我的问题。

我的类型是这样的:

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);
  }
}

T是标量时,MyObject<int>等式正常,但是当我定义了某些内容时 像MyObject<IEnumerable<int>>等式失败。

原因是当T为IEnumerable<T>时,我应该致电this.Value.SequenceEqual(other.Value)

处理这种差异会导致Equals(MyObject<T>)过于局部的类型检查和反射(对我来说,导致违反SOLID / SRP)。

我无法在MSDN指南中找到这个具体案例,所以如果有人已经遇到过这个问题;如果可以分享这些知识,那就太好了。

编辑:替代

对于K.I.S.S.,我想知道做类似的事情:

class MyObject<T> : IEquatable<MyObject<T>> {
  private readonly IEnumerable<T> value;

  // remainder omitted
}

通过这种方式Equal的实现将非常简单。当我只需要一个值时,我就收集了1个项目。 显然T不是可枚举的(但数据结构是私有的,所以没有问题)。

4 个答案:

答案 0 :(得分:4)

如果TIEnumerable<T>,您的代码会比较IEnumerable<T>的两个引用,当然,这些引用可能不相等。实际上,当T没有被覆盖的Equals方法的任何引用类型时,你会得到这种行为。

如果你不想在这里比较引用,你应该编写一个代码,它将按照内容比较这些序列。但是,你应该注意,这个序列可能是无穷无尽的。

答案 1 :(得分:3)

您可以让MyObject<T>为您的类型T采用相等比较器并使用它:

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);
    }
}

然后对于IEnumerable<T>,您可以使用比较器来比较序列而不是引用。您可能希望使用工厂方法来创建对象,以确保对同一T使用相同的比较器类型,以确保相等性保持对称。

答案 2 :(得分:1)

不需要反思。一种选择是在Equals方法中检查值是否为IEnumerable:

IEnumerable e = other.Value as IEnumerable;

if(e != null){
 // use  SequenceEqual
}else{
 // use Equals
}

答案 3 :(得分:1)

这种情况下你应该学习帮助类,你的MyObject类型可能会使用单独的EqualityChecker。

我认为使用静态工厂方法实现策略模式将简化您的设计。

如下所示

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
}