C# 泛型

泛型是 2.0 版 C# 语言和公共语言运行库 (CLR) 中的一个新功能。

泛型将类型参数的概念引入 .NET Framework,类型参数使得设计如下类和方法成为可能:这些类和方法将一个或多个类型的指定推迟到客户端代码声明并实例化该类或方法的时候。例如,通过使用泛型类型参数 T,您可以编写其他客户端代码能够使用的单个类,而不致引入运行时强制转换或装箱操作的成本或风险。

  • 使用泛型类型可以最大限度地重用代码、保护类型的安全以及提高性能。

  • 泛型最常见的用途是创建集合类。

  • .NET Framework 类库在 System.Collections.Generic 命名空间中包含几个新的泛型集合类。应尽可能地使用这些类来代替普通的类,如 System.Collections 命名空间中的 ArrayList。

  • 您可以创建自己的泛型接口、泛型类、泛型方法、泛型事件和泛型委托。

  • 可以对泛型类进行约束以访问特定数据类型的方法。

  • 关于泛型数据类型中使用的类型的信息可在运行时通过反射获取。

类型参数的约束

在定义泛型类时,可以对客户端代码能够在实例化类时用于类型参数的类型种类施加限制。如果客户端代码尝试使用某个约束所不允许的类型来实例化类,则会产生编译时错误。这些限制称为约束。约束是使用 where 上下文关键字指定的。下表列出了六种类型的约束:
约束 说明

T:结构

类型参数必须是值类型。可以指定除 Nullable 以外的任何值类型。

T:类

类型参数必须是引用类型,包括任何类、接口、委托或数组类型。

T:new()

类型参数必须具有无参数的公共构造函数。当与其他约束一起使用时,new() 约束必须最后指定。

T:<基类名>

类型参数必须是指定的基类或派生自指定的基类。

T:<接口名称>

类型参数必须是指定的接口或实现指定的接口。可以指定多个接口约束。约束接口也可以是泛型的。

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)          {              //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 约束时,建议不要对类型参数使用 ==!= 运算符,因为这些运算符仅测试引用同一性而不测试值相等性。即使在用作参数的类型中重载这些运算符也是如此。下面的代码说明了这一点;即使 String 类重载 == 运算符,输出也为 false。

 
public static void OpTest<T>(T s, T t) where T : class  {      System.Console.WriteLine(s == t);  }  static void Main()  {      string s1 = "foo";      System.Text.StringBuilder sb = new System.Text.StringBuilder("foo");      string s2 = sb.ToString();      OpTest<string>(s1, s2);  }

这种情况的原因在于,编译器在编译时仅知道 T 是引用类型,因此必须使用对所有引用类型都有效的默认运算符。如果需要测试值相等性,建议的方法是同时应用 where T : IComparable<T> 约束,并在将用于构造泛型类的任何类中实现该接口。

C 泛型 - chy2z - 黑暗行动  未绑定的类型参数

没有约束的类型参数(如公共类 SampleClass<T>{} 中的 T)称为未绑定的类型参数。未绑定的类型参数具有以下规则:

  • 不能使用 !=== 运算符,因为无法保证具体类型参数能支持这些运算符。

  • 可以在它们与 System.Object 之间来回转换,或将它们显式转换为任何接口类型。

  • 可以将它们与 null 进行比较。将未绑定的参数与 null 进行比较时,如果类型参数为值类型,则该比较将始终返回 false。

C 泛型 - chy2z - 黑暗行动   裸类型约束

用作约束的泛型类型参数称为裸类型约束。当具有自己的类型参数的成员函数需要将该参数约束为包含类型的类型参数时,裸类型约束很有用,如下面的示例所示:

 
class List<T>  {      void Add<U>(List<U> items) where U : T {/*...*/}  }

在上面的示例中,TAdd 方法的上下文中是一个裸类型约束,而在 List 类的上下文中是一个未绑定的类型参数。

裸类型约束还可以在泛型类定义中使用。注意,还必须已经和其他任何类型参数一起在尖括号中声明了裸类型约束:

 
//naked type constraint  public class SampleClass<T, U, V> where T : V { }

泛型类的裸类型约束的作用非常有限,因为编译器除了假设某个裸类型约束派生自 System.Object 以外,不会做其他任何假设。在希望强制两个类型参数之间的继承关系的情况下,可对泛型类使用裸类型约束。

 

 

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值