天天看點

泛型

    通過使用泛型,我們可以極大地提高代碼的重用度,同時還可以獲得強類型的支援,避免了隐式的裝箱、拆箱,在一定程度上提升了應用程式的性能。

    泛型類執行個體化的理論

    C#泛型類在編譯時,先生成中間代碼IL,通用類型T隻是一個占位符。在執行個體化類時,根據使用者指定的資料類型代替T并由即時編譯器(JIT)生成本地代碼,這個本地代碼中已經使用了實際的資料類型,等同于用實際類型寫的類,是以不同的封閉類的本地代碼是不一樣的。按照這個原理,我們可以這樣認為:

    泛型類的不同的封閉類是分别不同的資料類型。

    為什麼要有泛型

    它可以避免重複代碼。如果有幾個類或者方法類似,實作功能相同,差別隻是類型不一樣,如int和string是,這是可以定義一個泛型。

    泛型類就類似于一個模闆,可以在需要時為這個模闆傳入任何我們需要的類型。

  由一個例子引入:

  我們在算法中會學到排序,一個很經典的排序為冒泡排序法。将幾個亂序的數字用冒泡發排序,我想大多數人會很快的寫出來。

  我将例子弄詳細一點。建立一張aspx的頁面,放一個button和一個label伺服器控件,輕按兩下button,自動生成響應方法。網頁關鍵代碼如下:

1 <asp:Button ID="Button1" runat="server" Text="對1,8,3,7,2排序" onclick="Button1_Click" />
2 <asp:Label ID="Label1" runat="server" Text=""></asp:Label>      

  要實作排序,需要現有實作冒泡排序的類,我們建立一個ShortHelper類:

1 public class ShortHelper {
 2         public void BubbleSort(int[] array)
 3         {
 4             int length = array.Length;
 5             for(int i=0;i<=length-2;i++)
 6                 for (int j = length-1; j >= 1; j--)
 7                 {
 8                     if (array[j] < array[j - 1])
 9                     {
10                         int temp = array[j];
11                         array[j] = array[j - 1];
12                         array[j - 1] = temp;
13                     }
14                 }
15         }
16 }      

将相應函數補充完整:

protected void Button1_Click(object sender, EventArgs e)
{
            string endsort = "";
            ShortHelper shorter = new ShortHelper();
            int[] array = { 1, 8, 3, 7, 2 };
            shorter.BubbleSort(array);
            foreach (int i in array)
            {
                endsort += i.ToString() + ",";
            }
            int length = endsort.Length - 1;
            endsort = endsort.Substring(0, length);
            Label1.Text = endsort;
}
      

結果如下:

泛型

但是,我現在又想對英文字母或者byte進行排序,怎麼辦呢,當然直接修改類型是可以的。但是這樣會造成代碼的不簡潔,重用性差。現在我們就引入泛型,泛型其實就是類似于C++中的模版,是一種解決方法的同法,将類型用一個類似于占位符的 T 代替。修改後的類如下:

1 public class ShortHelper<T> where T : IComparable {
 2         public void BubbleSort(T[] array)
 3         {
 4             int length = array.Length;
 5             for (int i = 0; i <= length - 2; i++)
 6                 for (int j = length - 1; j >= 1; j--)
 7                 {
 8                     if (array[j].CompareTo(array[j - 1]) < 0)//因為object是沒有比較大小的方法的,不用IComparable會照成無法比較大小,CompareTo方法用于比較,前一個比後一個小,傳回值<0,反之傳回>0
 9                     {
10                         T temp = array[j];
11                         array[j] = array[j - 1];
12                         array[j - 1] = temp;
13                     }
14                 }
15         }
16 }      

在網頁中添加

1 <asp:Button ID="Button2" runat="server" Text="對f,a,s,t,v,b,e排序" onclick="Button2_Click" />
2 <asp:Label ID="Label2" runat="server" Text=""></asp:Label>      

相應的相應函數為

1         protected void Button1_Click(object sender, EventArgs e)
 2         {
 3             string endsort = "";
 4             ShortHelper<int> shorter = new ShortHelper<int>();   //注意此處
 5             int[] array = { 1,8,3,7,2};
 6             shorter.BubbleSort(array);
 7             foreach (int i in array)
 8             {
 9                 endsort += i.ToString() + ",";
10             }
11             int length = endsort.Length - 1;
12             endsort = endsort.Substring(0, length);
13             Label1.Text = endsort;
14         }
15 
16         protected void Button2_Click(object sender, EventArgs e)
17         {
18             string endsort = "";
19             ShortHelper<char> shorter = new ShortHelper<char>();   //注意此處
20             char[] array = { 'f', 'a', 's', 't', 'v', 'b', 'e' };
21             shorter.BubbleSort(array);
22             foreach (char i in array)
23             {
24                 endsort += i + ",";
25             }
26             int length = endsort.Length - 1;
27             endsort = endsort.Substring(0, length);
28             Label2.Text = endsort;
29         }      

