这篇文章主要来讲讲c#中的泛型,因为泛型在c#中有很重要的位置,对于写出高可读性,高性能的代码有着关键的作用。当我多次看到自己团队的代码中包含着大量的非泛型集合,隐式的装箱和拆箱操作时,我都会建议他们补一补泛型基
这篇文章主要来讲讲c#中的泛型,因为泛型在c#中有很重要的位置,对于写出高可读性,高性能的代码有着关键的作用。当我多次看到自己团队的代码中包含着大量的非泛型集合,隐式的装箱和拆箱操作时,我都会建议他们补一补泛型基础。
1,什么是泛型
2,为什么要使用泛型,泛型解决了什么问题
我们先来看看下面的代码(代码只是为了演示泛型,没有实际的意义),看看有什么问题?
<span style="color: #008080;"> 1</span> <span style="color: #0000ff;">class</span><span style="color: #000000;"> PRogram </span><span style="color: #008080;"> 2</span> <span style="color: #000000;"> { </span><span style="color: #008080;"> 3</span> <span style="color: #0000ff;">static</span> <span style="color: #0000ff;">void</span> Main(<span style="color: #0000ff;">string</span><span style="color: #000000;">[] args) </span><span style="color: #008080;"> 4</span> <span style="color: #000000;"> { </span><span style="color: #008080;"> 5</span> ArrayList array = <span style="color: #0000ff;">new</span><span style="color: #000000;"> ArrayList(); </span><span style="color: #008080;"> 6</span> array.Add(<span style="color: #800080;">1</span><span style="color: #000000;">); </span><span style="color: #008080;"> 7</span> array.Add(<span style="color: #800080;">2</span><span style="color: #000000;">); </span><span style="color: #008080;"> 8</span> array.Add(<span style="color: #800080;">3</span><span style="color: #000000;">); </span><span style="color: #008080;"> 9</span> ArrayList resultArray =<span style="color: #000000;"> NumberSqrt(array); </span><span style="color: #008080;">10</span> <span style="color: #0000ff;">foreach</span> (<span style="color: #0000ff;">var</span> item <span style="color: #0000ff;">in</span><span style="color: #000000;"> resultArray) </span><span style="color: #008080;">11</span> <span style="color: #000000;"> { </span><span style="color: #008080;">12</span> <span style="color: #000000;"> Console.WriteLine(item); </span><span style="color: #008080;">13</span> <span style="color: #000000;"> } </span><span style="color: #008080;">14</span> <span style="color: #000000;"> } </span><span style="color: #008080;">15</span> <span style="color: #0000ff;">public</span> <span style="color: #0000ff;">static</span><span style="color: #000000;"> ArrayList NumberSqrt(ArrayList array) </span><span style="color: #008080;">16</span> <span style="color: #000000;"> { </span><span style="color: #008080;">17</span> ArrayList sqrtArray = <span style="color: #0000ff;">new</span><span style="color: #000000;"> ArrayList(); </span><span style="color: #008080;">18</span> <span style="color: #0000ff;">foreach</span> (<span style="color: #0000ff;">var</span> item <span style="color: #0000ff;">in</span><span style="color: #000000;"> array) </span><span style="color: #008080;">19</span> <span style="color: #000000;"> { </span><span style="color: #008080;">20</span> sqrtArray.Add(Math.Sqrt((<span style="color: #0000ff;">double</span>)(<span style="color: #0000ff;">int</span><span style="color: #000000;">)item)); </span><span style="color: #008080;">21</span> <span style="color: #000000;"> } </span><span style="color: #008080;">22</span> <span style="color: #0000ff;">return</span><span style="color: #000000;"> sqrtArray; </span><span style="color: #008080;">23</span> <span style="color: #008080;">24</span> <span style="color: #000000;"> } </span><span style="color: #008080;">25</span> }
下面来看看泛型是如何解决这些问题的:
<span style="color: #008080;"> 1</span> <span style="color: #0000ff;">class</span><span style="color: #000000;"> Program </span><span style="color: #008080;"> 2</span> <span style="color: #000000;"> { </span><span style="color: #008080;"> 3</span> <span style="color: #0000ff;">static</span> <span style="color: #0000ff;">void</span> Main(<span style="color: #0000ff;">string</span><span style="color: #000000;">[] args) </span><span style="color: #008080;"> 4</span> <span style="color: #000000;"> { </span><span style="color: #008080;"> 5</span> Listdouble> array = <span style="color: #0000ff;">new</span> Listdouble><span style="color: #000000;">(); </span><span style="color: #008080;"> 6</span> array.Add(<span style="color: #800080;">1</span><span style="color: #000000;">); </span><span style="color: #008080;"> 7</span> array.Add(<span style="color: #800080;">2</span><span style="color: #000000;">); </span><span style="color: #008080;"> 8</span> array.Add(<span style="color: #800080;">3</span><span style="color: #000000;">); </span><span style="color: #008080;"> 9</span> Listdouble> resultArray =<span style="color: #000000;"> NumberSqrt(array); </span><span style="color: #008080;">10</span> <span style="color: #0000ff;">foreach</span> (<span style="color: #0000ff;">var</span> item <span style="color: #0000ff;">in</span><span style="color: #000000;"> resultArray) </span><span style="color: #008080;">11</span> <span style="color: #000000;"> { </span><span style="color: #008080;">12</span> <span style="color: #000000;"> Console.WriteLine(item); </span><span style="color: #008080;">13</span> <span style="color: #000000;"> } </span><span style="color: #008080;">14</span> <span style="color: #000000;"> } </span><span style="color: #008080;">15</span> <span style="color: #0000ff;">public</span> <span style="color: #0000ff;">static</span> Listdouble> NumberSqrt(Listdouble><span style="color: #000000;"> array) </span><span style="color: #008080;">16</span> <span style="color: #000000;"> { </span><span style="color: #008080;">17</span> Listdouble> sqrtArray = <span style="color: #0000ff;">new</span> Listdouble><span style="color: #000000;">(); </span><span style="color: #008080;">18</span> <span style="color: #0000ff;">foreach</span> (<span style="color: #0000ff;">var</span> item <span style="color: #0000ff;">in</span><span style="color: #000000;"> array) </span><span style="color: #008080;">19</span> <span style="color: #000000;"> { </span><span style="color: #008080;">20</span> sqrtArray.Add(Math.Sqrt((<span style="color: #0000ff;">double</span>)(<span style="color: #0000ff;">int</span><span style="color: #000000;">)item)); </span><span style="color: #008080;">21</span> <span style="color: #000000;"> } </span><span style="color: #008080;">22</span> <span style="color: #0000ff;">return</span><span style="color: #000000;"> sqrtArray; </span><span style="color: #008080;">23</span> <span style="color: #008080;">24</span> <span style="color: #000000;"> } </span><span style="color: #008080;">25</span> <span style="color: #008080;">26</span> }
3,如何使用泛型:语法和规则
上面已经说了什么是泛型,以及为什么要用泛型,下面我们来聊聊如何使用泛型
<span style="color: #008080;"> 1</span> <span style="color: #0000ff;">public</span> <span style="color: #0000ff;">class</span> Demo<t><span style="color: #000000;"> { } </span><span style="color: #008080;"> 2</span> <span style="color: #0000ff;">class</span><span style="color: #000000;"> Program </span><span style="color: #008080;"> 3</span> <span style="color: #000000;"> { </span><span style="color: #008080;"> 4</span> <span style="color: #0000ff;">static</span> <span style="color: #0000ff;">void</span> Main(<span style="color: #0000ff;">string</span><span style="color: #000000;">[] args) </span><span style="color: #008080;"> 5</span> <span style="color: #000000;"> { </span><span style="color: #008080;"> 6</span> <span style="color: #0000ff;">object</span> o = <span style="color: #0000ff;">null</span><span style="color: #000000;">; </span><span style="color: #008080;"> 7</span> Type t = <span style="color: #0000ff;">typeof</span>(Demo<span style="color: #000000;">); </span><span style="color: #008080;"> 8</span> o =<span style="color: #000000;"> Activator.CreateInstance(t); </span><span style="color: #008080;"> 9</span> <span style="color: #000000;"> } </span><span style="color: #008080;">10</span> }</t>
如果为泛型类型的所有类型实参传递的都是实际数据类型,类型就称为封闭类型,CLR允许构造封闭类型的实例。
<span style="color: #008080;"> 1</span> <span style="color: #008000;">//</span><span style="color: #008000;"> 摘要: </span><span style="color: #008080;"> 2</span> <span style="color: #008000;">//</span><span style="color: #008000;"> 支持在泛型集合上进行简单迭代。 </span><span style="color: #008080;"> 3</span> <span style="color: #008000;">//</span> <span style="color: #008080;"> 4</span> <span style="color: #008000;">//</span><span style="color: #008000;"> 类型参数: </span><span style="color: #008080;"> 5</span> <span style="color: #008000;">//</span><span style="color: #008000;"> T: </span><span style="color: #008080;"> 6</span> <span style="color: #008000;">//</span><span style="color: #008000;"> 要枚举的对象的类型。</span> <span style="color: #008080;"> 7</span> <span style="color: #0000ff;">public</span> <span style="color: #0000ff;">interface</span> IEnumeratorout T><span style="color: #000000;"> : IDisposable, IEnumerator </span><span style="color: #008080;"> 8</span> <span style="color: #000000;"> { </span><span style="color: #008080;"> 9</span> <span style="color: #008000;">//</span><span style="color: #008000;"> 摘要: </span><span style="color: #008080;">10</span> <span style="color: #008000;">//</span><span style="color: #008000;"> 获取集合中位于枚举数当前位置的元素。 </span><span style="color: #008080;">11</span> <span style="color: #008000;">//</span> <span style="color: #008080;">12</span> <span style="color: #008000;">//</span><span style="color: #008000;"> 返回结果: </span><span style="color: #008080;">13</span> <span style="color: #008000;">//</span><span style="color: #008000;"> 集合中位于枚举数当前位置的元素。</span> <span style="color: #008080;">14</span> T Current { <span style="color: #0000ff;">get</span><span style="color: #000000;">; } </span><span style="color: #008080;">15</span> }
<span style="color: #008080;"> 1</span> <span style="color: #0000ff;">class</span><span style="color: #000000;"> Program </span><span style="color: #008080;"> 2</span> <span style="color: #000000;"> { </span><span style="color: #008080;"> 3</span> <span style="color: #0000ff;">static</span> <span style="color: #0000ff;">void</span> Main(<span style="color: #0000ff;">string</span><span style="color: #000000;">[] args) </span><span style="color: #008080;"> 4</span> <span style="color: #000000;"> { </span><span style="color: #008080;"> 5</span> MyClassstring> myclass = <span style="color: #0000ff;">new</span> MyClassstring><span style="color: #000000;">(); </span><span style="color: #008080;"> 6</span> myclass.ShowInfo(<span style="color: #800000;">"</span><span style="color: #800000;">myClass</span><span style="color: #800000;">"</span><span style="color: #000000;">); </span><span style="color: #008080;"> 7</span> <span style="color: #000000;"> } </span><span style="color: #008080;"> 8</span> <span style="color: #000000;"> } </span><span style="color: #008080;"> 9</span> <span style="color: #008080;">10</span> <span style="color: #0000ff;">class</span> MyClass<ta> <span style="color: #008080;">11</span> <span style="color: #000000;"> { </span><span style="color: #008080;">12</span> <span style="color: #0000ff;">public</span> <span style="color: #0000ff;">void</span><span style="color: #000000;"> ShowInfo(Ta a) </span><span style="color: #008080;">13</span> <span style="color: #000000;"> { </span><span style="color: #008080;">14</span> Type t = <span style="color: #0000ff;">typeof</span><span style="color: #000000;">(Ta); </span><span style="color: #008080;">15</span> Console.WriteLine(t.FullName + <span style="color: #800000;">"</span><span style="color: #800000;">非泛型方法</span><span style="color: #800000;">"</span><span style="color: #000000;">); </span><span style="color: #008080;">16</span> <span style="color: #000000;"> } </span><span style="color: #008080;">17</span> <span style="color: #0000ff;">public</span> <span style="color: #0000ff;">void</span> ShowInfo<ta><span style="color: #000000;">(Ta a) </span><span style="color: #008080;">18</span> <span style="color: #000000;"> { </span><span style="color: #008080;">19</span> Type t = <span style="color: #0000ff;">typeof</span><span style="color: #000000;">(Ta); </span><span style="color: #008080;">20</span> Console.WriteLine(t.FullName + <span style="color: #800000;">"</span><span style="color: #800000;">泛型方法</span><span style="color: #800000;">"</span><span style="color: #000000;">); </span><span style="color: #008080;">21</span> <span style="color: #000000;"> } </span><span style="color: #008080;">22</span> <span style="color: #0000ff;">public</span> <span style="color: #0000ff;">void</span> ShowInfo<tb><span style="color: #000000;">(Ta a, Tb b) </span><span style="color: #008080;">23</span> <span style="color: #000000;"> { </span><span style="color: #008080;">24</span> Type t = <span style="color: #0000ff;">typeof</span><span style="color: #000000;">(Tb); </span><span style="color: #008080;">25</span> <span style="color: #000000;"> Console.WriteLine(t.FullName); </span><span style="color: #008080;">26</span> <span style="color: #000000;"> } </span><span style="color: #008080;">27</span> }</tb></ta></ta>
泛型方法的类型推断:c#语法中包含大量""符号,所以导致代码的可读性和可维护性降低了,所以为了改变这种情况,c#编译器支持在调用一个方法时进行类型推断。例如下面的代码:myclass.ShowInfo("myClass", myclass);会调用ShowInfo
<span style="color: #008080;">1</span> <span style="color: #0000ff;">public</span> <span style="color: #0000ff;">static</span> Boolean MethodTakingAnyType<t><span style="color: #000000;">(T o) { </span><span style="color: #008080;">2</span> T temp =<span style="color: #000000;"> o; </span><span style="color: #008080;">3</span> <span style="color: #000000;"> Console.WriteLine(o.ToString()); </span><span style="color: #008080;">4</span> Boolean b =<span style="color: #000000;"> temp.Equals(o); </span><span style="color: #008080;">5</span> <span style="color: #0000ff;">return</span><span style="color: #000000;"> b; </span><span style="color: #008080;">6</span> }</t>
看这个方法里面有一个临时变量temp。方法里面执行两次变量赋值和几次方法调用,无论T是值类型还是引用类型还是接口类型或者是委托类型这个方法都能工作。这个方法适用于当前存在的所以类型,也适用于将来可能定义的任何类型。比如你再看一下下面这个方法:
<span style="color: #008080;">1</span> <span style="color: #0000ff;">public</span> <span style="color: #0000ff;">static</span> T MethodTakingAnyType<t><span style="color: #000000;">(T o1,T o2) { </span><span style="color: #008080;">2</span> <span style="color: #0000ff;">if</span> (o1.CompareTo(o2) 0<span style="color: #000000;">) </span><span style="color: #008080;">3</span> <span style="color: #000000;"> { </span><span style="color: #008080;">4</span> <span style="color: #0000ff;">return</span><span style="color: #000000;"> o1; </span><span style="color: #008080;">5</span> <span style="color: #000000;"> } </span><span style="color: #008080;">6</span> <span style="color: #0000ff;">else</span><span style="color: #000000;"> { </span><span style="color: #008080;">7</span> <span style="color: #0000ff;">return</span><span style="color: #000000;"> o2; </span><span style="color: #008080;">8</span> <span style="color: #000000;"> } </span><span style="color: #008080;">9</span> }</t>
在编译时会报如下错误(error CS0117:"T"不包含"CompareTo"的定义),因为并不是所有的类型都提供了CompareTo方法。那么在什么情况下T应该是什么类型呢?幸好,编译器和CLR支持一个称为约束的机制,可利用它使泛型变得真正有用!
<span style="color: #008080;">1</span> <span style="color: #0000ff;">public</span> <span style="color: #0000ff;">static</span> Boolean MethodTakingAnyType<t>(T o) <span style="color: #0000ff;">where</span> T : <span style="color: #0000ff;">struct</span> <span style="color: #008080;">2</span> <span style="color: #000000;"> { </span><span style="color: #008080;">3</span> T temp =<span style="color: #000000;"> o; </span><span style="color: #008080;">4</span> <span style="color: #000000;"> Console.WriteLine(o.ToString()); </span><span style="color: #008080;">5</span> Boolean b =<span style="color: #000000;"> temp.Equals(o); </span><span style="color: #008080;">6</span> <span style="color: #0000ff;">return</span><span style="color: #000000;"> b; </span><span style="color: #008080;">7</span> }</t>
c#的where关键字告诉编译器,为T指定的任何类型都必须是值类型。所以当你为T指定其它类型时,编译器会报错,例如你指定为string类型时(MethodTakingAnyType
<span style="color: #008080;">1</span> <span style="color: #0000ff;">public</span> <span style="color: #0000ff;">static</span> Boolean MethodTakingAnyType<t tbase>(T o) <span style="color: #0000ff;">where</span><span style="color: #000000;"> T : TBase </span><span style="color: #008080;">2</span> <span style="color: #000000;"> { </span><span style="color: #008080;">3</span> T temp =<span style="color: #000000;"> o; </span><span style="color: #008080;">4</span> <span style="color: #000000;"> Console.WriteLine(o.ToString()); </span><span style="color: #008080;">5</span> Boolean b =<span style="color: #000000;"> temp.Equals(o); </span><span style="color: #008080;">6</span> <span style="color: #0000ff;">return</span><span style="color: #000000;"> b; </span><span style="color: #008080;">7</span> }</t>
T类型参数由TBase类型单数约束,也就是说不管T为什么类型,都必须兼容于TBase指定的类型实参。
<span style="color: #008080;">1</span> <span style="color: #0000ff;">public</span> <span style="color: #0000ff;">static</span> Boolean MethodTakingAnyType<t tbase>(T o) <span style="color: #0000ff;">where</span> T : <span style="color: #0000ff;">new</span><span style="color: #000000;">() </span><span style="color: #008080;">2</span> <span style="color: #000000;"> { </span><span style="color: #008080;">3</span> T temp =<span style="color: #000000;"> o; </span><span style="color: #008080;">4</span> <span style="color: #000000;"> Console.WriteLine(o.ToString()); </span><span style="color: #008080;">5</span> Boolean b =<span style="color: #000000;"> temp.Equals(o); </span><span style="color: #008080;">6</span> <span style="color: #0000ff;">return</span><span style="color: #000000;"> b; </span><span style="color: #008080;">7</span> }</t>
<span style="color: #008080;">1</span> <span style="color: #0000ff;">public</span> <span style="color: #0000ff;">static</span> <span style="color: #0000ff;">void</span> MethodTakingAnyType<t tbase><span style="color: #000000;">(T o) </span><span style="color: #008080;">2</span> <span style="color: #000000;"> { </span><span style="color: #008080;">3</span> <span style="color: #0000ff;">int</span> x = (<span style="color: #0000ff;">int</span><span style="color: #000000;">)o; </span><span style="color: #008080;">4</span> <span style="color: #0000ff;">string</span> s = (<span style="color: #0000ff;">string</span><span style="color: #000000;">)o; </span><span style="color: #008080;">5</span> }</t>
2,将一个泛型类型变量设置为默认值:o = default(T);这样的话,不管T为值类型还是引用类型都可以成功,如果T为引用类型时就设置为null,如果T为值类型时将默认值设为0;
3,将一个泛型类型变量与Null进行比较:使用==或者=!将一个泛型类型变量于null进行比较都是合法的。但是如果T为值类型时,o永远都不会为null查看下面的代码:<span style="color: #008080;">1</span> <span style="color: #0000ff;">public</span> <span style="color: #0000ff;">static</span> <span style="color: #0000ff;">void</span> MethodTakingAnyType<t tbase><span style="color: #000000;">(T o) </span><span style="color: #008080;">2</span> <span style="color: #000000;"> { </span><span style="color: #008080;">3</span> <span style="color: #0000ff;">if</span> (o == <span style="color: #0000ff;">null</span><span style="color: #000000;">) { </span><span style="color: #008080;">4</span> <span style="color: #008000;">//</span><span style="color: #008000;">do something</span> <span style="color: #008080;">5</span> <span style="color: #000000;"> } </span><span style="color: #008080;">6</span> }</t>
4,将两个泛型类型变量相互比较:如果T是值类型,下面的代码就是非法的。
<span style="color: #008080;">1</span> <span style="color: #0000ff;">public</span> <span style="color: #0000ff;">static</span> <span style="color: #0000ff;">void</span> MethodTakingAnyType<t><span style="color: #000000;">(T o1,T o2) </span><span style="color: #008080;">2</span> <span style="color: #000000;"> { </span><span style="color: #008080;">3</span> <span style="color: #0000ff;">if</span> (o1 ==<span style="color: #000000;"> o2) { </span><span style="color: #008080;">4</span> <span style="color: #008080;">5</span> <span style="color: #000000;"> } </span><span style="color: #008080;">6</span> }</t>
4,泛型在使用过程的注意事项