我正在用C#6编写一段代码,由于某种奇怪的原因,它可以工作
var value = objectThatMayBeNull?.property;
但这不是:
int value = nullableInt?.Value;
不起作用是我收到编译错误的说法Cannot resolve symbol 'Value'
。知道为什么空条件运算符?.
不起作用吗?
Answers:
好的,我已经做了一些思考和测试。这是发生了什么:
int value = nullableInt?.Value;
编译时给出此错误消息:
类型“ int”不包含“值”的定义
这意味着将?
“转换”int?
为实际int
值。这实际上与以下内容相同:
int value = nullableInt ?? default(int);
结果是一个整数Value
,显然没有。
好的,这可以帮助吗?
int value = nullableInt?;
不,该语法是不允许的。
那又如何呢?只是继续使用.GetValueOrDefault()
这种情况。
int value = nullableInt.GetValueOrDefault();
这样做的原因是,使用空条件运算符访问该值将毫无意义:
x?.p
wherep
是不可为空的值类型时T
,结果为type 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;
在可为nullstruct
的情况下,运算符允许您访问基础类型的属性PointStruct
,并且以与引用类型的不可为null的属性相同的方式向结果中添加可为空性PointClass
。
?.
解析为包装的int
,而不是可为空的类型本身。
?.
nullable为非null时的结果是int
其中没有Value
或没有HasValue
属性的实际值。如我的回答,请尝试nullableInt?.ToString()
。
DateTime? dt=DateTime.Now; int? day=dt?.Day;
。之后?.
应该来的财产DateTime
不是财产DateTime?
。
int
。因此,的最终结果someInt?.XXX
将与相同someInt.GetValueOrDefault().XXX
。
关于可空类型,?.
操作员说if not null, use the wrapped value
。因此,对于可为null的int而言,如果nullable具有值8
,则其结果?.
将为8
,而不是包含的nullable 8
。由于Value
不是的属性int
,因此会出现错误。
因此,尝试Value
正确使用属性的示例失败了,但是以下方法可以工作,
var x = nullableInt?.ToString();
考虑空合并运算符??
。
var x = nullableInt ?? 0;
在这里,操作员说,if null, return 0, otherwise return the value inside the nullable
在这种情况下为int
。?.
操作员在提取可为空的内容方面的操作类似。
对于您的特定示例,您应该使用??
运算符和适当的默认值而不是?.
运算符。
var x = nullableInt ?? 0
将不会编译,因为nullableInt
和0
是不同的类型:Nullable<int>
和int
。你必须使用nullableInt.GetValueOrDefault(0)
; 或者nullableInt.GetValueOrDefault()
,因为default(int)
是0
。
我基本上同意其他答案。我只是希望可以通过某种形式的权威文档来支持观察到的行为。
由于我在任何地方都找不到C#6.0规范(是否存在?),因此我发现最接近“文档”的是2014年2月3日发布的C#语言设计说明。假设在此找到的信息仍反映当前的事务状态,以下是正式解释观察到的行为的相关部分。
语义就像将三元运算符应用于null相等性检查,null文字和该运算符的非问题标记应用程序,不同之处在于该表达式仅被评估一次:
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
...语义上等价的表达式变为:
((nullableInt == null) ? null : nullableInt.Value.Value)
显然,nullableInt.Value.Value
无法编译,这就是您所观察到的。
至于为什么要做出将特殊规则专门应用于可空类型的设计决定,我认为dasblinkenlight
的答案很好地涵盖了这一点,因此在此不再赘述。
此外,我应该提到的是,即使假设我们没有针对可空类型的特殊规则,并且该表达式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
表达式的类型为int?
not int
。因此,您需要将value
变量的类型更改为int?
。
结果的类型取决于
T
基础运算符的右侧类型:
- 如果
T
是(已知为)引用类型,则表达式的类型为T
- 如果
T
是(已知为)非空值类型,则表达式的类型为T?
- 如果
T
是(已知为)可为空的值类型,则表达式的类型为T
- 否则(即,如果不知道
T
是引用类型还是值类型),则表达式为编译时错误。
但是,如果您随后被迫编写以下代码进行编译:
int? value = nullableInt?.Value;
...那么这似乎毫无意义,这与简单地做没有什么不同:
int? value = nullableInt;
仅仅是因为(基于上述斯坦的回答)
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;
那对via没有意义,int
因此对int?
via也没有意义?.
老GetValueOrDefault()
做用,虽然有意义int?
。
或就此而言,由于?
必须返回可为空的内容,因此只需:
int? value = nullableInt;
空条件运算符还会解开可空变量。所以在“?”之后。运算符,则不再需要“值”属性。
我写了一篇文章,详细介绍了我是如何遇到的。如果您想知道
http://www.ninjacrab.com/2016/09/11/c-how-the-null-conditional-operator-works-with-nullable-types/