null条件运算符不能使用可为空的types吗?
我在c#6中写了一段代码,出于一些奇怪的原因
var value = objectThatMayBeNull?.property;
但是这不是:
int value = nullableInt?.Value;
通过不工作,我的意思是我得到一个编译错误,说Cannot resolve symbol 'Value'
。 任何想法为什么空条件运算符?.
不工作?
好吧,我已经做了一些思考和testing。 这是发生了什么事情:
int value = nullableInt?.Value;
编译时出现此错误消息:
types“int”不包含“Value”的定义
那意味着?
'转换' int?
进入实际的int
值。 这实际上是一样的:
int value = nullableInt ?? default(int);
结果是一个整数,显然没有一个Value
。
好的,这可能有帮助吗?
int value = nullableInt?;
不,这个语法是不允许的。
那么呢? 只要继续使用.GetValueOrDefault()
这种情况。
int value = nullableInt.GetValueOrDefault();
原因是用一个空的条件运算符访问值是没有意义的:
- 当你应用
x?.p
p
,其中p
是一个不可为空值的typesT
,结果是T?
。 同样,nullableInt?.Value
操作的结果必须是可空的。 - 当你的
Nullable<T>
有一个值的时候,nullableInt?.Value
的结果和它本身的值是一样的 - 当你的
Nullable<T>
没有值的时候,结果会是null
,也就是这个值本身。
尽pipe用“ ?.
来访问Value
是没有意义的?.
运算符,它是有道理的访问可空值types的其他属性。 运算符一致地使用可为空的值types和引用types,所以这两个实现产生相同的行为:
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;
在可空struct
的情况下,运算符允许您访问基础typesPointStruct
的属性,并且以与引用typesPointClass
不可为null属性相同的方式向结果添加可空性。
关于可空types, ?.
运算符说if not null, use the wrapped value
。 因此,对于一个可空的int,如果可为空的值为8
,那么?.
的结果?.
将是8
,而不是包含8
的可为空。 由于Value
不是int
的属性,所以会出错。
所以,试图使用财产Value
的例子是正确的失败,但下面的工作,
var x = nullableInt?.ToString();
考虑空合并运算符??
。
var x = nullableInt ?? 0;
在这里,运算符说, if null, return 0, otherwise return the value inside the nullable
,在这种情况下是int
。 那个?.
运算符在提取可为空的内容方面也是类似的。
对于你的具体例子,你应该使用??
运算符和适当的默认值而不是?.
运营商。
我基本上同意其他答案。 我只是希望观察到的行为可以通过某种forms的权威文件来支持。
由于我无法在任何地方findC#6.0规范(是否已经出来?),最接近我发现的“文档”是2014年2月3日的C#语言devise说明 。 假设在那里发现的信息仍然反映了当前的情况,下面是正式解释观察到的行为的相关部分。
语义就像应用三元运算符到一个空的相等性检查,一个空文本和一个没有问题标记的运算符的应用程序,除了expression式只被计算一次:
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
是可空值types,在这种情况下e0
是e.Value
。
将最后一条规则应用于:
nullableInt?.Value
…语义等价的expression式变成:
((nullableInt == null) ? null : nullableInt.Value.Value)
显然, nullableInt.Value.Value
无法编译,这就是你所观察到的。
至于为什么devise决定将这个特殊规则应用于可空types,我认为dasblinkenlight
的答案很好地覆盖了这个,所以在这里我不再重复。
此外,我应该提到,即使假设我们没有这个可为空的types的特殊规则,并且expression式nullableInt?.Value
也按照您原先的想法进行了编译和行为。
// let's pretend that it actually gets converted to this... ((nullableInt == null) ? null : nullableInt.Value)
仍然,从您的问题下面的声明将是无效的,并产生一个编译错误:
int value = nullableInt?.Value; // still would not compile
之所以仍然不行,是因为nullableInt?.Value
expression式的types是int?
,而不是int
。 所以你需要改变value
variables的types为int?
。
2014年2月3日的“ C#语言devise手册”也对此进行了正式的介绍:
结果的types取决于底层运算符右侧的types
T
:
- 如果
T
(已知是)引用types,则expression式的types是T
- 如果
T
(已知是)不可空值types,则expression式的types是T?
- 如果
T
(已知)是可以为空的值types,则expression式的types为T
- 否则(即如果不知道
T
是一个引用还是值types)expression式是编译时错误。
但是,如果你将被迫编写以下内容来编译它:
int? value = nullableInt?.Value;
那么这似乎是毫无意义的,和简单的做法没有什么两样:
int? value = nullableInt;
正如其他人指出的,在你的情况下,你可能打算使用空合并运算符??
一直以来,不是空条件运算符?.
。
null条件运算符还打开可空的variables。 所以在“?”之后。 运营商,“价值”属性不再需要。
我写了一篇关于如何处理这个问题的文章。 如果你想知道
http://www.ninjacrab.com/2016/09/11/c-how-the-null-conditional-operator-works-with-nullable-types/
只是因为(基于上面的斯坦答案)
var value = objectThatMayBeNull?.property;
由编译器来评估
var value = (objectThatMayBeNull == null) ? null : objectThatMayBeNull.property
和
int value = nullableInt?.Value;
喜欢
int value = (nullableInt == null) ? null : nullableInt.Value.Value;
当nullableInt.Value.Value
是Cannot resolve symbol 'Value'
语法错误!
int
没有Value
属性。
考虑:
var value = obj?.Property
相当于:
value = obj == null ? null : obj.Property;
这对int
来说是没有意义的,因此不是int?
通过?.
旧的GetValueOrDefault()
虽然有意义与int?
。
或者就此而言,因为?
必须返回可空的,只是:
int? value = nullableInt;