泛型编程

泛型概述
 所谓泛型,即通过参数化类型来实现在同一份代码上操作多种数据类型。泛型编程是一种编程范式,它利用“参数化类型”将类型抽象化,从而实现更为灵活的复用。C#泛型赋予了代码更强的类型安全,更好的复用,更高的效率,更清晰的约束(这个特点褒贬不一)。

C#泛型机制简介
 A。C#泛型能力由CLR在运行时支持,区别于C++的编译时模板机制,和Java的编译时“搽拭法”。(C++所有的泛型的处理都在编译时,运行时看不到泛型。Java编译器实际上只是做了类型安全的控制,不能获得高更的效率。)这使得泛型能力可以在各个支持CLR的语言(如VB.NET)之间进行无缝的互操作。
 B。C#泛型代码在被编译为IL代码和元数据时,采用特殊的占位符来表示泛型类型,并用专有的IL指令支持泛型操作。而真正的泛型实例化工作以“on-demand”的方式(按需所取,当代码真正碰到实例化的代码时才去实例化。比如一个方法对泛型进行了实例化,但程序没有去调用这个方法,则这个实例化工作就不会发生),发生在JIT编译时。
 反编译工具路径(查看IL代码):C:\Program Files\Microsoft Visual Studio 8\SDK\v2.0\Bin\ildasm.exe

C#泛型编译机制
 A。第一轮编译时,编译器只为Stack<T>类型产生“泛型版”的IL代码与元数据--并不进行泛型类型的实例化,T在中间只充当占位符。
 B。JIT编译时,当JIT编译器第一次遇到Stack<int>时,将用int替代“泛型版”IL代码与元数据中的T--进行泛型类型的实例化,此时T才成为一个可用的类型。(以后再遇到Stack<int>时会拿内存中存放的上次实例化过的类型,不会再实例化。但如果是新的类型,如Stack<byte>,则会再一次实例化,放到内存中。)
 C。CLR为所有类型参数为“引用类型”的泛型类型产生同一份代码;但如果类型参数为“值类型”,对每一个不同的“值类型”,CLR将为其产生一份独立的代码。

C#泛型的几个特点
 A。如果实例化泛型的参数相同,那么JIT编译器会重复使用该类型,因此C#的动态泛型能力避免了C++静态模板可能导致的代码膨胀的问题。
 B。C#泛型类型携带有丰富的元数据,因此C#的泛型类型可以应用于强大的反射技术。
 C。C#的泛型采用“基类,接口,构造器,值类型/引用类型”的约束方式来实现对类型参数的“显式约束”,提高了类型安全的同时,也丧失了C++模板基于“签名”的隐式约束所具有的高灵活性。

C#泛型类与结构
 class C<U,V>{} //合法
 class D:C<string,int>{} //合法(泛型类型可以作为父类被继承)
 class E<U,V>:C<U,V>{} //合法(父类C的类型参数使用了子类E的类型参数)
 class F<U,V>:C<string,int>{} //合法(父类可以不使用子类的类型参数,直接实例化)
 class G:C<U,V>{} //非法(子类是具体类型,父类是泛型类型但没有实例化。当使用G时,不用去实例化,那么C中的U和V是不确定的。所以这样定义是非法的)
 C#除可单独声明泛型类型(包括类与结构)外,也可以在基类中包含泛型类型的声明。但基类如果是泛型类,它的类型参数要么已实例化,要么来源于子类(同样是泛型类型)声明的类型参数。
 
泛型类型的成员
 class C<V>{
  public V f1; //声明字段
  public D<V> f2; //作为其它泛型类型的参数
  public C(V x){
   this.f1 = x;
  }
 }
 泛型类型的成员可以使用泛型类型声明中的类型参数。但类型参数如果没有任何约束,则只能在该类型上使用从System.Object继承的公有成员。

泛型接口
 interface IList<T>{
  T[] GetElements(); //泛型数组
 }
 interface IDictionary<K,V>{
  void Add(K key, V value);
 }
 //泛型接口的类型参数要么已实例化,要么来源于实现类声明的类型参数,要么来源于实现类声明的类型参数
 class List<T>:IList<T>,IDictionary<int,T>{
  public T[] GetElements() { return null; }
  public void Add(int index,T value) {}
 }

泛型委托
 delegate bool Predicate<T>(T value);
 class X{
  static bool F(int i) { ... }
  static bool G(string s) { ... }
  static void Main(){
   Predicate<string> p2 = G; //简略写法
   Predicate<int> p1 = new Predicate<int>(F);
  }
 }
 泛型委托支持在委托返回值和参数上应用参数类型,这些参数类型同样可以附带合法的约束。

泛型方法
 A。C#泛型机制只支持“在方法声明上包含类型参数”--即泛型方法。
 B。C#泛型机制不支持在除在方法外的其它成员(包括属性、事件、索引器、构造器、析构器)的声明上包含类型参数,但这些成员本身可以包含在泛型类型中,并使用泛型类型的类型参数。
 C。泛型方法既可以包含在泛型类型中,也可以包含在非泛型类型中。
 
泛型方法的声明与调用
 public class Finder{
  //泛型方法的声明
  public static int Find<T> (T[] items,T item){
   for(int i=0;i<items.Length;i++){
    if(items[i].Equals(item)) { return i; }
   }
   return -1;
  }
 }
 //泛型方法的调用
 int i = Finder.Find<int> (new int[]{1,2,3,4,5},3);

