关闭。这个问题需要更多focused .它目前不接受答案。
7年前关闭。
锁定。这个问题及其答案是locked因为这个问题是题外话,但具有历史意义。它目前不接受新的答案或互动。
我主要使用 Java,泛型相对较新。我一直读到 Java 做出了错误的决定,或者 .NET 有更好的实现等等。
那么,C++、C#、Java 在泛型方面的主要区别是什么?每个的优点/缺点?
最佳答案
我会将我的声音添加到噪音中,并尝试将事情弄清楚:
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.0Java泛型允许你声明这样的东西。
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 二进制代码。一切都不是对象,也没有底层虚拟机需要了解
Person
类(class)。没有装箱或拆箱,函数也不必属于类,甚至任何东西。因此,C++ 编译器对您可以使用模板执行的操作没有任何限制——基本上任何您可以手动编写的代码,您都可以获得模板来为您编写。
最明显的例子是添加东西:
在 C# 和 Java 中,泛型系统需要知道类有哪些方法可用,并且需要将其传递给虚拟机。告诉它这一点的唯一方法是通过硬编码实际类或使用接口(interface)。例如:
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 接口(interface)等等。 java 语法不同( <T extends IHasName>
),但它遇到相同的问题。此问题的“经典”案例是尝试编写一个执行此操作的函数
string addNames<T>( T first, T second ) { return first + second; }
您实际上无法编写此代码,因为无法使用 +
声明接口(interface)。其中的方法。你失败了。C++ 没有这些问题。编译器不关心将类型传递给任何虚拟机——如果你的两个对象都有一个 .Name() 函数,它就会编译。如果他们不这样做,就不会。简单的。
所以你有它 :-)
关于c# - C# 和 Java 中的泛型与 C++ 中的模板有什么区别?,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/31693/