C#10 可为空模式:如何告诉编译器我在构造函数中间接设置了不可为空的 属性?
C#10 nullable pattern: how to tell the compiler I set the non-nullable property in the constructor indirectly?
考虑一个例子:
class Test {
string S { get; set; }
public Test() {
Init();
}
private void Init() {
S = "hello";
}
}
使用可为空的 C# 项目功能,此示例将触发编译器警告:
Warning CS8618 Non-nullable property 'S' must contain a non-null value when exiting the constructor. Consider declaring the property as nullable.
然而,属性在退出构造函数时确实包含非空值,它只是不直接在构造函数中设置,而是间接地在从构造函数无条件调用的方法中设置。
这个例子清楚地表明 S
属性 不可能为空。当 Test
class 的实例被创建时, Init()
方法被无条件调用,所以 S
属性 总是被设置为“hello”。
当然,可以在代码中抑制此警告,但这看起来很丑陋。
这是告诉编译器我确实在其他地方将 S
属性 设置为非空值的更好方法吗?
顺便说一句,如果您真的想知道为什么要在构造函数中间接设置值,让我们考虑一下 Derived
类型的另一种派生 属性 D
。要创建 Derived
的实例,必须首先解析字符串,我们不想在每次读取 D
属性.
时都解析字符串
所以,更现实的代码看起来更像这样:
class Test {
public string S {
get => _S;
set => D = new Derived(_S = value);
}
public Derived D { get; private set; }
public Test(string s) => D = new Derived(_S = s);
private string _S;
}
如您所见,S
和 D
在退出构造函数时都设置为非空值。
然而代码仍然触发编译器警告CS8618。
使用MemberNotNullAttribute
标记你的功能:
using System.Diagnostics.CodeAnalysis;
class Test
{
string S { get; set; }
public Test()
{
Init();
}
[MemberNotNull(nameof(S))]
private void Init()
{
S = "hello";
}
}
编译器现在会抱怨 如果 你没有在 Init
:
中初始化 S
查看更多场景in this article: Attributes for null-state static analysis
考虑一个例子:
class Test {
string S { get; set; }
public Test() {
Init();
}
private void Init() {
S = "hello";
}
}
使用可为空的 C# 项目功能,此示例将触发编译器警告:
Warning CS8618 Non-nullable property 'S' must contain a non-null value when exiting the constructor. Consider declaring the property as nullable.
然而,属性在退出构造函数时确实包含非空值,它只是不直接在构造函数中设置,而是间接地在从构造函数无条件调用的方法中设置。
这个例子清楚地表明 S
属性 不可能为空。当 Test
class 的实例被创建时, Init()
方法被无条件调用,所以 S
属性 总是被设置为“hello”。
当然,可以在代码中抑制此警告,但这看起来很丑陋。
这是告诉编译器我确实在其他地方将 S
属性 设置为非空值的更好方法吗?
顺便说一句,如果您真的想知道为什么要在构造函数中间接设置值,让我们考虑一下 Derived
类型的另一种派生 属性 D
。要创建 Derived
的实例,必须首先解析字符串,我们不想在每次读取 D
属性.
所以,更现实的代码看起来更像这样:
class Test {
public string S {
get => _S;
set => D = new Derived(_S = value);
}
public Derived D { get; private set; }
public Test(string s) => D = new Derived(_S = s);
private string _S;
}
如您所见,S
和 D
在退出构造函数时都设置为非空值。
然而代码仍然触发编译器警告CS8618。
使用MemberNotNullAttribute
标记你的功能:
using System.Diagnostics.CodeAnalysis;
class Test
{
string S { get; set; }
public Test()
{
Init();
}
[MemberNotNull(nameof(S))]
private void Init()
{
S = "hello";
}
}
编译器现在会抱怨 如果 你没有在 Init
:
查看更多场景in this article: Attributes for null-state static analysis