我对CoVariance和ContraVariance有一点怀疑。请参阅以下代码。
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
发布于 2011-09-15 22:54:46
在James的回答上做一点扩展:
运行时的局部变量fruits引用Collection<Apple>
类型的对象。也就是说,一个只包含苹果的集合。
你在问“我能不能把any水果添加到这个集合中?”不是的。你只能添加苹果,不能添加任何水果。因此,结果为空。
这在运行时失败而不是在编译时失败的原因是因为水果的编译时类型是接口类型,而您正在将其转换为不同的接口类型。任何两个接口都可以由给定的对象实现;编译器不会进行流分析来确定fruits只分配了一种特定的类型。因此,只有在运行时才能进行检查。
这里有两个转换。首先,苹果被转换为IGetElement<Apple>
,然后被协变地转换为IGetElement<Fruit>
。
第一次转换成功是因为局部变量apples引用了实现IGetElement<Apple>
的Collection<Apple>
。你在问“这个物体能递给我一个苹果吗?”答案是肯定的。
第二次转换成功是因为编译器知道“一个可以递给我一个苹果的对象”可以被安全地视为“一个可以递给我一个水果的对象”。
现在清楚了吗?
发布于 2011-09-15 22:38:53
在 #1上,IAddElement<in T>
是逆变的,in让你知道它与更“限制性”的类型兼容,即更窄。由于您是as,将Collection<Apple>
转换为IAddElement<Fruit>
,您正在扩大(使其更通用)协方差而不是逆方差的类型,因此这在运行时失败,并且从返回空作为强制转换。
请注意,它失败的原因是您的作为强制转换,而不是从IAddElement<Fruit>
到IAddElement<Apple>
的逆变量赋值,这是逆变量和合法的。但是由于作为的转换不起作用,它返回null,然后赋值。
在 #2上,是一个Collection<Apple>
,而苹果仍然指水果。当你把苹果扔给IGetElement<Apple>
时,这是可行的,因为Collection<Apple>
很好地实现了IGetElement<Apple>
。
这既不是协变也不是逆变,因为类型是相同的(Apple
)。然后可以将其分配给IGetElement<Fruit>
,这是一个加宽操作(协变),由于IGetElement<Fruit>
被标记出来,因此它支持这一点。
希望这能有所帮助!
https://stackoverflow.com/questions/7432447
复制相似问题