Nullable types in generic class

Dor*_*ork 1 c# generics null

I need to create a class similar to this:

class GenericClass<T>
{
    public T[] Arr {get; }
    public GenericClass(int n)
    {
        Arr = new T[n];
        for (int i = 0; i < n; i++)
        {
            Arr[i] = null;
        }
    }
}
Run Code Online (Sandbox Code Playgroud)

But there is a compiler error:

CS0403 Cannot convert null to type parameter 'T' because it could be a non-nullable value type. Consider using 'default(T)' instead.

I don't want to use default(T), because it could be the same as normal value but I need to distinguish. I don't know the type so I can't use minimal value. How can I use null?

Abi*_*n47 6

泛型的问题在于,它们必须考虑T从字面上看可能是任何东西的可能性,包括不能为null类型的值(值类型包括基元和结构)。为了将通用参数限制为可以是的类型null,您需要添加class约束:

class GenericClass<T> where T : class
{
    public T[] Arr { get; private set; }
    public GenericClass(int n)
    {
        Arr = new T[n];
        for (int i = 0; i < n; i++)
        {
            Arr[i] = null;
        }
    }
}
Run Code Online (Sandbox Code Playgroud)

或者,您可能根本不想与您打交道null。相反,您可以更换

Arr[i] = null;
Run Code Online (Sandbox Code Playgroud)

Arr[i] = default(T);
Run Code Online (Sandbox Code Playgroud)

它将正常工作。default(T)将返回null任何可为null的类型,以及默认值为所有非可为null的类型。(0代表int,false代表bool,等等。)

编辑:作为另一种选择,您可以在内部用Nullable包装器类型表示对象。C#允许使用该?运算符来简化此语法:

class GenericClass<T>
{
    public T?[] Arr { get; private set; }
    public GenericClass(int n)
    {
        Arr = new T?[n];
    }
}
Run Code Online (Sandbox Code Playgroud)

顺便说一句,使用这种方法,不需要遍历数组的每个索引并将其设置为null,因为C#会为您处理。


das*_*ght 5

如果您知道它T 必须是可为null 的类型,例如Nullable<Xyz>,您可以强制用户传递Xyz,并在内部 make Nullable<Xyz>(又名Xyz?):

class GenericClass<T> where T : struct {
    public T?[] Arr {get; }
    public GenericClass(int n) {
        Arr = new T?[n]; // This will create an array of nulls, no need for a loop
    }
}
Run Code Online (Sandbox Code Playgroud)

where T : struct添加到声明中的约束将阻止使用GenericClass<T>值类型的参数进行实例化。例如,尝试 make将导致编译时错误。GenericType<string>


Mic*_*ter 5

您实际上是在这里射击自己。.NET中的数组会自动设置为该类型的默认值,因此,只要类型的默认值为null(所有对象和可为null的对象),那么只需创建数组就足以将所有项目设置为null。没有这些多余的代码,您可能根本不会遇到任何问题,这取决于您尝试使用此类的方式。

如果您试图允许类似的情况GenericClass<int>并使它公开一个int?s 数组,请执行此操作。该where T : struct强制执行所提供的类型是值类型,从而让您的使用T?。在这种情况下,不能为通用参数使用对象类型。

class GenericClass<T> where T : struct
{
    public T?[] Arr { get; }
    public GenericClass(int n)
    {
        Arr = new T?[n];
    }
}
Run Code Online (Sandbox Code Playgroud)

如果要创建一个同时支持对象实例和可为null的类,请尝试此操作。这里的问题是您不能设置类型约束-如果使用where T : struct,则不能使用对象类型;如果使用where T : class,则不能使用可为空的值。因此,此类型允许使用GenericClass<MyObject>(如您想要的作品),GenericClass<int?>(如您想要的作品)和GenericClass<int>(无法按您想要的方式工作,并且无论如何都无法按您想要的方式工作)的用法。不幸的是,无法定义此泛型来禁止后一种情况。不过,您可以根据需要在运行时进行检查。

class GenericClass<T>
{
    public T[] Arr { get; }
    public GenericClass(int n)
    {
        Arr = new T[n];
    }
}
Run Code Online (Sandbox Code Playgroud)