運作結果為:

泛型

當然,泛型的用法不僅僅就這,還有泛型接口,泛型方法等等。

    優點:

    1、性能:避免隐式的裝箱和拆箱

    如AarryList(在添加資料時裝箱,讀取時拆箱)和List<T>差別

    2、類型安全:

    泛型的另一個特性是類型安全。與ArrayList類一樣,如果使用對象,可以在這個集合中添加任意類型。下面的例子在ArrayList類型的集合中添加一個整數、一個字元串和一個MyClass類型的對象:

1 ArrayList list = new ArrayList();
2 list.Add(44);
3 list.Add("mystring");
4 list.Add(new MyClass());      

  如果這個集合使用下面的foreach語句疊代,而該foreach語句使用整數元素來疊代,編譯器就會編譯這段代碼。但并不是集合中的所有元素都可以轉換為int,是以會出現一個運作異常:

1 foreach (int i in list)
2 {
3     Console.WriteLine(i);
4 }      

  錯誤應盡早發現。在泛型類List<T>中,泛型類型T定義了允許使用的類型。有了List<int>的定義,就隻能把整數類型添加到集合中。編譯器不會編譯這段代碼,因為Add()方法的參數無效:

1 List<int> list = new List<int>();
2 list.Add(44);
3 list.Add("mystring");   // compile time error
4 list.Add(new MyClass());   // compile time error      

    3、二進制代碼的重用:

    泛型允許更好地重用二進制代碼。泛型類可以定義一次,用許多不同的類型執行個體。

   類型參數限制

    程式員在編寫泛型類時,總是會對通用資料類型T進行有意或無意地有假想,也就是說這個T一般來說是不能适應所有類型,但怎樣限制調用者傳入的資料類型呢?這就需要對傳入的資料類型進行限制,限制的方式是指定T的祖先,即繼承的接口或類。因為C#的單根繼承性,是以限制可以有多個接口,但最多隻能有一個類,并且類必須在接口之前。

    除了可以限制類型參數T 實作某個接口以外,還可以限制T 是一個結構、T 是一個類、T 擁有構造函數、T 繼承自某個基類等。

    泛型方法

    在泛型方法中,泛型類型用方法聲明來定義。

1 void Swap<T>(ref T x, ref T y)
2 {
3   T temp;
4   temp = x;
5   x = y;
6   y = temp;
7 }      

把泛型類型賦予方法調用,就可以調用泛型方法:

1 int i = 4;
2 int j = 5;
3 Swap<int>(ref i, ref j);      

但是,因為C#編譯器會通過調用Swap方法來擷取參數的類型,是以不需要把泛型類型賦予方法調用。泛型方法可以像非泛型方法那樣調用:

1 int i = 4;
2 int j = 5;
3 Swap(ref i, ref j);      

當一般方法與泛型方法具有相同的簽名時,會覆寫泛型方法。

    泛型類的特性

    1、預設值:不能把null賦予泛型類型。原因是泛型類型也可以執行個體化為值類型,而null隻能用于引用類型。為了解決這個問題,可以使用default關鍵字。通過default關鍵字,将null賦予引用類型,将0賦予值類型。

        T doc = default(T);

  2、限制:如果泛型類需要調用泛型類型上的方法,就必須添加限制。

約    束 說    明
where T : struct 使用結構限制,類型T必須是值類型
where T : class 類限制指定,類型T必須是引用類型
where T : IFoo 指定類型T必須執行接口IFoo
where T : Foo 指定類型T必須派生于基類Foo
where T : new() 這是一個構造函數限制,指定類型T必須有一個預設構造函數
where T : U 這個限制也可以指定,類型T1派生于泛型類型T2。該限制也稱為裸類型限制

  3、繼承:泛型類型可以執行泛型接口,也可以派生于一個類。泛型類可以派生于泛型基類:

1 public class Base<T>
2 {
3 }
4 public class Derived<T> : Base<T>{}      

  4、靜态成員:泛型類的靜态成員需要特别關注。泛型類的靜态成員隻能在類的一個執行個體中共享。

總結:

  泛型。通過泛型類可以建立獨立于類型的類,泛型方法是獨立于類型的方法。增加代碼的重用性。接口、結構和委托也可以用泛型的方式建立。泛型引入了一種新的程式設計方式。

參考資料:

http://www.cnblogs.com/JimmyZhang/archive/2008/12/17/1356727.html

,《C#進階程式設計第六版》

繼續閱讀