IEnumerable<匿名类型>调用ToList()为何不返回List<object>?
嘿,这个问题其实涉及到C#泛型类型推断和协变的核心细节,咱们一步步拆解来看:
为什么
names.ToList()不返回List<object>? 首先,匿名类型虽然编译后确实是继承自object的具体密封类型(编译器会自动生成类似<>f__AnonymousType0<string>这类内部名称),但C#的泛型类型推断是严格匹配实际元素类型的。当你调用names.Select(n => new { FirstName = n }).ToList()时,编译器会把ToList()的泛型参数T推断为这个匿名类型,而非object——因为Select返回的是IEnumerable<<匿名类型>>,ToList()会直接基于这个枚举的元素类型创建对应的List<T>,最终得到的是List<<匿名类型>>,而非List<object>。
另外,List<T>是不变泛型类型,也就是说哪怕Derived继承自Base,List<Derived>也不能隐式转换为List<Base>(这里匿名类型继承自object)。这是C#泛型的安全设计:如果允许这种转换,你可能会把一个不兼容的object实例塞进原本只能存储匿名类型的列表里,破坏类型安全。
为什么有些代码不会报错?
你提到的“下述代码不会报错”,大概率是这几种场景:
- 用
var接收结果:var list = names.Select(n => new { FirstName = n }).ToList();这里var会被编译器自动推断为List<<匿名类型>>,类型完全匹配,自然不会报错。 - 直接将匿名类型实例赋值给
object:比如object obj = new { FirstName = "Alice" };这是合法的,因为匿名类型确实是object的子类,隐式向上转换是C#的基础特性。 - 使用协变泛型接口:比如
IEnumerable<object> seq = names.Select(n => new { FirstName = n });这能正常编译,因为IEnumerable<out T>是协变接口,当T是引用类型时,IEnumerable<Derived>可以隐式转换为IEnumerable<Base>。
解决编译错误的方法
如果你确实需要得到List<object>,可以显式将每个匿名类型元素转换为object:
// 方式1:在Select阶段直接转换 List<object> namesList = names.Select(n => (object)new { FirstName = n }).ToList(); // 方式2:用Cast<object>()批量转换 List<object> namesList = names.Select(n => new { FirstName = n }).Cast<object>().ToList();
当然,如果业务场景允许,更推荐定义一个具体类来替代匿名类型,这样类型更清晰,也能避免这类转换问题:
public class PersonName { public string FirstName { get; set; } } List<PersonName> namesList = names.Select(n => new PersonName { FirstName = n }).ToList();
内容的提问来源于stack exchange,提问作者pwaivers
相关产品推荐
相关产品推荐

