【问题标题】:How to detect whether a type can be nullable at runtime?如何检测一个类型在运行时是否可以为空?
【发布时间】:2019-06-21 14:46:37
【问题描述】:

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

使用可空引用类型启用

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

使用可为空的引用类型禁用

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

我无法调整检测到类型可空性的代码:

bool isNullable = !typeInfo.IsValueType;

如何更改它以使其适用于启用和禁用的可空引用类型?

【问题讨论】:

  • 你的意思是检查一个字段、属性、方法返回值或方法参数是否可以为空,包括对可空引用类型的支持?
  • 只是确定一个类型(例如TypeInfo)是否接受null
  • 我问是因为与新支持的名称相反,这实际上不是关于 type,而是更多关于具有那种。换句话说,这个新的支持是附加到属性上的(例如),而不是属性的类型。
  • @LasseVågsætherKarlsen 我很困惑。他们为什么会选择这样做?
  • 我似乎找不到任何东西,在这里写 cmets 时尝试了几次。让我写一个答案的开头,我们会看看我得到的深度有多远:)

标签: c# c#-8.0 nullable-reference-types


【解决方案1】:

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

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

让我先总结一下我的问题,因为它们很重要。

与新功能的名称相反,可空引用 types 不是关于 types,而是关于这些类型的 things用于。这些东西是:

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

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

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

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 忽略了这个问号的存在与否。

我上面的 cmets 有一些细节,但主要原因是所有现有的 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();

及其输出:

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

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

【讨论】:

  • 这也可能有帮助:blog.rsuter.com/…
  • @Shoe 太好了,我不记得那篇博文的链接或名称,但这是我了解它时读到的文章之一。已在我的答案中编辑了提及。
  • 一位 Microsoft 员工 (@333fred) 将我链接到 github.com/dotnet/corefx/issues/38087,以跟踪解决原始问题的反射机制的实现。
  • 由于问题上的 cmets 可能不会永远持续下去,因此通过使其完全独立,您的答案将会得到改善。
  • @IanRingrose 你能告诉我你的想法吗?你的意思是我应该去掉LINQPad的使用吗?通过使其完全独立,不确定您的确切含义。请指教。此外,在最近关于射击模组的争吵之后,我正在冷静下来。我将在 12 月回来,前提是在此期间没有更多的 s**t 击中粉丝。随意编辑我的答案,或者如果您有改进的想法,甚至可以挖出大部分答案并自己制作。
猜你喜欢
  • 1970-01-01
  • 2023-03-22
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2017-06-10
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多