为什么这个foreach循环不会抛出异常“集合被修改,枚举操作可能无法执行”。

时间:2014-03-05 06:38:11

标签: c# collections foreach ienumerable

我有以下代码,它修改了foreach循环内集合的状态, 但是,它不会抛出任何异常。

// _topQueue.QueuedJobs is of type List<Job>        
var slaJobs = _topQueue.QueuedJobs
    .Where(job => job.JobInformation.SLA.HasValue)
    .OrderBy(job=>job.ScheduledPriority);
foreach (var job in slaJobs) {
    // Why this does not throw the exception:
    // "collection was modified enumeration operation may not execute."
    job.SetRunning();
    job.JobInformation.StaticPriority = (int)Common.DjsConstant.PriorityPenalty;
    job.ReprioritizedPriority = (int)Common.DjsConstant.PriorityBlocking;
}

另一方面,这段代码确实抛出异常:

//_topQueue.QueuedJobs is of type List<Job>
foreach (var job in _topQueue.QueuedJobs) {
    job.SetRunning();
    job.JobInformation.StaticPriority = (int)Common.DjsConstant.PriorityPenalty;
    job.ReprioritizedPriority = (int)Common.DjsConstant.PriorityBlocking;
}

有人知道为什么吗? foreach循环中的哪些确切修改会导致异常?

4 个答案:

答案 0 :(得分:3)

我很确定这是因为你有OrderBy枚举整个集合才能给出任何结果。因此,原始集合已被完全读取,因此,更改原始集合不会影响OrderBy已准备好的结果。

更新

防止异常的简单解决方案是在linq查询中添加.ToList()。然后结果是一个列表,并在内部更新对象不会影响列表的内容。

// _topQueue.QueuedJobs is of type List<Job>        
var slaJobs = _topQueue.QueuedJobs
    .Where(job => job.JobInformation.SLA.HasValue)
    .OrderBy(job=>job.ScheduledPriority)
    .ToList();
foreach (var job in slaJobs) {
    // etc
}

答案 1 :(得分:0)

IEnumerable<T>用于枚举,而不是修改。尽量避免修改枚举。

答案 2 :(得分:0)

有趣。 第一个枚举在子集上循环,而第二个枚举在所有元素上循环。

现在,如果第一个子集上的操作(即您提到的“删除”)不会影响子集本身,则它可能不会抱怨,因为查询背后的代码无法识别删除。当然,这只是一个理论。证明它(或至少获得更多信息): 删除的真正影响是什么?它会以任何方式改变或影响子集吗?

答案 3 :(得分:0)

我的建议是,job.SetRunning()中的方法中的某些内容正在从_topQueue.QueuedJobs中的集合中删除它。在第一个示例中,通过返回linq语句的结果,您将获得一个独立于原始_topQueue.QueuedJobs的克隆枚举。