为什么 C# 在表达式中使用可空值时需要括号?

Why does C# require parentheses when using nullables in an expression?

我是 C# 的新手,在探索语言特性时,我遇到了一些奇怪的事情:

struct Foo
{
    public Foo Identity() { return this; }

    public static void Bar(Foo? foo)
    {
        Foo foo1 = foo?.Identity().Value; // Does not compile
        Foo foo2 = (foo?.Identity()).Value; // Compiles
    }
}

谁能给我解释一下为什么需要括号?

Could anyone explain to me why the parenthesis are needed?

因为Identity()returns有Foo(不是Foo?)所以没有Value属性。如果 foo 为 null,则 null 将通过 Identity 调用传播。

当你用括号括起来时,表达式的结果是一个Nullable<Foo>确实有一个Value 属性.

另请注意,如果 foo 空值,那么您将在没有值的 Nullable<Foo> 上调用 Value,并且将在 运行-time 得到一个异常 。一些静态分析器会识别出您可能有空引用异常等待发生并警告您。

如果你将它们扩展为它们的等价物而不进行空值传播,它会更清楚:

Foo foo1;
if(foo != null)
{
    foo1 = foo.Identity().Value;  // not possible - Foo has no Value property.
}
else
{
    foo1 = null;  // also not possible 
}

Foo foo2;
Foo? temp;
if(foo != null)
{
    temp = foo.Identity();
}
else
{
   temp = null;  // actually a Nullable<Foo> with no value
}
foo2 = temp.Value;  // legal, but will throw an exception at run-time if foo is null

If Identity() returns Foo, why does Foo foo3 = foo?.Identity(); not compile ?

相当于:

Foo foo3
if(foo != null)
{
    foo3 = foo.Identity();
}
else
{
    foo3 = null;  // not possible 
}

我认为 c# 团队这样做是一个很好的决定。考虑以下情况:

如果结构是:

struct Foo
{
    public int ID { set; get; }

    public Foo Identity() { return this; }

    public static void Bar(Foo? foo)
    {
        int? foo1 = foo?.Identity().ID; // compile
        Foo foo2 = (foo?.Identity()).Value; // Compiles
    }
}

如果您不需要括号来访问 Nullable 结果,您将无法访问 ID 属性。由于以下将无法编译:

int? foo2 = (foo?.Identity()).GetValueOrDefault()?.ID

当你写 foo?.Identity(). 时,. 之后的内容是 Identity() 返回的 Foo 类型。但是在 (foo?.Identity()).. 之后的是 Foo?,这是整个语句 foo?.Identity().

的实际结果