空条件运算符不能与可空类型一起使用?


68

我正在用C#6编写一段代码,由于某种奇怪的原因,它可以工作

var value = objectThatMayBeNull?.property;

但这不是:

int value = nullableInt?.Value;

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


您是否尝试过:int value =(nullableInt?)。Value; ???
lem2802 2015年

6
??在这种情况下,您应该使用运算符。就像在nullableInt ?? 0
杰夫·耶茨

Answers:


37

好的,我已经做了一些思考和测试。这是发生了什么:

int value = nullableInt?.Value;

编译时给出此错误消息:

类型“ int”不包含“值”的定义

这意味着将?“转换”int?为实际int值。这实际上与以下内容相同:

int value = nullableInt ?? default(int);

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

好的,这可以帮助吗?

int value = nullableInt?;

不,该语法是不允许的。

那又如何呢?只是继续使用.GetValueOrDefault()这种情况。

int value = nullableInt.GetValueOrDefault();

16
好吧,我本以为这将是该运算符的主要用例之一,尤其是因为它只是句法糖。
2015年

19

这样做的原因是,使用空条件运算符访问该值将毫无意义:

  • 当应用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


8
这里不是这样。这是因为,与null-coalescing运算符一样,它?.解析为包装的int,而不是可为空的类型本身。
杰夫·耶茨

1
既然您指出了这一点,我就意识到我的逻辑多么和谐。对于科学来说不是好日子。
linkerro

1
它们之所以不会编译,是因为?.nullable为非null时的结果是int其中没有Value或没有HasValue属性的实际值。如我的回答,请尝试nullableInt?.ToString()
杰夫·耶茨

5
@dasblinkenlight DateTime? dt=DateTime.Now; int? day=dt?.Day;。之后?.应该来的财产DateTime不是财产DateTime?
user4003407

“因为?。可能返回null”>错误,它返回一个int。因此,的最终结果someInt?.XXX将与相同someInt.GetValueOrDefault().XXX
Patrick Hofman 2015年

10

关于可空类型,?.操作员说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将不会编译,因为nullableInt0是不同的类型:Nullable<int>int。你必须使用nullableInt.GetValueOrDefault(0); 或者nullableInt.GetValueOrDefault(),因为default(int)0
Suncat2000

1
@ Suncat2000确实是不正确的。 dotnetfiddle.net/NDPJ44
杰夫·耶茨

6

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

由于我在任何地方都找不到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是一个空值类型,在这种情况下e0e.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?

2014年2月3日C#语言设计说明中也正式涵盖了这一点:

结果的类型取决于T基础运算符的右侧类型:

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

但是,如果您随后被迫编写以下代码进行编译:

int? value = nullableInt?.Value;

...那么这似乎毫无意义,这与简单地做没有什么不同:

int? value = nullableInt;

正如其他人指出的那样,在您的情况下,您可能一直打算使用null运算符??,而不是空条件运算符?.


啊! 知道了!我实际上找到了这个线程,因为当我得到一个Obj Refer来做“ long?nullableLongVariable = objectVariable.NullableLongProperty.Value;”时,我脑子放屁了,将其更改为“ long?nullableLongVariable = objectVariable.NullableLongProperty?.Value” ;”,当我刚刚将其设置为“ long?nullableLongVariable = objectVariable.NullableLongProperty;”时。
汤姆(Tom)

1

仅仅是因为(基于上述斯坦的回答)

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


0

int没有Value财产。

考虑:

var value = obj?.Property

等效于:

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

那对via没有意义,int因此对int?via也没有意义?.

GetValueOrDefault()做用,虽然有意义int?

或就此而言,由于?必须返回可为空的内容,因此只需:

int? value = nullableInt;

1
这不是整数吗?
lem2802 2015年

By using our site, you acknowledge that you have read and understand our Cookie Policy and Privacy Policy.
Licensed under cc by-sa 3.0 with attribution required.