如何在运行时检测类型是否可以为空?

Sho*_*hoe 4 c# c#-8.0 nullable-reference-types

我试图检测一个类型在运行时是否可以为空,以将该类型转换为相应的 GraphQL 类型,例如:

启用可为空引用类型:

  • string 转换为 String!
  • string? 转换为 String

禁用可为空引用类型:

  • string 转换为 String
  • NonNull<string>转换为String!(NonNull是自定义库类型)

我在调整检测类型可为空性的代码时遇到问题:

bool isNullable = !typeInfo.IsValueType;
Run Code Online (Sandbox Code Playgroud)

如何更改它以便它与启用和禁用的可为空引用类型一起使用?

ang*_*son 5

请注意,有一些很好的方法可以检查适用于值类型的“旧的”可为空类型,Stack Overflow 上对此进行了详细记录。

然后,我将只关注可为空引用类型,并提供检查其中一种是否有效的方法。

我先总结一下我对这个问题的评论,因为它们相当重要。

相反,新功能的名称,可为空引用类型不是关于类型,而是有关的事情,这些类型用于。这些东西是:

  • 字段
  • 特性
  • 方法返回值
  • 方法参数

现在,当然这也适用于局部变量,但您需要一个完整的“另一种内省”来处理解码指令。我不知道这种信息是如何编码在局部变量的实际指令中的,甚至不知道。

好的,让我们看看一些代码(顺便说一句,我正在使用带有 Roslyn 实验模式的LINQPad来测试所有这些):

public string? Nullable;
public string NonNullable;
Run Code Online (Sandbox Code Playgroud)

这是两个公共字段。忽略这是否是一个好主意。您将如何检查这些字段的类型并检测此问号的存在与否?

好吧,让我们尝试简单的路线:

Type nullable = GetType().GetField("Nullable").FieldType;
Type nonNullable = GetType().GetField("NonNullable").FieldType;
Console.WriteLine(ReferenceEquals(nullable, nonNullable));
Run Code Online (Sandbox Code Playgroud)

运行这个给我:

True
Run Code Online (Sandbox Code Playgroud)

所以很明显这是行不通的。该Type对象是完全相同的实例。他们不只是比较相等,我得到了同样的东西,没有区别。基本上,FieldType忽略了这个问号的存在或缺乏。

我上面的评论有一些细节,但主要原因是所有现有的 nuget 包和编译的代码仍然可以使用这个新的支持。不需要重写任何代码来处理类似NullableReferenceType<T>突然的事情。这是一件好事,但也意味着您仍将传递空引用并从现有的 nuget 包中取回它们。

好的,那么,我们将如何检测到这一点?答案是关于可空性的信息不附加到类型上,正如我上面提到的,而是附加到具有类型的事物上,在这种情况下是字段。

让我们显示这些字段的属性(我再次使用 LINQPad):

GetType().GetField("Nullable").GetCustomAttributes().Dump();
GetType().GetField("NonNullable").GetCustomAttributes().Dump();
Run Code Online (Sandbox Code Playgroud)

这给出了这个输出:

可空字段属性

正如您在此处看到的,Nullable 字段有一个附加属性NullableAttribute。我必须承认,我不知道其他属性是关于什么的,我将不得不进行更多调查。

这个NullableAttribute属性比这个简单的例子显示的要复杂得多,因为它有一个带bool值的集合属性。让我们看一个稍微复杂的例子:

public List<string>? Nullable1;
public List<string?>? Nullable2;
Run Code Online (Sandbox Code Playgroud)

在这里,两个字段都是对列表的可为空引用,不同之处在于我已经说过其中一个列表包含对字符串的可为空引用,而另一个则不包含。

以下是对这些集合的一些反思:

GetType().GetField("Nullable1").GetCustomAttributesData().Dump();
GetType().GetField("Nullable2").GetCustomAttributesData().Dump();
Run Code Online (Sandbox Code Playgroud)

和他们的输出:

可为空的泛型类型

在这里你可以看到这个集合中的第二个元素有区别(我用红色“圈出”了它们......矩形......),我希望第一个元素适用于列表,第二个元素适用于列表第一个泛型类型参数。如果您有包含泛型类型的泛型列表,参数的数量将相应增加。

您还可以在Rico Suter这篇出色的博客文章中找到更多信息。