您的位置:首页 > 编程语言 > C#

C#语言 第三部分 集合框架 IList,IList——向量(2)

2010-03-31 00:27 483 查看
五、增加泛型

前面我们通过一个ArrayList类实现了IList接口,通过代码可以知道,ArrayList采用一个object[]类型的数组字段来存储对象引用。这种方式存在两个问题:

Object是引用类型,所以一旦我们向ArrayList集合中存储任何值类型对象,则会发生一次“装箱”操作,.net会将值类型对象转化为引用类型对象;其后每次访问集合中的该对象时,又可能会引起一次“拆箱”操作;

由于使用Object类型作为对象的引用类型,所以ArrayList无法限制存储对象引用的类型。

看一个例子:

1 class Program {
2 static void Main(string[] args) {
3
4 // 声明向量集合对象
5 ArrayList lst = new ArrayList();
6
7 // 当调用Add方法时, 值类型(int)会自动的"装箱"为引用类型(object)
8 lst.Add(100);
9
10 // 当调用索引器时, 返回的是一个object类型的对象引用
11 object o = lst[0];
12
13 // 要恢复存储前的类型, 这里还需要一次"拆箱"操作
14 int value = (int)o;
15
16 Console.WriteLine(value);
17
18 // 这里, 先进行了一次"拆箱"操作, 将lst[0]转化为int类型
19 // 接着进行了一次装箱操作
20 lst[0] = (int)lst[0] + 200;
21 Console.WriteLine(lst[0]);
22
23 // 对于ArrayList, 可以存放任意类型的对象引用
24 lst.Add("Hello");
25
26 foreach (object v in lst) {
27 // 这里只能访问object类型的ToString方法
28 // 并无法知道object类型的v变量到底是什么类型
29 Console.WriteLine(v);
30 }
31 }
32 }
通过上述例子,可以清楚的反映出上面提到的两个问题。

.net Framework添加的泛型特性就是为了解决上述问题的。为了支持泛型,我们需要额外的实现一个接口:List<E>接口。

泛型的代码理解起来颇为抽象,下面是代码,童鞋们尽量理解。

