我相信,对于 .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 指南,不仅因为它对我来说不自然,还因为重载 == 不会造成任何重大伤害。 这与不覆盖非通用的Equals 或GetHashCode 可以反击,因为框架不会在任何地方使用==,除非我们自己使用它。我从不重载== 和!= 中获得的唯一真正好处是与我无法控制的整个框架的设计保持一致。这确实是一件大事,很遗憾我会坚持下去。
带有引用语义(可变对象)
1) 覆盖Equals 和GetHashCode。
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) 覆盖Equals 和GetHashCode。
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...");
}
}
如果你的类可以被继承,请特别注意看看它应该如何处理,在这种情况下,你必须确定基类对象是否可以等于派生类对象。理想情况下,如果没有派生类的对象用于相等性检查,则基类实例可以等于派生类实例,在这种情况下,无需检查基类的通用Equals 中的Type 相等性.
一般注意不要重复代码。我本可以制作一个通用抽象基类(IEqualizable<T> 左右)作为模板,以便更轻松地重用,但遗憾的是在 C# 中这阻止了我从其他类派生。