zoukankan      html  css  js  c++  java
  • 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

  • 相关阅读:
    QLineEdit控件只能输入数字--QValidator结合正则
    谈 Linux,Windows 和 Mac (转自 王垠)
    看了王垠的文章《对Rust语言的分析》
    unsigned int 无符号整型的使用
    Qt布局Layout设置完全填充(设置Layout的Margin值)
    C#批量删除Mysql中相同前缀的表格
    libusb
    NPOI -- 读取excel表格中的时间格式
    spring项目启动执行
    kafka安全(一)SASL+ACL
  • 原文地址:https://www.cnblogs.com/atskyline/p/2547010.html
Copyright © 2011-2022 走看看