2012-01-25 79 views
-1

好的,所以我在這裏遇到了一些問題。 這是循環。c#foreach with Action.BeginInvoke

lock (ClientLocker) 
{ 
    Trace.WriteLine("#WriteAll: " + sm.Header); 
    foreach (Client c in Clients) 
    { 
     if (c.LoggedIn) 
     { 
      Trace.WriteLine("#TryWriteTo[" + c.Id + "](" + sm.Header + ")"); 
      LazyAsync.Invoke(() => c.WriteMessage(sm)); 
     } 
    } 
} 

這裏是LazyAsync

public static class LazyAsync 
{ 
    public static void Invoke(Action a) 
    { 
     a.BeginInvoke(a.EndInvoke, null); 
    } 
} 

每個Client包含socket,所以我不能幾乎Clone它。 問題是,當我執行Invokec.WriteMessage時,由於執行延遲,它通常不會觸發列表中的第一對,並且有時實際上只會激發最後一個項目中的大部分。

我知道這與c是在Invoke實際被調用之前更改的引用有關,但有沒有辦法避免這種情況?

做一般的for(int i=0 etc循環似乎並沒有解決這個問題。

任何人有任何想法,我怎麼能解決這個問題?

請記住,不能CloneClient

+3

有人的WriteMessage幾乎每天都會問這個問題。有關此問題的一些鏈接和討論,請參閱http://stackoverflow.com/questions/8898925/is-there-a-reason-for-cs-reuse-of-the-variable-in-a-foreach/8899347#8899347 。 –

+0

我做了一個搜索,但沒有找到任何東西。不像我沒試過。 –

+0

確實;這是一個很難找到的。這就是爲什麼幾乎每天都會問這個問題的原因。除非您真的從resharper獲得「訪問修改後的關閉」警告,否則沒有理由爲什麼您會知道要搜索哪些關鍵字。 –

回答

5

複製你c局部變量是這樣的:

lock (ClientLocker) 
{ 
    Trace.WriteLine("#WriteAll: " + sm.Header); 
    foreach (Client c in Clients) 
    { 
     if (c.LoggedIn) 
     { 
      Client localC = c; 
      Trace.WriteLine("#TryWriteTo[" + c.Id + "](" + sm.Header + ")"); 
      LazyAsync.Invoke(() => localC.WriteMessage(sm)); 
     } 
    } 
} 

做一個網絡搜索:「進入修改封」,如果你想獲得更多的信息。

+0

謝謝,這似乎是一個這樣簡單的解決方案,我覺得有點愚蠢,我沒有想到它。我想我只是假設局部變量也會發生同樣的情況。謝謝。 –

1

您的懷疑是正確的:變量c被lambda表達式捕獲,但直到後期才被計算。

無論何時在lambda表達式中使用循環變量時都會彈出錯誤的風格,因爲循環變量的範圍在循環外部,而不是循環的每次迭代。

您可以解決此通過創建在foreach循環新的本地變量,分配c給它,然後傳遞新的局部變量到lambda表達式:

lock (ClientLocker) 
{ 
    Trace.WriteLine("#WriteAll: " + sm.Header); 
    foreach (Client c in Clients) 
    { 
     if (c.LoggedIn) 
     { 
      Trace.WriteLine("#TryWriteTo[" + c.Id + "](" + sm.Header + ")"); 

      Client copyOfC = c; 
      LazyAsync.Invoke(() => copyOfC.WriteMessage(sm)); 
     } 
    } 
} 

這裏有一些相關的StackOverflow帖子:

+0

我很欣賞這些鏈接,因此閱讀更多內容會很好。其中一條灰線,您不確定您所參考的內容:p –

1

嘗試設置C到一個局部變量,並呼籲LazyAsync.Invoke上,爲了避免c將要由foreach循環中調用發生之前重新分配。當LazyAsync.Invoke做c.WriteMessage,它呼籲任何Ç發生到現在指向,而不是它是什麼時候LazyAsync.Invoke(()=> c.WriteMessage(SM))進行了評估

foreach (Client c in Clients) 
{ 
    if (c.LoggedIn) 
    { 
     Trace.WriteLine("#TryWriteTo[" + c.Id + "](" + sm.Header + ")"); 

     Client client = c; 
     LazyAsync.Invoke(() => client.WriteMessage(sm)); 
    } 
} 
+0

感謝您的回答。 –