.NET 2.0 基础类库中的范型——其他范型类

其他范型类

.NET 2.0 基础类库对范型的应用当然并不仅限于范型集合和 Functional Programming 。下面所列的范型类也都有其明确的设计目的和用途。

Array

在 .NET 2.0 中, Array 类扩充了对范型编程的支持。当然, Array 类本身并不是范型类(出于兼容的考虑),而是提供了一系列支持范型的方法。除了前面提到的 Functional Programming 的支持外, Array 类还对以前很多基于 object 的方法提供了对应的范型版本,这样对值类型可以提高查找和排序时的性能。例如:

static int IndexOf (T[] array, T value);

static void Sort (T[] array);

另外,还添加了一些新的范型方法,例如:

static IList AsReadOnly (T[] array); // 返回一个只读的列表

static void Resize (ref T[] array, int newSize); // 改变数组大小

还有一个好消息是,在 .NET 2.0 中,数组将支持范型集合接口。我们知道,在 .NET 2.0 以前, Array 抽象类实现了 IList , ICollection 和 IEnumerable 集合接口,这样我们可以在需要传入这些接口的地方传入数组。在 .NET 2.0 中,范型集合需要使用如 IEnumerable

 1<t> 这样的范型接口,所以数组也将支持这些范型接口。然而,这些范型接口并不在  Array  类中实现(因为  Array  类本身并不是范型类),而是在运行时由  CLR  实现。例如,对于  int[]  ,可以按如下的伪定义理解它的实现: 
 2
 3class int[] : Array, **List <int> , ICollection<int> , IEnumerable<int> **
 4
 5###  ArraySegment<t>
 6
 7ArraySegment<t> 表示数组中的一段。我们知道,  C#/CLR  没有提供默认参数这一特性,而是要求使用函数重载。所以,不少类中有大量的针对数组参数(索引,长度)的重载方法(为了方便调用者),例如: 
 8
 9class Encoding { 
10
11public virtual byte[] GetBytes(char[] chars); 
12
13public virtual byte[] GetBytes(char[] chars, int index, int count); 
14
15... 
16
17} 
18
19对类的设计者来说,提供如此多的重载显得麻烦和笨拙,而且这些重载方法实际上都对应同一个实现。另外,设计如此多的虚函数也给子类的实现者带来了不少麻烦,尤其是当这些函数是  abstract  时。 
20
21在  .NET 2.0  中,微软试图通过提供  ArraySegment<t> 类来解决这一设计问题。使用  ArraySegment<t> 的话,类的设计者现在只需设计一个方法即可,即:(注意这不是  .NET 2.0  的真实代码,仅为说明问题) 
22
23class Encoding { 
24
25public virtual byte[] GetBytes(ArraySegment<char> chars); 
26
27... 
28
29} 
30
31而由调用者来决定如何传入数组参数,例如: 
32
33char[] chars = ...; 
34
35byte[] bytes = enc.GetBytes(new ArraySegment<char> (chars)); 
36
3738
39byte[] bytes = enc.GetBytes(new ArraySegment<char> (chars, 0, 10)); 
40
41可以看到,使用  ArraySegment<char> 的缺点是对使用者来说要多编写一些代码。可能是这个原因,所以目前  .NET  中并没有正式开始使用它。另外一个原因则可能是出于要和已有设计保持一致的考虑。 
42
43###  Nullable<t>
44
45Nullable<t> 值类型用于表示可能无效或者不存在的值(这个类最初的命名为  OptionalValue<t> )。例如,在数据库设计中可能有些字段是可选,数据访问接口的设计者可以用  Nullable<t> 来返回数据库字段。  Nullable<t> 类有两个只读实例属性  HasValue  和  Value  。前者是  bool  类型用于标识是否有效,后者是  T  类型的数据。在访问  Value  之前必须先判断  HasValue  是否为  true  ,否则将抛出异常。 
46
47Nullable<t> 通常用于值类型(如  Nullable<datetime> ),因为对引用类型来说,  null  本身就可以代表无效的状态,在这种情况下使用  Nullable<t> 并没有太多意义。 
48
49值得一提的是,  C# 2.0  为  Nullable<t> 类型提供了一个非常简洁而优美的语法,即在原始类型后加  ?  后缀,也就是说,  int?  等于  Nullable<int> 。这样使得  Nullable<t> 在  C#  中的使用非常的容易和自然(毕竟模板看起来要费眼一些  J  )。例如下面的代码示例: 
50
51int? a = null; // a  为空(即  HasValue  属性为  false  ) 
52
53int? b = 10; // b  为  10 
54
55以后,在设计可能返回无效值的  API  时,除了以前使用的抛出异常的方法外,我们也可以使用  Nullable<t> ,例如: 
56
57int ParseNumber(string s); //  使用异常 
58
59int? TryParseNumber(string s); //  不使用异常,而使用  Nullable<t>
60
61###  EventHandler<t>
62
63事件的定义和使用遍及  .NET Framework  的各个角落。在没有范型的情况下,每个事件委托都要单独定义,例如: 
64
65delegate void EventHandler(object sender, EventArgs e); 
66
67delegate void KeyEventHandler(object sender, KeyEventArgs e); 
68
69这样的缺点是对事件定义者来说每次都要定义新的事件委托,而对使用者来说又要多学习和记忆新的事件委托。在  .NET 2.0  中,引入了  EventHandler  范型事件委托来解决这个问题,它的原型如下(注意它位于  System.Collections.Generic  命名空间中): 
70
71delegate void EventHandler  (object sender, T e) where T: EventArgs; 
72
73使用  EventHandler  的话,就不需要自己定义新的事件委托了,仅需提供自己的事件参数类即可(需要从  EventArgs  派生)。这样的好处一方面是可用性更好(无论对事件定义者还是使用者),另外从  CLR  的角度来说,因为这个范型委托编译后对所有  T  类型都只对应一个二进制实现,所以会提高系统的整体性能。所以,在微软最新的设计指南中,建议事件委托使用  EventHandler<t> 。使用  EventHandler<t> 的代码示例如下: 
74
75class MyEventArgs : EventArgs { 
76
77... 
78
79} 
80
81class Foo { 
82
83public event EventHandler<myeventargs> MyEvent; 
84
85... 
86
87} 
88
89Foo foo = new Foo(); 
90
91foo.MyEvent += new EventHandler<myeventargs> (this.MyEventHandler); 
92
93...</myeventargs></myeventargs></t></t></t></t></t></t></int></t></t></datetime></t></t></t></t></t></t></char></char></char></char></t></t></t></t></int></int></int></t>
Published At
Categories with Web编程
Tagged with
comments powered by Disqus