原因是你不add到带有索引器的列表,您替换现有项目.
由于您尚未向列表添加任何项目,因此它是空的,并且任何使用索引器向其中“添加”项目的尝试都会引发该异常。
This:
new List<string>(11);
不会创建一个包含 11 个元素的列表,它会创建一个最初容量为 11 个元素的列表。这是一个优化。如果添加更多元素,则必须在内部调整列表的大小,并且您可以传入预期或已知的容量以避免过多的调整大小。
这是一个LINQPad程序演示:
void Main()
{
var l = new List<string>(10);
l.Dump(); // empty list
l.Add("Item");
l.Dump(); // one item
l[0] = "Other item";
l.Dump(); // still one item
l.Capacity.Dump(); // should be 10
l.AddRange(Enumerable.Range(1, 20).Select(idx => idx.ToString()));
l.Capacity.Dump(); // should be 21 or more
}
Output:
内部结构
在内部,在一个List<T>
,数组实际上是用来保存元素的。另外,一个Count
保留属性/值以跟踪实际使用了多少个数组元素。
当您构造一个空列表时,不传入容量,则使用默认列表,这就是该数组的初始大小。
当您不断向列表中添加新元素时,您将慢慢地填满该数组,直到最后。一旦填充了整个数组,并向其中添加另一个元素,就必须构造一个新的、更大的数组。然后,旧数组中的所有元素都会复制到这个更大的新数组中,从现在开始,将使用该数组。
这就是内部代码调用它的原因EnsureCapacity
方法。如果需要,此方法是执行调整大小操作的方法。
每次需要调整数组大小时,都会构造一个新数组并复制所有元素。随着阵列的增长,此操作的成本也会增加。它不是all这么多,但它仍然不是免费的。
这就是为什么,如果您知道需要在列表中存储(例如)1000 个元素,那么最好首先传入一个容量值。这样,该数组的初始大小可能足够大,永远不需要调整大小/替换。同时,仅传递非常大的容量值也不是一个好主意,因为这可能最终会使用大量不必要的内存。
还要知道,答案的这一部分中的所有内容都是未记录的(据我所知)行为,并且您可能从中学到的任何细节或特定行为都不应影响您编写的代码,除了有关传递良好的知识之外容量值。