null条件运算符不使用可空类型?

lin*_*rro 62 c# null-coalescing-operator c#-6.0

我在c#6中编写了一段代码,并且出于一些奇怪的原因,这是有效的

var value = objectThatMayBeNull?.property;
Run Code Online (Sandbox Code Playgroud)

但这不是:

int value = nullableInt?.Value;
Run Code Online (Sandbox Code Playgroud)

不工作我的意思是我得到一个编译错误说Cannot resolve symbol 'Value'.知道为什么null条件运算符?.不起作用吗?

Pat*_*man 34

好的,我做了一些思考和测试.这是发生的事情:

int value = nullableInt?.Value;
Run Code Online (Sandbox Code Playgroud)

编译时给出此错误消息:

类型'int'不包含`Value'的定义

这意味着?'将' int?转换为实际的int值.这实际上与:

int value = nullableInt ?? default(int);
Run Code Online (Sandbox Code Playgroud)

结果是一个整数,没有 Value,显然没有.

好的,这可能有帮助吗?

int value = nullableInt?;
Run Code Online (Sandbox Code Playgroud)

不,不允许使用该语法.

那么呢?只是继续使用.GetValueOrDefault()这种情况.

int value = nullableInt.GetValueOrDefault();
Run Code Online (Sandbox Code Playgroud)

  • 好吧,我原以为这可能是这个算子的主要用例之一,特别是因为它只是辛辣糖. (13认同)

das*_*ght 19

这样做的原因是使用null条件运算符访问该值是没有意义的:

  • 当您应用x?.pwhere p不可为空的值类型时T,结果是类型T?.出于同样的原因,nullableInt?.Value操作的结果必须是可空的.
  • 当你Nullable<T>有一个值时,结果nullableInt?.Value将与值本身相同
  • 当您Nullable<T>没有值时,结果将是null,也就是值本身.

尽管Value使用?.运算符进行访问没有意义,但访问可空值类型的其他属性确实有意义.运算符与可空值类型和引用类型一致,因此这两个实现产生相同的行为:

class PointClass {
    public int X { get; }
    public int Y { get; }
    public PointClass(int x, int y) { X = x; Y = y; }
}
struct PointStruct {
    public int X { get; }
    public int Y { get; }
    public PointStruct(int x, int y) { X = x; Y = y; }
}
...
PointClass pc = ...
PointStruct? ps = ...
int? x = pc?.X;
int? y = ps?.Y;
Run Code Online (Sandbox Code Playgroud)

在可为空struct的情况下,运算符允许您访问基础类型的属性PointStruct,并且它以与对引用类型的非可空属性相同的方式添加结果的可空性PointClass.

  • 情况并非如此.这是因为,正如null-coalescing运算符一样,`?.`解析为包装的`int`,而不是可空类型本身. (7认同)
  • @dasblinkenlight`DateTime?DT = DateTime.Now; 诠释?天= DT .DAY;`.在`?.`之后应该是`DateTime`的属性而不是`DateTime?`的属性. (4认同)

Jef*_*tes 8

关于可空类型,?.运营商说if not null, use the wrapped value.所以对于一个可以为null的int,如果nullable有值8,那么结果?.将是8,而不是包含的nullable 8.由于Value不是a的属性int,因此会出错.

因此,尝试使用属性的例子Value非常正确,但以下情况可行,

var x = nullableInt?.ToString();

考虑null-coalescing运算符??.

var x = nullableInt ?? 0;

在这里,操作员说,if null, return 0, otherwise return the value inside the nullable在这种情况下是一个int.该?.关于提取可空的内容,操作员执行类似操作.

对于您的具体示例,您应该使用??运算符和适当的默认值而不是?.运算符.


sst*_*tan 5

我基本同意其他答案.我只是希望观察到的行为可以通过某种形式的权威文档来支持.

由于我无法在任何地方找到C#6.0规范(它出来了吗?),我发现的最接近"文档"的是2014年2月3日C#语言设计说明.假设在那里找到的信息仍然反映了当前的事态,这里是正式解释观察到的行为的相关部分.

语义就像将三元运算符应用于空等式检查,空文字和运算符的非问题标记应用程序,除了表达式仅计算一次:

e?.m(…)   =>   ((e == null) ? null : e0.m(…))
e?.x      =>   ((e == null) ? null : e0.x)
e?.$x     =>   ((e == null) ? null : e0.$x)
e?[…]     =>   ((e == null) ? null : e0[…])
Run Code Online (Sandbox Code Playgroud)

其中e0是相同的e,如果除了e是一个空值类型,在这种情况下e0e.Value.

将最后一条规则应用于:

nullableInt?.Value
Run Code Online (Sandbox Code Playgroud)

...语义上等效的表达式变为:

((nullableInt == null) ? null : nullableInt.Value.Value)
Run Code Online (Sandbox Code Playgroud)

显然,nullableInt.Value.Value无法编译,这就是你所观察到的.

至于为什么设计决定将特殊规则专门应用于可空类型,我认为dasblinkenlight答案很好地涵盖了,所以我在此不再重复.


另外,我应该提一下,即使假设我们没有针对可空类型的这个特殊规则,并且表达式nullableInt?.Value确实按照您最初的想法编译和运行...

// let's pretend that it actually gets converted to this...
((nullableInt == null) ? null : nullableInt.Value)
Run Code Online (Sandbox Code Playgroud)

仍然,您的问题中的以下声明将无效并产生编译错误:

int value = nullableInt?.Value; // still would not compile
Run Code Online (Sandbox Code Playgroud)

它仍然不起作用的原因是因为nullableInt?.Value表达式的类型int?不是int.因此,您需要将value变量的类型更改为int?.

这也在2014年2月3日C#语言设计说明中正式涵盖:

结果的类型取决于T底层运算符右侧的类型:

  • 如果T(已知是)引用类型,则表达式的类型为T
  • 如果T(已知是)非可空值类型,则表达式的类型为T?
  • 如果T(已知是)可空值类型,则表达式的类型为T
  • 否则(即,如果不知道T是引用还是值类型)表达式是编译时错误.

但是,如果您将被迫编写以下内容以使其编译:

int? value = nullableInt?.Value;
Run Code Online (Sandbox Code Playgroud)

......然后它似乎毫无意义,与简单地做完就没有什么不同:

int? value = nullableInt;
Run Code Online (Sandbox Code Playgroud)

正如其他人所指出的,在您的情况下,您可能一直在使用null-coalescing运算符??,而不是null-conditional运算符?..