我正在用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?.pwherep是不可为空的值类型时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/