LIST<> AddRange 抛出 ArgumentException

10

我有一个特定的方法,偶尔会出现ArgumentException错误:

Destination array was not long enough. Check destIndex and length, and the array's lower bounds.:
at System.Array.Copy(Array sourceArray, Int32 sourceIndex, Array destinationArray, Int32 destinationIndex, Int32 length, Boolean reliable)
at System.Collections.Generic.List`1.CopyTo(T[] array, Int32 arrayIndex)
at System.Collections.Generic.List`1.InsertRange(Int32 index, IEnumerable`1 collection)
at System.Collections.Generic.List`1.AddRange(IEnumerable`1 collection)

导致崩溃的代码大致如下:
List<MyType> objects = new List<MyType>(100);
objects = FindObjects(someParam);
objects.AddRange(FindObjects(someOtherParam);

根据MSDN的说明,List<>.AddRange()应该会自动根据需要调整大小:
如果新的Count(当前Count加上集合的大小)大于Capacity,则List<(Of <(T>)>)的容量将通过自动重新分配内部数组以容纳新元素来增加,并且在添加新元素之前将现有元素复制到新数组中。
有人能想到AddRange可能抛出这种类型的异常的情况吗?
编辑:
针对FindObjects()方法的问题,它基本上看起来像这样:
List<MyObject> retObjs = new List<MyObject>();

foreach(MyObject obj in objectList)
{
   if(someCondition)
       retObj.Add(obj);
}

1
FindObjects 返回什么?另外,为什么你在初始化 objects 后立即在下一行重新赋值它? - user203570
当您添加一个项目时,它会检查大小是否足够大,如果不够大,则会调整其使用的内部数组的大小。然而,在使用多个线程时,一个线程可能会检查并得到一个错误,然后调整数组大小,下一个线程读取到有足够的空间并且不会调整大小,然后它们同时到达实际的 this._items [this._size ++] = item; 代码...导致后一个线程崩溃。当Jon在下面的答案中说List<T>不是线程安全的时,这就是List<T>无法处理的问题之一。 - Nick Craver
1
我并没有编写这段代码,我只是在调试问题。当维护人员四处搜寻时,会发现很多奇怪的事情 :) - Tim
当您只调用FindObjects(someOtherParam)时会发生什么?我假设它会按预期提供列表? - AllenG
正确。我自己无法重现这个问题,但有人提供了一个堆栈跟踪,证明它至少发生过一次。 - Tim
2个回答

21

你是否尝试从多个线程更新同一列表?这可能会导致问题... List<T> 不适用于多个写入者。


FindObjects() 方法本身实例化一个新的 List<> 对象,填充它并返回它。它们都是单线程的,因此我认为没有多个线程在 List 上工作的机会。 - Tim
@Tim:那我不明白为什么会发生这种情况。看看能否编写一个简短但完整的程序来演示问题。如果我们能够重现它,就应该能够修复它。 - Jon Skeet
这差不多是我希望听到的。我无法重现这个问题,而且在我看来,它似乎永远不应该发生。在完全拒绝它之前,我只是想确保我没有漏掉什么。 - Tim
3
您遇到的问题确实存在,因为您有一个堆栈跟踪来证明它。我认为最有可能的是并发问题 - 您确定这不是由UI按钮启动的后台任务,例如用户快速连续点击两次吗? - Paul Ruane

0
老实说,我不太确定,但为什么不在列表初始化中删除大小声明呢?
List`<MyType>` list = new List`<MyType>`

3
因为这个初始化会被立即丢弃,所以它甚至不需要以这种方式进行初始化。 - Adam Robinson

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接