【问题标题】: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

【问题讨论】:

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


【解决方案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?
    • 但这将如何处理可为空的引用类型,例如可能为空的字符串?
    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2019-07-02
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多