是否有一个完整的IEquatable实现参考?是否有一个完整的IEquatable实现参考?(Is

2019-05-12 23:26发布

我的很多问题,在这里SO担忧IEquatable实施。 我发现这是非常困难的正确实施,因为有在幼稚的做法很多隐藏的错误,我发现关于它的文章是相当不完整的。 我想找到或编写一个明确的参考内容须包括:

  • 如何正确地贯彻执行IEquatable
  • 如何重写正确的Equals
  • 如何正确重写GetHashCode
  • 如何正确地贯彻执行ToString方法
  • 如何正确地实现运营商==
  • 如何正确执行操作!=

这样一个完整的参考已经存在?

PS:偶参考MSDN似乎有缺陷的我

Answer 1:

实施IEquatable<T>为一个值类型

实施IEquatable<T>为值类型是比引用类型有点不同。 假设我们有实现 - 你自己的值类型的原型,复数结构。

public struct Complex
{
    public double RealPart { get; set; }
    public double ImaginaryPart { get; set; }
}

我们的第一个步骤是执行IEquatable<T>并重写Object.EqualsObject.GetHashCode

public bool Equals(Complex other)
{
    // Complex is a value type, thus we don't have to check for null
    // if (other == null) return false;

    return (this.RealPart == other.RealPart)
        && (this.ImaginaryPart == other.ImaginaryPart);
}

public override bool Equals(object other)
{
    // other could be a reference type, the is operator will return false if null
    if (other is Complex)
        return this.Equals((Complex)other);
    else
        return false;
}

public override int GetHashCode()
{
    return this.RealPart.GetHashCode() ^ this.ImaginaryPart.GetHashCode();
}

用很少的努力,我们有一个正确的执行,除了运营商。 添加运营商也是一个微不足道的过程:

public static bool operator ==(Complex term1, Complex term2)
{
    return term1.Equals(term2);
}

public static bool operator !=(Complex term1, Complex term2)
{
    return !term1.Equals(term2);
}

聪明的读者会注意到,我们也许应该实现IEquatable<double>因为Complex数字可能是与潜在的价值型互换。

public bool Equals(double otherReal)
{
    return (this.RealPart == otherReal) && (this.ImaginaryPart == 0.0);
}

public override bool Equals(object other)
{
    // other could be a reference type, thus we check for null
    if (other == null) return base.Equals(other);

    if (other is Complex)
    {
        return this.Equals((Complex)other);
    }
    else if (other is double)
    {
        return this.Equals((double)other);
    }
    else
    {
        return false;
    }
}

我们需要四个运营商,如果我们添加IEquatable<double> ,因为你可以有Complex == doubledouble == Complex (与同为operator !=

public static bool operator ==(Complex term1, double term2)
{
    return term1.Equals(term2);
}

public static bool operator ==(double term1, Complex term2)
{
    return term2.Equals(term1);
}

public static bool operator !=(Complex term1, double term2)
{
    return !term1.Equals(term2);
}

public static bool operator !=(double term1, Complex term2)
{
    return !term2.Equals(term1);
}

所以你有它,以最小的努力,我们有一个正确和有用的执行IEquatable<T>的值类型:

public struct Complex : IEquatable<Complex>, IEquatable<double>
{
}


Answer 2:

我相信越来越作为检查对象平等正确的,因为简单的东西是有点棘手使用.NET的设计。

对于结构

1)实施IEquatable<T> 这显着提高了性能。

2)既然你有你自己Equals现在,重写GetHashCode ,并能够与各种平等检查覆盖相一致object.Equals为好。

3)重载==!=操作者无需做宗教,因为编译器会发出警告,如果你无意中等同于另一个结构用==!= ,但其良好这样做是一致Equals方法。

public struct Entity : IEquatable<Entity>
{
    public bool Equals(Entity other)
    {
        throw new NotImplementedException("Your equality check here...");
    }

    public override bool Equals(object obj)
    {
        if (obj == null || !(obj is Entity))
            return false;

        return Equals((Entity)obj);
    }

    public static bool operator ==(Entity e1, Entity e2)
    {
        return e1.Equals(e2);
    }

    public static bool operator !=(Entity e1, Entity e2)
    {
        return !(e1 == e2);
    }

    public override int GetHashCode()
    {
        throw new NotImplementedException("Your lightweight hashing algorithm, consistent with Equals method, here...");
    }
}

上课

从MS:

大多数引用类型不应该重载相等运算符,即使他们重载Equals。

对我来说==感觉值相等,更像是一个语法糖Equals方法。 编写a == b比写更直观a.Equals(b) 很少,我们需要检查引用相等。 在抽象的层次处理物理对象的逻辑表示这不是我们需要检查。 我认为有不同的语义==Equals实际上是混乱。 我相信它应该已经==为价值平等Equals参考(或类似一个更好的名字IsSameAs )平等摆在首位。 我很想不采取MS方针认真这里,不只是因为它是不自然的我,也是因为超载==没有做什么大的伤害。 这不像不重写非泛型EqualsGetHashCode能咬回来,因为框架不使用==任何地方,但只有当我们我们自己使用它。 我从没有得到过载的唯一的真正的好处==!=会超过我没有控制整个框架的设计一致性。 而这确实是一个很大的事情, 所以我黯然会坚持下去

参考语义(可变对象)

