为什么我不能使用索引器将项添加到通用列表?

时间:2013-12-30 13:29:46

标签: c# .net arrays generics

这是我今天看到的一个奇怪的情况:

我有一个通用列表,我想在我的列表中添加项目,它的索引器是这样的:

List<string> myList = new List<string>(10);
myList[0] = "bla bla bla...";

当我尝试这个时,我得到了ArgumentOutOfRangeException

enter image description here

然后我查看了List<T>索引器集方法,这里是:

[TargetedPatchingOptOut("Performance critical to inline across NGen image boundaries"),    __DynamicallyInvokable] 
  set
  {
    if ((uint) index >= (uint) this._size)  
      ThrowHelper.ThrowArgumentOutOfRangeException();  //here is exception
    this._items[index] = value;
    ++this._version;
  }

还看了Add方法:

[__DynamicallyInvokable]
public void Add(T item)
{
  if (this._size == this._items.Length)
    this.EnsureCapacity(this._size + 1);
  this._items[this._size++] = item;
  ++this._version;
}

现在,我看到两种方法都采用相同的方式:

// Add() Method
this._items[this._size++] = item; 
// Setter method
this._items[index] = value;

_itemsT类型的数组:

private T[] _items;

在构造函数_items中初始化如下:

this._items = new T[capacity]

现在,在所有这些之后,我很好奇为什么我不能用索引添加项目到我的列表中 ,虽然我明确指定列表容量?

4 个答案:

答案 0 :(得分:16)

原因是您没有使用索引器添加到列表中,替换现有项

由于您尚未向列表中添加任何项目,因此它是空的,并且任何尝试使用索引器向其“添加”项目都会抛出该异常。

此:

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 of linqpad program


<强>塔内

List<T>内部,一个数组实际上用于保存元素。此外,保留Count属性/值以跟踪实际使用了多少个数组元素。

当你构造一个空列表而不是传入一个容量时,会使用一个默认值,这就是该数组的初始大小。

当您不断向列表中添加新元素时,您将慢慢地填充该数组,直到最后。一旦填充了整个数组,并向其添加了另一个元素,就必须构造一个更大的新数组。然后将旧数组中的所有元素复制到这个新的更大的数组中,从现在开始,使用该数组。

这就是内部代码调用EnsureCapacity方法的原因。如有必要,此方法是执行调整大小操作的方法。

每次必须调整数组大小时,都会构造一个新数组并复制所有元素。随着阵列的增长,这种操作会增加成本。 all 并不是那么多,但它仍然不是免费的。

这就是为什么,如果你知道你需要在列表中存储1000个元素,那么最好先传入一个容量值。这样,该数组的初始大小可能足够大,永远不需要调整大小/替换。同时,传递一个非常大的容量值并不是一个好主意,因为这可能最终会占用大量的内存。

还要知道答案的这一部分中的所有内容都是未记录的(据我所知)行为,并且您可能从中学到的任何细节或特定行为都不应该影响您编写的代码,除了有关传递的知识具有良好的产能价值。

答案 1 :(得分:7)

不,这并不奇怪。您错误地假设接受容量的构造函数初始化列表中的许多元素。它不是。该列表为空,您必须添加元素。

如果由于某种原因你需要用元素初始化它,你可以这样做:

 new List<string>(Enumerable.Repeat<string>(string.Empty, 11));

听起来像string[]数组更合适。

答案 2 :(得分:6)

奇怪与否。记录在案:http://msdn.microsoft.com/en-us/library/0ebtbkkc(v=vs.110).aspx

Exception: ArgumentOutOfRangeException
index is less than 0.
-or-
index is equal to or greater than Count.

答案 3 :(得分:4)

列表的容量与列表的计数之间存在差异。

容量表示内部阵列有多大。 Count表示列表包含多少有效项目。您正在使用的构造函数是设置容量,但有效项的数量仍为0.并且索引器会检查有效项的数量,而不是容量。