泛型类中的可为空类型



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

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 转换为类型参数"T",因为它可能是不可为空的值类型。考虑改用"默认(T)"。

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

泛型的问题在于,它们必须考虑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 类型的null和任何不可为空类型的默认值。(0 表示int,假表示bool,等等)

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

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

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

你实际上是在搬起石头砸自己的脚。.NET 中的数组会自动设置为类型的默认值,因此只要类型的默认值为 null(所有对象和可为 null),则只需创建数组就足以将所有项设置为 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];
}
}

如果您尝试创建同时支持对象实例和可为空对象的类,请尝试此操作。这里的问题是你不能做类型约束——如果你使用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];
}
}

如果您知道T必须是可为空的类型,例如Nullable<Xyz>,则可以强制用户传递Xyz,并在内部进行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>实例化。例如,尝试进行GenericType<string>将导致编译时错误。

您要做的是用可为 null 的类型实例化您的类(如果它是值类型):

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

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

相关内容

  • 没有找到相关文章

最新更新