为什么编译器至少不警告这个== null

And*_*nea 21 c# compiler-construction null this

为什么C#编译器甚至没有对此代码发出警告?:

if (this == null)
{
   // ...
}
Run Code Online (Sandbox Code Playgroud)

显然,这种情况永远不会得到满足.

Mar*_*off 31

因为您可以覆盖operator ==以返回该情况的true.

public class Foo
{
    public void Test()
    {
        Console.WriteLine(this == null);
    }

    public static bool operator ==(Foo a, Foo b)
    {
        return true;
    }

    public static bool operator !=(Foo a, Foo b)
    {
        return true;
    }
}
Run Code Online (Sandbox Code Playgroud)

Running new Foo().Test()将打印"True"到控制台.

这里的另一个问题是:为什么编译器不发出警告ReferenceEquals(this, null)?从上面链接的底部:

在重载一个常见的错误operator ==是使用(a == b),(a == null)(b == null)以检查引用相等.这反而导致调用重载operator ==,导致无限循环.使用ReferenceEquals或转换类型为Object,以避免循环.

@ Aaronaught的回答可能会回答这个问题.这也是你在检查空引用时应该做的,(object)x == null或者ReferenceEquals(x, null)不做一个简单的原因x == null.当然,除非您确定==操作员没有超载.

  • @Jonathan Allen:从什么时候开始,C#"混淆"参考和价值平等?当你想要显式时,`System.Object`甚至有`ReferenceEquals`和`Equals`方法.允许`==`运算符重载的想法是允许对象本身决定哪个是合适的.唯一真正令人讨厌的是,重载的`==`运算符和`Equals`方法可能表现得不同(作为类设计者,你应该避免这样的事情). (4认同)
  • 顺便说一句,实际上有一个类(好吧,结构体)**已经**重载了“==”运算符并专门处理“null”比较:“Nullable<T>”。因此,这不仅仅是一些奇怪的极端情况,这样做是有正当理由的(包括空对象模式)。 (2认同)
  • 在其他语言中,包括VB,引用相等和值相等是不同的运算符.这样可以清楚地了解您正在进行的比较类型并防止出现许多细微错误. (2认同)

Eri*_*ert 24

哇...我猜我是可耻的错

我不同意.我觉得你还是说得好.

编译器知道比较是否将转到用户定义的比较运算符,并且编译器知道如果不是,那么'this'永远不会为空.

实际上,编译器跟踪给定表达式是否合法地为空,以便在非虚方法调用上实现次要优化.如果您有一个非虚方法M并且您说foo.M();那么编译器会将其生成为"使用接收器foo对M进行虚拟调用".为什么?因为如果foo为null我们想要抛出,并且虚拟调用总是对接收器进行空检查.非虚拟呼叫不会; 我们必须将其生成为"check foo for null,然后对M进行非虚拟调用",这是更长,更慢,更刺激的代码.

现在,如果我们可以在不进行空检查的情况下离开,我们就可以.如果您说this.M()或者(new Foo()).M()我们不生成虚拟呼叫.我们生成非虚拟调用而不进行空检查,因为我们知道它不能为null.

因此编译器具有关于是否有时,总是或永远不会成功的特定比较的优秀数据.

那么问题是"如果编译器知道特定的比较永远不会成功,为什么不为它生成警告?"

答案是"有时我们这样做,有时我们不这样做".

我们在这种情况下做:

int x = 123;
if (x == null) ...
Run Code Online (Sandbox Code Playgroud)

在两个可空的int上定义了一个相等运算符.x可转换为nullable int.null可以转换为nullable int.因此,相等运算符是有效的,因此被使用,当然总是错误的.编译器发出警告,表达式始终为false.

但是,由于我们在C#3中意外引入了一个错误,此代码不会产生该警告:

Guid x = whatever;
if (x == null) ...
Run Code Online (Sandbox Code Playgroud)

同样的交易.可为空的guid相等运算符有效,但警告被抑制.我不确定我们是否修复了C#4的这个bug.如果没有,希望我们能把它变成一个服务包.

至于"if(this == null)"我不知道为什么我们不为此发出警告.它似乎是警告的好候选人.最可能的解释是遵循这种逻辑三段论:

  • 警告是编译器功能
  • 编译器功能必须(1)思考,(2)设计,(3)实现,(4)测试,(5)记录和(6)在您利用该功能之前发送给您.
  • 没有人做过任何的这个功能那六个必备的东西; 我们无法提供我们从未想到的功能.
  • 因此,没有这样的功能.

我们还没有想到过无限多的编译器功能; 我们没有实现它们.

不在此发出警告的另一个原因是我们试图对代码发出警告,这些代码很可能是偶然输入的,而且由于非显而易见的原因几乎肯定是错误的."this == null"不太可能被意外输入,虽然几乎肯定是错的,但正如你在问题陈述中所指出的那样,这显然是错误的.

将其与我们的"guid == null"进行比较 - 这很可能是偶然发生的,因为开发人员可能会意外地认为Guid是一种引用类型.Guids通常在C++中通过引用传递,因此这是一个容易犯的错误.代码几乎肯定是错误的,但它以一种非显而易见的方式是错误的.所以这是警告的好候选人.(由于我们引入了一个错误,这就是为什么这是C#2中的警告而不是C#3的警告.)