Gaz*_*yer 9 c# generics covariance
如果我尝试做:
IDictionary<uint, IEnumerable<string>> dict = new Dictionary<uint, List<string>>();
Run Code Online (Sandbox Code Playgroud)
我收到错误:
错误CS0266:无法将类型'System.Collections.Generic.Dictionary>'隐式转换为'System.Collections.Generic.IDictionary>'.存在显式转换(您是否错过了演员?)
如果我添加演员:
IDictionary<uint, IEnumerable<string>> dict = (IDictionary<uint, IEnumerable<string>>)new Dictionary<uint, List<string>>();
Run Code Online (Sandbox Code Playgroud)
然后它编译.
为什么我需要显式演员?它安全吗?我认为协方差的全部意义在于能够安全地隐式投射?
编辑: C#防止不相关的铸造,例如
string s = (string)0L;
Run Code Online (Sandbox Code Playgroud)
错误CS0030:无法将类型'long'转换为'string'
当您知道对象实际上是子类时,它确实允许显式向下转换相关类型:
Animal animal = new Cat();
Cat cat = (Cat)animal;
Run Code Online (Sandbox Code Playgroud)
我很困惑为什么编译器提供,并允许我显式转换为具有不兼容类型的IDictionary.
IDictionary<TKey, TValue>
对于TKey或TValue,它不是协变的.
协方差意味着IDictionary可以单独生成TKey/TValue类型,但由于它可以生成和消费它们,因此它不能协变,也不能逆变.
我将用共同术语定义协方差/逆变;
IProducer<out T>
是协变的,所以这意味着它只产生T类型.因此,当您将其传递给具有更抽象 T 的IProducer的引用时,强制转换是隐式的,因为以下陈述为真:" Apple 的生产者是水果的生产者".(反对"水果生产者不一定是苹果的生产者")
IConsumer<in T>
是逆变的,这意味着只消耗T类型.当你将它传递给对更具体的 T 的引用时,转换是隐式的,因为以下陈述是真的:"水果的消费者是苹果的消费者".(反对"苹果的消费者不一定是任何水果的消费者")
这对于IDictionary来说意味着什么,特别是这里的TValue:
IDictionary具有生成TValues的方法以及消耗TValues的方法.话虽如此,这意味着它不是(也不可能)宣称为协变或逆变.(请参阅http://msdn.microsoft.com/en-us/library/s4ys34ea.aspx - 通用接口定义中没有"out"或"in")
这意味着当你试图隐含地将你Dictionary<uint, List<string>>
变成一个时IDictionary<uint, IEnumerable<string>>
,编译器会说"等一下,你构建的对象只能List<string>
在一个Add方法中接受,但是你把它放入一个允许任何进入的引用IEnumerable<string>
中,这是一个更大的子集.如果你添加任何IEnumerable<string>
但不是a的东西List<string>
,它将无法工作." 它没有(也不能)暗示允许它,这就是你需要硬铸造的原因.
(感谢mquander的具体例子)
这不安全.例如,你现在可以写dict.Add(5, new string[0])
,这会爆炸,因为a string[]
不是List<string>
.它不安全的事实是你需要演员的原因.
编辑以解决您最新的问题:
C#允许从任何引用类型S到任何接口T的任何显式转换("提供的S未被密封,并且如果S未实现T".)此行为在语言规范的6.2.4节中指定.所以这是合法的:
var foo = (IList<ICollection<IEnumerable<IntPtr>>>)new Uri(@"http://zombo.com");
Run Code Online (Sandbox Code Playgroud)
我不能说为什么会这样,除了C#类型系统最初比现在更加受限制的事实(例如没有泛型,没有变化)所以我确信有很多案例能够用演员阵容围攻它非常方便.
您可以使用
IDictionary<uint, IEnumerable<string>> dict = new Dictionary<uint, IEnumerable<string>>();
Run Code Online (Sandbox Code Playgroud)
您正在将代码中的TValue类型更改为具体的List实现.那样不行.你必须使用与声明类型相同的定义.
有了上述内容,您可以将其用作:
dict.Add(1, new List<string>());
Run Code Online (Sandbox Code Playgroud)
等等
归档时间: |
|
查看次数: |
2470 次 |
最近记录: |