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

时间:2019-06-21 14:46:37

标签: c# c#-8.0 nullablereferencetypes

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

启用了可空引用类型

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

具有可空引用类型已禁用

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

我无法适应检测到类型为空的代码:

bool isNullable = !typeInfo.IsValueType;

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

1 个答案:

答案 0 :(得分:2)

请注意,有很好的方法来检查适用于值类型的“旧的”可为空类型,这些类型已在Stack Overflow上详细记录。

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

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

与新功能的名称相反,可为空的引用 types types 无关,而是与 things 有关。用于。这些事物是:

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

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

好的,接下来,让我们来看一些代码(顺便说一句,我在罗斯林实验模式下使用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对象是完全相同的 instance 。他们不只是比较相等,我也回来了,没什么区别。基本上,FieldType会忽略此问号的存在与否。

我在上面的评论中包含了一些细节,但是主要原因是所有现有的nuget软件包,因此,编译后的代码仍可在此新支持下使用。无需重写任何代码即可突然处理类似NullableReferenceType<T>之类的代码。 这是一件好事,但这也意味着您仍将传递空引用,并从现有的nuget包中获取空引用。

好的,那么,我们如何检测到这一点?答案是,如上所述,关于可空性的信息不是附加到类型上,而是附加到具有该类型的 thing 上,在本例中是字段。

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

GetType().GetField("Nullable").GetCustomAttributes().Dump();
GetType().GetField("NonNullable").GetCustomAttributes().Dump();

这给出了这个输出:

Nullable fields attributes

如您在此处看到的,“可为空”字段具有一个附加属性NullableAttribute。我必须承认,我不知道其他属性的含义,我将不得不进行更多研究。

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

public List<string>? Nullable1;
public List<string?>? Nullable2;

在这里,两个字段都是对列表的可空引用,区别在于我说过其中一个列表包含对字符串的可空引用,而另一个不包含对字符串的空引用。

以下是这些收藏的反思:

GetType().GetField("Nullable1").GetCustomAttributesData().Dump();
GetType().GetField("Nullable2").GetCustomAttributesData().Dump();

及其输出:

nullable generic types

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

您还可以找到有关此excellent blog post by Rico Suter的更多信息。