Warning: file_get_contents(/data/phpspider/zhask/data//catemap/2/csharp/263.json): failed to open stream: No such file or directory in /data/phpspider/zhask/libs/function.php on line 167

Warning: Invalid argument supplied for foreach() in /data/phpspider/zhask/libs/tag.function.php on line 1116

Notice: Undefined index: in /data/phpspider/zhask/libs/function.php on line 180

Warning: array_chunk() expects parameter 1 to be array, null given in /data/phpspider/zhask/libs/function.php on line 181
C# 为什么我会得到这些奇怪的C“8”;“可为空的引用类型”;警告:在退出构造函数之前,非null字段必须包含非null值。?_C#_.net_C# 8.0 - Fatal编程技术网

C# 为什么我会得到这些奇怪的C“8”;“可为空的引用类型”;警告:在退出构造函数之前,非null字段必须包含非null值。?

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

我正在尝试处理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;
        _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值并抛出 辩论无效