我有这个foreach
- 循环:
foreach (var classId in m_ClassMappings[taAddressDefinition.Key])
{
if(!m_TAAddressDefinitions.ContainsKey(classId))
{
m_TAAddressDefinitions.Add(classId, taAddressDefinition.Value);
}
}
m_TAAddressDefinitions
是IDictionary
,其中classId用作唯一键值
Resharper建议将if
- 语句更改为LINQ过滤器,如下所示:
foreach (var classId in m_ClassMappings[taAddressDefinition.Key].Where(
classId => !m_TAAddressDefinitions.ContainsKey(classId)))
{
m_TAAddressDefinitions.Add(classId, taAddressDefinition.Value);
}
如果这可能无法正常工作,我感到很遗憾,因为m_TAAddressDefinitions
集合的内容在循环内部发生了变化,这导致LINQ过滤条件(classId => !m_TAAddressDefinitions.ContainsKey(classId)
)的来源发生变化在途中。
如果在循环内添加了两个具有相同值的classId
,或者在将值添加到集合时是否重新计算LINQ条件,这是否会失败?如果密钥已经存在,我原来的if语句意图不会导致异常。
答案 0 :(得分:3)
在这种情况下,重构版本中由IEnumerable
返回的Where
将在迭代时懒散地生成值,从而实现您想要的效果。在这种情况下,包含ToList
调用的建议不你想要什么,因为这会使IEnumerable
成为一个集合,你很容易受到重复{{ 1 {}在classIds
集合中。
要记住的是,m_ClassMappings
调用中的谓词,即Where
,将根据classId => !m_TAAddressDefinitions.ContainsKey(classId)
的迭代结果为每个项目进行评估。因此,在Resharper建议的版本中,如果IEnumerable
中存在重复值,则遇到的第一个值将添加到m_ClassMappings
但是当到达下一个副本时,m_TAAddressDefinitions
谓词将返回false,因为该值以前已添加到Dictionary中。
答案 1 :(得分:2)
建议的更改不会改变逻辑。 lambda表达式classId => !m_TAAddressDefinitions.ContainsKey(classId)
将被编译成一个匿名类,您的字典将被传入。由于这是一个引用而不是它的副本,对字典的更改将反映在评估中。
这个概念被称为 closure 。有关更深入的信息,请参阅Jon Skeet的答案:What are 'closures' in .NET?。他的帖子The Beauty of Closures是一个非常代码阅读。
答案 2 :(得分:0)
您不是修改m_ClassMappings
而是修改m_TAAddressDefinitions
,因此不会引发CollectionModifiedException
。
更好地将其重写为
m_ClassMappings[taAddressDefinition.Key]
.Where(classId => !m_TAAddressDefinitions.ContainsKey(classId)))
.ToList()
.ForEach(
classId => m_TAAddressDefinitions.Add(classId, taAddressDefinition.Value));