返回IList< IList< T>祝辞

本文关键字:IList 祝辞 返回 lt | 更新日期: 2023-09-27 18:14:37

我有一个方法来构建列表的列表。我希望返回类型使用泛型的List<>接口,以减少与下游具体的List<>类型的耦合。但是,编译器很难处理类型转换。

public IList<IList<T>> Foo<T>()
    {
        return new List<List<T>>();
    } 

当这个工作时为什么会失败:

public IList<T> Foo<T>()
    {
        return new List<T>();
    } 

摆脱这种混乱的最优雅的方法是什么?

返回IList< IList< T>祝辞

就这样做:

return new List<IList<T>>();

因为List<T>实现了IList<T>,所以您将能够在结果中添加任何类型的IList<T>。例如:

Foo<int>().Add(new List<int>());

至于为什么,这是协方差/逆变的问题(我总是把这两个混淆)。基本上,如果您您返回IList<IList<T>>,那么有人应该能够将任何类型的IList<T>添加到结果中:

Foo<int>().Add(new []{1}); // arrays implement `IList`

显然,如果您返回了List<List<T>>,那么上面的代码行将不起作用:您将尝试将T[]添加到List<T>的集合中。因此,即使List<T>IList<T>, List<List<T>>也不是IList<IList<T>>

注意:下一部分只适用于。net 4及更高版本,因为这是第一个在接口中支持协方差和逆变的版本。

另一方面,如果您知道消费您的结果的人不打算向列表添加任何内容,但只是试图遍历它,您可以将返回类型更改为IEnumerable<IList<T>>,然后返回List<List<T>>就完全可以了。为什么?因为IEnumerable<T>接口是协变:

public interface IEnumerable<out T>

这个"out"告诉编译器这个接口将只具有返回与T相关的值的方法(如GetEnumerator),并且它不会有任何将与T相关的东西作为参数的方法(如Add)。

这与c#协方差和逆变有关。你可以在这里阅读更多。

要做interface of interface of T,你的接口必须同时标记为out T或in T。IEnumerable标记为out T