如何识别泛型类型的可为空引用类型?

Posted

技术标签:

【中文标题】如何识别泛型类型的可为空引用类型?【英文标题】:How to identify a nullable reference type for generic type? 【发布时间】:2020-09-21 16:53:49 【问题描述】:

在启用 nullable 的 C# 8 中,有没有办法为泛型类型识别 可为 null 的引用类型

对于可为空的值类型,有一个专门的部分。 https://docs.microsoft.com/en-us/dotnet/csharp/language-reference/builtin-types/nullable-value-types#how-to-identify-a-nullable-value-type

我们正在尝试根据泛型类型进行可选的 null 检查

#nullable enable
public static Result<T> Create<T>(T value)

   if (!typeof(T).IsNullable() && value is null)
      throw new ArgumentNullException(nameof(value));

   // Do something


public static bool IsNullable(this Type type)

   // If type is SomeClass, return false
   // If type is SomeClass?, return true
   // If type is SomeEnum, return false
   // If type is SomeEnum?, return true
   // If type is string, return false
   // If type is string?, return true
   // If type is int, return false
   // If type is int?, return true
   // etc

所以当T 不可为空时,以下将抛出ArgumentNullException 但是当T 可以为空时,允许值为空,例如

Create<Anything>(null); // throw ArgumentNullException

Create<Anything?>(null); // No excception

【问题讨论】:

这能回答你的问题吗? How to use .NET reflection to check for nullable reference type你也可以看看nullable-metadta规范 @JasonYu 是否需要定义引用类型是否可以为空? @Iliar Turdushev 这就是我想做的。但约束只能像where T : notnull 那样工作。我希望Create&lt;T&gt; 方法根据T 是否可以为空来抛出ArgumentNullException 【参考方案1】:

在启用 nullable 的 C# 8 中,有没有办法识别 nullable 泛型类型的引用类型

C# 8 中,无法检查传递给泛型方法的类型参数是否为可为空的引用类型。

问题是任何可空引用类型T? 都由相同的类型T (but with a compiler-generated attribute annotating it) 表示,而不是由实际.NET 类型Nullable&lt;T&gt; 表示的可空值类型T? .

当编译器生成调用泛型方法F&lt;T&gt; 的代码时,T 可以是可为空的引用类型,也可以不是,如果T 是可为空的引用类型的信息会丢失。让我们考虑下一个示例方法:

public void F<T>(T value)  

用于下一次调用

F<string>("123");
F<string?>("456");

编译器将生成下一个IL 代码(我简化了一点):

call    F<string>("123")
call    F<string>("456")

你可以看到第二个方法传递了一个类型参数string而不是string?,因为在执行期间可空引用类型string?的表示是相同的类型string

因此,在执行期间,无法定义传递给泛型方法的类型参数是否为可为空的引用类型。


我认为对于您的情况,最佳解决方案是传递一个 bool 值,该值将指示引用类型是否可为空。这是一个示例,它是如何实现的:

public static Result<T> Create<T>(T value, bool isNullable = false)

    Type t = typeof(T);

    // If type "T" is a value type then we can check if it is nullable or not.
    if (t.IsValueType) 
    
        if (Nullable.GetUnderlyingType(t) == null && value == null)
            throw new ArgumentNullException(nameof(value));
    
    // If type "T" is a reference type then we cannot check if it is nullable or not.
    // In this case we rely on the value of the argument "isNullable".
    else
    
        if (!isNullable && value == null)
            throw new ArgumentNullException(nameof(value));
    

    ...

【讨论】:

【参考方案2】:

你不能在约束中使用 nullable,但是你可以在方法签名中使用它。这有效地将其限制为可为空的类型。示例:

static Result<Nullable<T>> Create<T>(Nullable<T> value) where T  : struct

    //Do something

请注意,您可以将此方法与现有方法一起使用,作为 重载,它允许您执行不同的逻辑路径,如果它可以为空,而不是如果它不是。

static Result<Nullable<T>> Create<T>(Nullable<T> value) where T  : struct

    Log("It's nullable!");
    Foo(value);


public static Result<T> Create<T>(T value)

    Log("It's not nullable!");
    Foo(value);

【讨论】:

notnull 约束,如Result&lt;T&gt; Create&lt;T&gt;(T value) where T : notnull,我正在尝试使用新的可空引用类型而不是Nullable&lt;T&gt; 这仅考虑了struct。在我们的例子中,泛型类型可以是任何类或结构。 T 可能是SomeClassSomeClass?SomeEnumSomeEnum?int? 但这将如何处理可为空的引用类型,例如可能为空的字符串?

以上是关于如何识别泛型类型的可为空引用类型?的主要内容,如果未能解决你的问题,请参考以下文章

可空引用类型:如何指定“T?”类型不限于类或结构

未从 FirstOrDefault 公开的可空引用类型信息

C#中 ?? ? ?: ?.?[ ]

使用 C# 8 的可空引用类型

C# 8.0 和可为空引用类型

c#静态类中的8个可为空的引用类型