C# -- 泛型(1)


簡介:

  先看看泛型的概念--“通過參數化類型來實現在同一份代碼上操作多種數據類型。利用“參數化類型”將類型抽象化,從而實現靈活的復用”。

  很多初學者在剛開始接觸泛型的時候會比較難理解 “泛型” 在這里先把 “泛型”當作一個形容詞 這樣比較方便理解 因為很多東西都可以是泛型的 比如--

泛型的類” ,“泛型的方法”,“泛型的接口”,“泛型的委托” 等...很多時候我們使用泛型可以極大減少代碼重復使程序更加清爽,也可以避免不必要的‘裝箱’ ‘拆箱’過程。

 

 

<泛型的引入|為什么要有泛型?>

 

  在程序設計的過程中我們常常會遇到這樣的情況:為了實現某一個功能我們一開始把方法寫好,但后來我們發現同樣的功能需要我們再寫一次但是這次方法的參數類型和上次不一樣了,這個時候按照敏捷軟件開發的思想,不要過早的進行抽象和應對變化,當變化第一次出現時,使用最快的方法解決它,但變化第二次出現的時,在進行更好的架構設計,這樣的目的是為了避免過度設計,因為有可能第二次變化永遠也不會出現。考慮到功能一樣,所這里我們通常會直接復制原方法的代碼,然后修改一下參數類型即可快速解決;這樣做確實沒錯,但是有的時候不僅出現了第二次變化 還出現了第三次...或者是更多次變化,繼續使用CV大法修改方法的簽名將會導致大量重復代碼的出現,於是我們就會想,要是存在一個可以傳遞任何數據類型的方法那多好,即把這個方法的實現當成模板 把方法的簽名抽象出來,於是我們引入了泛型。

 

下面我們來看一下具體的例子:

 

1.1使用CV大法

---------------輸入多個 int類型,進行冒泡排序讓它們依次重小到大輸出,代碼如下:

    public class SortHelper { public void BubbleSort(int[] arr) { int length = arr.Length; for (int i = 0; i < length-1; i++) { for (int j = 0; j < length-1-i; j++) { if (arr[j]>arr[j+1]) { int temp=arr[j]; arr[j] = arr[j + 1]; arr[j + 1] = temp; } } } } }

 

測試:

static void Main(string[] args) { SortHelper sorter = new SortHelper(); int[] a = { 4,5,1,3,2,8,5,0,2}; sorter.BubbleSort(a);
       //輸出省略
}

 

輸出為:0,1,2,2,3,4,5,5,8

---------------輸入多個 Byte類型,進行冒泡排序讓它們依次重小到大輸出,代碼如下:

這個時候我只要復制一下原來的方法改一下簽名就可以了

    public class SortHelper { public void BubbleSort(byte[] arr) { int length = arr.Length; for (int i = 0; i < length-1; i++) { for (int j = 0; j < length-1-i; j++) { if (arr[j]>arr[j+1]) { byte temp = arr[j]; arr[j] = arr[j + 1]; arr[j + 1] = temp; } } } } }

 

這樣做雖然可以,但是往后若要 處理N次各種其他 數據類時就 就要大量重復復制 嚴重影響代碼的簡潔度,而且當功能要擴展時 ,每個方法都要修改,維護起來非常不方便。

 

1.2使用泛型(泛型類):

我們自然而然的會這樣想了如果可以把方法中的 參數類型 用一個 ”占位符“ 表示 每次 傳入 什么類型 他就變成什么類型,這樣就可以將這個方法當成一個模板用了(有點像Web編程中在Html中使用占位符)。

這里我們用 “T” 來便是這個特殊的參數類型,於是代碼就變成了這樣:

    public class SortHelper { public void BubbleSort(T[] arr) { int length = arr.Length; for (int i = 0; i < length-1; i++) { for (int j = 0; j < length-1-i; j++) { if (arr[j]>arr[j+1]) { T temp = arr[j]; arr[j] = arr[j + 1]; arr[j + 1] = temp; } } } } }

 

這里 T 代表 ”類型的類型“ 和 int ,string ...等數據類型相似,T 就是類型本身。讓人興奮的是真的有像 “T” 這樣的特別存在,在.NET中叫做類型參數. 下面我們看看規范的代碼--

