使用类属性的不可为null类型的预期模式或错误程度较低的模式



这里有一个关于可为null类型的大问题:

退出构造函数时,不可为null的属性必须包含非null值。考虑将属性声明为可为null的

它有一些很好的方法来克服警告,但我对使用任何特定方法背后的推理感兴趣。

我正在与英孚核心合作,以制定一个API。我很小心,永远不要在没有正确数据的情况下实例化模型类,但类不知道这一点,所以我们得到了典型的";不可为null的属性必须包含"警告

至少在我的脑海中,我希望空引用错误在适当的时候发生,传递回客户端,或者防止不适当的空/空值被传递。

示例:;该字段需要的情况";,如果它以某种方式通过了前端验证,则应该抛出异常,并且异常处理程序向客户端返回适当的BadRequest响应。我目前正在这样做,而不是使用显式验证方法来处理每个单独的属性,至少对于所需的属性是这样。

试图适应null被传递的可能性似乎是虚伪的:

  • 声明属性为null:这个属性真的不应该为null,所以我只允许它来抑制警告,这可能会在其他地方引起功能问题或无关的考虑
  • 设置null宽恕null!:在某些情况下似乎是合适的,但在应该设置属性且始终不为null的任何地方都不合适
  • 设置string.Empty这样的默认值:在某些情况下同样适用,但不适用于应该将属性设置为实质性和有意义的内容的情况
  • 设置构造函数:做本质上相同的事情似乎需要做很多额外的工作,我不确定这是否能像移动警告一样克服警告
  1. 当实例化没有设置null或将不可为null的属性设置为null时,我想使用异常来捕获,这错了吗?我之所以对此提出质疑,是因为对不可为null属性的警告
  2. 有没有其他我不知道的模式更合适,这就是为什么要发出警告
  3. 我只是对警告太过重视了吗

声明属性为null:这个属性真的不应该为null。。。

不应该为null。。。或者不能是?编译器只能告知可以为null或不能为null。

将类型声明为不可为null意味着您真的不希望该值为null。编译器帮助您确保永远不会为字段分配null或忘记初始化它。这就是为什么编译器希望您将所有不可为null的字段和属性初始化为非null值的原因。

如果您确信字段已初始化为非null值,则可以将它们声明为不可为null,并使用null宽容运算符:private string _name = default!;当您知道在使用字段之前,字段肯定会由初始化器方法而不是构造函数初始化时,这将非常有用。

如果你想在运行时抛出一个异常(如果值为null(,那么在某些情况下,你确实希望它可能为null。在这种情况下,您确实应该将这些字段标记为可以为null。然后,您可以决定是否需要null检查,或者何时可以使用!抑制警告,或者何时应该引发异常。

还有一些属性可以帮助编译器进行空状态分析。这是一个具有不可为null类型的属性的示例,其中setter无论如何都允许为null:

private string? _name;
[AllowNull]
public string Name
{
get => _name ?? string.Empty;
set => _name = value;
}

在这里,我们告诉编译器,当方法返回true时,out参数不是null:

public bool TryGetValue(string key, [NotNullwhen(true)] out string? value) ...

这在某些情况下也很有用:

private string? _name;
[MemberNotNull(namof(_name))]
private void NullGuard()
{
if (_name is null)
{
throw new InvalidOperationException("Name really shouldn't be null");
}

// _name must be known to be not null when returning from the method
}

属性告诉编译器NullGuard返回时_name不为空:

NullGuard();
int length = _name.Length; // no warning because NullGuard has the MemberNotNull attribute
  1. 我只是在警告中放了太多的存货吗

可能是。不要忘记,可为null的引用类型只是一种工具,它可以帮助您避免遇到不需要的NullReferenceException。这也是表达你意图的更好方式。

最新更新