为什么这段代码没有抛出异常?

6

我原以为下面的代码会抛出异常,因为我在访问一个可空变量的Value属性,而该变量被赋值为null。然而,当我执行以下代码时,却没有抛出任何异常:

int? x=null;
Console.WriteLine(x.Value==null);

但是当我执行以下操作:
Console.WriteLine(x.Value);

正如预期的那样,我遇到了一个异常。

但是访问 x.Value 的这两种方式有什么区别?为什么第一种情况下我没有遇到异常?毕竟,这两段代码都试图访问 x.Value 属性。

注:顺便说一句,我在 www.compileonline.com 网站上运行上述代码。不确定在 Visual Studio 编译器上尝试是否会产生不同的结果,但我目前无法访问 Visual Studio。

谢谢您!


2
Console.WriteLine(x.Value==null); 也会抛出 InvalidOperationException。 - Ramashankar
1
两者都会抛出异常。 - Konrad Kokosa
2
compileonline.com使用的是Mono编译器,而不是微软的编译器。我认为你可能在那个编译器中发现了一个错误。 - user743382
看起来你在在线编译器中发现了一个错误。 - Henrik
2
我得到了一个警告作为错误:表达式的结果始终为“false”,因为类型为“int”的值永远不等于类型为“int?”的“null”。所以可能是一种优化。 - Damien_The_Unbeliever
3个回答

5

两段代码都会抛出 InvalidOperationException,因为

来自 Nullable<T>.Value 属性

如果 HasValue 属性为 true,则当前 Nullable<T> 对象的值。如果 HasValue 属性为 false,则会引发异常。

在你的两个案例中,HasValue 属性将为 false。这就是为什么两个代码都会抛出 InvalidOperationException 的原因。

编辑:好的,看起来www.compileonline.com网站使用的是Mono 2.10.2.0,第一段代码只会产生警告信息,如下所示:

main.cs(9,34): warning CS0472: The result of comparing value type int' with null isfalse'

我查看了错误修复页面,但没有找到有关此问题的任何信息。继续搜索...
Mono 2.10.2.0中,即使HasValue为false,此代码也不会抛出异常,而只会产生警告。
来自编译器警告(级别2)CS0472
表达式的结果始终为'value1',因为类型为'value2'的值永远不等于'type3'的'null'。

所以在我们的情况下,看起来 Mono 2.10.2.0 检查xint?,但x.Valueint,它说(而不是抛出异常);

这是一个int,它是一个值类型,它永远不会等于null

编辑2hdv有一点我认为。这似乎与Bug 12608有关,该错误已在Mono 3.1.10中修复,但我无法确定。

仍在继续搜索..

EDIT3: 好的.. 我在 Ideone 上尝试了这些代码,并且在 示例页面 中它说它使用 Mono 2.8 作为 C# 编译器,结果是令人惊讶的..

你的第一个示例

int? x = null;
Console.WriteLine(x.Value == null);

它不会抛出任何异常,也不会显示任何警告。它完美地工作,并生成False作为结果。看起来它没有检查HasValue属性,甚至没有查看Nullable<T>的根(在我们的情况下是int)是否是值类型

您的第二个示例

int? x = null;
Console.WriteLine(x.Value);

按照我们的期望,抛出InvalidOperationException异常。

未处理的异常:System.InvalidOperationException:可空对象必须具有值。在 System.Nullable`1[System.Int32].get_Value () [0x00000] 中 :0 在 Test.Main () [0x00000] 中:0

等等..

第一个示例中,我们说:

看起来它没有检查 HasValue 属性..

如何在这个时候检查 HasValue 属性?我认为这是在线编译器的错误,但仍然无法在Mono 3.+版本上进行检查..

EDIT4:我从联系页面向创建此网站的人发送了一条消息,并解释了这种情况。


问题说第一段代码没有抛出异常,并问为什么。这怎么回答那个问题呢? - Barmar
1
这可能是bug 12608的另一种表现形式,已在Mono 3.1.1中修复(根据其发布说明)。不过我这里没有安装Mono来进行检查。 - user743382
@hvd _嗯_,是的,这可能与这个错误有关。我也没有Mono,所以我无法检查它。添加了我的答案。谢谢。 - Soner Gönül
@Barmar 现在怎么样? - Soner Gönül

5
当使用你提供的 在线编译网站 时,mono编译器会对你的代码进行重写和优化。
using System.IO;
using System;

class Program
{
    static void Main()
    {
        int? x=null;
        Console.WriteLine(x.Value==null);  //-> Console.WriteLine(false);    
    }
}

编译源代码中.... $mcs main.cs -out:demo.exe 2>&1 main.cs(9,38): warning CS0472: 比较值类型'int'与null的结果为false' 编译成功-有1个警告 执行程序.... $mono demo.exe False 警告CS0472告诉您,这是他们正在使用的在线/mono编译器中的错误。

1
-1 是因为警告没有告诉你 x.Value 不会被评估,警告只是告诉你与 null 的比较永远不会返回 true - user743382
如果代码没有被编译器重写,它将在运行时失败并抛出异常,但实际上它并没有在运行时失败。使用的Mono编译器存在一个bug,而Microsoft编译器会给出不同的结果。 - Peter
1
警告显示代码检索了 x.Value,但将其丢弃,并打印了 false。实际行为是在不检索 x.Value 的情况下打印了 false。因此,不,该警告仍然没有告诉你这一点。 - user743382
@peer:感谢您指出由mono编译器完成的重写事实。编译器似乎存在缺陷,未在对其进行优化重写之前检查代码可能出现的异常情况。 - user1510194

0

你使用的编译器错误地优化掉了对属性 get 访问器的调用。使用该编译器,此代码可以重现此错误:

using System;

static class Program
{
    static void Main()
    {
        Console.WriteLine(Prop == null);
    }

    static int Prop
    { 
        get
        {
            throw new NotImplementedException("This error must be!"); 
        } 
    }
}

Visual C# 编译器没有这个 bug。但在某些情况下,它会有糟糕的警告文本。尝试将我的代码中的 int 更改为 DateTime,并将 == 更改为 >,看看来自 Visual C# 编译器的警告文本。


网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接