我将把我的声音加入到噪音中,并尝试让事情变得清晰:
C# 泛型允许您声明类似的内容。
List<Person> foo = new List<Person>();
然后编译器会阻止你放入不属于的东西Person
进入列表。
C# 编译器只是在幕后放置List<Person>
到 .NET dll 文件中,但在运行时,JIT 编译器会构建一组新的代码,就好像您编写了一个专门用于包含人员的特殊列表类 - 类似于ListOfPerson
.
这样做的好处是它变得非常快。没有转换或任何其他东西,并且因为 dll 包含这是一个列表的信息Person
,稍后使用反射查看它的其他代码可以看出它包含Person
对象(这样你就可以获得智能感知等等)。
这样做的缺点是旧的 C# 1.0 和 1.1 代码(在添加泛型之前)无法理解这些新代码List<something>
,所以你必须手动将东西转换回普通的旧版本List
与他们进行互操作。这并不是什么大问题,因为 C# 2.0 二进制代码不向后兼容。唯一会发生这种情况的情况是您将一些旧的 C# 1.0/1.1 代码升级到 C# 2.0
Java 泛型允许您声明类似的内容。
ArrayList<Person> foo = new ArrayList<Person>();
从表面上看,它看起来是一样的,而且也确实如此。编译器还会阻止你放入不属于的东西Person
进入列表。
区别在于幕后发生的事情。与 C# 不同,Java 不会去构建一个特殊的ListOfPerson
- 它只是使用普通的旧的ArrayList
这一直是Java中的。当你从数组中取出东西时,通常Person p = (Person)foo.get(1);
选角舞蹈还得做。编译器正在为您节省按键操作,但速度影响/转换仍然像以前一样发生。
当人们提到“类型擦除”时,他们所说的就是这个。编译器会为您插入强制转换,然后“擦除”它应该是一个列表的事实Person
不只是Object
这种方法的好处是不理解泛型的旧代码不必关心。它仍然在处理同样的旧问题ArrayList
一如既往。这在 Java 世界中更为重要,因为他们希望支持使用带有泛型的 Java 5 编译代码,并使其在旧的 1.4 或以前的 JVM 上运行,而微软故意决定不去打扰。
缺点是我之前提到的速度打击,也是因为没有ListOfPerson
伪类或类似的东西进入 .class 文件,稍后查看它的代码(通过反射,或者如果你将它从另一个集合中取出,它被转换为Object
等等)无法以任何方式判断它是一个仅包含的列表Person
而不仅仅是任何其他数组列表。
C++ 模板允许你声明这样的东西
std::list<Person>* foo = new std::list<Person>();
它看起来像 C# 和 Java 泛型,并且它会做您认为它应该做的事情,但在幕后正在发生不同的事情。
它与 C# 泛型最共同之处在于它构建了特殊的pseudo-classes
而不是像 java 那样仅仅丢弃类型信息,但这是完全不同的事情。
C# 和 Java 都会产生专为虚拟机设计的输出。如果您编写一些具有Person
其中的类,在这两种情况下都有一些关于Person
class 将进入 .dll 或 .class 文件,JVM/CLR 将对此进行处理。
C++ 生成原始 x86 二进制代码。一切都是not一个对象,并且没有底层虚拟机需要了解一个对象Person
班级。没有装箱或拆箱,函数不必属于类,甚至不必属于任何东西。
因此,C++ 编译器对您可以使用模板执行的操作没有任何限制 - 基本上您可以手动编写的任何代码,您都可以让模板为您编写。
最明显的例子是添加东西:
在 C# 和 Java 中,泛型系统需要知道类可以使用哪些方法,并且需要将其传递给虚拟机。告诉它这一点的唯一方法是通过对实际类进行硬编码或使用接口。例如:
string addNames<T>( T first, T second ) { return first.Name() + second.Name(); }
该代码无法在 C# 或 Java 中编译,因为它不知道类型T
实际上提供了一个名为Name()的方法。你必须用 C# 来告诉它,如下所示:
interface IHasName{ string Name(); };
string addNames<T>( T first, T second ) where T : IHasName { .... }
然后你必须确保传递给 addNames 的东西实现 IHasName 接口等等。 java 语法不同(<T extends IHasName>
),但它也遇到了同样的问题。
这个问题的“经典”案例是尝试编写一个函数来执行此操作
string addNames<T>( T first, T second ) { return first + second; }
您实际上无法编写此代码,因为没有方法可以使用+
方法在其中。你失败了。
C++ 不存在这些问题。编译器不关心将类型传递给任何虚拟机 - 如果您的两个对象都有 .Name() 函数,它将编译。如果他们不这样做,那就不会。简单的。
所以你有它 :-)