1. 简介:
2.0 版 C# 语言和公共语言运行时 (CLR) 中增加了泛型。 泛型将类型参数的概念引入 .NET Framework,类型参数使得设计如下类和方法成为可能:这些类和方法将一个或多个类型的指定推迟到客户端代码声明并实例化该类或方法的时候。
2. 语法:
class ClassName<Type>
{
public void Add(Type input)
{
}
}
class Program
{
public void test0()
{
ClassName<int> testClass = new ClassName<int>();
testClass.Add(5);
print<int>(5, 6);
print(5, 6); // <>可以省略
}
public void print<T1>(T1 x, T1 y)
{
Console.WriteLine(x.ToString(),y.ToString());
}
}
3. 约束:
1> 约束简介
在定义泛型类时,可以对客户端代码能够在实例化类时用于类型参数的类型种类施加限制。 如果客户端代码尝试使用某个约束所不允许的类型来实例化类,则会产生编译时错误。 这些限制称为约束。 约束是使用 where
上下文关键字指定的。 下表列出了六种类型的约束:
约束 | 说明 |
---|---|
T : struct | 类型参数必须是值类型。 |
T : class | 类型参数必须是引用类型;这一点也适用于任何类、接口、委托或数组类型。 |
T:new() | 类型参数必须具有无参数的public构造函数。 当与其他约束一起使用时,new() 约束必须最后指定。 |
T:<基类名> | 类型参数必须是指定的基类或派生自指定的基类。 |
T:<接口名称> | 类型参数必须是指定的接口或实现指定的接口。 可以指定多个接口约束。 约束接口也可以是泛型的。 |
T:U | 为 T 提供的类型参数必须是为 U 提供的参数或派生自为 U 提供的参数。 |
2> 约束简介案例
class TestClass<TestType> where TestType : struct
{
}
static void Main(string[] args)
{
TestClass<int> t1 = new TestClass<int>();
//TestClass<string> t2 = new TestClass<string>(); 错误,只能传递值类型
}
3> 使用泛型约束的原因(优势)
如果要检查泛型列表中的某个项以确定它是否有效,或者将它与其他某个项进行比较,则编译器必须在一定程度上保证它需要调用的运算符或方法将受到客户端代码可能指定的任何类型参数的支持。 这种保证是通过对泛型类定义应用一个或多个约束获得的。 例如,基类约束告诉编译器:仅此类型的对象或从此类型派生的对象才可用作类型参数。 一旦编译器有了这个保证,它就能够允许在泛型类中调用该类型的方法。
可以对同一类型参数应用多个约束,并且约束自身可以是泛型类型,如下所示:
class EmployeeList<T> where T : Employee, IEmployee, System.IComparable<T>, new()
{
// ...
}
4> 泛型约束的注意点:
通过约束类型参数,可以增加约束类型及其继承层次结构中的所有类型所支持的允许操作和方法调用的数量。 因此,在设计泛型类或方法时,如果要对泛型成员执行除简单赋值之外的任何操作或用 System.Object不支持的任何方法,您将需要对该类型参数应用约束。在应用 where T : class 约束时,避免对类型参数使用 == 和 != 运算符,因为这些运算符仅测试引用同一性而不测试值相等性。
5> 约束多个参数
可以对多个参数应用约束,并对一个参数应用多个约束,如下面的示例所示
class Base { }
class Test<T, U>
where U : struct
where T : Base, new()
{ }
6> 未绑定的类型参数
没有约束的类型参数(如公共类 SampleClass<T>{}中的 T)称为未绑定的类型参数。 未绑定的类型参数具有以下规则:
不能使用 !=和 == 运算符,因为无法保证具体类型参数能支持这些运算符。
可以在它们与 System.Object之间来回转换,或将它们显式转换为任何接口类型。
可以将它们与 [null] 进行比较。 将未绑定的参数与 null进行比较时,如果类型参数为值类型,则该比较将始终返回 false。
7> 作为约束的类型参数
将泛型类型参数作为约束使用,在具有自己类型参数的成员函数必须将该参数约束为包含类型的类型参数时非常有用,如下示例所示:
class List<T>
{
void Add<U>(List<U> items) where U : T {/*...*/}
}
在上面的示例中,T在 Add方法的上下文中是一个类型约束,而在 List 类的上下文中是一个未绑定的类型参数。
类型参数还可在泛型类定义中用作约束。 请注意,必须在尖括号中声明此类型参数与任何其他类型的参数:
SampleClass<T, U, V> where T : V { }
8> 为泛型方法添加约束
void SwapIfGreater<T>(ref T lhs, ref T rhs) where T : System.IComparable<T>
{
T temp;
if (lhs.CompareTo(rhs) > 0)
{
temp = lhs;
lhs = rhs;
rhs = temp;
}
}
泛型方法可以使用许多类型参数进行重载。 例如,下列方法可以全部位于同一个类中:
void DoWork() { }
void DoWork<T>() { }
void DoWork<T, U>() { }
4. 泛型接口:
语法:
interface ITest1<K>
{
}
一个接口可定义多个类型参数,如下所示:
interface ITest1<K, V>
{
}
适用于类的继承规则同样适用于接口:
interface ITest1<S>
{
}
interface ITest2<K> : ITest1<K>
{
}
泛型接口的约束:
interface ITest1<S> where S : class
{
}
泛型接口的约束继承
interface ITest1<S> where S : class
{
}
interface ITest2<K> : ITest1<K> where K : class
{
}