.NET 6 中的可为空性和泛型

Nor*_*itt 10 .net c# generics nullable-reference-types .net-6.0

假设我们有一些带有 2 个参数的通用方法:

private void TestConstraints<TKey>(TKey key, TKey? nullableKey)
    where TKey : notnull { }
Run Code Online (Sandbox Code Playgroud)

从 .NET6 的角度来看,这完全没问题,因为没有添加可为空的引用类型。但使用尝试失败,原因是 CS8714 类型不能用作参数:

private void TestMethod()
{
    var id = 5;
    int? nullableId = null;
    TestConstraints(id, nullableId);
}
Run Code Online (Sandbox Code Playgroud)

警告 CS8714 类型“int?” 不能用作泛型类型或方法“TestClass.TestConstraints(TKey, TKey?)”中的类型参数“TKey”。类型参数“int”的可空性?与“notnull”约束不匹配。

明确指定类型 asint没有帮助。

有人可以澄清我是否可以这样操作(将输入参数定义为TKey?),以及文档中的说明位置吗?

编译器版本:

Microsoft (R) 构建引擎版本 17.2.0+41abc5629

Gur*_*ron 11

对于不受约束(既不是 tostruct也不是 to class)泛型类型参数T-T?对于值类型和引用类型的处理方式不同。来自文档

  • 如果 的类型参数T是引用类型,T?则引用相应的可为 null 的引用类型。例如,如果T是 a string,则T?是 a string?
  • 如果 的类型参数T是值类型,T?则引用相同的值类型T。例如,如果Tint,则T?也是int
  • 如果 的类型参数T是可为空引用类型,T?则引用相同的可为空引用类型。例如,如果T是 a string?,则T?也是 a string?
  • 如果 的类型参数T是可为空值类型,T?则引用相同的可为空值类型。例如,如果T是 a int?,则T?也是 a int?

发生这种情况的部分原因是可空引用和可空值类型的处理方式不同,因为可空值类型实际上是单独的类型 - Nullable<T>

TestConstraints我们想象一下,T根据int规则,签名TestConstraints<int>(int key, int nullableKey)显然不会TestConstraints<int>(id, nullableId)因类型不匹配而编译调用(TestConstraints<int>(id, nullableId.Value)将编译但在运行时抛出)。

对于int?( Nullable<int>) ,签名TestConstraints<int?>(int? key, int? nullableKey)将编译(由于隐式转换T-> Nullable<T>),但显然将无法通过警告的通用约束。

解决方法可以是引入两种重载,一种是 for struct,一种是 for class,然后让编译器计算出来

private void TestConstraints<TKey>(TKey key, TKey? nullableKey)
        where TKey : struct
    { }
    
private void TestConstraints<TKey>(TKey key, TKey? nullableKey)
        where TKey : class
    { }
Run Code Online (Sandbox Code Playgroud)