因为IList<T>的泛型参数T是invariant的,而不是题主所期待的covariant(或者说IList<out T>):
public interface IList<T> : ICollection<T>, IEnumerable<T>, IEnumerable
从 List<List<T>> 转成 IList<List<T>> 是完全没问题的,因为这个转换从泛型参数的角度看是invariant的。然而进一步转成 IList<IList<T>> 就不行了,因为把泛型参数中(内层)的 List<T> 转成 IList<T> 需要covariant。
传送门:
Covariance and Contravariance in Generics具体到题主的例子,其实这样就好了:
using System.Collections.Generic; using System.Linq; class Record { public List<Record> Data { get { return null; } } } class Program { static void Main(string[] args) { var records = new List<Record>(); var casted1 = (IList<IList<Record>>)records.Select(r => r.Data as IList<Record>).ToList(); // ok Console.WriteLine("cast1 succeeded."); var casted2 = (IList<IList<Record>>)records.Select(r => r.Data).ToList<IList<Record>>(); // ok Console.WriteLine("cast2 succeeded."); var casted3 = (IList<IList<Record>>)records.Select(r => r.Data).ToList(); // fail Console.WriteLine("cast3 succeeded."); } }
这是因为我的例子中,ToList()其实是个泛型方法,其泛型参数通常是被自动推导出来的;Record.Data的类型是List<Record>,于是用在那个ToList()调用时推导出来的就是ToList<List<Record>>。我们要么在Select()的时候就as一下来让Select推导出来的类型改变,要么直接在ToList()的地方指定清楚泛型参数,就好啦。