我需要创建一个类似于这样的类:
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?[]
数组,尽管你不需要显式使用它,因为数组中的每个元素都会在数组实例化时自动获得默认值。