c# - C# 和 Java 中的泛型与 C++ 中的模板有什么区别?

标签 c# java c++ generics templates

关闭。这个问题需要更多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.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 二进制代码。一切都不是对象,也没有底层虚拟机需要了解 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/

相关文章:

c# - 高效滚动最大和最小窗口

c# - 是否有与旧 WebApi IHttpControllerTypeResolver 等效的 AspNetCore?

java - 迭代器重用问题

c++ - 检查对象的类型是否继承自特定类

c++ - 使用 C++ 释放内存

c# - 您也可以将 Settings.Settings 文件用于 ASP.Net 站点吗?

c# - Entity Framework 中没有事务的存储过程

java - 尝试/最后 - 工作面试

Java/正则表达式 : Negate the same character repeated twice:

C++将文件中的文本放入字符