等于覆盖 Equals、IEquatable、== 和 != 的实现

Equals implementation with override Equals, IEquatable, == and !=

我看到很多关于 C# 中的相等性以及如何实现它的帖子和问题。我想尝试收集最佳实践并提供或多或少没有重复代码的样板代码的解决方案。

从一个简单的 class 开始:

public class Foo
{
    public int Bar { get; set; }
}

我现在想向这个 class 添加 Equals 调用的所有可能性,首先使用 ReSharper 建议覆盖它:

public override bool Equals(object obj)
{
    if (ReferenceEquals(null, obj)) return false;
    if (ReferenceEquals(this, obj)) return true;
    if (obj.GetType() != GetType()) return false;

    if(obj is Foo foo)
    {
        // here all the values should be checked
        return Bar == foo.Bar;
    }

    return false;
}

现在我已经覆盖了 Equals 调用,但只剩下 ==!= 运算符以及该建议 https://www.loganfranken.com/blog/698/overriding-equals-in-c-part-3/

public static bool operator ==(Foo a, 
                               Foo b)
{
    if(ReferenceEquals(null, a)) return false; 
            
    return a.Equals(b); 
}

public static bool operator !=(Foo a,
                               Foo b)
{
    return !(a == b);
}

现在的最后一步是像这样实现 IEquatable<> 接口并调整覆盖 Equals 留下 class 像这样:

public class Foo : IEquatable<Foo>
{
    public int Bar { get; set; }

    public bool Equals(Foo other)
    {
        if (ReferenceEquals(null, other)) return false;
        if (ReferenceEquals(this, other)) return true;

        // here all the values should be checked
        return Bar == other.Bar;
    }

    public override bool Equals(object obj)
    {
        if (ReferenceEquals(null, obj)) return false;
        if (obj.GetType() != GetType()) return false;

        return Equals(obj as Foo);
    }

    public static bool operator ==(Foo a,
                                   Foo b)
    {
        if(ReferenceEquals(null, a)) return false;
        
        return a.Equals(b);
    }

    public static bool operator !=(Foo a,
                                   Foo b)
    {
        return !(a == b);
    }
}

现在在我看来,我已经或多或少地删除了所有重复代码(不幸的是,我想不出摆脱空检查的方法)。我会在 Equals(Foo other) 方法中实现“核心逻辑”,其他的可以复制到任何其他 classes.

我在这里遗漏了什么陷阱或问题吗?或者关于获得更少代码的任何建议?我并不是要获得最佳性能,干净的代码应该是此实现的主要标准,并且可重用其他 classes。那么是否可以将最后 3 个方法毫无问题地复制到任何其他 class 并且只在那里实现 IEquatable<> 并当然改变调用?

另请注意,我现在没有实施 GetHashCode()

你不应该重复自己:实施public bool Equals(Foo other)就足够了; 所有其他:Equals==!= 可以从 Equals(Foo other):

派生
public class Foo : IEquatable<Foo>
{
    public int Bar { get; set; }

    public bool Equals(Foo other)
    {
        if (ReferenceEquals(this, other)) return true;
 
        if (other is null) return false;

        // here all the values should be checked
        return Bar == other.Bar;
    }

    // Here we can put it short 
    public override bool Equals(object obj) => obj is Foo other && Equals(other);

    //TODO: Don't forget about hash 
    // Here I put the easiest approach which exploits the fact we
    // have just one property of type int. 
    public override int GetHashCode() => Bar;

    // Note ReferenceEquals(a, b): we want null == null be true
    public static bool operator == (Foo a, Foo b) =>
      ReferenceEquals(a, b) || (a is not null && a.Equals(b));

    public static bool operator !=(Foo a, Foo b) => !(a == b);
}