具有可为 null 类型的 C# notnull 约束会出现意外行为

Han*_*ghi 15 c# constraints

我读过有关notnullC# 中的约束的内容,其中写道“这允许值类型或不可为空的引用类型,但不允许可为空的引用类型。” (引用自《Programming C# - 10.0 By Ian Griffiths》)

我尝试在下面的代码中检查这个约束:

MyTestClass<int?> instance1 = new MyTestClass<int?>();
MyTestClass<string?> instance2 = new MyTestClass<string?>();

public class MyTestClass<T> where T : notnull
{
    T Value { get; set; }

    public MyTestClass()
    {
        Value = default(T);
        if (Value == null)
            Console.WriteLine($"Type of T is {typeof(T)} and its default value is 'Null'");
        else
            Console.WriteLine($"Type of T is {typeof(T)} and its default value is {Value}");
    }
}
Run Code Online (Sandbox Code Playgroud)

如您所见,我使用可为空类型int?(可为空值类型)和string?(可为空引用类型)实例化了我的泛型类,它仍然适用于我。

它还为我打印这样的输出:

Type of T is System.Nullable`1[System.Int32] and its default value is 'Null'
Type of T is System.String and its default value is 'Null'
Type of T is System.Int32 and its default value is 0
Type of T is System.String and its default value is 'Null'"
Run Code Online (Sandbox Code Playgroud)

它的行为是“字符串?” 作为“字符串”并将两者检测为不可为空。发生这些情况的原因可能是什么?

Han*_*ghi 8

这几天我一直在搜索这个问题,发现原因可能是:

  1. C# 中的 notnull 约束用于确保类型参数不为 null。根据官方文档,notnull 约束可以应用于值类型或不可为空的引用类型,但不能应用于可为空的引用类型

  2. 这段代码实例化了一个具有可为空类型 int 的泛型类?和字符串?它仍然有效。但是,不会对这些类型强制执行“notnull”约束,因为“notnull”约束仅在编译时强制执行,而不是在运行时强制执行。从 C# 8.0 开始,开发人员可以将引用类型注释为可空(例如,字符串?),以向编译器指示该变量可以保留空值。此功能称为可为空引用类型。它旨在由编译器进行静态分析以发出警告,并且不会影响变量的实际运行时类型。string和string的运行时类型?相同,因此它们都满足 notnull 约束。

  3. 即使有 notnull 约束,也允许可为 null 的值类型,因为它们表示为 System.Nullable 结构,该结构本身就是值类型并且实际上不为 null。它只是能够通过 HasValue 属性来表示 null。那么代码编译是因为 int 吗?是一个可为空的结构体,并且这不是一个可为空的引用类型,而是一个值类型,它满足 notnull 约束。

另外,通过下面的链接您可以查看 notnull 约束的文档。

https://learn.microsoft.com/en-us/dotnet/csharp/programming-guide/generics/constraints-on-type-parameters

  • 如果“'notnull'约束仅在编译时强制执行”,那么为什么在这种情况下不在编译时强制执行呢? (2认同)

小智 7

正如所定义的,约束notnull“将类型参数限制为不可为空的类型。该类型可以是值类型或不可为空的引用类型”。[ 1 ]

该约束可用于可空启用上下文中的代码,并且在编译时检查与约束不匹配的类型参数(即string?int[]?),并创建警告而不是错误,特别是CS8714: Nullability of type argument doesn't match 'notnull' constraint.

因此,程序将在打破notnull约束时进行编译,但会在可能的情况下发出编译时警告。值得一提的是,它只会在可能的情况下发出警告,因为“包含 notnull 约束的通用声明可以在可为 null 的不经意上下文中使用,但编译器不会强制执行该约束。” [ 1 ]

因此,可为 null 的引用类型可以作为有效类型参数传递给使用notnull约束指定的类型参数,但不应该这样做。除非演示没有在上下文中运行,否则应该在演示中发出警告#nullable enable

资料来源:

  1. https://learn.microsoft.com/en-us/dotnet/csharp/language-reference/keywords/where-generic-type-constraint