如何在 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>>();
就可以了,但我的问题是:
- 第三个例子和第四个例子有什么区别?
- 我如何使用多态性实现我需要的东西(类似于
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>>();
我在 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>>();
就可以了,但我的问题是:
- 第三个例子和第四个例子有什么区别?
- 我如何使用多态性实现我需要的东西(类似于
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>>();