.Net 2+:为什么if(1 == null)不再抛出编译器异常?

时间:2020-03-05 18:53:24  来源:igfitidea点击:

我以" int"为例,但这适用于.Net中的任何值类型

在.Net 1中,以下内容将引发编译器异常:

int i = SomeFunctionThatReturnsInt();

if( i == null ) //compiler exception here

现在(在.Net 2或者3.5中),该异常消失了。

我知道为什么会这样:

int? j = null; //nullable int

if( i == j )   //this shouldn't throw an exception

问题在于,因为" int"是可为空的,并且" int"现在隐式转换为" int"。上面的语法是编译器魔术。确实我们在做:

Nullable<int> j = null; //nullable int

//compiler is smart enough to do this
if( (Nullable<int>) i == j)   

//and not this
if( i == (int) j)

所以现在,当我们执行" i == null"时,我们得到:

if( (Nullable<int>) i == null )

考虑到Cis正在执行编译器逻辑来计算此值,为什么在处理诸如`null'之类的绝对值时,为什么它不足够聪明而不执行该操作呢?

解决方案

回答

当我们将不可为null的类型与null进行比较时,编译器仍会生成警告,这只是应有的方式。可能是警告级别过低或者在最新版本中已更改(我仅在.net 3.5中进行了更改)。

回答

奇怪...以VS2008编译,目标是.NET 3.5:

static int F()
{
    return 42;
}

static void Main(string[] args)
{
    int i = F();

    if (i == null)
    {
    }
}

我收到编译器警告

warning CS0472: The result of the expression is always 'false' since a value of type 'int' is never equal to 'null' of type 'int?'

并生成以下IL ...大概是JIT可以优化的

L_0001: call int32 ConsoleApplication1.Program::F()
L_0006: stloc.0 
L_0007: ldc.i4.0 
L_0008: ldc.i4.0 
L_0009: ceq 
L_000b: stloc.1 
L_000c: br.s L_000e

我们可以发布代码段吗?

回答

我不认为这本身就是编译器问题;整数永远不会为空,但是将它们相等的想法并不是无效的。这是一个始终返回false的有效函数。而且编译器知道;代码

bool oneIsNull = 1 == null;

可以编译,但会警告编译器:`表达式的结果始终为'false',因为类型'int'的值永远不等于类型'<null>'的'null'。

因此,如果我们希望返回编译器错误,请转至项目属性,然后为该错误打开"将警告作为错误处理",然后我们将开始再次将它们视为构建中断问题。

回答

警告是新的(我认为是3.5),错误与我执行过'1 == 2'一样,它很聪明,可以发现从未有过的错误。

我怀疑通过3.5的优化,整个语句将被删除,因为它很聪明,而且从来没有真正的评估。

虽然我可能希望1 == 2进行编译(例如在测试其他内容时关闭功能块),但我不想1 == null来编译。

回答

这应该是编译时错误,因为类型不兼容(值类型永远不能为null)。很遗憾,事实并非如此。

回答

2.0框架引入了可为空的值类型。即使文字常量" 1"永远不能为null,现在也可以将其基础类型(int)强制转换为Nullable int类型。我的猜测是,即使编译器是文字常量,编译器也不能再假定它不能为空。编译2.0时,我确实收到警告:

警告1由于类型'int'的值永远不会等于'int?'类型的'null',因此表达式的结果始终为'false'。