我对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。
谢谢和问候, 迪内希
答案 0 :(得分:5)
稍微谈谈詹姆斯的回答:
- 为什么这不可能?在这种情况下,没有编译器错误,但在运行时apples1为null。我不知道为什么。
醇>
运行时的局部变量果实是指类型为Collection<Apple>
的对象。也就是说,只包含苹果的集合。
您问“我可以在此系列中添加任何水果吗?”不。您只能添加苹果,而不能添加任何水果。因此结果为空。
这在运行时而不是在编译时失败的原因是因为编译时类型的水果是一种接口类型,并且您将它转换为不同的接口类型。任何两个接口可能由给定对象实现;编译器不进行流分析以确定仅为水果分配了一种特定类型。因此,直到运行时才能进行检查。
- 为什么这是可行的?在这里,我们正在共同转换。
醇>
这里有两次转换。第一个苹果转换为IGetElement<Apple>
,然后将其协变转换为IGetElement<Fruit>
。
第一次转换成功,因为局部变量apples指的是实现Collection<Apple>
的{{1}}。你在问“这个对象能给我一个苹果吗?”答案是肯定的。
第二次转换成功,因为编译器知道“可以递给我一个苹果的对象”可以被安全地视为“可以给我一个水果的对象”。
现在清楚了吗?
答案 1 :(得分:4)
在 #1 上,IAddElement<in T>
是逆变的,中的会让您知道它与<的类型兼容em>更多“限制性”,即更窄。由于您为将Collection<Apple>
投射到IAddElement<Fruit>
,因此您正在扩大类型(使其更通用),这是协方差,而不是逆变,因此在运行时失败并且< strong> null 从 as 强制转换返回。
请注意,失败的原因是为强制转换,而不是从IAddElement<Fruit>
到IAddElement<Apple>
的逆变量分配 逆变且法律。但由于 as 强制转换不起作用,它会返回 null ,然后将其分配。
在 #2 上,水果是Collection<Apple>
, apples 仍然是指< EM>水果。当您将 apples 投射到IGetElement<Apple>
时,这可行,因为Collection<Apple>
可以很好地实现IGetElement<Apple>
。
这既不是共变量也不是逆变量,因为类型相同(Apple
)。然后可以将其分配给IGetElement<Fruit>
这是一个扩展操作(协变),并且由于IGetElement<Fruit>
标记为 out ,它支持此功能。
希望这有帮助!