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

How to identify a nullable reference type for generic type?

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

对于可空值类型,有一节专门介绍它。 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 可以为 null 时,允许 value 毫无例外地为 null,例如

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

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

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

static Result<Nullable<T>> Create<T>(Nullable<T> value) where T  : struct
{
    //Do something
}

请注意,您可以将此与现有方法并排使用,作为 重载,它允许您执行不同的逻辑路径,如果它可以为 null 与如果它不是.

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);
}

In C# 8 with nullable enabled, is there a way to identify a nullable reference type for generic type?

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

问题在于任何可为空的引用类型 T? 都由相同的类型 T (but with a compiler-generated attribute annotating it) 表示,与可为空的值类型 T? 相反由实际的 .NET 类型表示 Nullable<T>.

当编译器生成调用泛型方法 F<T> 的代码时,其中 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));
    }

    ...
}