通用方法可以使用逆变/协变类型吗?

JPC*_*PCF 8 c# oop generics t4 .net-4.0

我正在编写一个通用方法,在T4模板的特殊任务中使用它.该方法应该允许我使用通用接口中的专用类型.我想到了以下签名:

interface IGreatInterface {
    Object aMethodAlpha<U>(U parameter) where U : IAnInterface;
    Object aMethodBeta(IAnInterface parameter)
}

public class AnInterestingClass : IAnInterface{}
Run Code Online (Sandbox Code Playgroud)

当我尝试实现IGreatInterface编译器标记错误时,aMethodBeta()因为我已经让我的T4使用子类型编写该方法IAnInterface(即我想像这样实现该方法:) Object aMethodBeta(AnInterestingClass parameter).

方法aMethodAlpha<U>()可以使用,但不是我想要的干净,因为我的T4必须生成一些额外的代码.我(也许错误地)提出必须由T4完成该方法的实现
Object aMethodAlpha<AnInterestingClass>(AnInterestingClass parameter).

我认为泛型方法不支持逆变类型,但我不确定; 我想这是编译器阻止编码器使用具有未在通用类型中定义的方法的特定类型的方式...

  1. 通用方法在实现时是否必须使用确切类型?
  2. 有什么技巧可以改变这种行为吗?

Eri*_*ert 22

这个问题很混乱.让我看看我是否可以澄清它.

当我尝试实施IGreatInterface的编译器标志的错误aMethodBeta(),因为我做了使用亚型的那个方法IAnInterface我想实现像这样的方法:Object aMethodBeta(AnInterestingClass parameter).

那不合法.有点简化:

class Food {}
class Fruit : Food {}
class Meat : Food {}
interface IEater
{
    void Eat(Food food);
}
class Vegetarian : IEater
{
    public void Eat(Fruit fruit);
}
Run Code Online (Sandbox Code Playgroud)

班级Vegetarian不履行合同IEater.你应该可以通过任何食物吃,但Vegetarian只能接受水果.C#不支持虚方法形式参数协方差,因为它不是类型安全的.

现在,你可能会说,这是怎么回事:

interface IFruitEater
{
    void Eat(Fruit fruit);
}
class Omnivore : IFruitEater
{
    public void Eat(Food food);
}
Run Code Online (Sandbox Code Playgroud)

现在我们有了类型安全; Omnivore可以用作IFruitEater因为Omnivore可以吃水果,以及任何其他食物.

不幸的是,C#不支持虚方法形式参数类型的逆变,即使这样做在理论上是类型安全的.很少有语言支持这一点.

同样,C#也不支持虚方法返回类型方差.

我不确定这是否真的回答了你的问题.你能澄清一下这个问题吗?

更新:

关于什么:

interface IEater
{
    void Eat<T>(T t) where T : Food;
}
class Vegetarian : IEater
{
    // I only want to eat fruit!
    public void Eat<Fruit>(Fruit food) { }
}
Run Code Online (Sandbox Code Playgroud)

不,那也不合法.的合同IEater是,你将提供一种方法Eat<T>,可以采取任何T这是一个Food.您无法部分实施合同,只能执行此操作:

interface IAdder
{
    int Add(int x, int y);
}
class Adder : IAdder
{
    // I only know how to add two!
    public int Add(2, int y){ ... }
}
Run Code Online (Sandbox Code Playgroud)

但是,您可以这样做:

interface IEater<T> where T : Food
{
    void Eat(T t);
}
class Vegetarian : IEater<Fruit>
{
    public void Eat(Fruit fruit) { }
}
Run Code Online (Sandbox Code Playgroud)

这完全合法.但是,你做不到:

interface IEater<T> where T : Food
{
    void Eat(T t);
}
class Omnivore : IEater<Fruit>
{
    public void Eat(Food food) { }
}
Run Code Online (Sandbox Code Playgroud)

因为C#不支持虚方法形式参数逆变或协方差.

请注意,C#确实支持参数多态协方差,因此已知类型安全.例如,这是合法的:

IEnumerable<Fruit> fruit = whatever;
IEnumerable<Food> food = fruit;
Run Code Online (Sandbox Code Playgroud)

一系列水果可用作一系列食物.要么,

IComparable<Fruit> fruitComparer = whatever;
IComparable<Apples> appleComparer = fruitComparer;
Run Code Online (Sandbox Code Playgroud)

如果你有可以比较任何两个水果的东西,那么它可以比较任何两个苹果.

然而,这种协方差和逆变只有在以下所有条件都成立时才合法:(1)方差可证明是类型安全的,(2)类型的作者添加了方差注释,表明所需的共同和反向差异,( 3)所涉及的变量类型参数都是引用类型,(4)泛型类型是委托或接口.