この記事では主に C# のジェネリックスについて説明します。ジェネリックスは C# で非常に重要な役割を果たし、可読性とパフォーマンスの高いコードを作成する上で重要な役割を果たすためです。私のチームのコードに多数の非ジェネリック コレクションと暗黙的なボックス化およびボックス化解除操作が含まれているのを何度も見たとき、私はジェネリック ベースを追加することを提案します
この記事では主に C# のジェネリックスについて説明します。ジェネリックスは C# で非常に重要な役割を果たし、可読性とパフォーマンスの高いコードを作成する上で重要な役割を果たすためです。私のチームのコードに多数の非ジェネリック コレクションと暗黙的なボックス化およびボックス化解除操作が含まれているのを何度も目にしたとき、私はジェネリックの基本を改善することを提案します。
1. ジェネリック医薬品とは
2. ジェネリックを使用する理由とジェネリックが解決する問題
まず次のコードを見てみましょう (このコードはジェネリクスを説明するためだけのものであり、実際的な意味はありません) 何が間違っているのか見てみましょう。
リーリー コードを表示リーリー コードを表示
ジェネリックとは何か、なぜジェネリックを使用する必要があるのかについてはすでに説明しました。次に、ジェネリックの使用方法について説明します。
<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> }
如果为泛型类型的所有类型实参传递的都是实际数据类型,类型就称为封闭类型,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> IEnumerator<<span style="color: #0000ff;">out</span> 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> MyClass<<span style="color: #0000ff;">string</span>> myclass = <span style="color: #0000ff;">new</span> MyClass<<span style="color: #0000ff;">string</span>><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> }
泛型方法的类型推断: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> }
看这个方法里面有一个临时变量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) < <span style="color: #800080;">0</span><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> }
在编译时会报如下错误(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> }
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类型参数由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> }
<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> }
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> }
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> }
4,泛型在使用过程的注意事项