泛型概述
C#中的泛型
C#泛型和java泛型的比较
C#泛型和C++模板的比较
C#泛型中的约束
泛型概述
Bruce Eckel :您能对泛型做一个快速的介绍么?
Anders Hejlsberg : 泛型其实就是能够向你的类型中加入类型参数的一种能力,也称作参数化的类型或参数多态性。最著名的例子就是List集合类。一个List是一个易于增长的 数组。它有一个排序方法,你可以为 它做索引,等等。现在,如果没有参数化的类型,那么不论使用数组还是使用List都不是很好。如果你使用数组,你能获得强类型,因为你可以声明一个 Customer类型的数组,但你失去了可增长性和那些方便的方法;如果你使用一个List,你能够得到所有的便利,但你失去了强类型。你难以说出一个 List是什么(类型的)List,它只是一个Object的List【译注:“什么类型的List”指的是List存放的元素是什么类型的】。这会给你 带来麻烦 ,因为类型只能在运行形时进行检查,也就是说在编译时不会进行类型检查。就算你硬要把一个Customer放进一个List并试图从中得到一个 String,编译器也不会不高兴。在运行之前你根本无法发现它不能工作。同时,当你将简单类型【译注:指值类型】放入List时,还必须对它们进行装 箱。正是由于这些问题,你不得不在List和数组之间徘徊,你经常要很痛苦地决定应该使用哪一个。
泛型的伟大之处在于你现在可以尽情地享受你的蛋糕了,因为你能够定义一个List(读 作:List of T)【译注:中文可以说成“T类型的List”】。当你使用List时,你居然能够说出它是什么类型的List,并且你将获得强类型,编译器会为你检查它 的类型。这些只是直觉上的好处,它还有其它许多优点。当然,你并不是只能将它用于List,Hastable、Dictionary(将键影射到值上的数 据结构)——所有你想调用的都行。你可能想将String影射到Customer、将int影射到Order,在这些情况下你都能获得强类型。
![](https://www.cnblogs.com/Images/OutliningIndicators/None.gif)
![](https://www.cnblogs.com/Images/OutliningIndicators/None.gif)
![](https://www.cnblogs.com/Images/OutliningIndicators/None.gif)
![](https://www.cnblogs.com/Images/OutliningIndicators/None.gif)
![](https://www.cnblogs.com/Images/OutliningIndicators/None.gif)
![](https://www.cnblogs.com/Images/dot.gif)
![](https://www.cnblogs.com/Images/dot.gif)
![](https://www.cnblogs.com/Images/OutliningIndicators/None.gif)
![](https://www.cnblogs.com/Images/OutliningIndicators/None.gif)
![](https://www.cnblogs.com/Images/OutliningIndicators/None.gif)
![](https://www.cnblogs.com/Images/OutliningIndicators/None.gif)
![](https://www.cnblogs.com/Images/OutliningIndicators/None.gif)
![](https://www.cnblogs.com/Images/OutliningIndicators/None.gif)
![](https://www.cnblogs.com/Images/OutliningIndicators/None.gif)
![](https://www.cnblogs.com/Images/OutliningIndicators/None.gif)
![](https://www.cnblogs.com/Images/OutliningIndicators/None.gif)
![](https://www.cnblogs.com/Images/OutliningIndicators/None.gif)
![](https://www.cnblogs.com/Images/OutliningIndicators/None.gif)
![](https://www.cnblogs.com/Images/OutliningIndicators/None.gif)
![](https://www.cnblogs.com/Images/OutliningIndicators/None.gif)
![](https://www.cnblogs.com/Images/OutliningIndicators/None.gif)
![](https://www.cnblogs.com/Images/OutliningIndicators/None.gif)
![](https://www.cnblogs.com/Images/OutliningIndicators/None.gif)
![](https://www.cnblogs.com/Images/OutliningIndicators/None.gif)
![](https://www.cnblogs.com/Images/OutliningIndicators/None.gif)
![](https://www.cnblogs.com/Images/OutliningIndicators/None.gif)
![](https://www.cnblogs.com/Images/OutliningIndicators/None.gif)
![](https://www.cnblogs.com/Images/OutliningIndicators/None.gif)
![](https://www.cnblogs.com/Images/OutliningIndicators/None.gif)
![](https://www.cnblogs.com/Images/OutliningIndicators/None.gif)
![](https://www.cnblogs.com/Images/OutliningIndicators/None.gif)
![](https://www.cnblogs.com/Images/OutliningIndicators/None.gif)
![](https://www.cnblogs.com/Images/OutliningIndicators/None.gif)
![](https://www.cnblogs.com/Images/OutliningIndicators/None.gif)
![](https://www.cnblogs.com/Images/OutliningIndicators/None.gif)
![](https://www.cnblogs.com/Images/OutliningIndicators/None.gif)
![](https://www.cnblogs.com/Images/OutliningIndicators/None.gif)
![](https://www.cnblogs.com/Images/OutliningIndicators/None.gif)
![](https://www.cnblogs.com/Images/OutliningIndicators/None.gif)
![](https://www.cnblogs.com/Images/OutliningIndicators/None.gif)
![](https://www.cnblogs.com/Images/OutliningIndicators/None.gif)
![](https://www.cnblogs.com/Images/OutliningIndicators/None.gif)
![](https://www.cnblogs.com/Images/OutliningIndicators/None.gif)
![](https://www.cnblogs.com/Images/OutliningIndicators/None.gif)
![](https://www.cnblogs.com/Images/OutliningIndicators/None.gif)
![](https://www.cnblogs.com/Images/OutliningIndicators/None.gif)
![](https://www.cnblogs.com/Images/OutliningIndicators/None.gif)
![](https://www.cnblogs.com/Images/OutliningIndicators/None.gif)
![](https://www.cnblogs.com/Images/OutliningIndicators/None.gif)
![](https://www.cnblogs.com/Images/OutliningIndicators/None.gif)
![](https://www.cnblogs.com/Images/OutliningIndicators/None.gif)
![](https://www.cnblogs.com/Images/OutliningIndicators/None.gif)
![](https://www.cnblogs.com/Images/OutliningIndicators/None.gif)
C#泛型中的约束
Bruce Eckel:约束是如何在C#泛型中工作的呢?
Anders Hejlsberg:在C#泛型中,我们能够为类型参数施加约束。以我们的List < T > 为例,你可以说class List < T > where T : IComparable。这意味着T必须实现IComparable接口。
Bruce Eckel:有意思。在C ++ 中,约束是隐式的。
Anders Hejlsberg:是的。在C#中我们也可以这样做。譬如我们有一个Dictionary < K, V > , 它有一个Add()方法,这个方法带有K key和V value参数。Add()方法的实现将希望能够将传递进来的key和Dictionary中已经 存在的key进行比较,而且它希望使用一个称作IComparable的接口。唯一的途径就是将key参数转换为IComparable接口,然后调用 CompareTo方法。当然,当你这么做的时候,你就为K类型和key参数建立了一个隐式的约束。如果传递进来的key没有实现IComparable 接口,你会得到一个运行时错误。这在你的所有方法中都有可能出现,因为你的约定没有要求key必须实现IComparable接口。当然,你还得为运行时 类型检查付出代价,因为你实际上进行了动态类型转换。
使 用约束,你可以消除代码中的动态检查,而在编译时或装载时进行。当你要求K必须实现IComparable接口时,会发生很多事情。对于K类型的值,你现 在可以直接访问接口方法而无需类型转换。因为程序在语义上可以保证它实现了这个接口。无论什么时候你尝试建立这个类型的一个实例时,编译器都会检查这些类 型是否实现了这个接口,如果没有实现,会给你一个编译错误。如果你使用的是反射,你会得到一个异常。
Bruce Eckel:你是说编译器和运行时(都会进行检查)?
Anders Hejlsberg:编译器会检查它,但你仍有可能在运行时通过反射来做这些,因此系统还会检查它。正像我前面说的,编译时可以做的任何事都可以在运行是通过反射来做。
Bruce Eckel:我可以做一个函数模板,换句话说,一个带有不知道类型的参数的函数?你为约束添加了强类型检查,但我是不是能像C ++ 模板那样得到一个弱类型模板? 例如,我能否写一个函数,它带有两个参数A a和B b,并在代码中写a + b?我能不能说我不在乎对于A和B是否有operator + ,因为 它们是弱类型的?
Anders Hejlsberg: 你真正要问的问题应该是这在约束中如何说吧?约束,和其他特性一样,最终将可以是任意复杂的。当你考虑它的时候,约束只是一个模式匹配机制。你可能希望能 够说“这个类型参数必须有一个带有两个参数的构造器、实现了operator + 、有这个静态方法、有那两个实例方法、等等”。问题是,你希望这种模式匹配机制有多复杂?
从 没有任何东西到完全模式匹配是一个整个的连续体。没有任何东西(的模式匹配)太小了,不能说明问题;而完全模式匹配又太复杂了,因此我们需要在中间找一个 平衡点。我们允许你将约束指定为一个类、一个或多个接口,以及一些构造器约束。譬如,你可以说:“这个类型必须实现IFoo和IBar”或“这个类型必须 继承基类X”。一旦你这么做了,在编译时和运行时都会检查这个约束是否为真。这个约束所隐含的任何方法对于类型参数所指定的类型的值都是直接有效的。
现在,在C#中,运算符是静态成员。因此,运算符不能是接口的成员,因此接口约束不能带给你operator +。你只能通过类约束获得operator + ,你可以说这个类型参数必须继承自比如说Number类,并且Number类对于两个Nubmer有operator + 。但你不能抽象地说“必须有一个operator + ”,我们无法知道这句话的具体含义。
Bill Venners:你通过类型进行约束,而不是签名。
Anders Hejlsberg:是的。
Bill Venners:因此这个类型必须扩展一个类或实现一个接口。
Anders Hejlsberg:是的。而且我们还能够走得更远。实际上我们也想过再走远一些,但这会变得相当复杂。而且增加的复杂性与所得到的相比很不值得。如果你想做的事情在约束系统中不直接支持,你可以使用一个工厂模式。例如你有一个Martix < T > , 而在这个Martix(矩阵)中,你可能想定义一个“点乘”【译注:矩阵上的一种乘法运算,另一种称为“叉乘”】方法。这意味着你最终将要考虑如何将两个 T相乘,但你不能将这说成是一个约束,至少当T不是int、double或float时你不能这么说。但你可以让你的Martix带有一个 Calculator < T > 作为参数,而在Calculator < T > 中,有一个称为Multiply的方法。你可以在其中进行实现,并将结果传递给Martix。
Bruce Eckel:而且Calculator也是一个参数化的类型。
Anders Hejlsberg:是的。这有些像工厂模式,还有很多方法可以做到,这也许不是你最喜欢的方法,但做任何事情都要付出代价。
Bruce Eckel: 是呀,我开始认为C ++ 模板是一种弱类型机制。而当你想其中添加了约束后,你从弱类型走向了强类型。但这一定会带来更多的复杂性。这就是代价吧。
Anders Hejlsberg: 关于类型你可以认为它是一个轮盘。这个轮盘放得越高,程序员的日子就会越不好过,但更高的安全性随之而来。但你可以把这个轮盘向任何一个方向转得任意远。