带有泛型的可为空引用的注释

mal*_*lat 5 c# generics nullable

给定以下通用Foo1函数:

struct Key<T> {}
static readonly Key<double> MyKey = new Key<double>();
T? Foo1<T>(Key<T> key)
{
    return default;
}
Run Code Online (Sandbox Code Playgroud)

天真的读者会认为:

var foo1 = Foo1(MyKey);
Run Code Online (Sandbox Code Playgroud)

foo1类型为double?,结果表明编译器正在选择double返回类型。我需要显式添加一个约束来获取可为空的返回值:

T? Foo2<T>(Key<T> key) where T : struct // really return a nullable
{
    return default;
}
Run Code Online (Sandbox Code Playgroud)

?有人可以解释为什么我的第一个函数中没有选择可空引用的注释吗Foo1

Gyö*_*zeg 4

让我们从一些背景开始:

C# 9.0之前Foo1无效。即使在启用可为空引用的 C# 8.0 中:

CS8627: A nullable type parameter must be known to be a value type or non-nullable reference type
Run Code Online (Sandbox Code Playgroud)

Foo2即使在 C# 8.0 之前也是有效的,因为仅当是结构体T?时才有意义,并且在本例中具有与( )不同的类型。到目前为止,这很简单。TT?TNullable<T>

从 C# 8.0 开始引入了可空引用,这引起了一些混乱。从现在开始,T?可以是意味着Nullable<T>,也可以是仅仅T。此版本不允许T?没有约束,但在您指定时也允许where T : class

如果不使用约束,您必须使用属性来指示T可以作为null返回值:

// C# 8.0: Poor man's T?
[return: MaybeNull] T Foo1<T>(Key<T> key) => default;
Run Code Online (Sandbox Code Playgroud)

如果T现在是值类型怎么办?它显然不会将其类型更改为Nullable<T>返回值。要返回double?您的类型参数也必须是double?,也就是说,MyKey也必须是Key<double?>

在 C# 9.0 中,对 的限制T?已经放宽,现在不需要约束:

// C# 9.0: this is valid now
T? Foo1<T>(Key<T> key) => default;
Run Code Online (Sandbox Code Playgroud)

但现在它本质上与 C# 8.0 版本的含义相同。如果没有where T : struct约束T?,则类型相同,T因此它只是表明结果可以是null,这可能出现在编译器警告中。要返回可为空值类型,您必须将其用作double?泛型参数,这也意味着您Key还必须定义可为空类型:

static readonly Key<double?> MyKey = new Key<double?>();
Run Code Online (Sandbox Code Playgroud)

where T : struct如果可为空的键在您的情况下没有意义,那么您除了指定约束之外什么也做不了,Foo2这样旧规则就会生效:T?并且T具有不同的类型,其中T?means Nullable<T>


更新:如果您看到它们的反编译源代码Foo1,则 和之间的主要区别Foo2可能会更明显:

[System.Runtime.CompilerServices.NullableContext(2)]
private static T Foo1<T>([System.Runtime.CompilerServices.Nullable(new byte[] {
    0,
    1
})] Key<T> key)
{
    return default(T);
}

private static Nullable<T> Foo2<T>(Key<T> key) where T : struct
{
    return null;
}
Run Code Online (Sandbox Code Playgroud)

请注意, 的返回类型Foo1只是T带有一些注释,以便编译器可以发出适当的警告。