CoVariance Contra Variance

本文关键字:Variance Contra CoVariance | 更新日期: 2023-09-27 18:08:46

我对协方差和反方差有一点怀疑。参见以下代码..

interface IGetElement<out T>
{
    int Counter { get; }
    T GetNext();
}
interface IAddElement<in T>
{
    void Add(T t);
}
class Collection<T> : IAddElement<T>, IGetElement<T> where T : Fruit
{
    List<T> tList = new List<T>();
    private int _counter = 0;
    public int Count { get { return tList.Count; } }
    public void Add(T t)
    {
        tList.Add(t);
    }
    int IGetElement<T>.Counter
    {
        get { return _counter; }
    }
    public T GetNext()
    {
        return tList[_counter++];
    }
    public void Rset()
    {
        _counter = 0;
    }
}
abstract class Fruit
{
    public abstract void Taste();
}
class Apple : Fruit
{
    public override void Taste()
    {
        Console.WriteLine("Like Apple");
    }
}

这是示例代码。现在客户端是

 static void Main(string[] args)
 {
        IGetElement<Fruit> covarience = new Collection<Apple>(); // CoVarience..
        IAddElement<Apple> contravarience = new Collection<Fruit>();//ContraVarience.. Compiling fine and working also fine... :)
        IGetElement<Fruit> fruits = new Collection<Apple>();
        IAddElement<Fruit> apples2 = fruits as IAddElement<Apple>;//Here its Compiler error : Cannot implicitly convert type 'Test.IAddElement<Test.Apple>' to 'Test.IAddElement<Test.Fruit>'. An explicit conversion exists (are you missing a cast?)  
        IAddElement<Apple> apples1 = fruits as IAddElement<Fruit>;//This is not posible  
        /* Why this is not possible..? In this case No Compiler Error But Run Time error ie., 
       apples1 is NULL.. I don't know why.. because.. in fruits cantains apple only but 
       It is unable it caste it as ContrVariantly..  ?------------1 */
        IAddElement<Apple> apples = fruits as IAddElement<Apple>;//This is posible 

        IGetElement<Fruit> f = apples as IGetElement<Apple>;//This is posible.. 
        /* Why this is posible.. ? here we are casting t as CoVariantly..
        If ------------1 is not posible how this would be posible... yes.. I am casting to    
        actual object which is not Null.. ? -----------2 */
  }

请在注释源代码中回答我的问题…:) -------- 1 ,--------- 2 .

谢谢Dinesh

CoVariance Contra Variance

对James的回答进行一点扩展:

  1. 为什么不可能?在这种情况下,没有编译器错误,但在运行时apples1为null。我不知道为什么。

运行时局部变量fruit指的是Collection<Apple>类型的对象。也就是说,一个只包含苹果的集合。

你问"我可以添加任何水果到这个集合吗?"不。只能添加苹果,不能添加任何水果。因此结果为null。

这在运行时失败而不是在编译时失败的原因是因为fruit的编译时类型是接口类型,而您正在将其转换为不同的接口类型。任意两个接口可以由给定对象实现;编译器不做流分析来确定fruit是否只被分配了一个特定的类型。因此,直到运行时才能进行检查。

    为什么这是可能的?这里是协变转换。

这里有两个转换。首先苹果转化为IGetElement<Apple>然后协变转化为IGetElement<Fruit>

第一次转换成功,因为局部变量apple引用了实现IGetElement<Apple>Collection<Apple>。你在问"这个物体能递给我一个苹果吗?"答案是肯定的。

第二次转换成功,因为编译器知道"一个可以递给我一个苹果的对象"可以安全地处理为"一个可以递给我一个水果的对象"。

现在清楚了吗?

#1上,IAddElement<in T>是逆变的,中的让你知道它与"限制性"的类型兼容,即更窄。由于您将作为Collection<Apple>转换为IAddElement<Fruit>,您正在扩大类型(使其更通用),这是协方差,而不是逆变,因此这在运行时失败,并且null作为转换返回。

请注意,它失败的原因是您的作为强制转换,而不是从IAddElement<Fruit>IAddElement<Apple>的逆变赋值,其中是逆变且合法的。但是由于as强制转换不起作用,它返回null,然后赋值。

#2上,fruitsCollection<Apple>,而apples仍然是指fruits。当您将apple 转换为IGetElement<Apple>时,这可以工作,因为Collection<Apple>实现了IGetElement<Apple>

这既不是协变也不是逆变,因为类型是相同的(Apple)。然后可以将此赋值给IGetElement<Fruit>,这是一个扩展操作(协变),并且由于IGetElement<Fruit>被标记为而不是,因此它支持此操作。

希望这对你有帮助!