等于(item, null) 或 item == null

发布于 2024-09-14 10:35:27 字数 575 浏览 7 评论 0原文

使用 static Object.Equals 检查 null 的代码是否更健壮比使用 == 运算符或常规 Object.Equals 的代码?后两者是否容易被覆盖,导致检查 null 无法按预期工作(例如,当比较值 null 时返回 false)?

换句话说,是这样的:

if (Equals(item, null)) { /* Do Something */ }

比这样更健壮:

if (item == null) { /* Do Something */ }

我个人发现后者的语法更容易阅读。在编写处理作者控制之外的对象(例如库)的代码时是否应该避免这种情况?是否应该始终避免(检查 null 时)?这只是吹毛求疵吗?

Is code that uses the static Object.Equals to check for null more robust than code that uses the == operator or regular Object.Equals? Aren't the latter two vulnerable to being overridden in such a way that checking for null doesn't work as expected (e.g. returning false when the compared value is null)?

In other words, is this:

if (Equals(item, null)) { /* Do Something */ }

more robust than this:

if (item == null) { /* Do Something */ }

I personally find the latter syntax easier to read. Should it be avoided when writing code that will handle objects outside the author's control (e.g. libraries)? Should it always be avoided (when checking for null)? Is this just hair-splitting?

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(7

白龙吟 2024-09-21 10:35:27

这个问题没有简单的答案。在我看来,任何说总是使用其中之一的人都给了你不好的建议。

实际上,您可以调用多种不同的方法来比较对象实例。给定两个对象实例 ab,您可以编写:

  • Object.Equals(a,b)
  • Object.ReferenceEquals(a, b)
  • a.Equals(b)
  • a == b

这些都可以做不同的事情!

Object.Equals(a,b) 将(默认情况下)对引用类型执行引用相等比较,对值类型执行按位比较。来自 MSDN 文档:

Equals 的默认实现
支持引用相等
引用类型和按位相等
对于值类型。引用相等
表示对象引用
比较指的是同一个对象。
按位相等意味着对象
被比较的具有相同的二进制
代表。

请注意,派生类型可能
重写 Equals 方法以
落实价值平等。价值
相等是指比较对象
具有相同但不同的值
二进制表示。

请注意上面的最后一段......我们稍后会讨论这个问题。

Object.ReferenceEquals(a,b) 仅执行引用相等性比较。如果传递的类型是装箱值类型,则结果始终为 false

a.Equals(b) 调用 Object 的虚拟实例方法,a 的类型可以重写该方法做任何它想做的事。该调用是使用虚拟调度执行的,因此运行的代码取决于 a 的运行时类型。

a == b 调用 a 的**编译时类型*的静态重载运算符。如果该运算符的实现调用 ab 上的实例方法,它还可能取决于参数的运行时类型。由于调度是基于表达式中的类型,因此以下可能会产生不同的结果:

Frog aFrog = new Frog();
Frog bFrog = new Frog();
Animal aAnimal = aFrog;
Animal bAnimal = bFrog;
// not necessarily equal...
bool areEqualFrogs = aFrog == bFrog;
bool areEqualAnimals = aAnimal = bAnimal;

因此,是的,使用operator ==检查空值存在漏洞。实际上,大多数类型不会重载== - 但永远无法保证。

实例方法 Equals() 在这里也好不到哪儿去。虽然默认实现执行引用/按位相等检查,但类型可能会覆盖 Equals() 成员方法,在这种情况下将调用此实现。用户提供的实现可以返回它想要的任何内容,即使与 null 进行比较也是如此。

但是您会问 Object.Equals() 的静态版本怎么样?这最终可以运行用户代码吗?好吧,事实证明答案是肯定的。 Object.Equals(a,b) 的实现扩展为以下内容:

((object)a == (object)b) || (a != null && b != null && a.Equals(b))

您可以自己尝试一下:

class Foo {
    public override bool Equals(object obj) { return true; }  }

var a = new Foo();
var b = new Foo();
Console.WriteLine( Object.Equals(a,b) );  // outputs "True!"

因此,语句可能为:Object.Equals( a,b) 在调用中的任何类型都不为 null 时运行用户代码。请注意,当任一参数为 null 时,Object.Equals(a,b) 不会调用 Equals() 的实例版本。

简而言之,您获得的比较行为类型可能会有很大差异,具体取决于您选择调用的方法。不过,这里有一个评论:微软没有正式记录 Object.Equals(a,b) 的内部行为。 如果您需要在不运行任何其他代码的情况下将引用与 null 进行比较的铁证,那么您需要 Object.ReferenceEquals()

Object.ReferenceEquals(item, null);

此方法使意图极其清晰 - 您特别期望结果是两个引用的比较以确保引用相等。与使用诸如 Object.Equals(a,null) 之类的东西相比,这里的好处是稍后不太可能有人会说:

“嘿,这很尴尬,让我们替换它与:a.Equals(null)a == null

这可能会有所不同,

让我们在这里注入一些实用主义。a == null。 - 在 .NET 类中,例如 StringNullable 具有明确定义的比较语义,此外,它们是密封的 - 防止对其进行任何更改。以下是很常见的(也是正确的):

string s = ...
if( s == null ) { ... }

没有必要(而且丑陋)编写:

if( ReferenceEquals(s,null) ) { ... }

因此在某些有限的情况下,使用 == 是安全且合适的。

There's no simple answer for this question. Anyone who says always use one or the other is giving you poor advice, in my opinion.

There are actually several different methods you can call to compare object instances. Given two object instances a and b, you could write:

  • Object.Equals(a,b)
  • Object.ReferenceEquals(a,b)
  • a.Equals(b)
  • a == b

These could all do different things!

Object.Equals(a,b) will (by default) perform reference equality comparison on reference types and bitwise comparison on value types. From the MSDN documentation:

The default implementation of Equals
supports reference equality for
reference types, and bitwise equality
for value types. Reference equality
means the object references that are
compared refer to the same object.
Bitwise equality means the objects
that are compared have the same binary
representation.

Note that a derived type might
override the Equals method to
implement value equality. Value
equality means the compared objects
have the same value but different
binary representations.

Note the last paragraph above ... we'll discuss this a bit later.

Object.ReferenceEquals(a,b) performs reference equality comparison only. If the types passed are boxed value types, the result is always false.

a.Equals(b) calls the virtual instance method of Object, which the type of a could override to do anything it wants. The call is performed using virtual dispatch, so the code that runs depends on the runtime type of a.

a == b invokes the static overloaded operator of the **compile-time type* of a. If the implementation of that operator invokes instance methods on either a or b, it may also depend on the runtime types of the parameters. Since the dispatch is based on the types in the expression, the following may yield different results:

Frog aFrog = new Frog();
Frog bFrog = new Frog();
Animal aAnimal = aFrog;
Animal bAnimal = bFrog;
// not necessarily equal...
bool areEqualFrogs = aFrog == bFrog;
bool areEqualAnimals = aAnimal = bAnimal;

So, yes, there is vulnerability for check for nulls using operator ==. In practice, most types do not overload == - but there's never a guarantee.

The instance method Equals() is no better here. While the default implementation performs reference/bitwise equality checks, it is possible for a type to override the Equals() member method, in which case this implementation will be called. A user supplied implementation could return whatever it wants, even when comparing to null.

But what about the static version of Object.Equals() you ask? Can this end up running user code? Well, it turns out that the answer is YES. The implementation of Object.Equals(a,b) expands to something along the lines of:

((object)a == (object)b) || (a != null && b != null && a.Equals(b))

You can try this for yourself:

class Foo {
    public override bool Equals(object obj) { return true; }  }

var a = new Foo();
var b = new Foo();
Console.WriteLine( Object.Equals(a,b) );  // outputs "True!"

As a consequence, it's possible for the statement: Object.Equals(a,b) to run user code when neither of the types in the call are null. Note that Object.Equals(a,b) does not call the instance version of Equals() when either of the arguments is null.

In short, the kind of comparison behavior you get can vary significantly, depending on which method you choose to call. One comment here, however: Microsoft doesn't officially document the internal behavior of Object.Equals(a,b). If you need an iron clad gaurantee of comparing a reference to null without any other code running, you want Object.ReferenceEquals():

Object.ReferenceEquals(item, null);

This method makes the intent extremently clear - you are specifically expecting the result to be the comparison of two references for reference equality. The benefit here over using something like Object.Equals(a,null), is that it's less likely that someone will come along later and say:

"Hey, this is awkward, let's replace it with: a.Equals(null) or a == null

which potentially may be different.

Let's inject some pragmatism here, however. So far we've talked about the potential for different modalities of comparison to yield different results. While this is certainly the case, there are certain types where it's safe to write a == null. Built-in .NET classes like String and Nullable<T> have well defined semantics for comparison. Furthermore, they are sealed - preventing any change to their behavior through inheritance. The following is quite common (and correct):

string s = ...
if( s == null ) { ... }

It's unnecessary (and ugly) to write:

if( ReferenceEquals(s,null) ) { ... }

So in certain limited cases, using == is safe, and appropriate.

叹沉浮 2024-09-21 10:35:27

if (Equals(item, null)) 并不比 if (item == null) 更强大,而且我发现启动起来更混乱。

if (Equals(item, null)) is no more robust than if (item == null), and I find it more confusing to boot.

椵侞 2024-09-21 10:35:27

当您想要测试 IDENTITY(内存中的同一位置)时:

ReferenceEquals(a, b)

处理空值。并且是不可重写的。 100%安全。

但请确保您确实想要身份测试。考虑以下几点:

ReferenceEquals(new String("abc"), new String("abc"))

返回 false。相比之下:

Object.Equals(new String("abc"), new String("abc"))

(new String("abc")) == (new String("abc"))

都返回 true

如果您在这种情况下期望得到 true 答案,那么您需要的是 EQUALITY 测试,而不是 IDENTITY 测试。
请参阅下一部分。


当你想测试EQUALITY(相同内容)时:

  • 如果编译器没有抱怨,请使用“a == b”。

  • 被拒绝(如果变量 a 的类型没有定义“==”运算符),则使用“Object.Equals(a, b)”。

  • 如果您处于已知a不为空的逻辑内部,那么您可以使用更具可读性的“a.Equals(b)< /代码>”。例如,“this.Equals(b)”是安全的。或者,如果“a”是在构造时初始化的字段,并且如果传入 null 作为要在该字段中使用的值,则构造函数会抛出异常。

现在,解决最初的问题:

问:这些是否容易在某些类中被覆盖,代码不能正确处理 null,从而导致异常?

答:是的。获得 100% 安全的 EQUALITY 测试的唯一方法是自己预先测试 null。

但你应该吗?错误就在那个(假设的未来坏类别)中,并且这将是一种简单的失败类型。易于调试和修复(由提供该类的人进行)。我怀疑这是一个经常发生的问题,或者当它发生时仍然持续很长时间的问题。

更详细的答:Object.Equals(a, b) 最有可能在编写糟糕的类时起作用。如果“a”为 null,则 Object 类将自行处理它,因此没有风险。如果“b”为空,则“a”的动态(运行时而非编译时)类型决定调用什么“Equals”方法。当“b”为空时,被调用的方法只需正确工作即可。除非被调用的方法写得非常糟糕,否则它所做的第一步是确定“b”是否是它理解的类型。

因此,Object.Equals(a, b) 是可读性/编码工作量和安全性之间的合理折衷。

When you want to test IDENTITY (same location in memory):

ReferenceEquals(a, b)

Handles nulls. And is not overridable. 100% safe.

But make sure you really do want IDENTITY test. Consider the following:

ReferenceEquals(new String("abc"), new String("abc"))

which returns false. In contrast:

Object.Equals(new String("abc"), new String("abc"))

and

(new String("abc")) == (new String("abc"))

both return true.

If you are expecting an answer of true in this situation, then you want an EQUALITY test, not an IDENTITY test.
See the next part.


When you want to test EQUALITY (same contents):

  • Use "a == b" if the compiler doesn't complain.

  • If that is rejected (if the type of variable a does not define "==" operator), then use "Object.Equals(a, b)".

  • IF you are inside of logic where a is known to not be null, THEN you may use the more readable "a.Equals(b)". For example, "this.Equals(b)" is safe. Or if "a" is a field that is initialized at construction time, and the constructor throws exception if null is passed in as the value to be used in that field.

NOW, to address the original question:

Q: Are these susceptible to being overridden in some class, with code that does not handle null correctly, resulting in an exception?

A: Yes. The only way to get 100% safe EQUALITY test would be to pre-test for nulls yourself.

But should you? The bug would be in that (hypothetical future bad class), and it would be a straightforward type of failure. Easy to debug and fix (by whoever supplies the class). I doubt it is a problem that happens often, or persists long when it does happen.

More detailed A: Object.Equals(a, b) is most likely to work in the face of a poorly written class. If "a" is null, the Object class will handle it itself, so no risk there. If "b" is null, then the DYNAMIC (run-time not compile-time) type of "a" determines what "Equals" method gets called. The called method merely has to work correctly when "b" is null. Unless the called method is extremely poorly written, the first step it does is determine whether "b" is a type that it understands.

So Object.Equals(a, b) is a reasonable compromise between readability/coding_effort and safety.

偏爱你一生 2024-09-21 10:35:27

框架指南建议您处理Equals 作为值相等(检查两个对象是否表示相同的信息,即比较属性),以及 == 作为引用相等,但不可变对象除外,对于这些对象,您可以使用也许应该重写 == 来实现值相等。

因此,假设准则适用于此,请选择语义上合理的那个。如果您正在处理不可变对象,并且您希望两种方法产生相同的结果,那么为了清楚起见,我将使用 ==

The framework guidelines suggest that you treat Equals as value equality (checking to see whether two objects represent the same information, i.e. comparing properties), and == as reference equality, with the exception of immutable objects, for which you should probably override == to be value equality.

So, assuming the guidelines apply here, pick whichever is semantically sensible. If you're dealing with immutable objects, and you expect both methods to produce identical results, I'd use == for clarity.

凉世弥音 2024-09-21 10:35:27

关于“...编写将处理作者控制之外的对象的代码...”,我会指出 static Object.Equals==运算符是静态方法,因此不能虚拟/重写。调用哪个实现是在编译时根据静态类型确定的。换句话说,外部库无法为编译后的代码提供不同版本的例程。

In reference to "...writing code that will handle objects outside the author's control...", I would point out that both static Object.Equals and the == operator are static methods and therefore cannot be virtual/overridden. Which implementation gets called is determined at compile time based on the static type(s). In other words, there is no way for an external library to provide a different version of the routine to your compiled code.

北风几吹夏 2024-09-21 10:35:27

这样做是因为两个空值无法进行比较

(a == null ? "NA" : a).equals(b == null ? "NA" : b)

Do this as two nulls can not be compared

(a == null ? "NA" : a).equals(b == null ? "NA" : b)
滥情哥ㄟ 2024-09-21 10:35:27

当我试图比较本身可能为空的对象的唯一 ID 时,我最终来到这里。发现先估算缺失的数据,然后进行比较更容易。

Guid currentId = (Object1 == null) ? Guid.Empty : Object1.Id;
Guid newId = (Object2 == null) ? Guid.Empty : Object2.Id;
If (currentId == newId)
{
    //do happyface
}
else
{
   //do sadface
}

I ended up here when I was trying to compare the unique Id of objects that could themselves be null. Found it easier to just impute the missing data first, and then do the comparison.

Guid currentId = (Object1 == null) ? Guid.Empty : Object1.Id;
Guid newId = (Object2 == null) ? Guid.Empty : Object2.Id;
If (currentId == newId)
{
    //do happyface
}
else
{
   //do sadface
}
~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文