C#:泛型 T 的默认值?不为空;行为随通用约束而改变

C#: default of generic T? is not null; behavior changes with generic constraint

我有一个通用的 class,它应该对(不可为 null 的)引用和值类型(参数,returns ...)进行操作,但内部需要可以为 null 的字段。

using System;

public class Gen<T> // where T : struct
{
    public class Data
    {
        public T? t;
    }

    public static void Write(string s)
    {
        Data d = new Data();
        Console.WriteLine("Default of {0} is {1}", s, d.t == null ? "null" : "NOT null");
    }

    // ... other stuff that uses T and not T? like
    // public T DoSomething(T value) ...
}

static class Program
{
    static void Main(string[] args)
    {
        Gen<int>.Write("int?");
        Gen<string>.Write("string?");
    }
}

此代码在启用可为空的情况下编译 (.NET 5) 时不会产生任何错误或警告。 然而,行为并不像我预期的那样。

Default of int? is NOT null
Default of string? is null

在寻找解决方案时,我发现添加 where T : struct 约束(并删除 Gen.Write())时,行为会变为

Default of int? is null

奇怪的是约束会改变行为。

有没有人知道编写这样一个通用 class 的优雅解决方案? 使用也支持引用类型的自定义 Nullable class 或每个 T 的单独 bool 标志?归档有点乏味。

如果你想使用 Nullable<int> 你不应该使用 int,所以使用:

Gen<int?>.Write("int?");

那么输出将是

Default of int? is null
Default of string? is null

The code in the question is an example. The real class does not have a Write method and never uses the string of the type. However as I indicated by 'other stuff' it uses T as well as T?. So it is not desired to instantiate it with int? instead of int.

首先,我想解释一下为什么通用 class 中的 struct 约束会改变行为并不奇怪。因为实际上,如果您使用的是 < C#8,那么该约束会使其编译。然后 T? 表示 Nullable<T>,因此如果您使用 Gen<int>.Write("int?"),字段 t 将是 Nullable<int>。但是 Gen<string>.Write("string") 根本无法编译,因为 string 不是 struct。所以它与约束的含义完全不同。

启用 C#8 后,您可以删除 struct 约束,然后 t 仍然是 int,而 string 将是可为空的 string .所以问号的意思是:在引用类型的情况下,它是一个可为空的引用类型,否则就是它的本来面目。

你不能同时拥有,泛型可以是可为空的引用类型或可为空的值类型而不使用所需的泛型,因此如果它必须是可为空的,请使用 int?