.NET Framework泛型是在.NET Framework 2.0的公共语言运行库(CLR)中的增加的一项新功能,类似于C++的模板,但不如C++的模板灵活,不过也有一些自己的特性。
泛型为.NET Framework引入了类型参数的概念,这样便可以设计出这样的类和方法:它们把指定类型的工作推迟到客户端代码声明并实例化类或方法的时候执行。比如,通过泛型类型参数T,程序员就可以编写其他客户端代码能够使用的单个类,而不用担心强制转换或装箱操作而带来的额外的运行成本或风险。
泛型类和方法较之非泛型类和方法具有更高的可用性、类型安全和效率,且通常用在集合和集合上运行的方法中。在.NET Framework2.0的类库中添加了一个新的命名空间System.Collections.Generic,其中含有一些新的基于泛型的集合类。微软建议程序员在设计面向.NET Framework 2.0的应用程序时不要使用旧的非泛型集合类(像ArrayList集合类),而去使用新的泛型集合类。
当然,程序员也可以创建自己的泛型类和方法,提供自己需要的解决方案,但微软不推荐自己创建泛型类,而建议使用.NET Framework类库中提供的List<T>类。
可以通过泛型类型参数传递一个类型(如一个类TestClass<T>)中的T是泛型的类型参数,可以通过它传递一个类型(如int),那么,在这个类中所有用类型参数(在本例中是T)定义的字段或方法等的类型都会根据传递进来的类型(本例中为int类型)所改变。
在定义泛型类或方法中,类型参数是客户端程序实例化泛型类变量时指定的类型占位符。泛型类不能像通常的类那样使用,因为它实际上并不是一个类型,可能更像一个类型的蓝图。要使用泛型类,客户端代码必须要指定尖括号中的类型参数并实例化类型才能构造类型。指定的类型参数可以是编译器可以识别的任何类型,并且可以构造任意数目的使用不同类型参数的实例。
通常,微软推荐采用如下命名规则:
对于泛型参数的约束是指对客户端代码实例化类时指定的类型参数进行限制,这样如果客户端的程序参试使用某个不允许使用的类型实例化类时会产生编译时错误。
以C# 为例,约束采用where上下文关键字指定。
若要对泛型列表中的项与其他某个项进行比较,便需要在一定程度上保证所调用的运算符或方法可以得到指定的类型参数的支持。这个保证正是建立在对泛型类定义的一个或多个约束获得的。一旦编译器得到了这样的保证,它就能够允许在泛型类中调用一些无约束的泛型中不允许使用的方法。
没有进行约束的泛型类型参数称为未绑定的类型参数。
当使用未绑定的类型参数时,相对于使用了约束的类型参数,有以下规则:
泛型类可以拥有至少一个泛型类型参数,泛型类型参数可以代替泛型类的字段、属性、方法的参数和返回值、事件的参数和返回值、构造函数的参数的类型,也可作为内部类和基类以及实现接口的泛型类型参数。
泛型类必须在创建其实例时明确指定其所有泛型类型参数的类型。泛型类的构造函数不能自动推断其泛型类型的泛型类型参数。
如果从泛型类派生,可以选择确定的泛型类型参数,也可以用泛型派生类,利用泛型派生类的泛型类型参数确定基类的泛型类型参数。
泛型接口的使用大体上与泛型类相同,只是接口不允许包含字段和构造函数,也不允许创建实例。实现接口时,同样也可以选择实现确定的泛型类型接口,或者利用泛型类型参数确定接口的泛型类型参数。
泛型方法可以拥有至少一个泛型类型参数,泛型类型参数可以作为方法的参数或返回值的类型。泛型方法不用明确指定各泛型类型参数,可由上下文自动推断,如果上下文无法自动推断,则必须明确指定。
泛型委托的使用大体上与泛型方法相同,泛型委托的泛型类型参数可以在创建时通过上下文自动推断。泛型委托实例总是具有确定的泛型类型参数。
泛型和模板都是用于提供类型参数支持的语言功能,但这两者有着诸多差异。
从语法层面上来说,一般认为泛型是一种相对模板简单的方法,而不具有模板的复杂性。但同时,泛型也不能提供模板的完整功能。
另一方面,从实现层面,泛型的替换是在运行时执行的,这样可以为实例化的对象保留泛型类型的信息。
下面列出了.NET Framework泛型和C++模板之间的主要区别:
泛型类型参数自身不能是泛型,但C++确实允许模板参数。
C++允许程序员编写可能对模板中某些类型参数无效的代码,然后将检查该代码中是否有用作类型参数的特定类型。比如说可以在C++中编写对类型参数对象的算术运算符的函数,这样在用不支持这些运算符的类型实例化模板时出现出现错误;而泛型是不允许这样的,它则要求相应地编写类当中的代码,使他们能够满足任何约束的类型。因此唯一允许的语言构造是那些能够从约束中推导出来的结构。