如何在 C# 中对列表的列表使用多态性

How to use polymorphism with a list of lists in C#

我在 C# 中对 int 列表的列表使用多态性时遇到问题。我需要“外部”列表才能 add/remove “内部”列表,但“内部”列表不需要更改。于是想到了用变量类型ICollection<IEnumerable<int>>。 现在,我现在可以了:

IEnumerable<int> someList = new List<int>(); //Works fine

现在我也知道这有效了:

ICollection<int> anotherList = new List<int>(); //Also works fine

但是,我很惊讶地知道这会引发编译错误:

ICollection<IEnumerable<int>> listOfLists = new List<List<int>>(); //Doesn't compile

Cannot implicitly convert type 'System.Collections.Generic.List<System.Collections.Generic.List>' to 'System.Collections.Generic.ICollection<System.Collections.Generic.IEnumerable>'. An explicit conversion exists (are you missing a cast?)

而且我也很惊讶地发现这确实编译得很好:

IEnumerable<ICollection<int>> anotherListOfLists = new List<List<int>>(); //Works fine (?)

我知道一个简单的 List<List<int>> listOfListsWithoutPolymorphism = new List<List<int>>(); 就可以了,但我的问题是:

  1. 第三个例子和第四个例子有什么区别?
  2. 我如何使用多态性实现我需要的东西(类似于 ICollecion<IEnumerable<int>>)?

这与 实际 接口方差有关,而不是[= 接口实现的27=]方差

让我们刷新一下:

  • 协变允许方法具有比接口的泛型类型参数定义的类型更派生的 return 类型

  • 相反,不变接口不允许更多派生类型

您示例中的接口签名:

public interface IEnumerable<out T> : IEnumerable {}

public interface ICollection<T> : IEnumerable<T> {}

让我们用一个人为的例子更容易理解:

public interface ICovariant<out T> { }

public interface IInvariant<T> : ICovariant<T> { }

public class SomeClass<T> : IInvariant<T> { }

测试

// works because `SomeClass` implements IInvariant, type parameters are the same
IInvariant<int> test1 = new SomeClass<int>(); 

// works because SomeClass implements IInvariant which implements ICovariant, type parameters are the same
ICovariant<int> test2 = new SomeClass<int>();

// works because SomeClass implements IInvariant, type parameters are the same
IInvariant<ICovariant<int>> test3 = new SomeClass<ICovariant<int>>();

// works because IInvariant implements ICovariant, type parameters are the same
ICovariant<IInvariant<int>> test4 = new SomeClass<IInvariant<int>>();

// works because ICovariant is covariant and SomeClass implements IInvariant
// remembering Covariance permits a more derived type 
ICovariant<IInvariant<int>> test6 = new SomeClass<SomeClass<int>>();

// hold up, this does not work
// IInvariant is invariant and the type parameters are different!
// an Invariant interface doesn't allow a more derived type
IInvariant<ICovariant<int>> test5 = new SomeClass<SomeClass<int>>();