泛型方法的重载
 class MyClass{
  void F1<T>(T[] a,int i); //不可以构成重载方法(实例化时没办法区分调用的方法)
  void F1<U>(U[] a,int i);
  
  void F2<T>(int x); //可以构成重载方法
  void F2(int x);
  
  void F3<T>(T t) where T:A; //不可以构成重载方法
  void F3<T>(T t) where T:B;
 }
 
泛型方法的重写
 abstract class Base
 {
  public abstract T F<T,U>(T t,U u) where U:T;
  public abstract T G<T>(T t) where T:IComparable;
 }
 class Derived:Base{
  //合法的重写,约束被默认继承
  public override X F<X,Y>(X x,Y y) {}
  //非法的重写,指定任何约束都是多余的(不管是重新写一遍父类的约束,又或是想添加新的约束都是不合法的)
  public override T G<T>(T t) where T:IComparable {}
 }

泛型约束
 A。C#泛型要求对“所有泛型类型或泛型方法的类型参数”的任何假定,都要基于“显示的约束”,以维护C#所要求的类型安全。
 B。“显式约束”由where子句表达,可以指定“基类约束”,“接口约束”,“构造器约束”,“值类型/引用类型约束”共四种约束。
 C。“显式约束”并非必须,如果没有指定“显式约束”,泛型类型参数将只能访问System.Object类型中的公有方法。

基类约束
 class A { public void F1() { ... } }
 class B { public void F2() { ... } }
 class C<S,T> where S:A where T:B //S必须继承于A,T必须继承于B
 {
  //可以在类型为S的变量上调用F1,在类型为T的变量上调用F2
 }

接口约束
 interface IPrintable { void Print(); }
 interface IComparable<T> { int CompareTo(T v);}
 interface IKeyProvider<T> { T GetKey(); }
 class Dictionary<K,V> where K:IComparable<K> where V:IPrintable,IKeyProvider<K> //K必须实现IComparable,V必须实现IPrintable和IKeyProvider
 {
  //可以在类型为K的变量上调用CompareTo,在类型为V的变量上调用Print和GetKey
 }

构造器约束:
 class A { public A(){} }
 class B { public B(int i){} }
 class C<T> where T:new() //实例类型必须存在无参构造器
 {
  //可以在其中使用T t = new T();
 }
 C<A> c = new C<A>(); //可以,A有无参构造器
 C<B> c = new C<B>(); //错误,B没有无参构造器(因为自定义了一个构造器后C#不会自动产生一个无参构造器)

值类型/引用类型约束:
 public struct A { ... }
 public class B { ... }
 class C<T> where T:struct
 {
  //T在这里面是一个值类型
 }
 C<A> c = new C<A>(); //可以,A是一个值类型
 C<B> c = new C<B>(); //错误,B是一个引用类型


其它资料

泛型的类型安全
 ArrayList al = new ArrayList();  //引入System.Collections
 al.Add(1);
 al.Add(2);
 //al.Add(3.0);  //加上这句,编译通过但运行出错
 int total = 0;
 foreach (int var in al)
 {
     total += var;
 }
 Console.WriteLine("Total is {0}", total);
 
 List<int> aLst = new List<int>();  //引入System.Collections.Generics
 aLst.Add(1);
 aLst.Add(2);
 //aLst.Add(3.0);  //加上这句,编译通不过
 int total2 = 0;
 foreach (int var in aLst)
 {
     total2 += var;
 }
 Console.WriteLine("Total2 is {0}", total2);


.NET CLR识别泛型
 public class MyList<T>  //泛型类
 {
     private static int objCount = 0;
     public MyList() { objCount++; }
     public int Count
     {
         get { return objCount; }
     }
 }
 public class SampleClass
 { }
 static void Main(string[] args)
 {
     MyList<int> myIntList = new MyList<int>();
     MyList<int> myIntList2 = new MyList<int>();
     MyList<double> myDoubleList = new MyList<double>();
     MyList<SampleClass> mySampleList = new MyList<SampleClass>();
     Console.WriteLine(myIntList.Count);  //2
     Console.WriteLine(myIntList2.Count);  //2
     Console.WriteLine(myDoubleList.Count);  //1
     Console.WriteLine(mySampleList.Count);  //1
     Console.WriteLine(new MyList<SampleClass>().Count);  //2
 }

泛型方法:
public static void Copy<T>(List<T> source, List<T> destination)
{
    foreach (T obj in source)
    {
        destination.Add(obj);
    }
}
static void Main(string[] args)
{
    List<int> lst1 = new List<int>();
    lst1.Add(2);
    lst1.Add(4);
    List<int> lst2 = new List<int>();
    Copy(lst1, lst2);
    Console.WriteLine(lst2.Count);
}

泛型的意义何在:
 A。泛型可以减少重复的代码,类似的函数,如double和int型的比较函数,可以通过一个泛型方法实现。
 B。类型安全和减少装箱、拆箱(指定泛型方法/类的参数类型),并不是泛型的意义,而是泛型带来的两个最明显的好处而已。
 C。泛型的真正意义在于:把类型作为参数。它实现了代码之间的很好的横向联系(继承为代码提供了一种从上往下的纵向联系)。

原文地址:https://www.cnblogs.com/vipcjob/p/1555809.html