c# - Java 的使用站点差异与 C# 的声明站点差异相比如何?

标签 c# java generics variance

我的理解是,在 C# 中为泛型指定方差发生在类型声明级别:当您创建泛型类型时,您指定类型参数的方差。另一方面,在 Java 中,在使用泛型时会指定变体:当您创建某个泛型类型的变量时,您需要指定其类型参数如何变化。

每个选项的优缺点是什么?

最佳答案

我只是要回答声明站点和使用站点差异之间的差异,因为尽管 C# 和 Java 泛型在许多其他方面存在差异,但这些差异大多与差异正交。

首先,如果我没记错的话,use-site 变异严格来说比声明-site 变异更强大(尽管以简洁为代价),或者至少 Java 的通配符是(实际上比 use-site 变异更强大) .这种增强的功能对于大量使用有状态构造的语言特别有用,例如 C# 和 Java(但 Scala 则少得多,特别是因为它的标准列表是不可变的)。考虑List<E> (或 IList<E>)。由于它具有添加 E 和获取 E 的方法,因此它相对于 E 是不变的,因此不能使用声明站点方差。但是,对于使用站点差异,您可以说 List<+Number>得到 List 的协变子集和 List<-Number>得到 List 的逆变子集.在声明站点语言中,库的设计者必须为每个子集创建单独的接口(interface)(或类,如果您允许类的多重继承)并具有 List扩展这些接口(interface)。如果库设计者不这样做(请注意,C# 的 IEnumerable 只处理了 IList 的协变部分的一小部分),那么你就不走运了,你必须求助于你必须做的同样的麻烦用一种没有任何差异的语言。

这就是使用点继承相对于声明点继承的优势。声明站点继承相对于使用站点继承的优势基本上对用户来说是简洁的(假设设计者努力将每个类/接口(interface)分成协变和逆变部分)。对于像 IEnumerable 这样的东西或 Iterator ,很高兴不必在每次使用接口(interface)时都指定协方差。 Java 通过使用冗长的语法使这一点特别烦人(Java 的解决方案基本上是理想的双变量除外)。

当然,这两种语言特性可以共存。对于自然协变或逆变的类型参数(例如在 IEnumerable/Iterator 中),请在声明中声明。对于自然不变的类型参数(例如在 (I)List 中),请在每次使用时声明您想要什么样的方差。只是不要为具有声明站点差异的参数指定使用站点差异,因为这只会使事情变得困惑。

还有其他更详细的问题我没有涉及(例如通配符实际上比使用站点差异更强大),但我希望这能回答您对内容的问题。我承认我对使用站点的差异有偏见,但我试图描述在我与程序员和语言研究人员的讨论中出现的两者的主要优势。

关于c# - Java 的使用站点差异与 C# 的声明站点差异相比如何?,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/4231305/

相关文章:

c# - 如何从 WPF 中的原始帧渲染视频?

c# - 使用 C# 监控多个 com 端口

java - JSON 路径结果中的通用列表类型

typescript - 采用 GenericType 列表的函数不接受提供的参数

TypeScript Generic Factory Function Type,匹配数组元素顺序

c# - 不支持 MySQL 文件源

c# - Unity wiki 的 Singleton 是如何工作的?

java.sql.SQLException : Cannot get a connection, 池错误 等待空闲对象超时。如何恢复连接

java - JUnit:针对一种方法创建多个测试方法?

java - Android 无法解析日期 : "2017-12-01 00:00:00+01"