一次编码多次使用这就是引入泛型的根源在以前的C++中称为模板C#泛型通过算法和数据结构支持独立编码例如泛型列表意味着你不必再重写一个强类型集合在本文中作者将向你展示定义和使用泛型是多么容易的事情请注意长期以来泛型一直被认为是最高级和最困难的术语
一 简介
泛型现在在任何一种语言中都被认为是一个高级的强有力的术语当我在C++中第一次接触模板时我对之有些疑惑之后我读了Bjarne Stroustrop的《The Design and Evolution of C++》才发现模板的使用就象C中的宏和用之来取代的简单串替换模板一样容易其实模板和泛型是相同的东西尽管它们的实现稍微不同
C#泛型支持在使用点处才定义算法及其数据类型在C#的一些早期版本中我们可以证明没有泛型也可以工作因为每种类型都是派生于一个公共基类型object这意味着程序员可以基于object类型定义一个栈类并且把一切东西放到该栈上(因为一切都派生于object)然而一个object栈意味着Customer对象Integer对象以及假想的对象都能被放置到同一个栈的实例上结果是开发者要子类化数据类型来把数据类型绑定到他们要与之交互的东西上去例如在编写定制的商业对象时我们就建议定义派生于SystemCollectionsCollectionBase的强类型集合原因很简单基于object定义一切被认为是弱类型定义
业界的高手们在数十年前就确信强类型优于弱类型所以NET最终支持强类型这看上去是很自然的事情强类型算法当然建议类型化参数这正是我们在泛型中所用的东西
十几年来我们一直在使用字母T作为类型化参数的名字这样在任何泛型类使用者所提供的数据类型的地方你都能够找到T使用泛型的关键仅仅是提供这个T定义泛型的关键在于实现一个方法或类并且用特定数据类型来替换掉T
C#中的泛型支持另外一些提炼例如一个方法或类可以有多个参数化的类型并且C#泛型还支持WHERE约束它用来具体要求类型化参数的类型例如如果一个泛型类型必须实现接口IDisposable那么C#泛型是支持实现这一限制的在文章的最后我们还要看一下约束问题
闲话少说让我们言归正传
二 使用泛型集合
有些人问我面向对象编程(OOP)的承诺在哪里?我的回答是应该从两个方面来看OOP你所使用的OOP和你创建的OOP如果我们简单地看一下如果没有如例如Microsoft的NETBorland的VCL以及所有的第三方组件这样的OO框架那么很多高级的应用程序几乎就无法创建所以我们可以说OOP已经实现了它的承诺不错生产好的OOP代码是困难的并且可能是极具挫败性的但是记住你不必须一定要通过OOP来实现你的目标因此下面首先让我们看一下泛型的使用
当你用Visual Studio或C# Express等快速开发工具创建工程时你会看到对于SystemCollectionsGeneric命名空间的参考引用在这个命名空间中存在若干泛型数据结构它们都支持类型化的集合散列队列栈字典以及链表等为了使用这些强有力的数据结构你所要做的仅是提供数据类型
列表显示出我们定义一个强类型集合的Customer对象是很容易的
列表 这个控制台应用程序包含一个Customer类和一个基于List<T>的强类型集合Customers
using System;
using SystemCollectionsGeneric;
using SystemText;
namespace Generics{
class Program{
static void Main(string[] args){
List<Customer> customers = new List<Customer>();
customersAdd(new Customer(MotownJobs));
customersAdd(new Customer(Fatmans));
foreach (Customer c in customers)
ConsoleWriteLine(cCustomerName);
ConsoleReadLine();
}
}
public class Customer{
private string customerName = ;
public string CustomerName{
get { return customerName; }
set { customerName = value; }
}
public Customer(string customerName){
thiscustomerName = customerName;
}
}
}
注意我们有一个强类型集合List<Customer>对这个集合类本身来说不需要写一句代码如果我们想要扩展列表customer我们可以通过从List<Customer>继承而派生一个新类
三 实现一个泛型类
一种合理的实现某种新功能的方法是在原有的事物上进一步构建我们已经了解强类型集合并知道一种不错的用来构建泛型类的技术是使用一个特定类并删除数据类型也就是说让我们定义一个强类型集合CustomerList并且来看一下它要把什么东西转化成一个泛型类
列表定义了一个类CustomerList后面的部分把CustomerList转化成List<T>
列表定义类CustomerList:
using System;
using SystemCollections;
using SystemText;
namespace Generics{
public class CustomerList : CollectionBase{
public CustomerList() { }
public Customer this[int index]{
get { return (Customer)List[index]; }
set { List[index] = value; }
}
public int Add(Customer value)
{return ListAdd(value);}
}
}
四 定义类头
如果我们定义一个泛型类我们需要把类头转化成一个泛型类所有我们需要做的是命名参数并且把类名改成某种泛型List<T>只有一个参数T并且因为我们在以一种向后兼容的方式工作所以我们知道类名是List列表显示出列表中类的新类头
列表 一个泛型类头显示出参数化的参数T
using System;
using SystemCollections;
using SystemText;
namespace Generics{
public class List<T> : CollectionBase {}
五 实现泛型字段
如果我们需要把任何字段转换成泛型字段我们将只需简单地把它们的类型改变成T(或该字段所描述的任何参数)泛型List不需要任何字段但是假定存在一个私有的整型字段叫foo我们将把它泛型化我们将如下重新定义它:
private T foo;
当参数T被填充到类中时List T也将因foo被填充
六 定义泛型方法
接下来我们为所需要的参数化类型定义其它一些特性这包括属性方法和事件在我们的实例中在Customer出现的每一处我们都用参数T替换它完成后的泛型列表类显示于列表中
列表 一个基于SystemCollectionsCollectionBase的轻量级的参数化泛型列表类
using System;
using SystemCollections;
using SystemText;
namespace Generics{
public class List<T> : CollectionBase {
public List(){ }
public T this[int index] {
get { return (T)List[index]; }
set { List[index] = value; }
}
public int Add(T value) {
return ListAdd(value);
}
}
}
为了测试该定制列表注释掉使用SystemCollectionsGeneric命名空间一句并且把列表中的List<T>使用在列表的代码中它将以同样的方式工作
全面地修改NET的List<T>是不必要的而且它也包含远比我们的示例多得多的特性但是列表显示出这种机制对于定义定制泛型类是多么容易
七 增加类型约束
最后要讨论的是约束问题约束被应用于类或其它特性上并且使用下面的语法
Where T : constraint_type
例如任何我们想要通过using语句所使用的如一个SqlDataReader必须实现Idisposable接口这是因为如下方式使用的using语句:
using(Form f = new Form()){}
就象一个tryfinally块一样工作总是清除新创建的资源其工作原理很简单只需要CLR针对在该using语句中创建的对象发出一个到IDisposableDispose的调用即可例如在上面这一句中一个新的表单被创建并且在using语句退出之前即调用FormDispose
要对一个泛型类施加以确保该类实现了接口IDisposable我们将添加先行词where T:Idisposable在列表中的泛型列表上施加约束后我们将重新修改列表如下面的列表所示
列表 增加一个约束到泛型类以确保我们的List<T>中的所有的值T实现接口Idisposable
using System;
using SystemCollections;
using SystemText;
namespace Generics{
public class List<T> : CollectionBase where t : IDisposable{
public List(){ }
public T this[int index]{
get { return (T)List[index]; }
set { List[index] = value; }
}
public int Add(T value){return ListAdd(value);}
}
}
先行词where的值可以是类接口结构实现一个无参的公共构造器或有一个特定的基类的类详见有关帮助文档
八 总结
泛型的设计是用来减少你重复实现的代码的次数只需改变数据类型即可因为抽象数据结构如队列栈和列表皆是典型的数据结构所以存在针对这些东西的泛型类完全可以理解你可以从NET中派生大量的值通过使用现有的泛型类如在SystemCollectionsGeneric命名空间中的那些
可以肯定在一段相当长的时间里泛型将会象模式和重构等革新一样对开发带来越来越大的价值而且新的数据结构能被转换成可重用的如泛型等的代码元素