为什么编译器至少不警告 this == null
为什么 C# 编译器甚至不抱怨这段代码的警告? :
if (this == null)
{
// ...
}
显然这个条件永远不会被满足。
Why does the C# compiler not even complain with a warning on this code? :
if (this == null)
{
// ...
}
Obviously the condition will never be satisfied..
如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。
绑定邮箱获取回复消息
由于您还没有绑定你的真实邮箱,如果其他用户或者作者回复了您的评论,将不能在第一时间通知您!
发布评论
评论(5)
这也与 C# 所做的(或没有做的)其他警告一致,例如:
无论如何,这些警告也将始终具有相同的结果。
This also falls in line with other warnings C# does (or not does for that matter) like:
These will also always have the same result no matter what.
因为您可以覆盖
operator ==
对于这种情况返回 true。运行
new Foo().Test()
将在控制台打印“True”。这里的另一个问题是:为什么编译器不针对
ReferenceEquals(this, null)
发出警告?从上述链接的底部:这可能可以通过 @Aaronaught 的回复来回答。这也是为什么您应该执行
(object)x == null
或ReferenceEquals(x, null)
,而不是执行简单的x == null
code>,当您检查空引用时。当然,除非您确定==
运算符没有重载。Because you could override
operator ==
to return true for that case.Running
new Foo().Test()
will print "True" to the console.The other question here is: why doesn't the compiler issue a warning for
ReferenceEquals(this, null)
? From the bottom of the above link:That might be answered by @Aaronaught's response. And that's also why you should be doing
(object)x == null
orReferenceEquals(x, null)
, not doing a simplex == null
, when you're checking for null references. Unless, of course, you're sure that the==
operator is not overloaded.我不同意。我觉得你说的还是有道理的。
编译器知道比较是否将进行到用户定义的比较运算符,并且编译器知道如果不是,则“this”永远不会为空。
事实上,编译器确实会跟踪给定表达式是否可以合法地为 null,以便对非虚拟方法调用实现较小的优化。如果您有一个非虚拟方法 M 并且您说
foo.M();
那么编译器会将其生成为“使用接收者 foo 对 M 进行虚拟调用”。为什么?因为我们想在 foo 为 null 时抛出异常,并且虚拟调用总是对接收者进行 null 检查。非虚拟调用则不会;我们必须将其生成为“检查 foo 是否为 null,然后对 M 进行非虚拟调用”,这是更长、更慢、更烦人的代码。现在,如果我们可以不进行空检查就可以逃脱,我们就会这样做。如果您说
this.M()
或(new Foo()).M()
那么我们不会生成虚拟调用。我们生成非虚拟调用而不进行 null 检查,因为我们知道它不能为 null。因此,编译器拥有关于与 null 的特定比较是否有时、总是或永远不会成功的出色数据。
那么问题是“如果编译器知道特定的比较永远不会成功,为什么不为其生成警告呢?”
答案是“有时我们这样做,有时我们不这样做”。
我们在这种情况下这样做:
在两个可为 null 的整数上定义了一个相等运算符。 x 可转换为可空 int。 null 可转换为可空 int。因此相等运算符是有效的,因此被使用,当然总是错误的。编译器会发出警告,指出该表达式始终为 false。
然而,由于我们在 C# 3 中意外引入的错误,此代码不会产生该警告:
Same deal。可空 guid 相等运算符有效,但警告被抑制。我不确定我们是否修复了 C# 4 的这个错误。如果没有,希望我们能将其纳入服务包中。
至于“if (this == null)”我不知道为什么我们不对此发出警告。这看起来确实是一个很好的警告候选者。最可能的解释是遵循这个逻辑三段论:
有无数的编译器特性是我们还没有想到的;我们没有实施其中任何一个。
此处不发出警告的另一个原因是,我们尝试对可能是意外键入的代码以及由于非明显原因几乎肯定是错误的代码发出警告 。 “this == null”不太可能是偶然输入的,尽管它几乎肯定是错误的,正如您在问题陈述中指出的那样,它显然是错误的。
与我们的“guid == null”相比——这很可能是偶然发生的,因为开发人员可能会意外地认为 Guid 是引用类型。在 C++ 中,指南通常通过引用传递,因此这是一个很容易犯的错误。该代码几乎肯定是错误的,但错误的方式并不明显。所以这是一个很好的警告候选者。 (这就是为什么不幸的是,由于我们引入了一个错误,这是 C# 2 中的警告,而不是 C# 3 中的警告。)
I disagree. I think you still make a good point.
The compiler knows whether the comparison is going to go to a user-defined comparison operator or not, and the compiler knows that if it does not, then 'this' is never null.
And in fact, the compiler does track whether a given expression can legally be null or not, in order to implement a minor optimization on non-virtual method calls. If you have a non-virtual method M and you say
foo.M();
then the compiler generates this as "do a virtual call to M with receiver foo". Why? Because we want to throw if foo is null, and a virtual call always does a null check on the receiver. A non-virtual call does not; we'd have to generate it as "check foo for null, and then do a non-virtual call to M", which is longer, slower, more irritating code.Now, if we can get away without doing the null check, we do. If you say
this.M()
or(new Foo()).M()
then we do NOT generate a virtual call. We generate the non-virtual call without the null check because we know that it cannot be null.So the compiler has excellent data on whether a particular comparison to null will sometimes, always, or never succeed.
The question then is "if the compiler knows that a particular comparison will never succeed, why not generate a warning for it?"
And the answer is "sometimes we do, and sometimes we don't".
We do in this situation:
There is an equality operator defined on two nullable ints. x is convertible to nullable int. null is convertible to nullable int. So that equality operator is valid, and therefore is used, and is of course always false. The compiler gives a warning that the expression is always false.
However, due to a bug we accidentally introduced in C# 3, this code does NOT produce that warning:
Same deal. The nullable guid equality operator is valid, but the warning is suppressed. I'm not sure if we fixed this bug for C# 4 or not. If not, hopefully we'll get it into a service pack.
As for "if (this == null)" I don't know why we don't warn for that. It certainly seems like a good candidate for a warning. The most likely explanation is to follow this logical syllogism:
There are infinitely many compiler features that we haven't thought of yet; we've implemented none of them.
Another reason to not give a warning here is that we try to give warnings on code which is both likely to be typed by accident and almost certainly wrong for a non-obvious reason. "this == null" is not likely to be typed in by accident, and though it is almost certainly wrong, as you note in the statement of your question, it is obviously wrong.
Compare that to our "guid == null" -- that is likely to happen by accident, because a developer might accidentally think that Guid is a reference type. Guids are usually passed around by reference in C++, so this is an easy mistake to make. The code is almost certainly wrong, but it is wrong in a non-obvious way. So this is a good candidate for a warning. (Which is why it is so unfortunate that this is a warning in C# 2 but not C# 3, due to a bug we introduced.)
实际上,这种情况确实可以 满意,至少在 Visual Studio 2008 中。他们已在 VS 2010 中修复了此行为,但并非完全不可想象可能存在另一种方法来创建这种情况。
(tl;dr 版本 - 在 C# 3.5 中,从作为构造函数参数传递的匿名函数引用
this
是合法的,但如果您实际尝试使用它,您会发现this
为null
。)Actually, the condition really can be satisfied, at least in Visual Studio 2008. They've fixed this behaviour in VS 2010, but it is not totally inconceivable that there might be another way to create such a condition.
(tl;dr version - in C# 3.5, it's legal to reference
this
from an anonymous function passed as a constructor argument, but if you actually try to use it, you'll find thatthis
isnull
.)尽管下面的代码很糟糕,但它仍然是 C# 代码。如果调用 Bar,您将收到 InvalidOperationException 并显示消息 null。
Though the code below is a bastard it's still all C#. an if you call Bar you will get an InvalidOperationException with the message null.