我有个同事喜欢把他的空值检查写成下面这样:
if (!ReferenceEquals(myObject, null))
然而,就我个人而言,我认为这种语法阅读起来很繁琐,更喜欢使用以下语法:
if (myObject != null)
我找到一些文章和stackoverflow的问题,讨论了关于在运算符重载方面使用ReferenceEquals的好处,但是除了运算符重载的情况外,使用ReferenceEquals相对于使用==有哪些好处呢?
我有个同事喜欢把他的空值检查写成下面这样:
if (!ReferenceEquals(myObject, null))
然而,就我个人而言,我认为这种语法阅读起来很繁琐,更喜欢使用以下语法:
if (myObject != null)
我找到一些文章和stackoverflow的问题,讨论了关于在运算符重载方面使用ReferenceEquals的好处,但是除了运算符重载的情况外,使用ReferenceEquals相对于使用==有哪些好处呢?
使用C# 7,您可以使用:
if ( !(myObject is null) )
它相当于
if (!ReferenceEquals(myObject, null))
Object.Equals(expr, constant)
方法的调用确定。”而且if ( !(myObject is null) )
在SharpLab中转换为if (myObject != null)
。相关:https://dev59.com/xFgQ5IYBdhLWcg3wazIi#42814441 - Kevinoidobject
实现,这确实相当于ReferenceEquals
。(就此而言,这也是Object.equals(..)
的意思-不使用任何覆盖。) - ToolmakerSteve如果有人重写了==或!=运算符,他们可以让它们执行任何想要的操作。他们甚至可以让它做一些真正危险的事情,比如return true;
或return false;
。此外,如果有一个重载的运算符,很可能它的性能不如ReferenceEquals
(不能保证,但可能并不重要)。
尽管如此,由于任何合理的实现都不太可能出现这种问题。我个人不使用ReferenceEquals
,除非我有充分的理由不在该类型或特定实例中使用==
运算符。
==
运算符实现得不好/不正确,那么应该修复那个 bug。通过使用 ReferenceEquals()
来绕过这个问题并不是正确的方法。 - Michael Burr==
操作符肯定要修复,但我认为使用ReferenceEquals()
是一种可靠的防御性编程,而不是一种变通方法。 - dark_perfectReferenceEquals
没有用处,因为 ==
应该具有引用语义,只是一个值在合理的实现中只有当它实际上为空时才应该等于 null。 - Servy==
的类型上执行非空值的引用比较,以获得其他语义,这 是 使用 ReferenceEquals
而不是 ==
的有力理由,因此即使在这种情况下,该语句仍然成立。 - Servyother
进行“尝试转换”为相同的类。如果结果为 null
,则已知它不相等。尽管您的观点是正确的:无论进行什么检查都需要一些嵌套调用。 - ToolmakerSteve==
,而应该使用ReferenceEquals
,因为这就是它的目的。尽管如此,我同意== null
是一个特例,可以正常工作。 - ToolmakerStevepublic static bool IsNull<T>(this T value) where T : class, new()
{
return value == null;
}
但是这段代码很无聊。
原因是 T 必须是一个类。为什么?因为我们不能将值类型与 null 进行比较。这使得编写良好的通用代码变得非常困难。
或者,您可以将相同的方法附加到对象上,并且永远不必关心该对象是什么。
public static bool IsNull(this object value)
{
return object.ReferenceEquals(value, null);
}
string str = null; if (str.IsNull()) // do something
将会工作。编译器将知道字符串类没有“IsNull”函数,但也将知道您已经定义了一个扩展方法。然后编译器调用 IsNull(str),然后调用 object 类上的静态 ReferenceEquals 方法。 - B.O.B.this.somefunc()
,而不是原始的扩展方法调用value.somefunc()
,这才导致了异常。感谢你的纠正! - ToolmakerSteveIs
等同于 Object.ReferenceEquals
,因此始终可以使用它来代替该调用。正如其他答案所解释的那样,这两个调用的语义有些不同。
如果您想使用 ReferenceEquals
的语义,但希望使用简化的语法,在引用类型中,您也可以使用:
if (myObject is object)
ReferenceEquals 可能稍微快一点。如前所述,它确保不会调用任何重载的 Equals 运算符。此外,ReferenceEquals 确保只执行一个比较,而不是可能根据重载 Equals 运算符的实现执行 2 次比较。虽然很可能重载运算符本身正在使用 ReferenceEquals 作为第一条语句。
我个人确实使用 ReferenceEquals,但仅在我真正努力挤出最后几个时钟周期的地方使用(这些地方可能每秒被调用数百万次)。或者当我无法控制类型时,例如泛型情况下。... 但再次强调,仅在性能真正关键时使用。
==
运算符的类型,否则使用==
将始终导致使用object
重载,它不需要进行任何检查并简单地调用ReferenceEquals
。在这些情况下,它不仅在功能上等效,而且性能几乎相同。这使得泛型成为使用==
而不是ReferenceEquals
的最安全的时间之一。 - Servy(object) ==
不是被定义为调用第一个参数的 Equals
方法吗?在自定义类中定义 Equals
方法的目的不就是为了覆盖这个相等测试吗? - ToolmakerSteveobject.Equals
。它将执行 null 检查,如果两者都不为空,则使用虚拟的 object.Equals
来确定结果。由于传入的一个值为 null,我们知道这里不会发生。 - Servy在原始答案被写下十年后,现在正确的做法是两者都不用。
相反,对于null
检查,请使用is null
或is not null
。
但是在你的问题正文中,你也问了一个更广泛的问题:
ReferenceEquals
与==
相比有什么好处吗?
巨大的好处是实际上想要地址相等是很少见的。所以我会建议你使用ReferenceEquals
来强调,并明确表明这不仅仅是你忘记重载operator==
的错误。
实际上,我希望如果你实现了IEquatable<T>
并且没有重载operator==
,那么C#编译器或IDE会发出警告。或者选择性地发出警告,完全禁止未重载operator==
,因为这种情况比我真正想要相同地址的情况要多得多。
if (!Object.ReferenceEquals(myObject, null))
- 所以实际上比描述的还要更长。 - Reed CopseyReferenceEquals
是否被隐藏(或者是否定义了另一个匹配的重载)。但是如果要遵循你的哲学,那么可以说global::System.Object.ReferenceEquals(myObject, null)
,假设有人定义了其他名为Object
和System
的类型或命名空间... - Jeppe Stig Nielsen