1 using System;
2 using System.Collections;
3 using System.Collections.Generic;
4
5 namespace Edu.Study.Collections.List {
6
7 /// <summary>
8 /// 定义向量集合类, 实现IList接口
9 /// </summary>
10 public class List<E> : IList, IList<E> {
11
12 /// <summary>
13 /// 泛型迭代子定义, 需要额外实现IEnumerator<E>接口
14 /// </summary>
15 public struct Enumertor<E> : IEnumerator, IEnumerator<E> {
16
17 /// <summary>
18 /// 表示迭代索引, 即迭代器访问到向量的下标
19 /// </summary>
20 private int index;
21
22 /// <summary>
23 /// 一个到迭代器所属的向量类对象的引用
24 /// </summary>
25 private List<E> list;
26
27 /// <summary>
28 /// 参数构造器
29 /// </summary>
30 /// <param name="container">表示迭代器所属的集合类</param>
31 public Enumertor(List<E> container) {
32 this.list = container;
33 this.index = -1;
34 }
35
36 /// <summary>
37 /// 析构器. 后面会讲, 这里空实现
38 /// </summary>
39 public void Dispose() {
40 }
41
42 /// <summary>
43 /// 实现IEnumerator接口的Current属性
44 /// 由于IEnumerator<T>接口同样具有一个Current属性,
45 /// 并且同IEnumerator接口的Current属性不同, 所以这里将
46 /// IEnumerator接口的Current属性显示实现
47 /// </summary>
48 object IEnumerator.Current {
49 get {
50 return list[index];
51 }
52 }
53
54 /// <summary>
55 /// 实现IEnumerator接口的Current属性
56 /// </summary>
57 public E Current {
58 get {
59 return list[index];
60 }
61 }
62
63 /// <summary>
64 /// 将迭代器指示到“下一个数据位置”
65 /// </summary>
66 public bool MoveNext() {
67 if (this.index < list.Count) {
68 ++this.index;
69 }
70 return this.index < list.Count;
71 }
72
73 /// <summary>
74 /// 将迭代器指示恢复到集合起始位置。
75 /// </summary>
76 public void Reset() {
77 this.index = -1;
78 }
79 }
80
81 /// <summary>
82 /// 保存数据的数组为泛型类型E类型
83 /// </summary>
84 private E[] array;
85
86 /// <summary>
87 /// 当前集合的长度
88 /// </summary>
89 private int count;
90
91 /// <summary>
92 /// 默认构造器
93 /// </summary>
94 public List()
95 : this(1) {
96 }
97
98 /// <summary>
99 /// 参数构造器
100 /// </summary>
101 public List(int capacity) {
102 if (capacity < 0) {
103 throw new ArgumentOutOfRangeException("caption");
104 }
105 if (capacity == 0) {
106 capacity = 1;
107 }
108 this.array = new E[capacity];
109 this.count = 0;
110 }
111
112 /// <summary>
113 /// 获取向量的长度
114 /// </summary>
115 public int Count {
116 get {
117 return this.count;
118 }
119 }
120
121 /// <summary>
122 /// 返回向量实际使用的长度
123 /// </summary>
124 public int Capacity {
125 get {
126 return this.array.Length;
127 }
128 }
129
130 /// <summary>
131 /// 是否固定大小的属性(返回常量false)
132 /// </summary>
133 public bool IsFixedSize {
134 get {
135 return false;
136 }
137 }
138
139 /// <summary>
140 /// 是否只读集合的属性(返回常量false)
141 /// </summary>
142 public bool IsReadOnly {
143 get {
144 return false;
145 }
146 }
147
148 /// <summary>
149 /// 集合是否可同步属性, 返回false, 表示集合无法同步
150 /// </summary>
151 public bool IsSynchronized {
152 get {
153 return false;
154 }
155 }
156
157 /// <summary>
158 /// 同步对象属性, 返回null, 表示集合无需同步
159 /// </summary>
160 public object SyncRoot {
161 get {
162 return null;
163 }
164 }
165
166 /// <summary>
167 /// 当this.array长度不够时, 重新分配长度足够的新数组。
168 /// </summary>
169 private E[] GetNewArray() {
170
171 // 这里分配的是泛型E类型的数组
172 return new E[(this.array.Length + 1) * 2];
173 }
174
175 /// <summary>
176 /// 实现IList<E>接口的Add方法, 该方法采用泛型参数value
177 /// </summary>
178 public void Add(E value) {
179 int newCount = this.count + 1;
180 if (this.array.Length < newCount) {
181
182 // 获取新的E类型数组
183 E[] newArray = GetNewArray();
184 Array.Copy(this.array, newArray, this.count);
185 this.array = newArray;
186 }
187
188 this.array[this.count] = value;
189 this.count = newCount;
190 }
191
192
193 /// <summary>
194 /// 向向量的末尾添加对象
195 /// 调用前一个Add方法即可
196 /// </summary>
197 int IList.Add(object value) {
198
199 // 通过下面这句代码, 看看怎么调用当前类实现另一个接口的同名方法
200 ((IList<E>)this).Add((E)value);
201 return this.count - 1;
202 }
203
204 /// <summary>
205 /// 实现IList<E>接口的索引器属性, 属性的类型为泛型类型E
206 /// </summary>
207 public E this[int index] {
208 get {
209 if (index < 0 || index >= this.count) {
210 throw new ArgumentOutOfRangeException("index");
211 }
212 return this.array[index];
213 }
214 set {
215 if (index < 0 || index >= this.count) {
216 throw new ArgumentOutOfRangeException("index");
217 }
218 this.array[index] = value;
219 }
220 }
221
222 /// <summary>
223 /// 显示实现IList接口的索引器属性
224 /// 调用前一个索引器属性即可
225 /// </summary>
226 object IList.this[int index] {
227 get {
228 return ((IList<E>)this)[index];
229 }
230 set {
231 ((IList<E>)this)[index] = (E)value;
232 }
233 }
234
235 /// <summary>
236 /// 删除向量中的元素
237 /// </summary>
238 public void RemoveRange(int index, int count) {
239 if (index < 0) {
240 throw new ArgumentOutOfRangeException("index");
241 }
242 int removeIndex = index + count;
243 if (count < 0 || removeIndex > this.count) {
244 throw new ArgumentOutOfRangeException("count");
245 }
246 Array.Copy(this.array, index + 1, this.array, index + count - 1, this.count - removeIndex);
247 this.count -= count;
248 }
249
250 /// <summary>
251 /// 实现IList<E>接口的Remove方法
252 /// 返回是否删除元素
253 /// </summary>
254 public bool Remove(E value) {
255 int index = this.IndexOf(value);
256 if (index >= 0) {
257 this.RemoveRange(index, 1);
258 return true;
259 }
260 return false;
261 }
262
263 /// <summary>
264 /// 实现IList接口的Remove方法, 此处为显示实现
265 /// 调用前一个Remove方法即可
266 /// </summary>
267 void IList.Remove(object value) {
268 ((IList<E>)this).Remove((E)value);
269 }
270
271 /// <summary>
272 /// 从集合的特定位置删除对象的引用
273 /// </summary>
274 public void RemoveAt(int index) {
275 RemoveRange(index, 1);
276 }
277
278 /// <summary>
279 /// 实现IList<E>接口的IndexOf方法
280 /// </summary>
281 public int IndexOf(E value) {
282 int index = 0;
283 if (value == null) {
284 while (index < this.count) {
285 if (this.array[index] == null) {
286 return index;
287 }
288 ++index;
289 }
290 } else {
291 while (index < this.count) {
292 if (value.Equals(this.array[index])) {
293 return index;
294 }
295 ++index;
296 }
297 }
298 return -1;
299 }
300
301 /// <summary>
302 /// 实现IList接口的IndexOf方法, 此时为显示实现
303 /// 调用前面的IndexOf方法即可
304 /// </summary>
305 int IList.IndexOf(object value) {
306 return ((IList<E>)this).IndexOf((E)value);
307 }
308
309 /// <summary>
310 /// 查找对应的数组项.
311 /// </summary>
312 public int IndexOf(object o, System.Collections.IComparer comparer) {
313 int index = 0;
314 while (index < this.count) {
315 if (comparer.Compare(this.array[index], o) == 0) {
316 return index;
317 }
318 ++index;
319 }
320 return -1;
321 }
322
323 /// <summary>
324 /// 弹出向量的最后一个元素(即获取最后一个元素的引用后删除最后一个元素)
325 /// </summary>
326 public object PopBack() {
327 object o = this.array[this.count - 1];
328 RemoveAt(this.count - 1);
329 return o;
330 }
331
332 /// <summary>
333 /// 弹出向量的第一个对象
334 /// </summary>
335 public object PopFront() {
336 object o = this.array[0];
337 RemoveAt(0);
338 return o;
339 }
340
341 /// <summary>
342 /// 实现IList<E>接口的Insert方法
343 /// </summary>
344 public void Insert(int index, E value) {
345 if (index >= this.count) {
346 throw new ArgumentOutOfRangeException("index");
347 }
348 int newCount = this.count + 1;
349 if (this.array.Length < newCount) {
350 E[] newArray = GetNewArray();
351 Array.Copy(this.array, newArray, index);
352 newArray[index] = value;
353 Array.Copy(this.array, index, newArray, index + 1, this.count - index);
354 this.array = newArray;
355 } else {
356 Array.Copy(this.array, index, this.array, index + 1, this.count - index);
357 this.array[index] = value;
358 }
359 this.count = newCount;
360 }
361
362 /// <summary>
363 /// 实现IList接口的Insert方法
364 /// </summary>
365 void IList.Insert(int index, object value) {
366 ((IList<E>)this).Insert(index, (E)value);
367 }
368
369 /// <summary>
370 /// 实现IList<E>接口的Contains方法
371 /// </summary>
372 public bool Contains(E value) {
373 return this.IndexOf(value) >= 0;
374 }
375
376
377 /// <summary>
378 /// 实现IList接口的Contains方法
379 /// </summary>
380 bool IList.Contains(object value) {
381 return ((IList<E>)this).IndexOf((E)value) >= 0;
382 }
383
384 /// <summary>
385 /// 将内部数组的长度压缩为向量的实际长度
386 /// </summary>
387 public void TrimToSize() {
388 if (this.array.Length > this.count) {
389
390 // 这里需要实例化一个泛型数组
391 E[] newArray = null;
392 if (this.count > 0) {
393 newArray = new E[this.count];
394 Array.Copy(this.array, newArray, this.count);
395 } else {
396 newArray = new E[1];
397 }
398 this.array = newArray;
399 }
400 }
401
402 /// <summary>
403 /// 清空向量
404 /// </summary>
405 public void Clear() {
406 this.count = 0;
407 }
408
409 /// <summary>
410 /// 实现IEnumerable<E>接口的GetEnumerator方法
411 /// </summary>
412 public IEnumerator<E> GetEnumerator() {
413 Enumertor<E> ator = new Enumertor<E>(this);
414 return ator;
415 }
416
417 /// <summary>
418 /// 实现IEnumerable接口的GetEnumerator方法, 显示实现
419 /// 调用前面的GetEnumerator方法即可
420 /// </summary>
421 IEnumerator IEnumerable.GetEnumerator() {
422 return ((IEnumerable<E>)this).GetEnumerator();
423 }
424
425 /// <summary>
426 /// 实现ICollection<E>接口的CopyTo方法
427 /// </summary>
428 public void CopyTo(E[] array, int index) {
429 Array.Copy(this.array, 0, array, index, this.count);
430 }
431
432
433 /// <summary>
434 /// 实现ICollection接口的CopyTo方法, 显示实现
435 /// </summary>
436 void ICollection.CopyTo(Array array, int index) {
437 Array.Copy(this.array, 0, array, index, this.count);
438 }
439 }
440 }
上述代码展示了一个支持泛型的向量集合,从中我们可以学习到泛型的一些知识。注意以下几点:

