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)
das*_*ght 19
这样做的原因是使用null条件运算符访问该值是没有意义的:
x?.p
where 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
.
关于可空类型,?.
运营商说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
.该?.
关于提取可空的内容,操作员执行类似操作.
对于您的具体示例,您应该使用??
运算符和适当的默认值而不是?.
运算符.
我基本同意其他答案.我只是希望观察到的行为可以通过某种形式的权威文档来支持.
由于我无法在任何地方找到C#6.0规范(它出来了吗?),我发现的最接近"文档"的是2014年2月3日的C#语言设计说明.假设在那里找到的信息仍然反映了当前的事态,这里是正式解释观察到的行为的相关部分.
语义就像将三元运算符应用于空等式检查,空文字和运算符的非问题标记应用程序,除了表达式仅计算一次:
Run Code Online (Sandbox Code Playgroud)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[…])
其中
e0
是相同的e
,如果除了e
是一个空值类型,在这种情况下e0
是e.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?
.
结果的类型取决于
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运算符?.
.