我的理解是,以下代码是安全的,因为Invoke是同步的,因此索引总是在操作后增加,但我在array.ElementAt(index)
行收到ArgumentOutOfRangeException的报告。
数组是在此代码之前从LINQ查询生成的IEnumerable,因此无法修改。
IEnumerable array = collection.Select(() => .....);
while (index < array.Count())
{
this.CurrentDispatcher.Invoke(new Action(() =>
{
...
object a = array.ElementAt(index)
...
}), DispatcherPriority.Input);
index++;
}
我可以想象获得ArgumentOutOfRangeException
的唯一方法是,在访问数组之前,索引会以某种方式增加。
这有可能吗?
答案 0 :(得分:3)
由于array
是惰性的,因此即使您使用缓存的局部变量更正了代码,集合中的更改也可能导致索引超出范围(即从collection
中删除元素)。 / p>
或者如Select
中的注释条件所指示,每次执行时都会返回不同的结果。
请注意,如果您拥有R#,您将获得多次枚举&#34;警告为array.Count()
和array.ElementAt()
都需要迭代收集以获得结果。所以你真的在Select
循环上多次重复执行while
。
修复:
foreach
(首选)执行单次迭代ToList()
调用对枚举进行评估(这也会使O(n)
的{{1}}的O(n ^ 2)复杂度降低为Count
和ElementAt
在IEnumerable<T>
上使用时,将是O(1)而不是至少O(n)。答案 1 :(得分:3)
根据您的评论,您每次拨打array.Count
或array.ElementAt(index)
时都会执行查询。
每次执行都可以返回不同的结果,这可能是您的例外情况的原因
使用.ToList()
扩展方法将查询结果“具体化”到List<T>
,可以安全使用
List array = collection.Select(() => .....).ToList();
由于您的代码只是循环查询结果,请考虑使用@Alexei的建议仅循环枚举
IEnumerable array = collection.Select(() => .....);
foreach(var item in array)
{
this.CurrentDispatcher.Invoke(new Action(() =>
{
//...
object a = item;
//...
}), DispatcherPriority.Input);
}
答案 2 :(得分:2)
是的,这被称为闭包..只需定义局部变量而不是使用索引。
还有一个建议是在linq查询结束时使用ToList,这样在每次调用Count和ElementAt时都不会评估Select
select productid, dispenserid from magazine where magazineid = 41659