带有泛型的可为空引用的注解
Annotation for nullable reference with generics
给定以下通用 Foo1
函数:
struct Key<T> {}
static readonly Key<double> MyKey = new Key<double>();
T? Foo1<T>(Key<T> key)
{
return default;
}
天真的 reader 会假设:
var foo1 = Foo1(MyKey);
foo1
是 double?
类型,事实证明编译器正在为 return 类型选择 double
。我需要显式添加约束以获得可为空的 return 值:
T? Foo2<T>(Key<T> key) where T : struct // really return a nullable
{
return default;
}
有人可以解释为什么我的第一个 Foo1
函数中没有提取可空引用 ?
的注释吗?
让我们从一些背景开始:
在 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
Foo2
甚至在 C# 8.0 之前就有效,因为 T?
只有在 T
是一个结构时才有意义,在这种情况下 T?
的类型与 T
(Nullable<T>
)。到目前为止,这很简单。
从 C# 8.0 开始引入了可空引用,这引起了一些混乱。从现在开始,T?
可以表示 Nullable<T>
或 T
。此版本不允许 T?
无限制,但在您指定 where T : class
.
时也允许
如果不使用约束,您必须使用属性来指示 T
可以 null
作为 return 值:
// C# 8.0: Poor man's T?
[return: MaybeNull] T Foo1<T>(Key<T> key) => default;
如果 T
现在是值类型呢?它显然不会在 return 值中将其类型更改为 Nullable<T>
。要 return 一个 double?
你的类型参数也必须是 double?
,也就是说,MyKey
也必须是一个 Key<double?>
.
在C# 9.0中放宽了对T?
的限制,现在不需要限制了:
// C# 9.0: this is valid now
T? Foo1<T>(Key<T> key) => default;
但它现在基本上与 C# 8.0 版本的含义相同。如果没有 where T : struct
约束,T?
与 T
的类型相同,因此它只是表示结果可以是 null
,这可能会出现在编译器警告中。对于 return 可空值类型,您必须使用 double?
作为通用参数,这也意味着您的 Key
也必须定义一个可空类型:
static readonly Key<double?> MyKey = new Key<double?>();
如果可为空的键在您的情况下没有意义,那么您只能像 Foo2
中那样指定 where T : struct
约束,因此旧规则开始生效:T?
和 T
有不同的类型,其中 T?
表示 Nullable<T>
.
更新: Foo1
和 Foo2
之间的主要区别可能更明显,如果你看到他们的 decompiled source:
[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;
}
请注意,Foo1
的 return 类型只是 T
带有一些注释,因此编译器可以发出适当的警告。
给定以下通用 Foo1
函数:
struct Key<T> {}
static readonly Key<double> MyKey = new Key<double>();
T? Foo1<T>(Key<T> key)
{
return default;
}
天真的 reader 会假设:
var foo1 = Foo1(MyKey);
foo1
是 double?
类型,事实证明编译器正在为 return 类型选择 double
。我需要显式添加约束以获得可为空的 return 值:
T? Foo2<T>(Key<T> key) where T : struct // really return a nullable
{
return default;
}
有人可以解释为什么我的第一个 Foo1
函数中没有提取可空引用 ?
的注释吗?
让我们从一些背景开始:
在 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
Foo2
甚至在 C# 8.0 之前就有效,因为 T?
只有在 T
是一个结构时才有意义,在这种情况下 T?
的类型与 T
(Nullable<T>
)。到目前为止,这很简单。
从 C# 8.0 开始引入了可空引用,这引起了一些混乱。从现在开始,T?
可以表示 Nullable<T>
或 T
。此版本不允许 T?
无限制,但在您指定 where T : class
.
如果不使用约束,您必须使用属性来指示 T
可以 null
作为 return 值:
// C# 8.0: Poor man's T?
[return: MaybeNull] T Foo1<T>(Key<T> key) => default;
如果 T
现在是值类型呢?它显然不会在 return 值中将其类型更改为 Nullable<T>
。要 return 一个 double?
你的类型参数也必须是 double?
,也就是说,MyKey
也必须是一个 Key<double?>
.
在C# 9.0中放宽了对T?
的限制,现在不需要限制了:
// C# 9.0: this is valid now
T? Foo1<T>(Key<T> key) => default;
但它现在基本上与 C# 8.0 版本的含义相同。如果没有 where T : struct
约束,T?
与 T
的类型相同,因此它只是表示结果可以是 null
,这可能会出现在编译器警告中。对于 return 可空值类型,您必须使用 double?
作为通用参数,这也意味着您的 Key
也必须定义一个可空类型:
static readonly Key<double?> MyKey = new Key<double?>();
如果可为空的键在您的情况下没有意义,那么您只能像 Foo2
中那样指定 where T : struct
约束,因此旧规则开始生效:T?
和 T
有不同的类型,其中 T?
表示 Nullable<T>
.
更新: Foo1
和 Foo2
之间的主要区别可能更明显,如果你看到他们的 decompiled source:
[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;
}
请注意,Foo1
的 return 类型只是 T
带有一些注释,因此编译器可以发出适当的警告。