是否有完整的IEquatable实现引用?

时间:2021-05-14 16:11:10

Many of my questions here on SO concerns IEquatable implementation. I found it being extremely difficult to implement correctly, because there are many hidden bugs in the naïve implementation, and the articles I found about it are quite incomplete. I want to find or write a definitive reference which must include:

我在这里提出的许多问题涉及IEquatable的实现。我发现正确实现起来非常困难,因为在天真的实现中存在许多隐藏的错误,我发现的关于它的文章非常不完整。我想找到或写出一个必须包括的权威性参考:

  • How to implement IEquatable correctly
  • 如何正确实现IEquatable
  • How to override Equals correctly
  • 如何正确覆盖Equals
  • How to override GetHashCode correctly
  • 如何正确覆盖GetHashCode
  • How to implement the ToString method correctly
  • 如何正确实现ToString方法
  • How to implement the operator == correctly
  • 如何正确实现operator ==
  • How to implement the operator != correctly
  • 如何实现运算符!=正确

Such a complete reference already exists?

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

PS: Even MSDN reference seems flawed to me

PS:即使是MSDN引用对我来说也是有缺陷的

5 个解决方案

#1


21  

Implementing IEquatable<T> for a Value Type

Implementing IEquatable<T> for a value type is a little bit different than for a reference type. Let's assume we have the Implement-Your-Own-Value-Type archetype, a Complex number struct.

为值类型实现IEquatable 与引用类型略有不同。假设我们有一个Implement-Your-Own-Value-Type原型,一个复数结构。

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

Our first step would be to implement IEquatable<T> and override Object.Equals and Object.GetHashCode:

我们的第一步是实现IEquatable 并覆盖Object.Equals和Object.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();
}

With very little effort we have a correct implementation, excepting the operators. Adding the operators is also a trivial process:

除了操作员之外,我们只需要很少的努力就能得到正确的实施。添加运算符也是一个简单的过程:

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

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

An astute reader would notice that we should probably implement IEquatable<double> since Complex numbers could be interchangeable with the underlying value type.

精明的读者会注意到我们应该实现IEquatable ,因为复数可以与底层值类型互换。

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

We need four operators if we add IEquatable<double>, because you can have Complex == double or double == Complex (and the same for operator !=):

如果我们添加IEquatable ,我们需要四个运算符,因为你可以有Complex == double或double == 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);
}

So there you have it, with minimal effort we have a correct and useful implementation IEquatable<T> for a value type:

所以你有了它,只需要很少的努力,我们就可以为值类型提供正确且有用的实现IEquatable

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

#2


7  

I believe getting something as simple as checking objects for equality correct is a bit tricky with .NET's design.

我相信得到一些简单的东西,比如检查对象是否正确,对于.NET的设计来说有点棘手。

For Struct

对于Struct

1) Implement IEquatable<T>. It improves performance noticeably.

1)实现IEquatable 。它显着提高了性能。

2) Since you're having your own Equals now, override GetHashCode, and to be consistent with various equality checking override object.Equals as well.

2)因为你现在拥有自己的Equals,所以重写GetHashCode,并与各种相等性检查覆盖object.Equals一致。

3) Overloading == and != operators need not be religiously done since the compiler will warn if you unintentionally equate a struct with another with a == or !=, but its good to do so to be consistent with Equals methods.

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

For Class

上课

From MS:

来自MS:

Most reference types should not overload the equality operator, even if they override Equals.

大多数引用类型不应重载相等运算符,即使它们重写等于。

To me == feels like value equality, more like a syntactic sugar for Equals method. Writing a == b is much more intuitive than writing a.Equals(b). Rarely we'll need to check reference equality. In abstract levels dealing with logical representations of physical objects this is not something we would need to check. I think having different semantics for == and Equals can actually be confusing. I believe it should have been == for value equality and Equals for reference (or a better name like IsSameAs) equality in the first place. I would love to not take MS guideline seriously here, not just because it isn't natural to me, but also because overloading == doesn't do any major harm. That's unlike not overriding non-generic Equals or GetHashCode which can bite back, because framework doesn't use == anywhere but only if we ourself use it. The only real benefit I gain from not overloading == and != will be the consistency with design of the entire framework over which I have no control of. And that's indeed a big thing, so sadly I will stick to it.

对我来说==感觉像价值平等,更像是Equals方法的语法糖。写a == b比写a.Equals(b)更直观。我们很少需要检查参考平等。在处理物理对象的逻辑表示的抽象级别中,这不是我们需要检查的。我认为为==和Equals设置不同的语义实际上可能令人困惑。我认为首先应该是==对于值相等和等于引用(或更好的名称,如IsSameAs)相等。我不想在这里认真对待MS指南,不仅因为它对我来说不自然,而且因为超载==没有造成任何重大伤害。这与不重写非泛型Equals或GetHashCode不同,它可以咬回来,因为框架不会在任何地方使用==但只有在我们自己使用它的时候。我没有超载==和!=获得的唯一真正的好处是与我无法控制的整个框架的设计的一致性。这确实很重要,所以很遗憾我会坚持下去。

