.NET 2.0 基础类库中的范型——其他范型类
2024-07-10 12:59:29
供稿:网友
其他范型类
.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<t> 这样的范型接口,所以数组也将支持这些范型接口。然而,这些范型接口并不在 array 类中实现(因为 array 类本身并不是范型类),而是在运行时由 clr 实现。例如,对于 int[],可以按如下的伪定义理解它的实现:
class int[] : array, list<int>, icollection<int>, ienumerable<int>
arraysegment<t>
arraysegment<t> 表示数组中的一段。我们知道,c#/clr 没有提供默认参数这一特性,而是要求使用函数重载。所以,不少类中有大量的针对数组参数(索引,长度)的重载方法(为了方便调用者),例如:
class encoding {
public virtual byte[] getbytes(char[] chars);
public virtual byte[] getbytes(char[] chars, int index, int count);
...
}
对类的设计者来说,提供如此多的重载显得麻烦和笨拙,而且这些重载方法实际上都对应同一个实现。另外,设计如此多的虚函数也给子类的实现者带来了不少麻烦,尤其是当这些函数是 abstract 时。
在 .net 2.0 中,微软试图通过提供 arraysegment<t> 类来解决这一设计问题。使用 arraysegment<t> 的话,类的设计者现在只需设计一个方法即可,即:(注意这不是 .net 2.0 的真实代码,仅为说明问题)
class encoding {
public virtual byte[] getbytes(arraysegment<char> chars);
...
}
而由调用者来决定如何传入数组参数,例如:
char[] chars = ...;
byte[] bytes = enc.getbytes(new arraysegment<char>(chars));
或
byte[] bytes = enc.getbytes(new arraysegment<char>(chars, 0, 10));
可以看到,使用 arraysegment<char> 的缺点是对使用者来说要多编写一些代码。可能是这个原因,所以目前 .net 中并没有正式开始使用它。另外一个原因则可能是出于要和已有设计保持一致的考虑。
nullable<t>
nullable<t> 值类型用于表示可能无效或者不存在的值(这个类最初的命名为 optionalvalue<t>)。例如,在数据库设计中可能有些字段是可选,数据访问接口的设计者可以用 nullable<t> 来返回数据库字段。nullable<t> 类有两个只读实例属性 hasvalue 和 value。前者是 bool 类型用于标识是否有效,后者是 t 类型的数据。在访问 value 之前必须先判断 hasvalue 是否为 true,否则将抛出异常。
nullable<t> 通常用于值类型(如 nullable<datetime>),因为对引用类型来说,null 本身就可以代表无效的状态,在这种情况下使用 nullable<t> 并没有太多意义。
值得一提的是,c# 2.0 为 nullable<t> 类型提供了一个非常简洁而优美的语法,即在原始类型后加 ? 后缀,也就是说,int? 等于 nullable<int>。这样使得 nullable<t> 在 c# 中的使用非常的容易和自然(毕竟模板看起来要费眼一些j)。例如下面的代码示例:
int? a = null; // a为空(即hasvalue属性为false)
int? b = 10; // b为10
以后,在设计可能返回无效值的 api 时,除了以前使用的抛出异常的方法外,我们也可以使用 nullable<t>,例如:
int parsenumber(string s); // 使用异常
int? tryparsenumber(string s); // 不使用异常,而使用nullable<t>
eventhandler<t>
事件的定义和使用遍及 .net framework 的各个角落。在没有范型的情况下,每个事件委托都要单独定义,例如:
delegate void eventhandler(object sender, eventargs e);
delegate void keyeventhandler(object sender, keyeventargs e);
这样的缺点是对事件定义者来说每次都要定义新的事件委托,而对使用者来说又要多学习和记忆新的事件委托。在 .net 2.0 中,引入了 eventhandler 范型事件委托来解决这个问题,它的原型如下(注意它位于 system.collections.generic 命名空间中):
delegate void eventhandler(object sender, t e) where t: eventargs;
使用 eventhandler 的话,就不需要自己定义新的事件委托了,仅需提供自己的事件参数类即可(需要从 eventargs 派生)。这样的好处一方面是可用性更好(无论对事件定义者还是使用者),另外从 clr 的角度来说,因为这个范型委托编译后对所有 t 类型都只对应一个二进制实现,所以会提高系统的整体性能。所以,在微软最新的设计指南中,建议事件委托使用 eventhandler<t>。使用 eventhandler<t> 的代码示例如下:
class myeventargs : eventargs {
...
}
class foo {
public event eventhandler<myeventargs> myevent;
...
}
foo foo = new foo();
foo.myevent += new eventhandler<myeventargs>(this.myeventhandler);
...
本文来源于网页设计爱好者web开发社区http://www.html.org.cn收集整理,欢迎访问。