子类型和协变返回类型

我找到了一个解释什么是协变返回类型? 但我不明智地理解这一切。

我认为Covariant返回类型在理论上是由函数返回的类型,该函数具有与内置基类函数相同的签名,其返回类型不同。

class Base{ TypeX func( int i ){return typex;} // builtin function }; class Derived:Base{ TypeY func(int i){return typey;} } 

我对这种所谓的协变返回类型的理解是否正确? [这个词让我很困惑。]

与java不同,C#不支持协变返回类型。 我相信这是由于C#属性的实现,如果允许使用协变返回类型,则可能存在以下情况:

 class TypeX { } class TypeY : TypeX { } class Base { public virtual TypeX Prop { get; set; } } class Derived : Base { public override TypeY Prop { get; set; } } Derived derived = new Derived(); derived.Prop = new TypeY(); // Valid Base @base = derived; @base.Prop = new TypeX(); // Invalid - As this would be using the derived property which should be of TypeY 

有关更多信息,请参阅Eric Lippert的答案。

这将是协变返回类型的示例:

 class Food {} class Fruit : Food {} class FoodEater { public virtual Food GetFavouriteFood() { ... } } class FruitEater : FoodEater { public override Fruit GetFavouriteFood() { ... } } 

在支持返回类型协方差的语言中,这是合法的。 这是一个返回Food的方法,可以通过返回Fruit的方法覆盖Food,因为Fruit是一种Food 。 它被称为“协方差”,因为“方差”在同一方向

 A Fruit may be used as a Food, therefore: A Fruit-returning-method may be used as a Food-returning-method 

看看方差如何朝同一方向发展

与参数类型相反的对比:

 class Food {} class Fruit : Food {} class Apple : Fruit {} class Orange : Fruit {} class Cake : Food {} class FruitComparer { public virtual bool Compare(Fruit f1, Fruit f2) { ... } } class FoodComparer : FruitComparer { public override bool Compare(Food f1, Food f2) { ... } } 

FruitComparer可以将苹果与橘子进行比较。 FoodComparer还可以将苹果与橙子进行比较,但可以比较更多 – 它可以将苹果与蛋糕,或蛋糕与橙子等比较。

在支持参数类型逆变的语言中,这是合法的。 查看现在如何逆转方差方向:

 A Fruit may be used as a Food, therefore A Food-taking-method may be used as a Fruit-taking-method 

现在关系已经倒退了 ,所以它是逆变的

C#不支持虚拟重载的任何方法差异。 虚方法覆盖必须完全匹配。 但是,C#确实支持方法组委派转换的两种方法差异,以及generics委托类型转换

当且仅当TypeY派生自TypeX您的理解才是正确的。

共变体返回类型是一种从函数返回“较窄”类型的方法。 “较窄”类型将是原始返回类型的子类。

  class A { } class B extends A { } // Classes demonstrating method overriding: class C { A getFoo() { return new A(); } } class D extends C { B getFoo() { return new B(); } } 

我们可以从getFoo()返回一个B的事实是一个共变量返回。 重要的是要注意,虽然overriding不允许更改返回类型,但使用java 5.0允许使用共变类型。

允许这种类型的重载的特性称为协变返回类型iff TypeYTypeY的子类型。 维基百科对该function进行了很好的讨论

在Java中使用协变返回类型的能力就是你刚刚演示的,是的 – 假设TypeYTypeY的子类。 关键是任何只“知道” Base中指定的签名的调用者仍然可以,因为Derived实现返回的任何TypeY仍然是有效的TypeX引用。 请注意,它仅适用于引用类型(类) – 使用时无效:

 // In the base class long func() { ... } // In the derived class @Override int func() { ... } 

…即使存在从intlong的隐式转换, int本身也不是long值。 引用的表示有效性不同(其中String引用 Object引用 – 如果您知道一组特定的位是String引用,则可以将这些Object引用完全相同 。) 。

C#不支持协变返回类型,但从C#4开始,它支持在接口和委托上声明的generics方差 。 例如, IEnumerable在.NET 4中声明为

 public interface IEnumerable { ... } 

out表明T在接口中是协变的,这意味着这种转换是有效的:

 IEnumerable strings = ...; IEnumerable objects = strings; 

反向赋值无效,因为一系列的arbirary object引用可能不是有效的string引用序列。

有关此主题的更多信息,请参阅MSDN (或搜索c#generic variance)。