请注意,在Stack Overflow上有很好的方法可以检查适用于值类型的“旧”可空类型,这些方法已经有详细的文档记录。
我将只关注可空的
引用类型,并提供检查是否存在这些类型的方法。
首先,让我总结一下对问题的评论,因为它们非常重要。
与新功能的名称相反,可空引用
类型并不是关于
类型的,而是关于这些类型用于的
事物。这些
事物包括:
- 字段
- 属性
- 方法返回值
- 方法参数
当然,这也适用于局部变量,但您需要另一种内省方式来处理解码指令。我不知道这种信息是否编码在局部变量的实际指令中。
好的,现在,让我们看一些代码(顺便说一下,我正在使用带有Roslyn实验模式的
LINQPad来测试所有这些):
public string? Nullable;
public string NonNullable;
这是两个公共字段。忽略是否是一个好主意。你如何检查这些字段的类型并检测是否存在或缺少这个问号?
好吧,让我们尝试简单的方法:
Type nullable = GetType().GetField("Nullable").FieldType;
Type nonNullable = GetType().GetField("NonNullable").FieldType;
Console.WriteLine(ReferenceEquals(nullable, nonNullable));
运行此代码会给我以下结果:
True
显然,这行不通。
Type
对象是完全相同的
实例。它们不仅
比较相等,我得到了相同的东西,没有任何区别。基本上,
FieldType
忽略了这个问号的存在或缺失。
上面我的评论中有一些细节,但这样做的主要原因是由于现有的 nuget 包和已编译的代码仍将与此新支持一起工作。因此,没有必要重写任何代码来处理突然出现的
NullableReferenceType<T>
等内容。 这是一件好事,但也意味着您仍将传递空引用并从现有的 nuget 包中获取它们。
那么,我们如何检测此问题?答案是,关于可空性的信息未附加到类型上,正如我上面提到的那样,而是附加到具有该类型的
对象,在此情况下为字段。
让我们在这些字段上显示属性(再次使用 LINQPad):
GetType().GetField("Nullable").GetCustomAttributes().Dump();
GetType().GetField("NonNullable").GetCustomAttributes().Dump();
这将产生以下输出:
正如您在这里看到的那样,Nullable字段有一个额外的属性,NullableAttribute
。我必须承认,我不知道那个其他属性是关于什么的,我需要进一步调查。
这个NullableAttribute
属性比这个简单的例子显示的要复杂得多,因为它有一个包含bool
值的集合属性。让我们看一个稍微复杂一点的例子:
public List<string>? Nullable1;
public List<string?>? Nullable2;
这里,两个字段都是指向列表的可空引用,不同之处在于我说其中一个列表包含了可空字符串引用,而另一个则没有。
以下是一些反射来查看这些集合:
GetType().GetField("Nullable1").GetCustomAttributesData().Dump();
GetType().GetField("Nullable2").GetCustomAttributesData().Dump();
和它们的输出:
在这里,您可以看到此集合中的第二个元素存在差异(我用红色...矩形...标出),我希望第一个元素适用于列表,第二个元素适用于第一个通用类型参数。如果您有包含通用类型的通用列表,则参数数量将相应增加。
您还可以在 Rico Suter 的博客文章中找到更多信息。
Type
对象将不知道该字段已被配置为非空。 - Lasse V. Karlsen