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[…]) 

其中e0e相同, 除非e是可空值types,在这种情况下e0e.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?.Valueexpression式的types是int? ,而不是int 。 所以你需要改变valuevariables的types为int?

2014年2月3日的“ C#语言devise手册”也对此进行了正式的介绍:

结果的types取决于底层运算符右侧的typesT

  • 如果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.ValueCannot resolve symbol 'Value'语法错误!

int没有Value属性。

考虑:

 var value = obj?.Property 

相当于:

 value = obj == null ? null : obj.Property; 

这对int来说是没有意义的,因此不是int? 通过?.

旧的GetValueOrDefault()虽然有意义与int?

或者就此而言,因为? 必须返回可空的,只是:

 int? value = nullableInt;