1

デリゲート ステートメントで i を参照することは有効ですか?

targets[i].PingReply = e.Reply;

で定義された同じ配列要素を参照しますか?

pingSender.SendAsync( targets[i].IPAddress, targets[i].Timeout);

または、デリゲートが発火したときに i の値に何か別のことが起こっていますか? PingCompleted で i=3 の範囲外のインデックスを取得しているため、理由がわかりません。

public void Ping(PingTest[] targets)
{
    var finished = new CountdownEvent(targets.Count());
    for (int i = 0; i < targets.Count(); i++)
    {
        finished.AddCount();
        var pingSender = new Ping();
        pingSender.PingCompleted += (sender, e) =>
                                        {
                                            targets[i].PingReply = e.Reply;
                                            finished.Signal();
                                        };
        pingSender.SendAsync(targets[i].IPAddress, targets[i].Timeout);
    }
    finished.Signal();
    finished.Wait();
}

これが呼び出しです...

var pingTests = new PingTest[]
                    {
                        new PingTest("Router", new IPAddress(new byte[] {192, 168, 1, 8}), 2),
                        new PingTest("Exchange", new IPAddress(new byte[] {192, 168, 1, 78}), 3),
                        new PingTest("SQL", new IPAddress(new byte[] {192, 168, 1, 99}), 3)
                    };
netwrkService.Ping(pingTests);
4

3 に答える 3

5

このプログラム フラグメントに何を期待しますか?

int i = 0;
Func<int> f = ()=>i;
i = 3;
Console.WriteLine(f());

それを試してみてください。それはあなたがすべきだと思うことをしましたか?


無名関数は、変数が過去に持っていた値ではなく、変数に対して閉じられます。ラムダを呼び出しているとき、ループ変数には、デリゲートを作成したときの値がありません。

詳細については、 http://ericlippert.com/2009/11/12/closing-over-the-loop-variable-considered-harmful-part-one/を参照してください。

于 2013-03-23T02:45:50.030 に答える
3

C# では、クロージャは値ではなく変数を閉じます。ループにforは 1 つだけの variableiがあり、各PingCompletedハンドラーが の値を読み取ると、ハンドラーが接続されたときの backの値ではなく、その単一の変数の現在のi値が取得されます。したがって、ループが終了した後にハンドラーが実行されると、 3 になります。ifori

この問題を解決するには、ループ内で宣言されている別の変数に の値をコピーし、iその新しい変数を使用するようにハンドラーを変更します。

for (int i = 0; i < targets.Count(); i++)
{
    ...
    int j = i;
    pingSender.PingCompleted += (sender, e) =>
                                    {
                                        targets[j].PingReply = e.Reply; // <== j, not i
                                        finished.Signal();
                                    };

ループ内で変数を宣言すると、反復ごとに変数の新しいインスタンスが論理的に作成されます。したがって、PingCompletedハンドラは の異なるインスタンスを参照するようになりj、それぞれがそのハンドラの正しいインデックスを保持します。

于 2013-03-23T02:46:21.150 に答える
0

PingCompleted が呼び出される前にループが終了するため、PingCompleted が初めて呼び出されると、i は 3 にインクリメントされます。次に、さらに2回呼び出されます(forループがすでに終了していて、iをもうインクリメントしていないため、forループがiで初期化したため、3のままです。

なぜ私がいつも範囲外なのか理解できますか? インデックス i は、最初の反復で 0 から 1 になり、2 番目の反復で 1 から 2 になり、最後の反復で 2 から 3 になります (最後に 3 が設定されます)。すべての反復が完了すると、PingCompleted への最初の呼び出しが、既に 3 にあるインデックスで呼び出されます。

于 2013-03-23T01:55:22.123 に答える