2011-06-24 64 views
21

鑑於以下(大量編輯,僞)代碼:爲什麼resharper建議「在數組中包裝變量」來訪問修改後的閉包警告?

int count = 0; 
thing.Stub(m => m.AddBlah()).WhenCalled(o => count++); 
thing.Stub(m => m.RemoveBlah()).WhenCalled(o => count--); 

DoStuff(thing); 

Assert.AreEqual(1, count); 

ReSharper的提供了一個警告,對數 - 「進入修改關閉」。我明白爲什麼我會得到這個警告(count變量在兩個不同的lambdas中被修改,並可能有不希望的語義),但我不明白ReSharper的建議:「在數組中包裝局部變量」。如果我讓ReSharper這樣做,我會得到:

int count[] = { 0 }; 
thing.Stub(m => m.AddBlah()).WhenCalled(o => count[0]++); 
thing.Stub(m => m.RemoveBlah()).WhenCalled(o => count[0]--); 

DoStuff(thing); 

Assert.AreEqual(1, count[0]); 

並沒有警告。

爲什麼使用數組安全?

+0

您看到過哪個版本的ReSharper? –

回答

2

這是因爲2種類型不同。 int是一個Value類型,而該數組是一個Reference類型。這意味着int在堆棧上,而數組的指針在堆棧上。

當你更新一個值類型時,它更新那堆棧內存。另一方面,引用類型將單獨存儲堆棧內存並修改它指向的內容。

Resharper不會抱怨數組,因爲2種不同的Lambda方法正在內存周圍創建一個指向哪裏更新值的閉包。兩個Lambdas都獲得相同的地址,並且不會更改原始地址。

+1

但是,當然,關閉一個int變量的lambda表達式也都指向內存中的同一個點,所以有什麼區別?代碼在兩種情況下都執行相同的操作,因此執行方面沒有區別。但我不明白爲什麼resharper以不同的方式對待他們。 – citizenmatt

+0

@citizenmatt,想想當方法已經返回的情況下,ReSharper不知道lambda只會在你調用DoStuff(thing)的時候運行 –

+0

這不是正確的原因,請看另一個答案。在lambda內部捕獲引用或值變量沒有區別,這就是爲什麼行爲是相同的原因。 http://stackoverflow.com/questions/5438307/detailed-explanation-of-variable-capture-in-closures –

12

我注意到ReSharper自己也有同樣的事情,並且還想知道爲什麼當值被包裝在一個數組中時沒有發出警告。這裏的其他答案不幸是錯誤的,似乎誤解了如何實現閉包,所以我想我會試圖解釋(我認爲是)重構背後的基本原理。

正如你所看到的,無論是否包含數組,封裝的結果都是一樣的,因此重構並不真正「修復」任何東西,並且在應用後存在訪問普通修改閉包時可能遇到的問題改變。但是,自從count數組本身沒有被修改(僅其內容)後,「訪問修改的關閉」警告不再相關。

的變化並沒有真正的問題在任何更加明顯(至少在我看來),所以它似乎這一建議實質上是告訴ReSharper的忽略這個問題,而不必求助於相當混亂// ReSharper disable AccessToModifiedClosure機制來抑制錯誤。

+0

這似乎是合理的,這是他們的理由,因爲我想不出任何其他原因這個代碼「修復」。如果這確實是他們實現這個目標的原因,那麼他們認爲在一個元素數組中包含一個變量的所有訪問將不會比僅在使用該註釋時僅使用註釋禁用該警告更麻煩。 – jam40jeff

相關問題