我有一个.NET 4.0程序集;它已在GAC中注册,并且是BizTalk“业务流程”的一部分。 有时会出现以下错误-“集合已修改;枚举操作可能无法执行。 :System.InvalidOperationException:集合已修改;枚举操作可能无法执行。”。我无法复制它;当我对相同数据执行相同处理时,我的程序集在该位置不会产生错误。
当我为数据表对象(分类为System.Data.TypedTableBase的对象)调用“ .Where()。ToArray()”时,会发生错误。
这是代码: ..................
int? setTypeGroupId;
...
return instances.WorkContributors.Where
(
c =>
!c.IsInterestedPartyNoNull()
&& c.InterestedPartyNo == publisherIpNo
&& c.SetTypeNo == 1
&& (c.RecordType == "SPU")
&& c.TypeCode == "E"
&& (!setTypeGroupId.HasValue ||
(setTypeGroupId.HasValue && c.SetTypeGroupID == setTypeGroupId))
).ToArray();
..................
“ instances”对象是一个数据集-我的类是从System.Data.DataSet生成的。 “ instances.WorkContributors”属性是一个数据表:System.Data.TypedTableBase类的对象。 MyDataRowClass类是从System.Data.DataRow生成的。
错误后的调用堆栈如下:
集合已修改;枚举操作可能无法执行。 :System.InvalidOperationException:集合已修改;枚举操作可能无法执行。
在System.Data.RBTree 1.RBTreeEnumerator.MoveNext()
at System.Linq.Enumerable.<CastIterator>d__97
1.MoveNext()
在System.Linq.Enumerable.WhereEnumerableIterator 1.MoveNext()
at System.Linq.Buffer
1..ctor(IEnumerable 1 source)
at System.Linq.Enumerable.ToArray[TSource](IEnumerable
1来源)处
在MyProduct.FileParser.Types.CWR.PWRType.GetPublishers(CWRWorkInstances实例,可为1 setTypeGroupId)
在MyProduct.FileParser.Validation.Concreate.PwrTypeValidation.ValidatePublisherNumber()
在MyProduct.FileParser.Validation.Concreate.PwrTypeValidation.Validate()
在MyProduct.FileParser.Types.CWR.PWRType.StoreRecord(CWRWorkInstances workInstances,CWRWorkParsingContext上下文)
在MyProduct.FileParser.Groups.CWR.NWRGroup.StoreGroup(Int32 workBatchID,CWRFileCommonData commonData)
在MyProduct.FileParser.CWRParser.ProcessCWRFile(String fileName,Boolean wait,Boolean deleteFile,String sourceFileName)
我不明白为什么会发生错误;以及为什么它仅在某些情况下发生,而不会在相同的处理数据上再次发生。 错误“集合已修改;枚举操作可能不会执行。但我不明白为什么在我的代码中会发生这种情况。如果这样的代码,将排除该错误:
foreach (DataRow currRow in _someDataTable.Rows)
{
if (/*deletion condition*/)
{
someDataTable.Rows.Remove(currRow);
}
}
但是我上面的代码只想枚举System.Data.TypedTableBase并将结果转换为数组。
有什么想法吗?
答案 0 :(得分:0)
将.ToArray()更改为.ToList()。两者之间存在语义差异,the answer here
可以很好地说明另外两个(好的)答案集中在微观上 将会发生的性能差异。这篇文章只是 补充提到在语义之间存在的差异 与返回的数组相比,由数组(T [])生成的IEnumerator 通过列表。最佳示例说明:
IList<int> source = Enumerable.Range(1, 10).ToArray(); // try changing to .ToList()
foreach (var x in source) { if (x == 5)
source[8] *= 100; Console.WriteLine(x); }
The above code will run with no exception and produces the output:
1
2
3
4
5
6
7
8
900
10
这表明int []返回的IEnumarator没有 跟踪自创建以来是否已修改数组 枚举数请注意,我将局部变量source声明为 一个IList。这样,我确保C#编译器不会 将foreach语句优化为等效于 for(var idx = 0; idx
1[System.Int32] but of course this is an implementation detail. Now, if I change .ToArray() into .ToList(), I get only: 1 2 3 4 5 followed by a System.InvalidOperationException blow-up saying: Collection was modified; enumeration operation may not execute. The underlying enumerator in this case is the public mutable value-type System.Collections.Generic.List 1 + Enumerator [System.Int32](带框 在这种情况下,则是在IEnumerator框内,因为我使用了IList)。 总之,由List产生的枚举数保持不变 列表在枚举期间是否更改,而枚举器 T []产生的结果不是。所以在选择时要考虑这种差异 在.ToList()和.ToArray()之间。人们通常会多加一 .ToArray()或.ToList()绕过跟踪的集合 是否在枚举器的生存期内对其进行了修改。 (如果 任何人都想知道List <>如何跟踪收集是否 被修改,此类中有一个私有字段_version 每次List <>更新时更改。)
答案 1 :(得分:0)
在使用foreach遍历集合时,您无法修改集合。 复制并从副本中删除。
DataTable Junk = new DataTable();
foreach (DataRow currRow in _someDataTable.Rows)
{
if (/*deletion condition*/)
{
Junk.Add(currRow);
}
}
foreach (DataRow row in Junk)
{
_ someDataTable.Rows.REmove(row);
}