C# 和 Java 中的泛型与 C++ 中的模板有什么区别? [关闭]

2024-01-17

我主要使用 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其中的类,在这两种情况下都有一些关于Personclass 将进入 .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() 函数,它将编译。如果他们不这样做,那就不会。简单的。

所以你有它 :-)

本文内容由网友自发贡献,版权归原作者所有,本站不承担相应法律责任。如您发现有涉嫌抄袭侵权的内容,请联系:hwhale#tublm.com(使用前将#替换为@)

C# 和 Java 中的泛型与 C++ 中的模板有什么区别? [关闭] 的相关文章

随机推荐