【问题标题】:IEquatable<T>.Equals from MSDNIEquatable<T>.Equals 来自 MSDN
【发布时间】:2016-04-24 09:43:14
【问题描述】:

我正在查看IEquatable.Equals on MSDN。特别是两个 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);
}

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;

还是我错过了什么?

【问题讨论】:

  • 您正在覆盖==,因此如果您不将其转换为对象,它将调用相同的== 并最终导致堆栈溢出异常。
  • 另外,如果它们都为null,它应该返回true,所以如果它们都为null,你不能简单地返回false。
  • @Rob 我认为你应该把你的 cmets 放在一个答案中,这样可以帮助其他人更容易找到他们是否有同样的问题。
  • 更改代码,运行它,然后自己找出来。你写这个问题所用的时间会少得多。
  • 我实际上就坐在这里,源代码在我旁边,所以是的,我在发布之前确实尝试过。这不是我的第一篇文章。因为某人不理解某事而否决某人的问题,就像在工作小组会议上嘲笑某人犯了错误一样。我相信你的父母为你感到骄傲。

标签: c# iequatable


【解决方案1】:

为什么在检查它是否为空之前强制转换为对象?

您处于== 的重载中,因此如果您没有强制转换,您将回调该重载,并且会出现堆栈溢出的无限递归(或者更糟,如果它设法尾调用优化,无限循环)。你需要确保你打电话给object==不是你自己的。

为什么要调用 Object.Equals?当然,如果其中一项为空,那么它就是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.

现在,除了处理它们都为 null 的情况外,我还要处理它们都是同一个对象的情况。由于无论哪种方式都进行相同的引用身份检查,这几乎不会增加该方法的成本,但是如果我们必须检查很多事情以确保两个项目相等(考虑检查两个非常大的字符串并且只知道它们是检查每个字符或排序单元后相同)它为我们提供了一个快速的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 为空时,前者(因为它避免了一个分支)可能会略微获胜,但后者可能会略微获胜,并且更简洁一些。我一般会选择后者。

因此,在您引用的示例中使用 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);
}

该模式将用作实现IEquatable&lt;T&gt; 的任何类的Equals(object) 覆盖(可能存在您希望将对象视为等于不同类型之一的情况,但这些情况很少见并且经常出错,即使它们都完成了,所以也许应该被认为是一个非常专业的案例)。对于您可以使用的任何结构:

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

【讨论】:

  • 这么小的胡子学了这么多知识?希望我能投 5 票!
猜你喜欢
  • 2011-09-11
  • 1970-01-01
  • 2011-12-27
  • 2013-08-09
  • 1970-01-01
  • 2012-11-24
  • 2020-06-30
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多