考虑以下类型:
public interface IPrimary{ void doBattle(); }
// an ISecondary "is" an IPrimary
public interface ISecondary : IPrimary { }
// An implementation of ISecondary is also an IPrimary:
internal class SecondaryImpl : ISecondary
{
// Required, since this is an IPrimary
public void doBattle(){ }
}
为什么我不这样做?
List<IPrimary> list = new List<ISecondary>();
这会导致以下编译错误:
参数类型'System.Collections.Generic.List'不能赋值给参数类型'System.Collections.Generic.List'
我理解错误,我意识到有解决方法。我只是没有看到任何明确的原因,为什么不允许这种直接转换。毕竟,ISecondary
列表中包含的值应该是({扩展名] IPrimary
类型的值。为什么List<IPrimary>
和List<ISecondary>
被解释为无关类型?
任何人都可以清楚地解释以这种方式设计C#的原因吗?
一个稍微扩展的例子:我在尝试执行类似以下操作时遇到了这个问题:
internal class Program
{
private static void Main(string[] args)
{
// Instance of ISecondary, and by extention, IPrimary:
var mySecondaryInstance = new SecondaryImpl();
// This works as expected:
AcceptImpl(mySecondaryInstance);
// List of instances of ISecondary, which are also,
// by extention, instances of IPrimary:
var myListOfSecondaries = new List<ISecondary> {mySecondaryInstance};
// This, however, does not work (results in a compilation error):
AcceptList(myListOfSecondaries);
}
// Note: IPrimary parameter:
public static void AcceptImpl(IPrimary instance){ }
// Note: List of type IPrimary:
public static void AcceptList(List<IPrimary> list){ }
}
答案 0 :(得分:10)
public class Animal
{
...
}
public class Cat: Animal
{
public void Meow(){...}
}
List<Cat> cats = new List<Cat>();
cats.Add(new Cat());
cats[0].Meow(); // Fine.
List<Animal> animals = cats; // Pretend this compiles.
animals.Add(new Animal()); // Also adds an Animal to the cats list, since animals references cats.
cats[1].Meow(); // cats[1] is an Animal, so this explodes!
这就是原因。
答案 1 :(得分:7)
为什么我不这样做?
List<IPrimary> list = new List<ISecondary>();
想象一下,你有一个像这样定义的方法:
public void PopulateList(List<IPrimary> listToPopulate)
{
listToPopulate.Add(new Primary()); // Primary does not implement ISecondary!
}
如果您将List<ISecondary>
作为参数传递,会发生什么?
List<ISecondary>
无法从List<IPrimary>
分配的错误是编译器让您摆脱这些麻烦的方法。
答案 2 :(得分:5)
class Evil : IPrimary {...}
list.Add(new Evil()); // valid c#, but wouldn't work
它可以保护您免受错误的侵害。列表实例(对象)需要辅助实例。并非每个小学都是次要的。然而,期望主要列表可以容纳任何主要。如果我们可以将二级列表视为主要列表:坏事。
实际上,数组做允许这样做 - 如果你弄错了,就会在运行时出错。
答案 3 :(得分:1)
列表类型在其泛型参数中不协变的原因,即List<ISecondary>
不是List<IPrimary>
的子类型,因为它们是读写的。在您的扩展示例中,您的方法AcceptList
可以list.Add(x)
x
是IPrimary
而不是ISecondary
。
请注意,IEnumerable<T>
是正确协变的,而数组是共同键入的(您可以执行上面尝试的操作),但出于同样的原因,这不是合理的 - 在集合中添加元素将在运行时失败。