关于C#中IList<T>类型集合表达式初始化的Numbers属性的类型假设及编译器行为的疑问
咱们来一步步拆解你提出的问题,把这些点说清楚:
关于当前Numbers的类型与属性判断
首先,就目前的C#版本来说,public IList<int> Numbers { get; } = []这段代码里,编译器确实会生成List<int>的实例。所以针对你问的两个属性:
IsFixedSize会返回false,因为List<T>本身是动态可变大小的集合IsReadOnly也会返回false,因为这是一个可读写的List<T>实例
你提到的Array.Empty<int>()确实也实现了IList<int>,但它是固定大小且只读的——不过编译器不会选择它,因为集合表达式是目标类型驱动的:当目标类型是IList<T>(一个包含增删元素方法的可变接口)时,编译器会优先匹配符合该接口契约的可变默认实现,也就是List<T>。
关于编译器未来行为的两个疑问
1. 编译器会不会分析代码判断集合是否被增删,再更换实现?
目前没有这样的设计计划,而且这也不符合C#的设计原则。C#的集合表达式行为是由声明时的目标类型决定的,不会去做后续代码的流分析(比如判断你之后有没有调用Add/Remove)来偷偷更换集合类型。这么做是为了保持行为的确定性——如果你声明的是IList<T>,那不管后续用不用它的可变方法,它的类型都是符合IList<T>契约的可变实现,避免因为后续代码改动导致集合类型变化,引发潜在的bug。
2. 编译器会不会始终认为IList<T>需要可变非固定大小?
是的,这是由IList<T>的接口契约决定的。IList<T>本身就定义了Add、Remove等修改集合的方法,编译器会默认认为当你把集合表达式赋值给IList<T>时,你需要的是一个能满足这些修改操作的可变实现。如果你想要只读的集合,应该显式把目标类型声明为IReadOnlyList<T>,这时候集合表达式会生成对应的只读实现(比如优化后的只读包装类)。
关于文档说明
C#官方文档里明确规定了集合表达式的目标类型匹配规则:当目标类型是IList<T>、ICollection<T>这类可变接口时,会生成对应的可变集合实现;当目标类型是IReadOnlyList<T>、IReadOnlyCollection<T>这类只读接口时,生成只读实现。同时文档也强调了集合表达式的行为仅由目标类型决定,不会基于后续代码使用情况调整。
备注:内容来源于stack exchange,提问作者Rob

