null 条件运算符不适用于可空类型?
null conditional operator not working with nullable types?
我正在用 c#6 编写一段代码,出于某种奇怪的原因,它可以工作
var value = objectThatMayBeNull?.property;
但这不是:
int value = nullableInt?.Value;
我说的不工作是指我收到一个编译错误 Cannot resolve symbol 'Value'
。
知道为什么 null 条件运算符 ?.
不起作用吗?
int
没有 Value
属性.
考虑:
var value = obj?.Property
相当于:
value = obj == null ? null : obj.Property;
这对 int
没有意义,因此对 int?
没有意义,通过 ?.
虽然旧 GetValueOrDefault()
对 int?
有意义。
或者就此而言,因为 ?
必须 return 一些可以为 null 的东西,简单地说:
int? value = nullableInt;
好的,我做了一些思考和测试。这是发生了什么:
int value = nullableInt?.Value;
编译时给出这个错误信息:
Type 'int' does not contain a definition for `Value'
表示?
'converts'将int?
转化为实际的int
值。这实际上等同于:
int value = nullableInt ?? default(int);
结果是一个整数,显然没有 Value
。
好的,这有帮助吗?
int value = nullableInt?;
不,不允许使用该语法。
然后呢?对于这种情况,请继续使用 .GetValueOrDefault()
。
int value = nullableInt.GetValueOrDefault();
关于可空类型,?.
运算符表示 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
。 ?.
运算符在提取可为 null 的内容方面执行类似。
对于您的具体示例,您应该使用 ??
运算符和适当的默认值而不是 ?.
运算符。
原因是使用 null 条件运算符访问值是没有意义的:
- 当您应用
x?.p
时,其中 p
是不可为 null 的值类型 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
的 属性,并以与它相同的方式为结果添加可空性对于引用类型的不可为 null 的属性 PointClass
.
我基本同意其他答案。我只是希望观察到的行为可以得到某种形式的权威文档的支持。
由于我在任何地方都找不到 C# 6.0 规范(已经出来了吗?),我发现最接近 "documentation" 的是 C# Language Design Notes for Feb 3, 2014。假设在那里找到的信息仍然反映了当前的事态,这里是正式解释观察到的行为的相关部分。
The semantics are like applying the ternary operator to a null equality check, a null literal and a non-question-marked application of the operator, except that the expression is evaluated only once:
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[…])
Where e0
is the same as e
, except if e
is of a nullable value type, in which case e0
is 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
变量的类型更改为 int?
.
C# Language Design Notes for Feb 3, 2014:
The type of the result depends on the type T
of the right hand side of the underlying operator:
- If
T
is (known to be) a reference type, the type of the expression is T
- If
T
is (known to be) a non-nullable value type, the type of the expression is T?
- If
T
is (known to be) a nullable value type, the type of the expression is T
- Otherwise (i.e. if it is not known whether
T
is a reference or value type) the expression is a compile time error.
但是,如果您随后被迫编写以下内容以使其编译:
int? value = nullableInt?.Value;
...那么这似乎毫无意义,与简单地做没有什么不同:
int? value = nullableInt;
正如其他人所指出的,在您的情况下,您可能打算使用 null-coalescing operator ??
all along, not the null-conditional operator ?.
。
null 条件运算符还会解包可为 null 的变量。所以在“?”之后运算符,不再需要“值”属性。
我写了一篇 post,其中更详细地说明了我是如何遇到这个问题的。如果您想知道
http://www.ninjacrab.com/2016/09/11/c-how-the-null-conditional-operator-works-with-nullable-types/
仅仅是因为(根据上面sstan的回答)
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'
语法错误!
我正在用 c#6 编写一段代码,出于某种奇怪的原因,它可以工作
var value = objectThatMayBeNull?.property;
但这不是:
int value = nullableInt?.Value;
我说的不工作是指我收到一个编译错误 Cannot resolve symbol 'Value'
。
知道为什么 null 条件运算符 ?.
不起作用吗?
int
没有 Value
属性.
考虑:
var value = obj?.Property
相当于:
value = obj == null ? null : obj.Property;
这对 int
没有意义,因此对 int?
没有意义,通过 ?.
虽然旧 GetValueOrDefault()
对 int?
有意义。
或者就此而言,因为 ?
必须 return 一些可以为 null 的东西,简单地说:
int? value = nullableInt;
好的,我做了一些思考和测试。这是发生了什么:
int value = nullableInt?.Value;
编译时给出这个错误信息:
Type 'int' does not contain a definition for `Value'
表示?
'converts'将int?
转化为实际的int
值。这实际上等同于:
int value = nullableInt ?? default(int);
结果是一个整数,显然没有 Value
。
好的,这有帮助吗?
int value = nullableInt?;
不,不允许使用该语法。
然后呢?对于这种情况,请继续使用 .GetValueOrDefault()
。
int value = nullableInt.GetValueOrDefault();
关于可空类型,?.
运算符表示 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
。 ?.
运算符在提取可为 null 的内容方面执行类似。
对于您的具体示例,您应该使用 ??
运算符和适当的默认值而不是 ?.
运算符。
原因是使用 null 条件运算符访问值是没有意义的:
- 当您应用
x?.p
时,其中p
是不可为 null 的值类型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
的 属性,并以与它相同的方式为结果添加可空性对于引用类型的不可为 null 的属性 PointClass
.
我基本同意其他答案。我只是希望观察到的行为可以得到某种形式的权威文档的支持。
由于我在任何地方都找不到 C# 6.0 规范(已经出来了吗?),我发现最接近 "documentation" 的是 C# Language Design Notes for Feb 3, 2014。假设在那里找到的信息仍然反映了当前的事态,这里是正式解释观察到的行为的相关部分。
The semantics are like applying the ternary operator to a null equality check, a null literal and a non-question-marked application of the operator, except that the expression is evaluated only once:
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[…])
Where
e0
is the same ase
, except ife
is of a nullable value type, in which casee0
ise.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
变量的类型更改为 int?
.
C# Language Design Notes for Feb 3, 2014:
The type of the result depends on the type
T
of the right hand side of the underlying operator:
- If
T
is (known to be) a reference type, the type of the expression isT
- If
T
is (known to be) a non-nullable value type, the type of the expression isT?
- If
T
is (known to be) a nullable value type, the type of the expression isT
- Otherwise (i.e. if it is not known whether
T
is a reference or value type) the expression is a compile time error.
但是,如果您随后被迫编写以下内容以使其编译:
int? value = nullableInt?.Value;
...那么这似乎毫无意义,与简单地做没有什么不同:
int? value = nullableInt;
正如其他人所指出的,在您的情况下,您可能打算使用 null-coalescing operator ??
all along, not the null-conditional operator ?.
。
null 条件运算符还会解包可为 null 的变量。所以在“?”之后运算符,不再需要“值”属性。
我写了一篇 post,其中更详细地说明了我是如何遇到这个问题的。如果您想知道
http://www.ninjacrab.com/2016/09/11/c-how-the-null-conditional-operator-works-with-nullable-types/
仅仅是因为(根据上面sstan的回答)
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'
语法错误!