C# 为什么我会得到这些奇怪的C“8”;“可为空的引用类型”;警告:在退出构造函数之前,非null字段必须包含非null值。?
我正在尝试处理C#8新的可空编译器检查。下面的结构给了我一些奇怪的警告C# 为什么我会得到这些奇怪的C“8”;“可为空的引用类型”;警告:在退出构造函数之前,非null字段必须包含非null值。?,c#,.net,c#-8.0,C#,.net,C# 8.0,我正在尝试处理C#8新的可空编译器检查。下面的结构给了我一些奇怪的警告 // This struct gives no warnings public struct Thing<T> where T : notnull { private readonly bool _hasValue; private readonly T _value; Thing(T value) { _value = value; _h
// This struct gives no warnings
public struct Thing<T> where T : notnull
{
private readonly bool _hasValue;
private readonly T _value;
Thing(T value)
{
_value = value;
_hasValue = value is { };
}
}
// This struct gives warning on the constructor:
// "Non-nullable field "_value" must contain a non-null value before exiting constructor.
// Consider declaring the field as nullable.
public struct Thing<T> where T : notnull
{
private readonly bool _hasValue;
private readonly T _value;
Thing(T value)
~~~~~
{
_value = value;
_hasValue = _value is { };
}
}
// This struct gives two warnings, one on the constructor and one on `_value = value`
// [1] "Non-nullable field "_value" must contain a non-null value before exiting constructor.
// Consider declaring the field as nullable.
// [2] Possible null reference assignment.
// This is true even if I check value for null and throw an ArgumentNullException before the assignment.
public struct Thing<T> where T : notnull
{
private readonly bool _hasValue;
private readonly T _value;
Thing(T value)
~~~~~ // [1]
{
_hasValue = value is { };
_value = value;
~~~~~ // [2]
}
}
//此结构不提供任何警告
公共结构对象,其中T:notnull
{
私有只读布尔值;
私有只读T_值;
事物(T值)
{
_价值=价值;
_hasValue=值为{};
}
}
//此结构对构造函数发出警告:
//退出构造函数之前,“非空字段”\u value“必须包含非空值。
(或)考虑将该字段声明为可空的。
公共结构对象,其中T:notnull
{
私有只读布尔值;
私有只读T_值;
事物(T值)
~~~~~
{
_价值=价值;
_hasValue=_值为{};
}
}
//此结构给出两个警告,一个在构造函数上,另一个在`\u value=value上`
//[1]“非空字段”\u value”必须包含非空值才能退出构造函数。
(或)考虑将该字段声明为可空的。
//[2]可能的空引用赋值。
//即使我检查值是否为null并在赋值之前抛出ArgumentNullException,这也是正确的。
公共结构对象,其中T:notnull
{
私有只读布尔值;
私有只读T_值;
事物(T值)
~~~~~ // [1]
{
_hasValue=值为{};
_价值=价值;
~~~~~ // [2]
}
}
我是否创造了一些编译器无法理解其意图的不可能的情况?这是与构造函数中的可空引用类型相关的编译器错误吗?我在这里遗漏了什么?所有这些场景对我来说都很有意义。让我们看看每一个,但首先让我们就一些事情达成一致:
- 不可为null/可为null的引用类型不是实类型。它们只是编译器知道如何解释的注释
- 该结构被约束为
,但是如果未启用可空分析(notnull
),则可以向其传递#可空启用
引用,而不会发出任何警告null
是一个val是{}
测试。使用它意味着该值可以是null
null
- 编译器使用流分析。它寻找各种模式来确定值的状态。它将接受你的话某些东西可以是/不是
,并且它将使用以前的赋值作为变量可空性的证明,即使它可能没有意义空的
- 这就是这里的踢球者。对于我们来说,没有完全构造的对象的场可以从一行改变到下一行是没有意义的;没有两个线程在更改值。但是编译器不是人类的智慧,当我们告诉它我们知道得更好时,它会听从我们
- 在大多数(所有?)情况下,流分析不会反向工作,这意味着第
上的断言状态不会影响第N行
N-1行
public struct ThingA其中T:notnull
{
私有只读布尔值;
私有只读T_值;
ThingA(T值)
{
_价值=价值;
_hasValue=值为{};
}
}
ThingA
不会发出警告。您已被约束为notnull
,并将其赋值为\u值
。在此之前,您还没有执行过任何可空性断言,因此编译器假设到目前为止,根据契约,参数实际上不是null
。流分析不能反向工作。对参数使用null
测试分配给\u hasValue
,只会影响值
参数的未来可为空状态。编译器没有说“嘿,我记得我用value
分配了一些东西,让我去把所有的东西都修好”——那太麻烦了。现在,我们退出构造函数,并且您没有重新分配\u值
字段,因此其先前决定的“notnull”状态仍然保持不变。没有警告
// This struct gives no warnings
public struct Thing<T> where T : notnull
{
private readonly bool _hasValue;
private readonly T _value;
Thing(T value)
{
_value = value;
_hasValue = value is { };
}
}
// This struct gives warning on the constructor:
// "Non-nullable field "_value" must contain a non-null value before exiting constructor.
// Consider declaring the field as nullable.
public struct Thing<T> where T : notnull
{
private readonly bool _hasValue;
private readonly T _value;
Thing(T value)
~~~~~
{
_value = value;
_hasValue = _value is { };
}
}
// This struct gives two warnings, one on the constructor and one on `_value = value`
// [1] "Non-nullable field "_value" must contain a non-null value before exiting constructor.
// Consider declaring the field as nullable.
// [2] Possible null reference assignment.
// This is true even if I check value for null and throw an ArgumentNullException before the assignment.
public struct Thing<T> where T : notnull
{
private readonly bool _hasValue;
private readonly T _value;
Thing(T value)
~~~~~ // [1]
{
_hasValue = value is { };
_value = value;
~~~~~ // [2]
}
}
public struct ThingB其中T:notnull
{
私有只读布尔值;
私有只读T_值;
ThingB(T值)
{
_价值=价值;
_hasValue=_值为{};
}
}
ThingB
以类似于ThingA
的流程开始。因为我们没有以前的null
测试,并且在T上有notnull
约束,所以根据合同,为\u值
字段赋值将加强其非null状态。但这里是它变得棘手的地方!现在对同一个字段执行null
测试,这意味着它实际上可能是null
。流分析不能反向工作,因此我们不会在分配给字段的行上收到警告,但您已经告诉编译器字段的状态可能是null
。我们正在退出构造函数,可能有一个null
字段。这是你的警告
public struct ThingC其中T:notnull
{
私有只读布尔值;
私有只读T_值;
ThingC(T值)
{
_hasValue=值为{};
_价值=价值;
}
}
对于ThingC
,您对参数执行null
检查,这意味着它可以是null
。参数的null
状态更改为“可能为null”,然后在分配给\u值时使用该参数。你刚才说它可以是null
,但根据约束条件,它不能是空的。这是第一个警告。现在,我们离开构造函数主体,字段的状态仍然是“maybe null”(根据赋值)。还有第二个警告
在你的评论中
即使我检查null值并抛出
辩论无效