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

标签 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]

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

最佳答案

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

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

这个:

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 的原因方法。如有必要,此方法是执行调整大小操作的方法。

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

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

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

关于c# - 为什么我不能使用索引器将项目添加到通用列表?,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/20840551/

相关文章:

c# - 如何对返回 void 的 DAO 更新/删除方法进行单元测试?

Java 遍历数组

c# - 使用 webclient DownloadFileAsync 多个文件

c# - IDisposable 的替代用途是什么?

c# - 重命名 Blazor 组件文件名时出现错误

c# - 我们可以在 BackgroundWorker 中使用 DispatcherTimer 吗?

c# - 使用 Agility Pack 用另一个节点包围现有节点

c# - 创建适用于 System.Object 的扩展方法的良好做法?

ruby - 在 Ruby 中将散列转换为字符串

php - 将数组数据插入 MYSQL/PHP