通过代码可以看到,将一个不支持泛型的集合类改为支持泛型的集合类非常容易。算法,类结构都无需变化,只需要将原本为object类型的字段改为泛型类型E类型的字段即可。当然访问该字段的所有方法和属性都要进行相应改动。

这个类总共实现了如下接口:IEnumerable, IEnumerable<E>, ICollection, ICollection<E>, IList, IList<E>,迭代子类则实现了IEnumerator和IEnumerator<E>接口。由于这些接口的主要区别在于泛型和非泛型,方法和属性名称都相同,所以采用显式实现的方式来实现非泛型的一系列接口。一方面将这些接口方法隐藏起来,另一方面防止因方法重名造成重载。

现在我们比较一下List<E>和ArrayList使用起来有什么不同。

1 class Program {
2 static void Main(string[] args) {
3
4 // 声明向量集合对象
5 ArrayList lst = new ArrayList();
6
7 // 当调用Add方法时, 值类型(int)会自动的"装箱"为引用类型(object)
8 lst.Add(100);
9
10 // 当调用索引器时, 返回的是一个object类型的对象引用
11 object o = lst[0];
12
13 // 要恢复存储前的类型, 这里还需要一次"拆箱"操作
14 int value = (int)o;
15
16 Console.WriteLine(value);
17
18 // 这里, 先进行了一次"拆箱"操作, 将lst[0]转化为int类型
19 // 接着进行了一次装箱操作
20 lst[0] = (int)lst[0] + 200;
21 Console.WriteLine(lst[0]);
22
23 // 对于ArrayList, 可以存放任意类型的对象引用
24 lst.Add("Hello");
25
26 foreach (object v in lst) {
27 // 这里只能访问object类型的ToString方法
28 // 并无法知道object类型的v变量到底是什么类型
29 Console.WriteLine(v);
30 }
31
32 // 这里使用了List<E>类, 和ArrayList类比较下, 看区别在哪里
33 List<int> slst = new List<int>();
34 slst.Add(100);
35 slst.Add(200);
36 foreach (int n in slst) {
37 Console.WriteLine(n);
38 }
39 }
40 }
本章代码下载

看,泛型确定并强制了集合存储的对象类型,并且避免了装箱拆箱操作。既高效又安全。
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: