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
对James的回答进行一点扩展:
- 为什么不可能?在这种情况下,没有编译器错误,但在运行时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上,fruits是Collection<Apple>
,而apples仍然是指fruits。当您将apple 转换为IGetElement<Apple>
时,这可以工作,因为Collection<Apple>
实现了IGetElement<Apple>
。
这既不是协变也不是逆变,因为类型是相同的(Apple
)。然后可以将此赋值给IGetElement<Fruit>
,这是一个扩展操作(协变),并且由于IGetElement<Fruit>
被标记为而不是,因此它支持此操作。
希望这对你有帮助!