2012-01-25 9 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)); 
     } 
    } 
} 

はここでそれぞれが Clientsocket含まLazyAsync

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

あるので、私はそれはほとんどCloneすることはできません。 問題は、Invokec.WriteMessageにすると、実行が遅れるため、通常はリストの最初のカップルでは起動せず、実際には最後のアイテムだけが一斉に起動することがあります。

これは、Invokeが実際に呼び出される前に変更される参照であるCと関係があることは知っていますが、これを避ける方法はありますか?

一般的に、for(int i=0 etcループを実行してもこの問題は解決されないようです。

誰でも私がこれをどのように修正することができますか?

CloneClientを忘れないでください。

+3

誰かを指すように起こるものは何でも、Cに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)); 
     } 
    } 
} 

がためのWeb検索を行います。

+0

ありがとう、このような簡単な解決策のように思えますが、私はそれを考えなかったので、ちょっとばかげています。私は、同じことがローカル変数でも起こると仮定したと思います。ありがとう。 –

1

あなたの疑惑は正しいです:変数cはラムダ式によって取り込まれますが、後で評価されることはありません。

ラムダ変数内でループ変数を使用すると、ループ変数がループ外にスコープされ、ループの各繰り返しではスコープされないため、このエラーが発生します。

あなたは、foreachループに新しいローカル変数を作成することによってこの問題を回避それにcを割り当て、その後、ラムダ式にその新しいローカル変数を渡すことができます。ここでは

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

私はリンクをありがとう、それはいくつかのものを読み上げてうれしいでしょう。あなたが参照しているものがわからない灰色の線の1つ:p –

1

呼び出しが起こる前に、foreachループによって再割り当てさcを避けるために、ローカル変数にCを設定し、その上LazyAsync.Invokeを呼び出してみてください。 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

答えをありがとう。 –