【基礎】泛型的簡單理解

前言

最近工做不是很忙,抽出時間來看看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

 聲明:本博客原創文字只表明本人工做中在某一時間內總結的觀點或結論,與本人所在單位沒有直接利益關係。非商業,未受權貼子請以現狀保留,轉載時必須保留此段聲明,且在文章頁面明顯位置給出原文鏈接。

相關文章
相關標籤/搜索