泛型类中的可空类型

时间:2017-05-22 16:16:31

标签: c# generics null

我需要创建一个类似于此的类:

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

但是存在编译错误:

  

CS0403 无法将null转换为类型参数&#39; T&#39;因为它可能是一个不可为空的值类型。考虑使用&#39;默认(T)&#39;代替。

我不想使用default(T),因为它可能与正常值相同,但我需要区分。我不知道类型,所以我不能使用最小的价值。我怎么能使用null?

4 个答案:

答案 0 :(得分:4)

泛型的东西是他们必须考虑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;
        }
    }
}

或者,您可能根本不想处理null。相反,您可以替换

Arr[i] = null;

Arr[i] = default(T);

它会正常工作。 default(T)将为任何可空类型返回null,并为任何非可空类型返回默认值。 (int为0,bool为false等)

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

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

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

答案 1 :(得分:3)

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

如果您尝试允许GenericClass<int>等案例并让它公开int?的数组,那么请执行此操作。 where T : struct强制提供的类型是值类型,从而允许您使用T?。在这种情况下,您不能将对象类型用于泛型参数。

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

如果您正在尝试创建一个既支持对象实例又支持nullables的类,请尝试此操作。这里的问题是你不能创建一个类型约束 - 如果你使用where T : struct,你就不能使用对象类型;如果你使用where T : class,你就不能使用nullables。因此,此类型允许使用GenericClass<MyObject>(按您想要的方式工作),GenericClass<int?>(按您想要的方式工作)和GenericClass<int>(不能按您的方式工作),并且不可能使用无论如何你的工作方式)。不幸的是,没有办法定义这种通用来禁止后一种情况。但是,如果您愿意,可以在运行时进行检查。

class GenericClass<T>
{
    public T[] Arr { get; }
    public GenericClass(int n)
    {
        Arr = new T[n];
    }
}

答案 2 :(得分:2)

如果您知道T 必须是可以为空的类型,例如Nullable<Xyz>,则可以强制您的用户改为通过Xyz,并使{内部{1}}(又名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 } } 约束将阻止具有值类型的参数的where T : struct实例化。例如,尝试生成GenericClass<T>将导致编译时错误。

答案 3 :(得分:1)

如果它是值类型,您要做的是使用可空类型实例化您的类:

var obj = new GenericClass<int?>();

然后您可以使用default(T)而不是null而没有问题。您最终会得到一个int?[]数组,但您不需要显式使用它,因为数组中的每个元素都会在实例化数组时自动获取默认值。