IEquatable<T>.Equals 来自 MSDN

IEquatable<T>.Equals from MSDN

我正在查看 IEquatable.Equals on MSDN。特别是两个等于运算符的部分:

public override bool Equals(Object obj)
{
   if (obj == null)
      return false;

   Person personObj = obj as Person;
   if (personObj == null)
      return false;
   else
      return Equals(personObj);
}

public static bool operator == (Person person1, Person person2)
{
   if (((object)person1) == null || ((object)person2) == null)
      return Object.Equals(person1, person2);

   return person1.Equals(person2);
}

我遇到的问题是:

if (((object)person1) == null || ((object)person2) == null)
   return Object.Equals(person1, person2);
  1. 为什么在检查对象是否为空之前强制转换为对象?是否还有更多内容或者是否可以很容易地表达出来if ( person1 == null || person2 == null )?
  2. 为什么调用 Object.Equals?当然,如果其中一项为空,那么它就是 false?

我的看法,

if (((object)person1) == null || ((object)person2) == null)
   return Object.Equals(person1, person2);

只是一种绕口令的写法:

if ( person1 == null || person2 == null )
   return false;

还是我遗漏了什么?

Why the cast to object before checking if it's null?

你处于 == 的超载中,所以如果你没有进行转换,你会回调到那个超载中,并且会有 stack-overflow 的无限递归(或更糟,如果它设法 tail-call 优化,一个无限循环)。您需要确保调用 object== 而不是您自己的。

Why the call to Object.Equals? Surely if one of the items is null then it's false?

不是,如果它们 为空,则为真。

因此,这是有道理的。我不推荐它。更简单的做法是完全自己进行空检查:

if ((object)person1 == null)
  return (object)person2 == null;
if ((object)person2 == null)
  return false; // we already know person1 isn't null
// Follow with rest of logic.

我们可以在这里做一些不同的模式。我们也可以这样做:

if ((object)person1 == null && (object)person2 == null) return true;
if ((object)person1 == null || (object)person2 == null) return false;
// Follow with rest of logic.

到目前为止,只需要进行一次额外的比较就大有裨益。仍然检查一个引用是否与另一个引用相同(包括检查它是否为空)很便宜。让我们摆脱分支:

if ((object)person1 == null & (object)person2 == null) return true;
if ((object)person1 == null | (object)person2 == null) return false;
// Follow with rest of logic.

每一行中可能不必要的检查的额外成本可能低于分支是否进行检查的成本,所以这是一个胜利。

但现在考虑第一行是检查它们是否都为空。实际上,这只是它们都是同一实例的情况的一个子集。让我们检查一下:

if ((object)person1 == (object)person2) return true;
if ((object)person1 == null | (object)person2 == null) return false;
// Follow with rest of logic.

现在除了处理它们都为空的情况外,我还处理它们都是同一个对象的情况。由于其相同的 reference-identity 检查两种方式,这几乎不会增加方法的成本,但是如果我们必须检查很多东西以确保两个项目相等(考虑检查两个非常大的字符串并且只知道它们'在检查每个字符或排序规则单元后都一样)它给了我们一个快速的 true 本来可以很慢的 true.

现在让我们考虑一下 Equals() 是我们将拥有大部分逻辑的地方。如果我们应用以上内容,我们可以在以下选项之间进行选择:

public static bool operator == (Person person1, Person person2)
{
  if ((object)person1 == (object)person2)
    return true;
  if ((object)person1 == null | (object)person2 == null)
    return false;
  return person1.Equals(person2);
}

public static bool operator == (Person person1, Person person2)
{
  if ((object)person1 == (object)person2)
    return true;
  return ((object)person1 != null  && person1.Equals(person2);
}

后者取决于 person1.Equals(person2) 将检查 person2 是否不为空这一事实。当 person2 为 null 时,前者(因为它避免了分支)可能会略胜一筹,但否则后者可能会略胜一筹,并且更加简洁。我通常会选择后者。

因此,在您引用的示例中使用 object.Equals() 是有效的,但这不是我推荐的方法。


顺便说一句,他们建议 object.Equals() 我根本不推荐:

public override bool Equals(Object obj)
{
  if (obj == null)
    return false;

  Person personObj = obj as Person;
  if (personObj == null)
    return false;
  else
    return Equals(personObj);
}

如果您删除第一个空检查,那么第二个空检查仍会捕获该情况。

如果您删除第二个空检查,那么对 Equals() 的调用(采用 Person 的重载检查)将会捕获它。

因此它应该只是:

public override bool Equals(object obj)
{
  return Equals(obj as Person);
}

该模式将作为任何 class 实现 IEquatable<T>Equals(object) 覆盖(在某些情况下,您可能希望将对象视为不同类型之一,但是这些很少见,而且即使完成了也经常是错误的,所以也许应该被认为是一个非常特殊的案例)。对于您可以使用的任何结构:

public override bool Equals(object obj)
{
  return obj is TheStructType && Equals((TheStructType)obj);
}