null条件运算符不使用可空类型?

时间:2015-08-04 13:59:52

标签: c# null-coalescing-operator c#-6.0

我在c#6中编写了一段代码,并且由于一些奇怪的原因,这可以使用

var value = objectThatMayBeNull?.property;

但这不是:

int value = nullableInt?.Value;

如果不行,我的意思是我收到编译错误Cannot resolve symbol 'Value'。 知道为什么空条件运算符?.不起作用吗?

7 个答案:

答案 0 :(得分:28)

好的,我做了一些思考和测试。这就是:

int value = nullableInt?.Value;

编译时给出此错误消息:

  

Type'int'不包含“Value”

的定义

这意味着?'将int?转换为实际的int值。这实际上与:

相同
int value = nullableInt ?? default(int);

结果是一个整数,显然没有Value

好的,这可能有帮助吗?

int value = nullableInt?;

不,不允许使用该语法。

那么呢?在这种情况下,请继续使用.GetValueOrDefault()

int value = nullableInt.GetValueOrDefault();

答案 1 :(得分:18)

原因是使用null条件运算符访问该值是没有意义的:

  • 当您应用x?.p 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;

如果是可空的struct,则运算符允许您访问基础类型PointStruct的属性,并且它会以与对于非可空属性相同的方式添加结果的可空性。引用类型PointClass

答案 2 :(得分:7)

关于可空类型,?.运算符正在说if not null, use the wrapped value。因此,对于可空的int,如果可空值为8,则?.的结果将为8,而不是包含8的可空。由于Value不是int的属性,因此会出错。

因此,尝试使用属性Value的示例完全失败,但以下方法可行,

var x = nullableInt?.ToString();

考虑null-coalescing运算符??

var x = nullableInt ?? 0;

此处,运营商说if null, return 0, otherwise return the value inside the nullable,在这种情况下是int?.运算符在提取可空内容方面的表现类似。

对于您的具体示例,您应该使用??运算符和适当的默认值而不是?.运算符。

答案 3 :(得分:5)

我基本同意其他答案。我只是希望观察到的行为可以通过某种形式的权威文档来支持。

由于我无法在任何地方找到C#6.0规范(它出来了吗?),我找到的最接近“文档”的是C# Language Design Notes for Feb 3, 2014。假设那里发现的信息仍然反映了当前的事态,这里是正式解释观察到的行为的相关部分。

  

语义就像将三元运算符应用于空等式检查,空文字和运算符的无问题标记的应用程序,除了表达式只计算一次:

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属于可以为空的值类型,在这种情况下e0为{{ 1}}。

将最后一条规则应用于:

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?。因此,您需要将int变量的类型更改为value

C# Language Design Notes for Feb 3, 2014

也正式涵盖了这一点
  

结果的类型取决于底层运算符右侧的类型int?

     
      
  • 如果T(已知是)引用类型,则表达式的类型为T
  •   
  • 如果T(已知是)非可空值类型,则表达式的类型为T
  •   
  • 如果T?(已知是)可以为空的值类型,则表达式的类型为T
  •   
  • 否则(即,如果不知道T是引用还是值类型),则表达式是编译时错误。
  •   

但是如果你被迫编写以下内容以使其编译:

T

...然后它似乎毫无意义,与简单的做法没有任何不同:

int? value = nullableInt?.Value;

正如其他人指出的那样,在您的情况下,您可能一直使用null-coalescing operator ??,而不是null-conditional operator ?.

答案 4 :(得分:0)

int没有Value属性。

考虑:

var value = obj?.Property

相当于:

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

对于int没有任何意义,因此int?没有通过?.

旧的GetValueOrDefault()确实对int?有意义。

或者就此而言,因为?必须返回可以为空的东西,只需:

int? value = nullableInt;

答案 5 :(得分:0)

null条件运算符也会打开可以为null的变量。所以在“?”之后。运算符,不再需要“值”属性。

我写了一篇文章,详细介绍了我是如何遇到这个问题的。万一你想知道

http://www.ninjacrab.com/2016/09/11/c-how-the-null-conditional-operator-works-with-nullable-types/

答案 6 :(得分:0)

仅仅因为(基于以上的答案)

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'出现语法错误时!