协变和逆变有什么好处?

17

C# 4.0将支持协变性和逆变性,但我不太清楚这个新特性的好处。您可以为我解释一下(清晰明了地)为什么需要它吗?

C# 4.0将支持协变性和逆变性。协变性允许您使用派生类作为基类,而逆变性允许您使用基类作为派生类。这些功能使得在泛型类型中传递参数更加灵活,同时保证了类型安全。因此,您可以更方便地使用泛型类型,并且代码更加简洁易懂。

Bart De Smet在他的博客中写了一篇关于协变性和逆变性的精彩文章这里 - Mike Thompson
4个回答

8

它们只是允许您执行某些在概念上有效、形式上可接受的操作,但由于语言限制而目前不允许。例如:

IEnumerable<int> ints = new List<int> { 1, 2, 3 };

Action<IEnumerable<object>> PrintThings =
    x => { foreach(var thing in x) Console.WriteLine(thing); };

PrintThings(ints); // doesn't compile right now :(  will compile in 4.0

这并没有根本性的原因不能或不应该工作;只是恰巧在这种语言中不被允许。如果允许它,当程序员需要执行这样的操作时,可以使他们的工作更容易。


上述代码在4.0之前无法正常工作的根本原因是,在语法上没有办法允许这样的操作而不丧失类型安全性。如果你允许在你的示例中这样做,那么你必然也会允许在其他不保持类型安全的示例中这样做。 - Eddie
没错,这就是为什么他们正在添加新的“in”和“out”关键字来支持它(而IEnumerable<T>将会是<out T>以允许这种情况)。 - mqp

6

关于4.0版本的一些观念误区,让很多人对于如何以及什么样的方式能够起作用产生了困惑。迄今为止我所读到的最好的解释来自Marc Gravell。你可以在他的博客文章中看到:

http://marcgravell.blogspot.com/2009/02/what-c-40-covariance-doesn-do.html

再次强调,很多人认为这将在4.0版本中奏效:

public class Base{}
public class Derived : Base {}

在其他一些类中

List<Derived> derived....

public void Method(List<Base> b){}

即使在4.0版本中,您也无法将List传递到此方法中。正如Marc所指出的那样,这就是泛型约束的用途,并且自2.0版本以来就可以实现。

这对我来说似乎是一个奇怪的不合逻辑。你不能直接传递List的唯一原因是List是可变的;当然你不能这样传递,因为添加项目会破坏对它的类型限制。 - mqp
@mquander:一个奇怪的非因果关系?API(4.0之前)没有任何要求,“只有当对象是不可变的时才能执行此操作”。 - Eddie
我的观点很简单,List<T>允许你添加和更改T,这就是为什么它没有意义的原因。如果你试图在IList<T>上添加"out"参数以允许此操作,那么这些方法将无法按预期工作。 - mqp

3

在我看来,协变性将会对泛型有很大的帮助。

我遇到了几种情况,需要显式使用Cast将一个特定类型转换为其基础类型。

class Foo { }
class Bar : Foo { }

// ...

IEnumerable<Foo> foos = new List<Foo>();
IEnumerable<Bar> bars = new List<Bar>();

foos = bars.Cast<Foo>();

// C# 4.0
foos = bars;

这里有一份关于该主题的好参考资料


然而,过度使用斜体字是一个不错的点缀。 - bzlm
抱歉 :( 忘记了类定义。 - bruno conde

1

这里有一篇很好的文章讨论了这个问题。


网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接