C#学习教程之泛型类型参数的限制约束和代码实例
在定义泛型类时,可以对客户端代码能够在实例化类时⽤于类型参数的类型种类施加限制。如果客户端代码尝试使⽤某个约束所不允许的类型来实例化类,则会产⽣编译时错误。这些限制称为约束。约束是使⽤ where 上下⽂关键字指定的。下表列出了六种类型的约束:
where T: struct
类型参数必须是值类型。可以指定除 Nullable 以外的任何值类型。有关更多信息,请参见使⽤可以为null的类型(C# 编程指南)。
where T : class
类型参数必须是引⽤类型;这⼀点也适⽤于任何类、接⼝、委托或数组类型。
where T:new()
类型参数必须具有⽆参数的公共构造函数。当与其他约束⼀起使⽤时,new() 约束必须最后指定。
where T:<;基类名>
类型参数必须是指定的基类或派⽣⾃指定的基类。
where T:<;接⼝名称>
类型参数必须是指定的接⼝或实现指定的接⼝。可以指定多个接⼝约束。约束接⼝也可以是泛型的。
where T:U
为 T 提供的类型参数必须是为 U 提供的参数或派⽣⾃为 U 提供的参数。
如果要检查泛型列表中的某个项以确定它是否有效,或者将它与其他某个项进⾏⽐较,则编译器必须在⼀定程度上保证它需要调⽤的运算符或⽅法将受到客户端代码可能指定的任何类型参数的⽀持。这种保证是通过对泛型类定义应⽤⼀个或多个约束获得的。例如,基类约束告诉编译器:仅此类型的对象或从此类型派⽣的对象才可⽤作类型参数。⼀旦编译器有了这个保证,它就能够允许在泛型类中调⽤该类型的⽅法。约束是使⽤上下⽂关键字 where 应⽤的。
public class Employee
{
private string name;
private int id;
public Employee(string s, int i)
{
name = s;
id = i;
}
public string Name
{
get { return name; }
set { name = value; }
}
public int ID
{
get { return id; }
set { id = value; }
}
}
public class GenericList<T> where T : Employee
{
private class Node
{
private Node next;
private T data;
public Node(T t)
{
next = null;
data = t;
}
public Node Next
{
get { return next; }
set { next = value; }
}
public T Data
{
get { return data; }
set { data = value; }
}
}
private Node head;
public GenericList() //constructor
{
head = null;
}
public void AddHead(T t)
{
Node n = new Node(t);
n.Next = head;
head = n;
}
public IEnumerator<T> GetEnumerator()
{
Node current = head;
while (current != null)
{
yield return current.Data;
current = current.Next;
}
}
public T FindFirstOccurrence(string s)
{
Node current = head;
T t = null;
while (current != null)
{writeline教程
//The constraint enables access to the Name property.
if (current.Data.Name == s)
{
t = current.Data;
break;
}
else
{
current = current.Next;
}
}
return t;
}
}
约束使得泛型类能够使⽤ Employee.Name 属性,因为类型为 T 的所有项都保证是 Employee 对象或从 Employee 继承的对象。
可以对同⼀类型参数应⽤多个约束,并且约束⾃⾝可以是泛型类型,如下所⽰:
class EmployeeList<T> where T : Employee, IEmployee, System.IComparable<T>, new()
{
// ...
}
通过约束类型参数,可以增加约束类型及其继承层次结构中的所有类型所⽀持的允许操作和⽅法调⽤的数量。因此,在设计泛型类或⽅法时,如果要对泛型成员执⾏除简单赋值之外的任何操作或调⽤ System.Object 不⽀持的任何⽅法,您将需要对该类型参数应⽤约束。
在应⽤where T : class约束时,避免对类型参数使⽤ == 和 != 运算符,因为这些运算符仅测试引⽤同⼀性⽽不测试值相等性。即使在⽤作参数的类型中重载这些运算符也是如此。下⾯的代码说明了这⼀点;即使类重载 == 运算符,输出也为 false。
public static void OpTest<T>(T s, T t) where T : class
{
System.Console.WriteLine(s == t);
}
static void Main()
{
string s1 = "target";
System.Text.StringBuilder sb = new System.Text.StringBuilder("target");
string s2 = sb.ToString();
OpTest<string>(s1, s2);
}
这种情况的原因在于,编译器在编译时仅知道 T 是引⽤类型,因此必须使⽤对所有引⽤类型都有效的默认运算符。如果必须测试值相等性,建议的⽅法是同时应
⽤where T : IComparable<T>约束,并在将⽤于构造泛型类的任何类中实现该接⼝。
约束多个参数
可以对多个参数应⽤约束,并对⼀个参数应⽤多个约束,如下⾯的⽰例所⽰:
class Base { }
class Test<T, U>
where U : struct
where T : Base, new() { }
未绑定的类型参数
没有约束的类型参数(如公共类 SampleClass<T>{} 中的 T)称为未绑定的类型参数。未绑定的类型参数具有以下规则:
不能使⽤ != 和 == 运算符,因为⽆法保证具体类型参数能⽀持这些运算符。
可以在它们与 System.Object 之间来回转换,或将它们显式转换为任何接⼝类型。
可以将它们与 null 进⾏⽐较。将未绑定的参数与 null 进⾏⽐较时,如果类型参数为值类型,则该⽐较将始终返回 false。
作为约束的类型参数
将泛型类型参数作为约束使⽤,在具有⾃⼰类型参数的成员函数必须将该参数约束为包含类型的类型参数时⾮常有⽤,如下⽰例所⽰:
class List<T>
{
void Add<U>(List<U> items) where U : T {/*...*/}
}
在上⾯的⽰例中,T 在 Add ⽅法的上下⽂中是⼀个类型约束,⽽在 List 类的上下⽂中是⼀个未绑定的类型参数。
类型参数还可在泛型类定义中⽤作约束。请注意,必须在尖括号中声明此类型参数与任何其他类型的参数:
//Type parameter V is used as a type constraint.
public class SampleClass<T, U, V> where T : V { }
泛型类的类型参数约束的作⽤⾮常有限,因为编译器除了假设类型参数派⽣⾃ System.Object 以外,不会做其他任何假设。在希望强制两个类型参数之间的继承关系的情况下,可对泛型类使⽤参数类型约束。
版权声明:本站内容均来自互联网,仅供演示用,请勿用于商业和其他非法用途。如果侵犯了您的权益请与我们联系QQ:729038198,我们将在24小时内删除。
发表评论