With reference semantics (mutable objects)

使用引用语义(可变对象)

1) Override Equals and GetHashCode.

1)覆盖Equals和GetHashCode。

2) Implementing IEquatable<T> isn't a must, but will be nice if you have one.

2)实现IEquatable 不是必须的,但如果你有一个就很好。

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

With value semantics (immutable objects)

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

This is the tricky part. Can get easily messed up if not taken care..

这是棘手的部分。如果不加以照顾,很容易搞砸..

1) Override Equals and GetHashCode.

1)覆盖Equals和GetHashCode。

2) Overload == and != to match Equals. Make sure it works for nulls.

2)重载==和!=匹配等于。确保它适用于空值。

2) Implementing IEquatable<T> isn't a must, but will be nice if you have one.

2)实现IEquatable 不是必须的,但如果你有一个就很好。

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

Take special care to see how it should fare if your class can be inherited, in such cases you will have to determine if a base class object can be equal to a derived class object. Ideally, if no objects of derived class is used for equality checking, then a base class instance can be equal to a derived class instance and in such cases, there is no need to check Type equality in generic Equals of base class.

如果您的类可以继承,请特别注意它应该如何运行,在这种情况下,您必须确定基类对象是否可以等于派生类对象。理想情况下,如果没有派生类的对象用于相等性检查,那么基类实例可以等于派生类实例,在这种情况下,不需要在基类的通用Equals中检查Type相等性。

In general take care not to duplicate code. I could have made a generic abstract base class (IEqualizable<T> or so) as a template to allow re-use easier, but sadly in C# that stops me from deriving from additional classes.

一般注意不要重复代码。我可以创建一个通用的抽象基类(IEqualizable 左右)作为模板,以便更容易重用,但遗憾的是在C#中阻止我从其他类派生。

#3


4  

Upon reading MSDN, I'm pretty certain the best example of a proper implementation is in the IEquatable.Equals Method page. My only deviation is the following:

在阅读MSDN之后,我非常确定正确实现的最佳示例是在IEquatable.Equals Method页面中。我唯一的偏差如下:

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

For those wondering about the deviation, it derives from the Object.Equals(Object) MSDN page:

对于那些想知道偏差的人,它来自Object.Equals(Object)MSDN页面:

Implementations of Equals must not throw exceptions.

Equals的实现不得抛出异常。

#4


4  

I found another reference, it's the .NET Anonymous Type implementation. For an anonymous type with an int and a double as properties I disassembled the following C# code:

我发现了另一个引用,它是.NET Anonymous Type实现。对于具有int和double属性的匿名类型,我反汇编了以下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();
    }
}

#5


1  

I only have to derive from this class

我只需要从这个班级派生出来

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

And then implement the abstract method like this

然后实现这样的抽象方法

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

#1


21  

Implementing IEquatable<T> for a Value Type

Implementing IEquatable<T> for a value type is a little bit different than for a reference type. Let's assume we have the Implement-Your-Own-Value-Type archetype, a Complex number struct.

为值类型实现IEquatable 与引用类型略有不同。假设我们有一个Implement-Your-Own-Value-Type原型,一个复数结构。

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

Our first step would be to implement IEquatable<T> and override Object.Equals and Object.GetHashCode:

我们的第一步是实现IEquatable 并覆盖Object.Equals和Object.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();
}

With very little effort we have a correct implementation, excepting the operators. Adding the operators is also a trivial process:

除了操作员之外,我们只需要很少的努力就能得到正确的实施。添加运算符也是一个简单的过程:

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

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

An astute reader would notice that we should probably implement IEquatable<double> since Complex numbers could be interchangeable with the underlying value type.

精明的读者会注意到我们应该实现IEquatable ,因为复数可以与底层值类型互换。

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

We need four operators if we add IEquatable<double>, because you can have Complex == double or double == Complex (and the same for operator !=):

如果我们添加IEquatable ,我们需要四个运算符,因为你可以有Complex == double或double == 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);
}

So there you have it, with minimal effort we have a correct and useful implementation IEquatable<T> for a value type:

所以你有了它,只需要很少的努力,我们就可以为值类型提供正确且有用的实现IEquatable

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

#2


7  

I believe getting something as simple as checking objects for equality correct is a bit tricky with .NET's design.

我相信得到一些简单的东西,比如检查对象是否正确,对于.NET的设计来说有点棘手。

For Struct

对于Struct

1) Implement IEquatable<T>. It improves performance noticeably.

1)实现IEquatable 。它显着提高了性能。

2) Since you're having your own Equals now, override GetHashCode, and to be consistent with various equality checking override object.Equals as well.