這里我們把BubbleSort定義成泛型類 定義泛型類的一種方法是在類后面加上“<T>” 

    //定義泛型類SortHelper 這里“where T:IComparable” 是給類型參數T一個限制 -- 參數類型必須實現IComparable接口,否則無法通過編譯
    public class SortHelper<T> where T:IComparable { public void BubbleSort(T[] arr) { int length = arr.Length; for (int i = 0; i < length-1; i++) { for (int j = 0; j < length-1-i; j++) { if (arr[j].CompareTo(arr[j+1])>0) { T temp = arr[j]; arr[j] = arr[j + 1]; arr[j + 1] = temp; } } } } }

 

測試:

        static void Main(string[] args) { SortHelper<byte> sorter = new SortHelper<byte>(); byte[] a = { 4,5,1,3,2,8,5,0,2}; sorter.BubbleSort(a); SortHelper<int> sorter1 = new SortHelper<int>(); int[] b = { 4, 5, 1, 3, 2, 8, 5, 0, 2 }; sorter1.BubbleSort(b);
       //輸出省略 }

 

輸出為:

0,1,2,2,3,4,5,5,8

0,1,2,2,3,4,5,5,8

 

---------------輸入多個 自定義類型的實例,進行冒泡排序讓它們依次重小到大輸出,代碼如下:

下面我們來模擬一下寵物店賣的貓 按價格排序

貓類:

    public class cat:IComparable { public string name; public int price; public int CompareTo(object obj) { cat catT = (cat)obj; return this.price.CompareTo(catT.price); } public cat(string name, int price) { this.price = price; this.name = name; } }

 

測試:

        static void Main(string[] args) { SortHelper<cat> sorter2 = new SortHelper<cat>(); cat cat1=new cat("貓1",1000); cat cat2=new cat("貓2",1400); cat cat3=new cat("貓3",400); cat[] c = { cat1, cat2, cat3 }; sorter2.BubbleSort(c); //輸出
            for (int i = 0; i < c.Length; i++) { Console.WriteLine("Name:"+c[i].name+" Price:"+c[i].price); } } 

 

結果如圖:

 

 

 

 

 

*泛型與集合類型(ArrayList)

 

 概要:通過泛型可以大大提高集合類型的的性能惡化安全性。

下面我們來看一個例子

2.1 非泛型的集合類

先是 往集合里 存放 3 個數據 

            ArrayList list = new ArrayList(); int listSize = 3; for (int i = 0; i < listSize; i++) { list.Add(i); } for (int i = 0; i < listSize; i++) { int value = (int)list[i]; Console.WriteLine(value); }

 

 

測試:

輸出

0

1

2

有經驗的讀者在這里可能會注意到了,這樣子寫雖然能運行通過,但是這里當 list每次調用Add方法時就做了一次 ” 裝箱 “ 操作,接着每次取數據時對list的元素進行一次強制轉換 (int)list[i] 同時也做了一次 “ 拆箱 ”操作,這兩個操作對.NET來說是比較耗時的,當操作的次數越多效果就越明顯;

 

2.2下面我們將 listSize 設置成 1000000 然后用 開始和結束DateTime.Now 來獲取消耗的時間:

            ArrayList list = new ArrayList(); int listSize = 1000000; long StarTime = DateTime.Now.Ticks; for (int i = 0; i < listSize; i++) { list.Add(i); } for (int i = 0; i < listSize; i++) { int value = (int)list[i]; } long EndTime = DateTime.Now.Ticks; Console.WriteLine("使用ArrayList,耗時:{0} Ticks", EndTime - StarTime);

 

 

測試:

結果

 

2.3 使用泛型集合類型(泛型數組)

            List<int> list = new List<int>(); int listSize = 1000000; long StarTime = DateTime.Now.Ticks; for (int i = 0; i < listSize; i++) { list.Add(i); } for (int i = 0; i < listSize; i++) { int value =list[i]; } long EndTime = DateTime.Now.Ticks; Console.WriteLine("使用List<int>,耗時:{0} Ticks", EndTime - StarTime);

 

測試:

 比較上述2次執行的結果我們會發現,使用 ArrayList 的耗時是使用 List<int> 的2倍多 ,隨着次數的增大差距會越來越明顯!

 

總結:

看到這里相信大家明白為什么要引入泛型了吧,通過使用泛型-

1.可以避免同種功能代碼的大幅度重復出現使我們的代碼更加簡潔/可讀性更高

2.方便擴展維護,靈活度高

3.避免隱式的裝箱拆箱,提高程序運行速度

 

推薦一篇對初學者較有幫助的文章:傳送門

出自Keiling_J'Blog:http://www.cnblogs.com/keiling/p/3672346.html 


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM