C#泛型简单使用

先简单的说一下泛型的作用,假如我们设计一个支持整形的排序函数,然后又需要一个支持浮点类型的排序函数,这样的写两份相似的代码,违反了OOP的原则。利用转型为object,又有封箱拆箱和安全性的问题。所以我们利用泛型,让程序到运行时才去确定到底是要用什么类型。

C#的泛型类似于C++中的模版但是不论在设计上还是功能上都有很多不同。具体可以参考MSDN中的介绍。

泛型的使用

先看一段代码

   1:      class Program
   2:      {
   3:          static void TestMethod(U u1)
   4:          {
   5:              Console.WriteLine(u1.ToString());
   6:          }
   7:          static void Main(string[] args)
   8:          {
   9:              int number = 10;
  10:              string str = "String";
  11:              TestMethod<int>(number);
  12:              TestMethod<string>(str);
  13:              Console.ReadKey();
  14:          }
  15:      }

可以看出TestMethod函数设计时并没有设定特定的参数类型,直到运行时才确定具体的类型是int还是string 或者其他类型。

明显可以看出使用泛型大大提高了代码的复用率。

C#泛型的机制

C#泛型能力有CLR在运行时支持

C#泛型代码在编译为IL代码和元数据时,采用特殊的占位符来表示范型类型,并用专有的IL指令支持泛型操作。

而真正的泛型实例化工作以“on-demand”的方式,发生在JIT编译时。

第一轮编译时,编译器只产生“泛型版”的IL代码与元数据——并不进行泛型的实例化,T在中间只充当占位符。

JIT编译时,当JIT编译器第一次遇到泛型版时,将用特定的类型(如int)替换“范型版”IL代码与元数据中的T——进行泛型类型的实例化。

CLR为所有类型参数为“引用类型”的泛型类型产生同一份代码;但是如果类型参数为“值类型”,对每一个不同的“值类型”,CLR将为其产生一份独立的代码。

因为实例化一个引用类型的泛型,它在内存中分配的大小是一样的,但是当实例化一个值类型的时候,在内存中分配的大小是不一样的。

泛型类与泛型委托

泛型不仅支持方法,还支持类,委托,接口等。

具体的使用于泛型方法类似

   1:      //泛型类
   2:      public class Test
   3:      {
   4:          private T member;
   5:          public Test(T member)
   6:          {
   7:              this.member = member;
   8:          }
   9:          public void Output()
  10:          {
  11:              Console.WriteLine(member.ToString());
  12:          }
  13:      }
  14:      class Program
  15:      {
  16:          static void Main(string[] args)
  17:          {
  18:              Test<int> t1 = new Test<int>(10);
  19:              Test<string> t2 = new Test<string>("string");
  20:              t1.Output();
  21:              t2.Output();
  22:              Console.ReadKey();
  23:          }
  24:      }
  25:      
  26:      //泛型委托
  27:      class Program
  28:      {
  29:          private delegate void TestDelegate(T output);
  30:   
  31:          static void Test(T output)
  32:          {
  33:              Console.WriteLine(output.ToString());
  34:          }
  35:   
  36:          static void Main(string[] args)
  37:          {
  38:              TestDelegate<int> d1;
  39:              d1 = Test<int>;
  40:              TestDelegate<string> d2;
  41:              d2 = Test<string>;
  42:   
  43:              d1(10);
  44:              d2("string");
  45:   
  46:              Console.ReadKey();
  47:          }
  48:      }

泛型约束

上面的泛型都是可以支持任何类型,但是我们有时候希望,他只要支持某些类和他的子类。或者说只支持值类型,甚至是两个泛型参数之间有继承关系。

这一些都可以利用where关键字来限定,这些限定我们成为泛型约束。

T:struct 类型参数必须是值类型。

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

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

T:<基类名> 类型参数必须是指定的基类或派生自指定的基类。(不可以于T:class一起使用)

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