2)因为你现在拥有自己的Equals,所以重写GetHashCode,并与各种相等性检查覆盖object.Equals一致。

3) Overloading == and != operators need not be religiously done since the compiler will warn if you unintentionally equate a struct with another with a == or !=, but its good to do so to be consistent with Equals methods.

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

For Class

上课

From MS:

来自MS:

Most reference types should not overload the equality operator, even if they override Equals.

大多数引用类型不应重载相等运算符,即使它们重写等于。

To me == feels like value equality, more like a syntactic sugar for Equals method. Writing a == b is much more intuitive than writing a.Equals(b). Rarely we'll need to check reference equality. In abstract levels dealing with logical representations of physical objects this is not something we would need to check. I think having different semantics for == and Equals can actually be confusing. I believe it should have been == for value equality and Equals for reference (or a better name like IsSameAs) equality in the first place. I would love to not take MS guideline seriously here, not just because it isn't natural to me, but also because overloading == doesn't do any major harm. That's unlike not overriding non-generic Equals or GetHashCode which can bite back, because framework doesn't use == anywhere but only if we ourself use it. The only real benefit I gain from not overloading == and != will be the consistency with design of the entire framework over which I have no control of. And that's indeed a big thing, so sadly I will stick to it.

对我来说==感觉像价值平等,更像是Equals方法的语法糖。写a == b比写a.Equals(b)更直观。我们很少需要检查参考平等。在处理物理对象的逻辑表示的抽象级别中,这不是我们需要检查的。我认为为==和Equals设置不同的语义实际上可能令人困惑。我认为首先应该是==对于值相等和等于引用(或更好的名称,如IsSameAs)相等。我不想在这里认真对待MS指南,不仅因为它对我来说不自然,而且因为超载==没有造成任何重大伤害。这与不重写非泛型Equals或GetHashCode不同,它可以咬回来,因为框架不会在任何地方使用==但只有在我们自己使用它的时候。我没有超载==和!=获得的唯一真正的好处是与我无法控制的整个框架的设计的一致性。这确实很重要,所以很遗憾我会坚持下去。

With reference semantics (mutable objects)

使用引用语义(可变对象)

1) Override Equals and GetHashCode.

1)覆盖Equals和GetHashCode。

2) Implementing IEquatable<T> isn't a must, but will be nice if you have one.

2)实现IEquatable 不是必须的,但如果你有一个就很好。

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

With value semantics (immutable objects)

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

This is the tricky part. Can get easily messed up if not taken care..

这是棘手的部分。如果不加以照顾,很容易搞砸..

1) Override Equals and GetHashCode.

1)覆盖Equals和GetHashCode。

2) Overload == and != to match Equals. Make sure it works for nulls.

2)重载==和!=匹配等于。确保它适用于空值。

2) Implementing IEquatable<T> isn't a must, but will be nice if you have one.

2)实现IEquatable 不是必须的,但如果你有一个就很好。

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

Take special care to see how it should fare if your class can be inherited, in such cases you will have to determine if a base class object can be equal to a derived class object. Ideally, if no objects of derived class is used for equality checking, then a base class instance can be equal to a derived class instance and in such cases, there is no need to check Type equality in generic Equals of base class.

如果您的类可以继承,请特别注意它应该如何运行,在这种情况下,您必须确定基类对象是否可以等于派生类对象。理想情况下,如果没有派生类的对象用于相等性检查,那么基类实例可以等于派生类实例,在这种情况下,不需要在基类的通用Equals中检查Type相等性。

In general take care not to duplicate code. I could have made a generic abstract base class (IEqualizable<T> or so) as a template to allow re-use easier, but sadly in C# that stops me from deriving from additional classes.

一般注意不要重复代码。我可以创建一个通用的抽象基类(IEqualizable 左右)作为模板,以便更容易重用,但遗憾的是在C#中阻止我从其他类派生。

#3


4  

Upon reading MSDN, I'm pretty certain the best example of a proper implementation is in the IEquatable.Equals Method page. My only deviation is the following:

在阅读MSDN之后,我非常确定正确实现的最佳示例是在IEquatable.Equals Method页面中。我唯一的偏差如下:

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

For those wondering about the deviation, it derives from the Object.Equals(Object) MSDN page:

对于那些想知道偏差的人,它来自Object.Equals(Object)MSDN页面:

Implementations of Equals must not throw exceptions.

Equals的实现不得抛出异常。

#4


4  

I found another reference, it's the .NET Anonymous Type implementation. For an anonymous type with an int and a double as properties I disassembled the following C# code:

我发现了另一个引用,它是.NET Anonymous Type实现。对于具有int和double属性的匿名类型,我反汇编了以下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();
    }
}

#5


1  

I only have to derive from this class

我只需要从这个班级派生出来

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

And then implement the abstract method like this

然后实现这样的抽象方法

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