(转)【基础】泛型的简单理解
原文地址:http://www.cnblogs.com/xhb-bky-blog/p/5200344.html
一、前言
最近工作不是很忙,抽出时间来看看C#中基础的东西,也算是“温故而知新”了,于是就看到了泛型这块儿,看了园子里其他园友的文章,讲的都很到位。这篇文章本着简单、容易理解为前提,记录下我自己对泛型的认识,并记录下来,方便以后查看。
二、泛型是什么?
泛型是一种开放式类型,它的出现保证了我们可以创建类型安全的集合。
三、泛型的应用场景
<1>当一种逻辑适用于多种数据类型时,可以采用泛型,简化代码,提高代码的复用性。
<2>避免不必要的装箱(Boxing)和拆箱(Unboxing)操作。
<3>避免不必要的类型强制转换。
下面通过一个自定义的栈,来对上面的应用场景进行解释。
首先定义一个栈,该栈有一个含参构造函数,有三个方法,元素的入栈Push、元素的出栈Pop、元素的输出Out。代码如下:
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就好咯,代码如下:
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类型的栈横空出世,代码如下:
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 string[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();
}
四、总结
以上是泛型的简单应用,实际应用中,泛型还有很多其他的特性,如泛型的约束、泛型的方法、接口、委托以及继承等。要理解这些特性,需要把基础打好,熟练掌握泛型的应用场景,才能事半功倍。
posted on 2016-02-19 11:43 shaozhuyong 阅读(231) 评论(0) 收藏 举报

浙公网安备 33010602011771号