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

Sel*_*enç 8 .net c# arrays generics

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

我有一个通用列表,我希望添加项目到我的列表与它的索引器像这样:

List<string> myList = new List<string>(10);
myList[0] = "bla bla bla...";
Run Code Online (Sandbox Code Playgroud)

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

在此输入图像描述

然后我看了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;
  }
Run Code Online (Sandbox Code Playgroud)

还看了一下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;
}
Run Code Online (Sandbox Code Playgroud)

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

// Add() Method
this._items[this._size++] = item; 
// Setter method
this._items[index] = value;
Run Code Online (Sandbox Code Playgroud)

_items是一个类型的数组T:

private T[] _items;
Run Code Online (Sandbox Code Playgroud)

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

this._items = new T[capacity]
Run Code Online (Sandbox Code Playgroud)

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

ang*_*son 16

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

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

这个:

new List<string>(11);
Run Code Online (Sandbox Code Playgroud)

不创建包含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
}
Run Code Online (Sandbox Code Playgroud)

输出:

linqpad程序的输出


内幕

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

构造空列表而不传入容量时,将使用默认值,这是该数组的初始大小.

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

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

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

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

还要知道答案的这一部分中的所有内容都没有记录(据我所知)行为,如果您从中学到的任何细节或特定行为都不应该影响您编写的代码,除了传递一个好的知识容量值.

  • 您可以在*添加元素到列表后使用*.尝试:`list.Add("Test"); list [0] ="其他测试";` (2认同)

Jam*_*rld 7

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

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

 new List<string>(Enumerable.Repeat<string>(string.Empty, 11));
Run Code Online (Sandbox Code Playgroud)

听起来像string[]阵列更合适.


Pat*_*man 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.
Run Code Online (Sandbox Code Playgroud)