T:U 为 T 提供的类型参数必须是为 U 提供的参数或派生自为 U 提供的参数。这称为裸类型约束。

   1:      class TestBase
   2:      {
   3:          public int foo;
   4:          
   5:          public TestBase()
   6:          {
   7:              foo = 10;
   8:          }
   9:   
  10:          public TestBase(int f)
  11:          {
  12:              foo = f;
  13:          }
  14:      }
  15:   
  16:      class Program
  17:      {
  18:          static void TestMethod(T t1) where T:TestBase where U:T,new()
  19:          {
  20:              U u1 = new U();
  21:              Console.WriteLine("{0} + {1}",t1.foo,u1.foo);
  22:          }
  23:          static void Main(string[] args)
  24:          {
  25:              TestBase t = new TestBase(11);
  26:              TestMethod(t);
  27:              Console.ReadKey();
  28:          }
  29:      }

这些限定让泛型有了很大的规范性,能避免很多问题。

泛型类继承

关于泛型类的继承问题。有一个简单的原则基类如果是泛型类,它的类型要么以实例化,要么来源于子类(同样是泛型类型)声明的类型参数

   1:      class C { } 
   2:      class D : C<string, int> { } 
   3:      class E : C { } 
   4:      class F : C<string, int> { }
   5:      //基类如果是泛型类,他的类型参数要么已实例化,
   6:      //要么来源子类(同样是泛型类型)声明的类型参数。
   7:      //class G : C { } 非法

关于泛型的多态

由于泛型是运行时才能确定具体的类型,在多态方面有一点点特别,容易产生二义性,如果同时存在确定类型的方法会默认先调用确定类型的方法,而后才寻找可能的泛型方法。

   1:      public class Test
   2:      {
   3:          public void TestMethod(T t1, U u1)
   4:          {
   5:              Console.WriteLine("1");
   6:          }
   7:          public void TestMethod(U u1, T t1)
   8:          {
   9:              Console.WriteLine("2");
  10:          }
  11:          public void TestMethod(int t, int u)
  12:          {
  13:              Console.WriteLine("3");
  14:          }
  15:      }
  16:      class Program
  17:      {
  18:          static void Main(string[] args)
  19:          {
  20:              Test<int,int> t1 = new Test<int,int>();
  21:              t1.TestMethod(123, 123);//call public void TestMethod(int t, int u)
  22:   
  23:              Test<string, int> t2 = new Test<string, int>();
  24:              t2.TestMethod("123", 123);//call public void TestMethod(T t1, U u1)
  25:   
  26:              //Test t3 = new Test();
  27:              //t3.TestMethod("123", "123");//产生二义性
  28:   
  29:              Console.ReadKey();
  30:          }
  31:      }
 
 
最后给出一堆参考资料

http://msdn.microsoft.com/zh-cn/library/sx2bwtw7%28v=VS.80%29.aspx
http://msdn.microsoft.com/zh-cn/library/twcad0zb%28v=VS.80%29.aspx
http://msdn.microsoft.com/zh-cn/library/sz6zd40f%28v=VS.80%29.aspx
http://msdn.microsoft.com/zh-cn/library/0x6a29h6%28v=VS.80%29.aspx
http://msdn.microsoft.com/zh-cn/library/b5bx6xee%28v=VS.80%29.aspx
http://msdn.microsoft.com/zh-cn/library/d5x73970%28v=VS.80%29.aspx
http://msdn.microsoft.com/zh-cn/library/c6cyy67b%28v=VS.80%29.aspx
http://msdn.microsoft.com/zh-cn/library/xwth0h0d%28v=VS.80%29.aspx
http://www.cnblogs.com/lianyonglove/archive/2007/04/20/720682.html
http://dev.csdn.net/htmls/80/80696.html
http://www.cnblogs.com/kid-li/archive/2006/11/29/577045.html
http://www.blogjava.net/Jack2007/archive/2008/05/05/198566.html

原文地址:https://www.cnblogs.com/atskyline/p/2547010.html