我找到了解释What is a covariant return type?,但我并不聪明地理解这一切。
我认为Covariant返回类型理论上是一个函数返回的类型,该函数与返回类型不同的内置基类函数具有相同的签名。
class Base{
TypeX func( int i ){return typex;} // builtin function
};
class Derived:Base{
TypeY func(int i){return typey;}
}
我对这种所谓的协变返回类型的理解是否正确? [这个词让我很困惑。]
答案 0 :(得分:6)
这是协变返回类型的一个例子:
class Food {}
class Fruit : Food {}
class FoodEater
{
public virtual Food GetFavouriteFood() { ... }
}
class FruitEater : FoodEater
{
public override Fruit GetFavouriteFood() { ... }
}
在支持返回类型协方差的语言中,这是合法的。那是返回Food的方法可以被返回Fruit的方法覆盖,因为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#支持方法组委派转化和通用委托类型转换的两种方法差异。
答案 1 :(得分:3)
与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's答案。
答案 2 :(得分:1)
当且仅当TypeY
来自TypeX
时,您的理解才是正确的。
答案 3 :(得分:1)
共变体返回类型是一种从函数返回“较窄”类型的方法。 “较窄”类型将是原始返回类型的子类。
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();
}
}
我们可以从B
返回getFoo()
这一事实是共同变体回归。重要的是要注意,虽然overriding
不允许更改返回类型,但java 5.0
允许使用共变类型。
答案 4 :(得分:0)
允许这种类型的重载的功能称为协变返回类型 iff TypeY
是TypeX
的子类型。维基百科对该功能进行了很好的讨论
答案 5 :(得分:0)
在Java中使用协变返回类型的能力就是你刚才所展示的,是的 - 假设TypeY
是TypeX
的子类。关键是,只有“知道”Base
中指定的签名的任何呼叫者仍然可以,因为TypeY
实现返回的任何Derived
仍然是有效的TypeX
参考。请注意, 仅适用于引用类型(类) - 使用时无效:
// In the base class
long func() { ... }
// In the derived class
@Override int func() { ... }
...即使存在从int
到long
的隐式转换,int
值本身不是 a long
值。对于引用(String
引用 Object
引用的代表性有效性不同 - 如果您知道特定的位集是String
引用,您可以将那些完全相同的位视为Object
引用。)。
C#不支持协变返回类型,但从C#4开始, 支持接口和委托上声明的泛型差异。例如,IEnumerable<T>
在.NET 4中声明为
public interface IEnumerable<out T> { ... }
out
在界面中显示T
协变,这意味着此转化有效:
IEnumerable<string> strings = ...;
IEnumerable<object> objects = strings;
反向分配将不有效,因为一系列arbirary object
引用可能不是string
引用的有效序列。
有关此主题的更多信息,请参阅MSDN(或搜索c#generic variance)。