gpt4 book ai didi

c# - C# 中的协变和逆变

转载 作者:搜寻专家 更新时间:2023-10-30 19:49:38 25 4
gpt4 key购买 nike

首先我要说我是一名学习使用 C# 编程的 Java 开发人员。因此,我将我所知道的与我正在学习的进行比较。

我已经玩 C# 泛型几个小时了,我已经能够在 C# 中重现我在 Java 中知道的相同内容,除了几个使用协变和逆变的示例。我正在读的这本书在主题上不是很好。我当然会在网上寻找更多信息,但在我这样做的同时,也许您可​​以帮我找到以下 Java 代码的 C# 实现。

一个例子胜过一千个单词,我希望通过查看好的代码示例,我能够更快地理解这一点。

协方差

在 Java 中我可以这样做:

public static double sum(List<? extends Number> numbers) {
double summation = 0.0;
for(Number number : numbers){
summation += number.doubleValue();
}
return summation;
}

我可以按如下方式使用此代码:

List<Integer> myInts = asList(1,2,3,4,5);
List<Double> myDoubles = asList(3.14, 5.5, 78.9);
List<Long> myLongs = asList(1L, 2L, 3L);

double result = 0.0;
result = sum(myInts);
result = sum(myDoubles)
result = sum(myLongs);

现在我确实发现 C# 仅在接口(interface)上支持协变/逆变,只要它们已明确声明这样做(输出/输入)。我想我无法重现这种情况,因为我找不到所有数字的共同祖先,但我相信如果存在共同祖先,我可以使用 IEnumerable 来实现这样的事情。由于 IEnumerable 是协变类型。对吧?

关于如何实现上面的列表有什么想法吗?只要指出我正确的方向。所有数字类型都有共同的祖先吗?

逆变

我试过的逆变例如下。在 Java 中,我可以这样做以将一个列表复制到另一个列表中。

public static void copy(List<? extends Number> source, List<? super Number> destiny){
for(Number number : source) {
destiny.add(number);
}
}

然后我可以将它与逆变类型一起使用,如下所示:

List<Object> anything = new ArrayList<Object>();
List<Integer> myInts = asList(1,2,3,4,5);
copy(myInts, anything);

我的基本问题是,尝试在 C# 中实现它是我找不到同时协变和逆变的接口(interface),就像我上面示例中的 List 的情况一样。也许可以使用 C# 中的两个不同接口(interface)来完成。

关于如何实现这个的任何想法?

非常感谢大家提供的任何答案。我很确定我会从您提供的任何示例中学到很多东西。

最佳答案

我不会直接回答您的问题,而是会回答一些稍微不同的问题:

Does C# have a way to genericize over types that support arithmetic operators?

不容易,不。如果有能力制作 Sum<T> 就好了可以添加整数、 double 、矩阵、复数、四元数等的方法。尽管这是一个相当频繁被请求的特性,但它也是一个重要特性,而且它在优先级列表中的优先级还没有高到足以证明将其包含在该语言中是合理的。我个人会喜欢它,但你不应该期望它出现在 C# 5 中。也许在该语言的一个假设的 future 版本中。

What is the difference between Java's "call site" covariance/contravariance and C#'s "declaration site" covariance/contravariance?

实现级别的根本区别当然是,作为一个实际问题,Java 泛型是通过删除实现的;尽管您获得了泛型类型的令人愉快的语法和编译时类型检查的好处,但您不一定获得在 C# 中会获得的性能优势或运行时类型系统集成优势。

但这实际上更像是一个实现细节。在我看来,更有趣的区别是 Java 的变化规则是本地强制执行的,而 C# 的变化规则是全局执行的。

也就是说:某些变体转换是危险的,因为它们意味着某些类型不安全的操作不会被编译器捕获。经典的例子是:

  • 老虎是哺乳动物。
  • X 的列表在 X 中是协变的。(假设。)
  • 因此,老虎列表就是哺乳动物列表。
  • 哺乳动物列表中可以插入长颈鹿。
  • 因此,您可以将长颈鹿插入到老虎列表中。

这显然违反了类型安全以及长颈鹿的安全。

C# 和 Java 使用两种不同的技术来防止这种类型安全违规。 C# 表示当 I<T>接口(interface)被声明,如果它被声明为协变的,那么必须没有接受 T 的接口(interface)方法。如果没有将 T 插入列表的方法,那么您永远不会将长颈鹿插入老虎列表,因为没有插入任何东西的方法。

相比之下,Java 说在这个本地站点,我们可以协变地处理类型,并且我们保证不会在这里调用任何可能违反类型安全的方法。

我对 Java 的功能没有足够的经验来判断在什么情况下哪个“更好”。 Java 技术当然很有趣。

关于c# - C# 中的协变和逆变,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/9806170/

25 4 0
Copyright 2021 - 2024 cfsdn All Rights Reserved 蜀ICP备2022000587号
广告合作:1813099741@qq.com 6ren.com