1)覆盖EqualsGetHashCode

2)实施IEquatable<T>不是必须的,但将是很好,如果你有一个。

public class Entity : IEquatable<Entity>
{
    public bool Equals(Entity other)
    {
        if (ReferenceEquals(this, other))
            return true;

        if (ReferenceEquals(null, other))
            return false;

        //if your below implementation will involve objects of derived classes, then do a 
        //GetType == other.GetType comparison
        throw new NotImplementedException("Your equality check here...");
    }

    public override bool Equals(object obj)
    {
        return Equals(obj as Entity);
    }

    public override int GetHashCode()
    {
        throw new NotImplementedException("Your lightweight hashing algorithm, consistent with Equals method, here...");
    }
}

用值语义(不可变的对象)

这是棘手的部分。 可以很容易被搞砸了,如果不采取照顾..

1)覆盖EqualsGetHashCode

2)过载==!=匹配Equals请确保它为空值

2)实施IEquatable<T>不是必须的,但将是很好,如果你有一个。

public class Entity : IEquatable<Entity>
{
    public bool Equals(Entity other)
    {
        if (ReferenceEquals(this, other))
            return true;

        if (ReferenceEquals(null, other))
            return false;

        //if your below implementation will involve objects of derived classes, then do a 
        //GetType == other.GetType comparison
        throw new NotImplementedException("Your equality check here...");
    }

    public override bool Equals(object obj)
    {
        return Equals(obj as Entity);
    }

    public static bool operator ==(Entity e1, Entity e2)
    {
        if (ReferenceEquals(e1, null))
            return ReferenceEquals(e2, null);

        return e1.Equals(e2);
    }

    public static bool operator !=(Entity e1, Entity e2)
    {
        return !(e1 == e2);
    }

    public override int GetHashCode()
    {
        throw new NotImplementedException("Your lightweight hashing algorithm, consistent with Equals method, here...");
    }
}

请特别注意,看看它如何票价如果你的类可以继承,在这种情况下,你必须确定一个基类对象可以等于一个派生类对象。 理想地,如果用于检查平等没有派生类的对象,那么基类的实例可以等于派生类实例,并在这种情况下,没有必要检查Type在通用平等Equals基类。

一般来说,请注意不要重复的代码。 我可以做出一个通用的抽象基类( IEqualizable<T>左右)作为模板,允许重复使用更容易,但遗憾的是在阻止我从其他类派生C#。



Answer 3:

在阅读MSDN,我敢肯定一个正确实施的最好的例子就是在IEquatable.Equals方法页。 我唯一的偏差如下:

public override bool Equals(Object obj)
{
   if (obj == null) return base.Equals(obj);

   if (! (obj is Person))
      return false; // Instead of throw new InvalidOperationException
   else
      return Equals(obj as Person);   
}

对于那些想知道关于偏离,它从派生的Object.Equals(对象) MSDN页面:

的Equals的实现必须不抛出异常。



Answer 4:

我发现了另外一个参考,它是.NET匿名类型的实现。 对于匿名类型与一个int和双作为属性我分解下面的C#代码:

public class f__AnonymousType0
{
    // Fields
    public int A { get; }
    public double B { get; }

    // Methods
    public override bool Equals(object value)
    {
        var type = value as f__AnonymousType0;
        return (((type != null)
            && EqualityComparer<int>.Default.Equals(this.A, type.A))
            && EqualityComparer<double>.Default.Equals(this.B, type.B));
    }

    public override int GetHashCode()
    {
        int num = -1134271262;
        num = (-1521134295 * num) + EqualityComparer<int>.Default.GetHashCode(this.A);
        return ((-1521134295 * num) + EqualityComparer<double>.Default.GetHashCode(this.B);
    }

    public override string ToString()
    {
        StringBuilder builder = new StringBuilder();
        builder.Append("{ A = ");
        builder.Append(this.A);
        builder.Append(", B = ");
        builder.Append(this.B);
        builder.Append(" }");
        return builder.ToString();
    }
}


Answer 5:

我只有从这个类派生

public abstract class DataClass : IEquatable<DataClass>
{
    public override bool Equals(object obj)
    {
        var other = obj as DataClass;
        return this.Equals(other);
    }

    public bool Equals(DataClass other)
    {
        return (!ReferenceEquals(null, other))
            && this.Execute((self2, other2) =>
                other2.Execute((other3, self3) => self3.Equals(other3), self2)
                , other);
    }

    public override int GetHashCode()
    {
        return this.Execute(obj => obj.GetHashCode());
    }

    public override string ToString()
    {
        return this.Execute(obj => obj.ToString());
    }

    private TOutput Execute<TOutput>(Func<object, TOutput> function)
    {
        return this.Execute((obj, other) => function(obj), new object());
    }

    protected abstract TOutput Execute<TParameter, TOutput>(
        Func<object, TParameter, TOutput> function,
        TParameter other);
}

然后实现这样的抽象方法

public class Complex : DataClass
{
    public double Real { get; set; }

    public double Imaginary { get; set; }

    protected override TOutput Execute<TParameter, TOutput>(
        Func<object, TParameter, TOutput> function,
        TParameter other)
    {
        return function(new
        {
            Real = this.Real,
            Imaginary = this.Imaginary,
        }, other);
    }
}


文章来源: Is there a complete IEquatable implementation reference?