【问题标题】:Why I can't add items to the Generic List with indexer?为什么我不能使用索引器将项目添加到通用列表?
【发布时间】:2013-12-30 13:29:45
【问题描述】:

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

我有一个通用列表,我想使用它的索引器将项目添加到我的列表中:

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

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

然后我查看了List&lt;T&gt; indexer set 方法,这里是:

[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]

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

【问题讨论】:

  • 索引器不添加项目。您有一个空列表,并且您正在尝试访问(不存在的)第一项

标签: c# .net arrays generics


【解决方案1】:

原因是你没有添加到带有索引器的列表中,你替换了现有的项目

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

这个:

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
}

输出:


内部

在内部,在List&lt;T&gt; 中,实际上使用了一个数组来保存元素。此外,还会保留 Count 属性/值,以跟踪实际使用了多少这些数组元素。

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

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

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

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

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

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

【讨论】:

  • 此外,还有一个Insert 方法正在使用索引,但抛出相同的异常是列表项没有被“初始化”为假值。
  • 你可以在一个空列表上调用Insert,传入索引0。它基本上相当于Add。也就是说,你可以传入Count 就可以了。
  • 当然。但是如果他尝试做Insert(5, "item"),就会抛出异常。
  • 谢谢,但我仍然不知道该索引器中设置方法的意义何在?因为 Add 方法不使用 setter 方法,如果我不能使用它为什么存在??
  • 您可以在向列表中添加一个元素后使用它。试试:list.Add("Test"); list[0] = "Other test";
【解决方案2】:

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

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

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

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

【讨论】:

  • 那么,我该如何设置尺寸呢?或者如果我不能为什么该索引器中有一个 setter 方法?
  • 容量就是为了避免内存重新分配。设置适当的容量并使用添加。或者,如果您必须使用加载空条目进行初始化。
【解决方案3】:

奇怪与否。记录在案: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.

【讨论】:

    【解决方案4】:

    列表的容量和列表的计数有区别。

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

    【讨论】:

      猜你喜欢
      • 2018-09-04
      • 2011-04-07
      • 1970-01-01
      • 2023-04-03
      • 2015-04-25
      • 2022-01-13
      • 2021-05-18
      • 1970-01-01
      • 1970-01-01
      相关资源
      最近更新 更多