最近工做不是很忙,抽出時間來看看C#中基礎的東西,也算是「溫故而知新」了,因而就看到了泛型這塊兒,看了園子裏其餘園友的文章,講的都很到位。這篇文章本着簡單、容易理解爲前提,記錄下我本身對泛型的認識,方便之後查看。html
泛型是一種開放式類型,它的出現保證了咱們能夠建立類型安全的集合。安全
<1>當一種邏輯適用於多種數據類型時,能夠採用泛型,簡化代碼,提升代碼的複用性。函數
<2>避免沒必要要的裝箱(Boxing)和拆箱(Unboxing)操做。測試
<3>避免沒必要要的類型強制轉換。this
下面經過一個自定義的棧,來對上面的應用場景進行解釋。spa
首先定義一個棧,該棧有一個含參構造函數,有三個方法,元素的入棧Push、元素的出棧Pop、元素的輸出Out。代碼以下:code
1 /// <summary> 2 /// 自定義棧 3 /// </summary> 4 /// <typeparam name="?"></typeparam> 5 class Stack 6 { 7 private int[] arr; 8 private int length = 0; 9 private int topElement; 10 11 /// <summary> 12 /// 設置或獲取棧頂元素 13 /// </summary> 14 public int TopElement 15 { 16 get 17 { 18 this.topElement= arr[length-1]; 19 return this.topElement; 20 } 21 set 22 { 23 this.topElement = value; 24 } 25 } 26 27 /// <summary> 28 /// 棧中已存在元素數量 29 /// </summary> 30 public int Length 31 { 32 get { return this.length; } 33 } 34 35 /// <summary> 36 /// 構造函數 37 /// </summary> 38 /// <param name="m"></param> 39 public Stack(int m) 40 { 41 this.arr = new int[m]; 42 } 43 44 /// <summary> 45 /// 將棧頂元素出棧 46 /// </summary> 47 /// <returns></returns> 48 public int Pop() 49 { 50 if (this.length <= 0) 51 return -1; 52 else 53 { 54 int popElement = this.topElement; 55 arr[this.length-1] = 0; 56 this.length = this.length - 1; 57 return popElement; 58 } 59 } 60 61 /// <summary> 62 /// 將元素入棧 63 /// </summary> 64 /// <param name="item">目標元素</param> 65 public void Push(int item) 66 { 67 if (length >= this.arr.Length) return; 68 this.arr[length] = item; 69 this.topElement = this.arr[length]; 70 this.length = this.length + 1; 71 } 72 73 /// <summary> 74 /// 輸出棧內全部元素 75 /// </summary> 76 /// <param name="stack">目標棧</param> 77 public void Out(Stack stack) 78 { 79 if (stack.arr.Length <= 0) return; 80 foreach (int item in arr) 81 { 82 Console.WriteLine(item); 83 } 84 Console.ReadLine(); 85 } 86 }
以上代碼能夠正常運行,實現了一個棧的基本功能,可是我想把這個邏輯放在string類型上也通用,怎麼辦?有人說那好辦,把int替換成string就好咯,代碼以下:htm
1 /// <summary> 2 /// 自定義棧 3 /// </summary> 4 /// <typeparam name="?"></typeparam> 5 class StringStack 6 { 7 private string[] arr; 8 private int length = 0; 9 private string topElement; 10 11 /// <summary> 12 /// 設置或獲取棧頂元素 13 /// </summary> 14 public string TopElement 15 { 16 get 17 { 18 this.topElement = arr[length - 1]; 19 return this.topElement; 20 } 21 set 22 { 23 this.topElement = value; 24 } 25 } 26 27 /// <summary> 28 /// 棧中已存在元素數量 29 /// </summary> 30 public int Length 31 { 32 get { return this.length; } 33 } 34 35 /// <summary> 36 /// 構造函數 37 /// </summary> 38 /// <param name="m"></param> 39 public StringStack(int m) 40 { 41 this.arr = new string[m]; 42 } 43 44 /// <summary> 45 /// 將棧頂元素出棧 46 /// </summary> 47 /// <returns></returns> 48 public string Pop() 49 { 50 if (this.length <= 0) 51 return "Empty"; 52 else 53 { 54 string popElement = this.topElement; 55 arr[this.length - 1] = string.Empty; 56 this.length = this.length - 1; 57 return popElement; 58 } 59 } 60 61 /// <summary> 62 /// 將元素入棧 63 /// </summary> 64 /// <param name="item">目標元素</param> 65 public void Push(string item) 66 { 67 if (length >= this.arr.Length) return; 68 this.arr[length] = item; 69 this.topElement = this.arr[length]; 70 this.length = this.length + 1; 71 } 72 73 /// <summary> 74 /// 輸出棧內全部元素 75 /// </summary> 76 /// <param name="stack">目標棧</param> 77 public void Out(StringStack stack) 78 { 79 if (stack.arr.Length <= 0) return; 80 foreach (string item in arr) 81 { 82 Console.WriteLine(item); 83 } 84 Console.ReadLine(); 85 } 86 }
通過測試發現,上面的棧也能夠正常工做了,可是問題又來了,我想把這個棧的邏輯再應用到long類型上怎麼辦?聰明的人不會在同一個問題上絆倒三次,既然有那麼多類型須要用到這個棧的邏輯,那我索性就給他來個Object,這下總能夠了吧~因而Object類型的棧橫空出世,代碼以下:blog
1 /// <summary> 2 /// 自定義棧 3 /// </summary> 4 /// <typeparam name="?"></typeparam> 5 class ObjectStack 6 { 7 private object[] arr; 8 private int length = 0; 9 private object topElement; 10 11 /// <summary> 12 /// 設置或獲取棧頂元素 13 /// </summary> 14 public object TopElement 15 { 16 get 17 { 18 this.topElement = arr[length - 1]; 19 return this.topElement; 20 } 21 set 22 { 23 this.topElement = value; 24 } 25 } 26 27 /// <summary> 28 /// 棧中已存在元素數量 29 /// </summary> 30 public int Length 31 { 32 get { return this.length; } 33 } 34 35 /// <summary> 36 /// 構造函數 37 /// </summary> 38 /// <param name="m"></param> 39 public ObjectStack(int m) 40 { 41 this.arr = new object[m]; 42 } 43 44 /// <summary> 45 /// 將棧頂元素出棧 46 /// </summary> 47 /// <returns></returns> 48 public object Pop() 49 { 50 if (this.length <= 0) 51 return null; 52 else 53 { 54 object popElement = this.topElement; 55 arr[this.length - 1] = null; 56 this.length = this.length - 1; 57 return popElement; 58 } 59 } 60 61 /// <summary> 62 /// 將元素入棧 63 /// </summary> 64 /// <param name="item">目標元素</param> 65 public void Push(object item) 66 { 67 if (length >= this.arr.Length) return; 68 this.arr[length] = item; 69 this.topElement = this.arr[length]; 70 this.length = this.length + 1; 71 } 72 73 /// <summary> 74 /// 輸出棧內全部元素 75 /// </summary> 76 /// <param name="stack">目標棧</param> 77 public void Out(ObjectStack stack) 78 { 79 if (stack.arr.Length <= 0) return; 80 foreach (object item in arr) 81 { 82 Console.WriteLine(item); 83 } 84 Console.ReadLine(); 85 } 86 }
通過重構以後,這個棧邏輯能夠適用於任何類型,這是毫無疑問的,可是還有一個問題,來看調用的代碼:繼承
1 static void Main(string[] args) 2 { 3 ObjectStack ostd = new ObjectStack(3); 4 ostd.Push(true); 5 ostd.Push("http://www.cnblogs.com/xhb-bky-blog/"); 6 ostd.Push(12); 7 8 int a = (int)ostd.Pop(); 9 string b = ostd.Pop().ToString(); 10 bool c = (bool)ostd.Pop(); 11 Console.ReadLine(); 12 } 13
在調用代碼中,咱們竟然看到了裝箱和拆箱操做,而裝箱操做和拆箱操做是要額外耗費cpu和內存資源的。再來看下面一段代碼:
1 static void Main(string[] args) 2 { 3 ObjectStack ostd = new ObjectStack(3); 4 Good good1 = new Good("pencil", 5); 5 Good good2 = new Good("eraser", 2); 6 Good good3 = new Good("pencilbox", 15); 7 8 ostd.Push(good1); 9 ostd.Push(good2); 10 ostd.Push(good3); 11 12 Good mygood = (Good)ostd.Pop();//注意 13 Console.ReadLine(); 14 }
上面的代碼中發生了類型強制轉換,這代表該段代碼是不安全的,雖然能夠經過編譯,可是若是類型對應不上,就會把錯誤推遲到運行期。爲了解決上面的三個問題,泛型應運而生,來看一下泛型是如何解決這些問題的,看代碼:
1 /// <summary> 2 /// 自定義棧 3 /// </summary> 4 /// <typeparam name="T"></typeparam> 5 class Stack<T> where T:IComparable 6 { 7 private T[] arr; 8 private int length = 0; 9 private T topElement; 10 11 /// <summary> 12 /// 設置或獲取棧頂元素 13 /// </summary> 14 public T TopElement 15 { 16 get 17 { 18 this.topElement = arr[length - 1]; 19 return this.topElement; 20 } 21 set 22 { 23 this.topElement = value; 24 } 25 } 26 27 /// <summary> 28 /// 棧中已存在元素數量 29 /// </summary> 30 public int Length 31 { 32 get { return this.length; } 33 } 34 35 /// <summary> 36 /// 構造函數 37 /// </summary> 38 /// <param name="m"></param> 39 public Stack(int m) 40 { 41 this.arr = new T[m]; 42 } 43 44 /// <summary> 45 /// 將棧頂元素出棧 46 /// </summary> 47 /// <returns></returns> 48 public T Pop() 49 { 50 //T t = new T();//必須聲明new 51 if (this.length <= 0) 52 return default(T); 53 else 54 { 55 T popElement = this.topElement; 56 arr[this.length - 1] = default(T); 57 this.length = this.length - 1; 58 return popElement; 59 } 60 } 61 62 /// <summary> 63 /// 將元素入棧 64 /// </summary> 65 /// <param name="item">目標元素</param> 66 public void Push(T item) 67 { 68 if (length >= this.arr.Length) return; 69 this.arr[length] = item; 70 this.topElement = this.arr[length]; 71 this.length = this.length + 1; 72 } 73 74 /// <summary> 75 /// 輸出棧內全部元素 76 /// </summary> 77 /// <param name="stack">目標棧</param> 78 public void Out(Stack<T> stack) 79 { 80 if (stack.arr.Length <= 0) return; 81 foreach (T item in arr) 82 { 83 Console.WriteLine(item); 84 } 85 Console.ReadLine(); 86 } 87 }
在泛型中,類名後面的Stack<T>中的T表示它操做的是一個未指定的數據類型,也稱爲開放式類型,由於T是一個不明確的類型,在實例化的時候才能知道T的實際類型是什麼,實例化以後也就變成了封閉式類型,如Stack<String>,由於Stack<String>已經明確了T是String類型的。另一點,Stack<String>和Stack<T>沒有任何繼承上的關係,而是兩種徹底獨立的類型。下面來看下泛型的神奇之處吧:
static void Main(string[] args) { Stack<int> std = new Stack<int>(2); std.Push(21); std.Push("Hello");//編譯不經過,保證了類型安全 int a = std.Pop();//未發生拆裝箱 Stack<Good> stdg = new Stack<Good>(2); Good sell = new Good("box", 10); stdg.Push(sell); Good buy = stdg.Pop();//未發生強制類型轉換 Console.ReadLine(); }
以上是泛型的簡單應用,實際應用中,泛型還有不少其餘的特性,如泛型的約束、泛型的方法、接口、委託以及繼承等。要理解這些特性,須要把基礎打好,熟練掌握泛型的應用場景,才能事半功倍。
做者:悠揚的牧笛
博客地址:http://www.cnblogs.com/xhb-bky-blog/p/4167108.html
聲明:本博客原創文字只表明本人工做中在某一時間內總結的觀點或結論,與本人所在單位沒有直接利益關係。非商業,未受權貼子請以現狀保留,轉載時必須保留此段聲明,且在文章頁面明顯位置給出原文鏈接。