C# 泛型约束

C#允许您使用约束来限制客户端代码在实例化泛型类型时指定某些类型。如果您尝试使用指定约束不允许的类型实例化泛型类型,则会产生编译时错误。

可以在泛型类型名称后使用 where 子句在泛型类型上指定一个或多个约束。

GenericTypeName<T> where T  : contraint1, constraint2

下面的示例演示了在实例化泛型类时具有对引用类型的约束的泛型类。

class DataStore<T> where T : class
{
    public T Data { get; set; }
}

上面,我们应用了类约束,这意味着在创建DataStore类对象时,只能将引用类型作为参数传递。因此,您可以传递引用类型,例如类,接口,委托或数组类型。传递值类型将产生编译时错误,因此我们无法传递原始数据类型或结构类型。

DataStore<string> store = new DataStore<string>(); // 有效
DataStore<MyClass> store = new DataStore<MyClass>(); // 有效
DataStore<IMyInterface> store = new DataStore<IMyInterface>(); // 有效
DataStore<IEnumerable> store = new DataStore<IMyInterface>(); // 有效
DataStore<ArrayList> store = new DataStore<ArrayList>(); // 有效
//DataStore<int> store = new DataStore<int>(); // 编译时错误

下表列出了泛型约束的类型。

约束描述
class

类型参数必须是任何类、接口、委托或数组类型。

class?

类型参数必须是可空或不可空的类、接口、委托或数组类型。

struct

类型参数必须是非空值类型,如原始数据类型 int、 char、 bool、 float 等。

new()

类型参数必须是具有公共无参数构造函数的引用类型。它不能与结构约束和非托管约束组合。

notnull

从C# 8.0开始提供。类型参数可以是不可为null的引用类型或值类型。否则,编译器将生成警告而不是错误。

unmanaged

类型参数必须是不可空的非托管类型。

base class name

类型参数必须是或派生自指定的基类。不允许将 Object、 Array、 ValueType 类作为基类约束。

在 c # 7.3之前,Enum,Delegate,MulticastDelegate 不允许作为基类约束。

<base class name>?

类型参数必须是或派生自指定的可空或不可空的基类

<interface name>

类型参数必须是或实现指定的接口。

<interface name>?

类型参数必须是或实现指定的接口。它可以是可空引用类型、不可空引用类型或值类型

where T: U

为 T 提供的类型参数必须是或派生自为 U 提供的参数。

where T : struct

下面的示例演示将类型参数限制为不可为null的值类型的struct约束。

class DataStore<T> where T : struct
{
    public T Data { get; set; }
}

DataStore<int> store = new DataStore<int>(); // 有效
DataStore<char> store = new DataStore<char>(); // 有效
DataStore<MyStruct> store = new DataStore<MyStruct>(); // 有效
//DataStore<string> store = new DataStore<string>(); // 编译时错误 
//DataStore<IMyInterface> store = new DataStore<IMyInterface>(); // 编译时错误 
//DataStore<ArrayList> store = new DataStore<ArrayList>(); // 编译时错误

where T:基类

下面的示例演示将类型参数限制为指定类、抽象类或接口的派生类的基类约束。

class DataStore<T> where T : IEnumerable
{
    public T Data { get; set; }
}
DataStore<ArrayList> store = new DataStore<ArrayList>(); // 有效
DataStore<List> store = new DataStore<List>(); // 有效
//DataStore<string> store = new DataStore<string>(); // 编译时错误 
//DataStore<int> store = new DataStore<int>(); // 编译时错误 
//DataStore<IMyInterface> store = new DataStore<IMyInterface>(); // 编译时错误 
//DataStore<MyClass> store = new DataStore<MyClass>(); // 编译时错误