2011-04-02 60 views
6
public virtual void OnRegistrationJoin(RegistrationJoinEventArgs e) 
{ 
    foreach (Mobile member in e.Team) 
    { 
     member.SendMessage(1161, "You join the {0}.", EventFullName); 

     if (e.Team.Count > 1) 
     { 
      Joinees.Remove(member); 
      member.SendMessage(1161, "Your team formation is:"); 

      int i = 0; 

      foreach (Mobile parter in e.Team.Where(partner => partner != member).ToList()) 
      { 
       member.SendMessage(1150, "{0}: {1}.", ++i, partner.Name); 
      } 
     } 
    } 

    Members.Add(e.Team); 
} 

我通過resharper得到「訪問修改後的閉包」警告,我想知道這段代碼有什麼問題,因爲我在內部循環中做的所有事情都是發送消息?C#訪問被修改的閉包

+1

[Access to Modified Closure](http://stackoverflow.com/questions/235455/access-to-modified-closure)和其他幾個可能的重複。 – adrianbanks 2011-04-03 00:13:00

回答

13

的問題是在使用一個封閉的可變:

e.Team.Where(partner => partner != member) 

變量member是直接引用member變量在外部範圍內。雖然在上面的代碼中對此可能沒有問題,但是在多線程上運行代碼或者如果您沒有立即評估Where方法中的lambda(例如,使用IQueryable而不是IEnumerable )。

這是一個問題,原因是C#生成一個方法,然後作爲代理傳遞給Where。該方法需要直接訪問memeber。如果要分配這樣的參考另一變量:

var m = member; 
// ... 
e.Team.Where(partner => partner != m); 

然後C#可以以被稱爲「封閉」構建體「捕獲」該值並把它傳遞給生成的方法。這將確保當member發生變化時,您將它傳遞給Where時的值不會改變。

+0

+1:最終,我認爲對大多數人來說,最大的問題是推遲執行。 – vcsjones 2011-04-02 23:49:34

+0

通過var closureMember = member輕鬆修復。並在LINQ中使用closureMember。 – 2011-04-02 23:50:49

+0

@Femaref - 但它是變化的參考,這是問題,而不是變化的實例。 – codekaizen 2011-04-02 23:53:06

0

我想member.SendMessage可以修改member

這是修改的拉姆達

+0

不是。問題是外部的foreach循環修改了lambda中綁定的'member'變量。 – Femaref 2011-04-02 23:53:00

2

部分resharper抱怨是e.Team.Where(partner => partner != member).ToList(),因爲引用member將被改變。在這種情況下,這不是問題,但在其他情況下,這可能是一個問題。

注意:您不必使用ToList(),這迫使迫切渴望評估IEnumerable<T>。只需遍歷e.Team.Where(partner => partner != member)即可。