使用索引初始化List<T>编译通过却报错,这是C#的Bug吗?
这既不是未完成的功能,也不应该被设为编译错误,核心原因在于C#对对象初始化器和集合初始化器的设计逻辑:
两种初始化方式的本质差异
Dictionary<TKey,TValue>的[0] = "Hello"是利用了C#的索引器赋值初始化:编译器会将其转换为对字典索引器的连续赋值操作,而字典的索引器本身支持新增键值对(不存在的键会自动添加),所以运行正常。
List的常规初始化 { "Hello", "World" }是集合初始化器:编译器会自动转换为多次调用Add方法,这是集合初始化器的标准行为,针对实现了IEnumerable且有Add方法的类型。为什么索引赋值初始化对List
能编译通过?
List公开了可写的索引器 public T this[int index] { get; set; },而C#的对象初始化器允许对对象的索引器进行赋值操作——编译器只检查语法是否合法、目标类型是否存在对应的索引器,不会验证索引在运行时的有效性。这和你在普通代码里写var list = new List<string>(); list[0] = "a";能编译但运行报错是同一个道理。为什么不设为编译错误?
编译器无法在编译阶段预判List的运行时状态:比如以下代码是完全合法且能正常运行的: // 初始化时指定容量并填充默认元素,Count为2 var listB = new List<string>(new string[2]) { [0] = "Hello", [1] = "World" };这种情况下索引0和1都是有效的,编译器没有理由禁止这类合法代码。因此,C#设计上选择将索引越界的检查留给运行时,由CLR抛出
ArgumentOutOfRangeException,这符合.NET的错误处理原则——运行时异常用于处理只有在执行时才能发现的非法操作。
内容的提问来源于stack exchange,提问作者user3163495

