我有一个要添加到嵌套 foreach 循环中的对象列表。该操作是同步的(或者我可能并不像我认为的那样理解 lambda)并且是单线程的,并且列表不是不合理的大。对于可能导致此异常的原因,我完全不知所措。
public string PromotionSpecificationIdGuid { get; set; }
public virtual List<ElementInstance> ElementInstances { get; set; }
public void UpdateInstanceGraph(OfferingInstance parentData, OfferingInstance offeringContainer = null)
{
ElementInstances = new List<ElementInstance>();
parentData.ActiveServices.ForEach(
service => service.ActiveComponents.ForEach(
component => component.Elements.ForEach(
element =>
{
if (element.PromotionId == this.PromotionSpecificationIdGuid)
{
ElementInstances.Add(element);
}
})));
}
结果是:
System.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.set_Capacity(Int32 value)
at System.Collections.Generic.List`1.EnsureCapacity(Int32 min)
at System.Collections.Generic.List`1.Add(T item)
试图通过一些单元测试来解决这个问题并对其进行锤击,但我希望同时有人能帮助我。
-编辑-
多亏了 Juan 和 Mark,我才知道这是怎么发生的。在我的应用程序中,此操作本身是单线程的,但它使用本质上是单例的内容并通过 ajax 调用。多个调用者可以启动他们自己的线程,当这些调用足够接近时,我们就会得到这种行为。我制作了一个控制台应用程序来说明这个概念。
using System;
using System.Collections.Generic;
using System.Threading;
using System.Threading.Tasks;
namespace listaccessmonster
{
public class Program
{
private static List<Guid> baseList = new List<Guid>();
private static List<Guid> activeList;
private static Random rand = new Random();
public static void Main(string[] args)
{
for(int i = 0; i < 1000000; i++)
{
baseList.Add(Guid.NewGuid());
}
var task1 = UpdateList(); //represents ajax call 1
var task2 = UpdateList(); //represents ajax call 2
var result = Task.WhenAll(task1, task2);
try
{
result.Wait();
}
catch(Exception e)
{
Console.WriteLine(e);
}
task1 = UpdateListFixed(); //represents ajax call 1
task2 = UpdateListFixed(); //represents ajax call 2
result = Task.WhenAll(task1, task2);
try
{
result.Wait();
}
catch (Exception e)
{
Console.WriteLine(e);
}
Console.WriteLine("press Enter to exit");
Console.ReadKey();
}
private static Task UpdateList()
{
return Task.Run(()=> {
Thread.Sleep(rand.Next(5));
Console.WriteLine("Beginning UpdateList");
activeList = new List<Guid>();
baseList.ForEach(x => {
activeList.Add(x);
});
});
}
private static Task UpdateListFixed()
{
return Task.Run(() => {
Thread.Sleep(rand.Next(5));
Console.WriteLine("Beginning UpdateListFixed");
var tempList = new List<Guid>();
baseList.ForEach(x => {
tempList.Add(x);
});
activeList = tempList;
});
}
}
}
大多数时候都会抛出异常或类似的异常,但不是每次都会抛出。它永远不会用 Fixed 方法抛出。
最佳答案
你是对的。处理列表的代码不使用线程。
但是,我认为在先前的运行有机会完成之前,某些东西正在重复调用 UpdateInstanceGraph
(从而引入线程)。这将导致 ElementInstances
在先前的调用仍在执行时重置为大小 0
。
更改您的代码以改为使用本地实例,然后设置公共(public)属性:
public void UpdateInstanceGraph(OfferingInstance parentData, OfferingInstance offeringContainer = null)
{
var instances = new List<ElementInstance>();
parentData.ActiveServices.ForEach(
service => service.ActiveComponents.ForEach(
component => component.Elements.ForEach(
element =>
{
if (element.PromotionId == this.PromotionSpecificationIdGuid)
{
instances.Add(element);
}
})));
ElementInstances = instances;
}
我还建议改用 SelectMany
并转换为 List
以直接分配给属性:
public void UpdateInstanceGraph(OfferingInstance parentData, OfferingInstance offeringContainer = null)
{
ElementInstances = parentData.ActiveServices
.SelectMany(s => s.ActiveComponents)
.SelectMany(c => c.Elements)
.Where(e => e.PromotionId == PromotionSpecificationIdGuid).ToList();
}
关于c# - 在单线程中的 List.Add 期间,什么可能导致 "Destination array was not long enough"?,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/50240932/