【发布时间】:2016-08-07 11:47:42
【问题描述】:
我正在查看 Microsoft 为覆盖 Equals 运算符而发布的指南。 https://msdn.microsoft.com/en-us/library/ms173147(v=vs.90).aspx
他们声明:
Equals 的新实现应该遵循 等于:
- x.Equals(x) 返回真。
- x.Equals (y) 返回与 y.Equals(x) 相同的值。
- 如果 (x.Equals (y) && y.Equals (z)) 返回 true,则 x.Equals(z) 返回 true。
- 只要 x 和 y 引用的对象没有被修改,连续调用 x.Equals(y) 就会返回相同的值。
- x.Equals (null) 返回 false(仅适用于不可为空的值类型。)
他们随后提供了基类和子类(TwoDPoint 和 ThreeDPoint,代码如下)的示例,它们实现了覆盖此方法的最佳实践。
但是,这两个示例类未能满足刚刚给出的“等式保证”。 IE,TwoDPoint.Equals(ThreeDPoint) 可以返回 true,但 ThreeDPoint.Equals(TwoDPoint) 将始终返回 false。这不符合上述第二个要点。
static void Main(string[] args)
{
TwoDPoint twoDPoint = new TwoDPoint(1, 2);
ThreeDPoint threeDPoint = new ThreeDPoint(1, 2, 3);
//this will assert because twoDPoint.Equals(threeDPoint) == true
//but, threeDPoint.Equals(twoDPoint) == false
AssertMicrosoftEqualsGuidelines(twoDPoint, threeDPoint, null);
}
/// <summary>
/// Will Assert() if any of microsofts rules for Equals overriding fail.
/// NOTE, x and y can not be null.
/// https://msdn.microsoft.com/en-us/library/ms173147(v=vs.90).aspx
/// </summary>
static void AssertMicrosoftEqualsGuidelines(object x, object y, object z)
{
System.Diagnostics.Debug.Assert(x.Equals(x), "FAILED x.Equals(x) returns true.");
System.Diagnostics.Debug.Assert(x.Equals(y) == y.Equals(x), "FAILED x.Equals(y) returns the same value as y.Equals(x).");
if(x.Equals(y) && y.Equals(z))
{
System.Diagnostics.Debug.Assert(x.Equals(z), "FAILED Successive invocations of x. Equals (y) return the same value as long as the objects referenced by x and y are not modified.");
}
System.Diagnostics.Debug.Assert(x.Equals(y) == x.Equals(y) == x.Equals(y) == x.Equals(y), "Successive invocations of x. Equals (y) return the same value as long as the objects referenced by x and y are not modified.");
System.Diagnostics.Debug.Assert(x.Equals(null) == false, "x.Equals (null) returns false");
}
}
class TwoDPoint : System.Object
{
public readonly int x, y;
public TwoDPoint(int x, int y) //constructor
{
this.x = x;
this.y = y;
}
public override bool Equals(System.Object obj)
{
// If parameter is null return false.
if (obj == null)
{
return false;
}
// If parameter cannot be cast to Point return false.
TwoDPoint p = obj as TwoDPoint;
if ((System.Object)p == null)
{
return false;
}
// Return true if the fields match:
return (x == p.x) && (y == p.y);
}
public bool Equals(TwoDPoint p)
{
// If parameter is null return false:
if ((object)p == null)
{
return false;
}
// Return true if the fields match:
return (x == p.x) && (y == p.y);
}
public override int GetHashCode()
{
return x ^ y;
}
}
class ThreeDPoint : TwoDPoint
{
public readonly int z;
public ThreeDPoint(int x, int y, int z)
: base(x, y)
{
this.z = z;
}
public override bool Equals(System.Object obj)
{
// If parameter cannot be cast to ThreeDPoint return false:
ThreeDPoint p = obj as ThreeDPoint;
if ((object)p == null)
{
return false;
}
// Return true if the fields match:
return base.Equals(obj) && z == p.z;
}
public bool Equals(ThreeDPoint p)
{
// Return true if the fields match:
return base.Equals((TwoDPoint)p) && z == p.z;
}
public override int GetHashCode()
{
return base.GetHashCode() ^ z;
}
}
那么“平等保证”的指导方针是错误的吗?人们是否应该覆盖 equals 也检查两个对象的类型是否相同?浏览器
if(GetType() != obj.GetType()){return false;} //include in Equals()?
真的,我想归结为,如果所有基类字段都匹配,那么基类 equals() 方法为子类返回 true 是否被认为是“可以的”?从基类的角度来看,这是有道理的,恕我直言,但是,您最终确实违反了上面的规则 #2。打破该规则会对字典和哈希集等对象产生什么影响。您只是要求一些细微的错误吗?
【问题讨论】:
-
我已经看到了两种方式的实现。答案是视情况而定。
-
你不应该仅仅为了代码重用的目的而使用继承。
-
有人在文章cmets中指出了这一点,团队似乎已经回复
This topic is no longer in the current library, and it was replaced long enough ago that I can no longer update it.