2009-11-06 120 views
34

我有以下代碼:ReSharper的警告 - 訪問修改關閉

string acctStatus = account.AccountStatus.ToString(); 
if (!SettableStatuses().Any(status => status == acctStatus)) 
    acctStatus = ACCOUNTSTATUS.Pending.ToString(); 

注意account.AccountStatus是類型ACCOUNTSTATUS的枚舉。在第二行中,ReSharper向我發出acctStatus的「訪問修改的關閉」警告。當我做推薦的操作,複製到本地變量,它修改代碼如下:

string acctStatus = realAccount.AccountStatus.ToString(); 
string s = acctStatus; 
if (!SettableStatuses().Any(status => status == s)) 
    acctStatus = ACCOUNTSTATUS.Pending.ToString(); 

這是爲什麼好還是最好什麼我原本?

編輯

委員會還建議在陣列裹局部變量,主要生產:

string[] acctStatus = {realAccount.AccountStatus.ToString()}; 
if (!SettableStatuses().Any(status => status == acctStatus[0])) 
    acctStatus[0] = ACCOUNTSTATUS.Pending.ToString(); 

這似乎是徹頭徹尾的古怪的我。

+0

檢查此問題並接受答案,可能有幫助。 http://stackoverflow.com/questions/235455/access-to-modified-closure – Chuck 2009-11-06 15:59:53

回答

34

警告的原因是在循環內部,您可能正在訪問正在更改的變量。然而,在這個非循環的上下文中,「修復」並不是真的爲你做任何事情。

想象一下,你有一個FOR循環,if是在它內部,並且字符串聲明在它之外。在這種情況下,錯誤會正確地識別抓取不穩定參考的問題。

你不想要的一個例子:

string acctStatus 

foreach(...) 
{ 
    acctStatus = account.AccountStatus[...].ToString(); 
    if (!SettableStatuses().Any(status => status == acctStatus)) 
     acctStatus = ACCOUNTSTATUS.Pending.ToString(); 
} 

的問題是,在關閉將搶acctStatus的引用,但每次循環迭代將會改變該值。在情況下,它會更好:

foreach(...) 
{ 
    string acctStatus = account.AccountStatus[...].ToString(); 
    if (!SettableStatuses().Any(status => status == acctStatus)) 
     acctStatus = ACCOUNTSTATUS.Pending.ToString(); 
} 

由於變量的情況下是循環,一個新的實例將會每次被創建,因爲我們已經搬進了當地的環境裏面的變量(for循環)。

該建議聽起來像是Resharper解析該代碼時的一個錯誤。但是,在很多情況下,這是一個有效的關注點(例如,第一個示例,儘管它在封閉中被捕獲,但引用正在改變)。

我的經驗法則是,如果有疑問做一個本地。

這是一個現實世界的例子,我被咬傷:

 menu.MenuItems.Clear(); 
     HistoryItem[] crumbs = policyTree.Crumbs.GetCrumbs(nodeType); 

     for (int i = crumbs.Length - 1; i > -1; i--) //Run through items backwards. 
     { 
      HistoryItem crumb = crumbs[i]; 
      NodeType type = nodeType; //Local to capture type. 
      MenuItem menuItem = new MenuItem(crumb.MenuText); 
      menuItem.Click += (s, e) => NavigateToRecord(crumb.ItemGuid, type); 
      menu.MenuItems.Add(menuItem); 
     } 

注意,我抓住的NodeType類型的地方,注意節點類型,並HistoryItem crumb.ItemGuid,不屑[I] .ItemGuid。這可以確保我的關閉不會引用將會更改的項目。

在使用本地化之前,事件會觸發當前值,而不是我預期的捕獲值。

+0

有很多意義,謝謝! – 2009-11-06 16:06:13

+1

「問題在於閉包將獲取對acctStatus的引用,但是每次循環迭代都會更改該值」 - 實際上,只要「Any」謂詞實際上它總是「account.AccountStatus [...]。ToString()」評估它(因爲'Any'在返回之前遍歷枚舉值),所以我不確定這是一個好例子。我相信你提出的更好的建議會有相同的行爲。 – 2010-10-28 22:38:26

+1

這將是不正確的。不同之處在於,在第一個示例中,只分配了一個變量:acctStatus。 Resharper擔心在實際評估Any()之前,該變量會發生變化。我的建議並沒有改變代碼的行爲(Any()在任何改變發生之前都被評估過),但是明確地說我們希望對於循環的每次迭代都需要一個變量acctStatus的不同實例。如果你注意我後面的例子,那麼Resharper會擔心實際觸發器,因爲我*不會立即評估lambda(減去本地數據)。 – Godeke 2010-10-29 20:39:08