Here is a strange situation I have seen today:
这是我今天看到的一个奇怪的情况:
I have a generic list and I want add items to my list with it's indexer like this:
我有一个通用列表,我希望添加项目到我的列表与它的索引器像这样:
List<string> myList = new List<string>(10);
myList[0] = "bla bla bla...";
When I try this, I'm getting ArgumentOutOfRangeException
当我尝试这个时,我得到了ArgumentOutOfRangeException
Then I looked at List<T>
indexer set method, and here it is:
然后我查看了List
[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;
}
And also looked at Add
method:
还看了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;
}
Now, as I see both methods are using the same way:
现在,我看到两种方法都使用相同的方式:
// Add() Method
this._items[this._size++] = item;
// Setter method
this._items[index] = value;
The _items
is an array of type T
:
_items是T类型的数组:
private T[] _items;
And in the constructor _items
initialized like this:
并在构造函数_items中初始化如下:
this._items = new T[capacity]
Now, after all of these I'm curious about why I can't add items into my list with an index
,although I specify list capacity explicitly?
现在,在所有这些之后,我很好奇为什么我不能用索引将项目添加到我的列表中,尽管我明确指定了列表容量?
4 个解决方案
#1
16
The reason is that you don't add to the list with the indexer, you replace existing items.
原因是您没有使用索引器添加到列表中,而是替换现有项目。
Since you have not yet added any items to the list, it is empty, and any attempt at using the indexer to "add" items to it will throw that exception.
由于您尚未向列表中添加任何项目,因此它是空的,并且任何使用索引器向其“添加”项目的尝试都将抛出该异常。
This:
这个:
new List<string>(11);
does not create a list with 11 elements, it creates a list with capacity for 11 elements initially. This is an optimization. If you add more elements, the list will have to be resized internally, and you can pass in the expected or known capacity to avoid too many of those resizes.
不创建包含11个元素的列表,它最初创建一个容量为11个元素的列表。这是一个优化。如果添加更多元素,则必须在内部调整列表大小,并且可以传递预期或已知容量以避免过多的调整大小。
Here's a LINQPad program that demonstrates:
这是一个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:
输出:
Internals
内幕
Internally, inside a List<T>
, an array is actually used to hold the elements. Additionally, a Count
property/value is kept to keep track of how many of those array elements have actually been used.
在内部,在List
When you construct an empty list, not passing in a capacity, a default one is used, and this is the initial size of that array.
构造空列表时,不传入容量,使用默认值,这是该数组的初始大小。
As you keep adding new elements to the list, slowly you will fill up that array, towards the end. Once you have filled the entire array, and add another element to it, a new, bigger, array will have to be constructed. All the elements in the old array are then copied over to this new, bigger, array, and from now on, that array is used instead.
当你不断向列表中添加新元素时,慢慢地你将填充该数组,直到最后。一旦填充了整个数组,并向其添加了另一个元素,就必须构造一个更大的新数组。然后将旧数组中的所有元素复制到这个新的更大的数组中,从现在开始,使用该数组。
That is why the internal code calls that EnsureCapacity
method. This method is the one doing the resize operation, if necessary.
这就是内部代码调用EnsureCapacity方法的原因。如有必要,此方法是执行调整大小操作的方法。
Every time the array has to be resized, a new array is constructed and all the elements copied over. As the array grows, this operation grows in cost. It's not all that much, but it still isn't free.
每次必须调整数组大小时,都会构造一个新数组并复制所有元素。随着阵列的增长,这种操作会增加成本。它并不是那么多,但它仍然不是免费的。
That is why, if you know that you will need to store, say, 1000 elements in the list, it is a good idea to pass in a capacity value to begin with. That way, the initial size of that array might be large enough to never need to be resized/replaced. At the same time, it's not a good idea to just pass in a very large value for capacity, as this might end up using a lot of memory unnecessary.
这就是为什么,如果你知道你需要在列表中存储1000个元素,那么最好先传入一个容量值。这样,该阵列的初始大小可能足够大,永远不需要调整大小/替换。同时,传递一个非常大的容量值并不是一个好主意,因为这可能最终会占用大量内存。
Also know that everything in this section of the answer is undocumented (as far as I know) behavior, and should any details or specific behavior you might learn from it should never influence the code you write, other than the knowledge about passing in a good capacity value.
还要知道答案的这一部分中的所有内容都是未记录的(据我所知)行为,如果您从中学到的任何细节或特定行为不应该影响您编写的代码,除了传递好的知识容量值。
#2
7
No, it's not strange. You are mistakenly assuming the constructor that accepts capacity initializes that many elements in the list. It does not. The list is empty and you must add elements.
不,这并不奇怪。您错误地假设接受容量的构造函数初始化列表中的许多元素。它不是。列表为空,您必须添加元素。
If for some reason you need to initialize it with elements, you could do this:
如果由于某种原因你需要用元素初始化它,你可以这样做:
new List<string>(Enumerable.Repeat<string>(string.Empty, 11));
Sounds like a string[]
array is a better fit though.
听起来像string []数组更合适。
#3
6
Strange or not. It is documented: http://msdn.microsoft.com/en-us/library/0ebtbkkc(v=vs.110).aspx
奇怪与否。它记录在案: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
4
There is difference between Capacity of the list and Count of the list.
列表的容量与列表的计数之间存在差异。
Capacity says how big the internal array is big. Count says how many valid items the list contains. The constructor you are using is setting the capacity, but the amount of valid items is still 0. And the indexer checks against amount of valid items, not against capacity.
容量表示内部阵列有多大。 Count表示列表包含多少有效项目。您正在使用的构造函数是设置容量,但有效项的数量仍为0.并且索引器会检查有效项的数量,而不是容量。
#1
16
The reason is that you don't add to the list with the indexer, you replace existing items.
原因是您没有使用索引器添加到列表中,而是替换现有项目。
Since you have not yet added any items to the list, it is empty, and any attempt at using the indexer to "add" items to it will throw that exception.
由于您尚未向列表中添加任何项目,因此它是空的,并且任何使用索引器向其“添加”项目的尝试都将抛出该异常。
This:
这个:
new List<string>(11);
does not create a list with 11 elements, it creates a list with capacity for 11 elements initially. This is an optimization. If you add more elements, the list will have to be resized internally, and you can pass in the expected or known capacity to avoid too many of those resizes.
不创建包含11个元素的列表,它最初创建一个容量为11个元素的列表。这是一个优化。如果添加更多元素,则必须在内部调整列表大小,并且可以传递预期或已知容量以避免过多的调整大小。
Here's a LINQPad program that demonstrates:
这是一个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:
输出:
Internals
内幕
Internally, inside a List<T>
, an array is actually used to hold the elements. Additionally, a Count
property/value is kept to keep track of how many of those array elements have actually been used.
在内部,在List
When you construct an empty list, not passing in a capacity, a default one is used, and this is the initial size of that array.
构造空列表时,不传入容量,使用默认值,这是该数组的初始大小。
As you keep adding new elements to the list, slowly you will fill up that array, towards the end. Once you have filled the entire array, and add another element to it, a new, bigger, array will have to be constructed. All the elements in the old array are then copied over to this new, bigger, array, and from now on, that array is used instead.
当你不断向列表中添加新元素时,慢慢地你将填充该数组,直到最后。一旦填充了整个数组,并向其添加了另一个元素,就必须构造一个更大的新数组。然后将旧数组中的所有元素复制到这个新的更大的数组中,从现在开始,使用该数组。
That is why the internal code calls that EnsureCapacity
method. This method is the one doing the resize operation, if necessary.
这就是内部代码调用EnsureCapacity方法的原因。如有必要,此方法是执行调整大小操作的方法。
Every time the array has to be resized, a new array is constructed and all the elements copied over. As the array grows, this operation grows in cost. It's not all that much, but it still isn't free.
每次必须调整数组大小时,都会构造一个新数组并复制所有元素。随着阵列的增长,这种操作会增加成本。它并不是那么多,但它仍然不是免费的。
That is why, if you know that you will need to store, say, 1000 elements in the list, it is a good idea to pass in a capacity value to begin with. That way, the initial size of that array might be large enough to never need to be resized/replaced. At the same time, it's not a good idea to just pass in a very large value for capacity, as this might end up using a lot of memory unnecessary.
这就是为什么,如果你知道你需要在列表中存储1000个元素,那么最好先传入一个容量值。这样,该阵列的初始大小可能足够大,永远不需要调整大小/替换。同时,传递一个非常大的容量值并不是一个好主意,因为这可能最终会占用大量内存。
Also know that everything in this section of the answer is undocumented (as far as I know) behavior, and should any details or specific behavior you might learn from it should never influence the code you write, other than the knowledge about passing in a good capacity value.
还要知道答案的这一部分中的所有内容都是未记录的(据我所知)行为,如果您从中学到的任何细节或特定行为不应该影响您编写的代码,除了传递好的知识容量值。
#2
7
No, it's not strange. You are mistakenly assuming the constructor that accepts capacity initializes that many elements in the list. It does not. The list is empty and you must add elements.
不,这并不奇怪。您错误地假设接受容量的构造函数初始化列表中的许多元素。它不是。列表为空,您必须添加元素。
If for some reason you need to initialize it with elements, you could do this:
如果由于某种原因你需要用元素初始化它,你可以这样做:
new List<string>(Enumerable.Repeat<string>(string.Empty, 11));
Sounds like a string[]
array is a better fit though.
听起来像string []数组更合适。
#3
6
Strange or not. It is documented: http://msdn.microsoft.com/en-us/library/0ebtbkkc(v=vs.110).aspx
奇怪与否。它记录在案: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
4
There is difference between Capacity of the list and Count of the list.
列表的容量与列表的计数之间存在差异。
Capacity says how big the internal array is big. Count says how many valid items the list contains. The constructor you are using is setting the capacity, but the amount of valid items is still 0. And the indexer checks against amount of valid items, not against capacity.
容量表示内部阵列有多大。 Count表示列表包含多少有效项目。您正在使用的构造函数是设置容量,但有效项的数量仍为0.并且索引器会检查有效项的数量,而不是容量。