写一个好的C#Equals方法

写一个好的C#Equals方法,c#,C#,有没有人有编写一个像样的equals方法的模板?我记得在有效的Java中,在处理子类时,在处理equals方面存在一些问题 我没有这本书,我记不起这是不是一个实用的建议——那么如何编写一个可靠的健壮的equals方法实现呢?我通常会这样做: public struct EmailAddress : IEquatable<EmailAddress> { public override bool Equals(object obj) { return ob

有没有人有编写一个像样的equals方法的模板?我记得在有效的Java中,在处理子类时,在处理equals方面存在一些问题


我没有这本书,我记不起这是不是一个实用的建议——那么如何编写一个可靠的健壮的equals方法实现呢?

我通常会这样做:

public struct EmailAddress : IEquatable<EmailAddress>
{
    public override bool Equals(object obj)
    {
        return obj != null && obj.GetType() == typeof(EmailAddress) && Equals((EmailAddress)obj);
    }

    public bool Equals(EmailAddress other)
    {
        return this.LocalPart == other.LocalPart && this.Domain == other.Domain;
    }
}
public结构电子邮件地址:IEquatable
{
公共覆盖布尔等于(对象对象对象)
{
返回obj!=null&&obj.GetType()==typeof(EmailAddress)&&Equals((EmailAddress)obj);
}
公共bool Equals(电子邮件地址其他)
{
返回this.LocalPart==other.LocalPart&&this.Domain==other.Domain;
}
}

您可能已经这样做了,但是您是否查阅了MSDN关于实现
Equals()
的文章


一个“good”equals方法是一个比较类的唯一部分的方法,同时忽略了对唯一性没有贡献的部分。所以,如果你有一个id是唯一的类,你可以用它来建立平等,而不考虑任何其他属性。不过,通常您也需要一些时间戳值。

请查看MSDN。

好的equals方法的属性:

  • 对称性:对于两个参考,a和b,a等于(b)当且仅当b等于(a)
  • 自反性:对于所有非空引用,a等于(a)
  • 及物性:如果a.等于(b)和b.等于(c),那么a.等于(c)

可能是墙外的建议:但是,首先考虑不要重写<代码>等于。正如您所提到的,基本上平等的本质不适合子类化。但是,在.NET API中,几乎所有使用相等的地方(例如字典、哈希集)都允许传入

IEqualityComparer
。让一个不同的对象负责平等使生活更加灵活:你可以使用不同的对象来决定使用哪种标准

实现
IEqualityComparer
要简单得多-您仍然需要检查空值,但不需要担心类型是否合适,或者
Equals
是否会被进一步覆盖


另一种使正常的
Equals
工作更顺利的方法是在大部分情况下完全避免继承-我不记得上次在我的代码中重写
Equals
并允许派生类是什么时候真正有意义的
sealed
FTW:)

如果您厌倦了为此编写大量样板文件,可以尝试使用一个基类来实现它

public abstract class ValueObject<T> : IEquatable<T>
    where T : ValueObject<T>
{
    protected abstract IEnumerable<object> Reflect();

    public override bool Equals(Object obj)
    {
        if (ReferenceEquals(null, obj)) return false;
        if (obj.GetType() != GetType()) return false;
        return Equals(obj as T);
    }

    public bool Equals(T other)
    {
        if (ReferenceEquals(null, other)) return false;
        if (ReferenceEquals(this, other)) return true;
        return Reflect().SequenceEqual(other.Reflect());
    }

    public override int GetHashCode()
    {
        return Reflect().Aggregate(36, (hashCode, value) => value == null ?
                                hashCode : hashCode ^ value.GetHashCode());
    }

    public override string ToString()
    {
        return "{ " + Reflect().Aggregate((l, r) => l + ", " + r) + " }";
    }
}
公共抽象类ValueObject:IEquatable
其中T:ValueObject
{
受保护的抽象IEnumerable Reflect();
公共覆盖布尔等于(对象对象对象)
{
if(ReferenceEquals(null,obj))返回false;
if(obj.GetType()!=GetType())返回false;
返回等于(obj为T);
}
公共布尔等于(T其他)
{
if(ReferenceEquals(null,other))返回false;
if(ReferenceEquals(this,other))返回true;
返回Reflect().SequenceEqual(其他.Reflect());
}
公共覆盖int GetHashCode()
{
return Reflect().Aggregate(36,(hashCode,value)=>value==null?
hashCode:hashCode^value.GetHashCode());
}
公共重写字符串ToString()
{
返回“{”+Reflect().Aggregate((l,r)=>l+”,“+r)+“}”;
}
}
现在要创建一个类似类的值,只需说:

public class Person : ValueObject<Person>
{
    public int Age { get; set; }
    public string Name { get; set; }

    protected override IEnumerable<object> Reflect()
    {
        return new object[] { Age, Name };
    }
}
公共类人物:ValueObject
{
公共整数{get;set;}
公共字符串名称{get;set;}
受保护的覆盖IEnumerable Reflect()
{
返回新对象[]{Age,Name};
}
}
Reflect
override中,您返回一系列需要有助于实现相等的值

不幸的是,这种方法无助于声明
运算符==
,因为必须在派生类型上专门声明它。

公共类Person { 公共字符串名称{get;set;}

    public int Age { get; set; }

    public override bool Equals(object obj)
    {
        var objAsPerson = obj as Person;

        if (obj == null)
        {
            return false;
        }

        if (this.Name != objAsPerson.Name)
        {
            return false;
        }

        if (this.Age != objAsPerson.Age)
        {
            return false;
        }

        return true;
    }
}