![](/img/trans.png)
[英]Does using a LINQ statement in a foreach re-evaluate the statement on each iteration
[英]Will the condition on a LINQ Where-statement re-evaluate during the loop?
我有這個foreach
-loop:
foreach (var classId in m_ClassMappings[taAddressDefinition.Key])
{
if(!m_TAAddressDefinitions.ContainsKey(classId))
{
m_TAAddressDefinitions.Add(classId, taAddressDefinition.Value);
}
}
m_TAAddressDefinitions
是一個IDictionary
,其中classId用作唯一的Key值。
Resharper建議將if
-statement更改為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語句意圖不會導致異常。
在這種情況下,重構版本中Where
返回的IEnumerable
會在迭代時懶散地生成值,從而實現您想要的效果。 包含的建議ToList
調用不是你想在這種情況下,有什么,因為這將兌現的IEnumerable
一個集合,你會容易有被復制classIds
在m_ClassMappings
集合。
需要記住的是, Where
調用中的謂詞,即classId => !m_TAAddressDefinitions.ContainsKey(classId)
將在每個項目中作為迭代IEnumerable
的結果進行評估。 因此,在Resharper建議的版本中,如果m_ClassMappings
存在重復值,則m_ClassMappings
的第一個值將被添加到m_TAAddressDefinitions
但是當到達下一個副本時, Where
謂詞將返回false,因為該值先前已添加到Dictionary中。
建議的更改不會改變邏輯。 lambda表達式classId => !m_TAAddressDefinitions.ContainsKey(classId)
將被編譯成一個匿名類,您的字典將被傳入。由於這是一個引用而不是它的副本,對字典的更改將反映在評估中。
這個概念被稱為閉包 。 有關更深入的信息,請參閱Jon Skeet的回答: .NET中的“閉包”是什么? 。 他的帖子The Beauty of Closures是一個非常代碼閱讀。
你不修改m_ClassMappings
但m_TAAddressDefinitions
,所以CollectionModifiedException
暫不上調。
更好地重寫它
m_ClassMappings[taAddressDefinition.Key]
.Where(classId => !m_TAAddressDefinitions.ContainsKey(classId)))
.ToList()
.ForEach(
classId => m_TAAddressDefinitions.Add(classId, taAddressDefinition.Value));
聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.