我有一个构建列表列表的方法。我想让返回类型使用通用IList<>减少与具体List<>的耦合的界面下游类型。但是,编译器正在努力进行类型转换。
public IList<IList<T>> Foo<T>()
{
return new List<List<T>>();
}
为什么在这种情况下会失败:
public IList<T> Foo<T>()
{
return new List<T>();
}
这种混乱中最优雅的方式是什么?
答案 0 :(得分:13)
这样做:
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及更高版本,因为这是第一个支持接口协方差和逆变的版本。
另一方面,如果您知道使用您的结果的人不打算在列表中添加任何内容,但只是尝试迭代它,您可以将返回类型更改为{{1}然后返回IEnumerable<IList<T>>
就好了。为什么?因为List<List<T>>
接口是协变:
IEnumerable<T>
那&#34; out&#34;告诉编译器该接口只有返回与public interface IEnumerable<out T>
相关的值的方法(如T
),并且它不会有任何相关的方法以GetEnumerator
作为参数(如T
)。
答案 1 :(得分:1)
这与C#协方差和逆变有关。您可以阅读更多here。
要做T接口的接口,你的接口必须都标记为out T或T. IEnumerable标记为out T。