在 Microsoft 的可空性文档中,似乎存在相互矛盾的信息。
在此页面上,它显示以下内容(重要部分以粗体/斜体显示):
通用定义和可空性
正确传达泛型类型和泛型方法的空状态需要特别小心。额外的关注源于可空值类型和可空引用类型根本不同的事实。An
int?
是 的同义词Nullable<int>
,而string?
isstring
带有编译器添加的属性。结果是编译器在T?
不知道T
是 aclass
还是 a的情况下无法生成正确的代码struct
。这个事实并不意味着您不能使用可为空的类型(值类型或引用类型)作为封闭泛型类型的类型参数。List<string?> 和 List<int?> 都是 List 的有效实例。
它的意思是你不能使用T?在没有约束的泛型类或方法声明中。例如,
Enumerable.FirstOrDefault<TSource>(IEnumerable<TSource>)
将不会更改为 returnT?
。您可以通过添加struct
orclass
约束来克服此限制。有了这些约束中的任何一个,编译器就知道如何为 T 和 T? 生成代码。
好的,所以如果你想T?
在泛型中使用,你必须将它限制为 astruct
或class
. 很简单。
但是然后在下一页中,他们这样说(再次以粗体/斜体强调):
指定后置条件:MaybeNull 和 NotNull
假设您有一个具有以下签名的方法:
public Customer FindCustomer(string lastName, string firstName)
您可能已经编写了这样的方法,以便
null
在未找到所搜索的名称时返回。null
清楚地表明没有找到该记录。在此示例中,您可能会将返回类型从 更改Customer
为Customer?
。将返回值声明为可空引用类型清楚地指定了此 API 的意图。由于通用定义和可空性中涵盖的原因,该技术不适用于通用方法。您可能有一个遵循类似模式的通用方法:
public T Find<T>(IEnumerable<T> sequence, Func<T, bool> predicate)
您不能指定返回值为
T?
[but the] 方法null
在未找到所查找的项目时返回。由于您不能声明T?
返回类型,因此您将MaybeNull
注释添加到方法返回:[return: MaybeNull] public T Find<T>(IEnumerable<T> sequence, Func<T, bool> predicate)
前面的代码通知调用者合约暗示了一个不可为空的类型,但返回值实际上可能是
null
.MaybeNull
当您的 API 应该是不可为空的类型(通常是泛型类型参数)时使用该属性,但可能存在null
会返回的实例。
然而...
即使直接从文档中复制该代码并为其提供一个简单地返回的默认实现null
,它也不会编译!
[return: MaybeNull]
public T Find<T>(IEnumerable<T> sequence, Func<T, bool> predicate)
=> null;
我尝试了第一个链接页面中也提到的null-forgiving
运算符null!
(在“将属性初始化为空”部分下),但这不起作用。您不能使用default
其中任何一个,因为它不会返回null
值类型int
,例如返回零,如下所示:
[return: MaybeNull]
public static T Find<T>(IEnumerable<T> sequence, Func<T, bool> predicate)
=> default;
var seq = new []{ 1, 2, 3 };
bool MyPredicate(int value) => false;
var x = Find(seq, MyPredicate);
Console.WriteLine($"X is {x}");
输出:
X is 0
那么我在这里错过了什么?你如何成功地实现他们的示例代码而不诉诸使用T?
which 需要将其类型约束为class
or struct
?如果你必须这样做,那有什么意义MaybeNull
呢?