65

Resharper からこの警告 ("Implicity Capture Closure: this") が表示されます: これは、このコードが囲んでいるオブジェクト全体を何らかの方法でキャプチャしていることを意味しますか?

    internal Timer Timeout = new Timer
                            {
                                Enabled = false,
                                AutoReset = false
                            };
    public Task<Response> ResponseTask
    {
        get
        {
            var tcs = new TaskCompletionSource<Response>();

            Timeout.Elapsed += (e, a) => tcs.SetException(new TimeoutException("Timeout at " + a.SignalTime));

            if (_response != null) tcs.SetResult(_response);
            else ResponseHandler += r => tcs.SetResult(_response);
            return tcs.Task;
        }
    }

どのように、またはなぜそうしているのかはわかりません-キャプチャする必要がある唯一の変数は、意図的なTaskCompletionSourceです。これは実際に問題ですか? 問題がある場合、どうすれば解決できますか?

編集: 警告は最初のラムダ (タイムアウト イベント) にあります。

4

2 に答える 2

27

問題は私が思う線ではないようです。

問題は、親オブジェクトにフィールドを参照する2つのラムダがあることです。コンパイラーは、2つのメソッドと親クラス(this)への参照を持つクラスを生成します。

thisへの参照がTaskCompletionSourceオブジェクト内にとどまり、GCされない可能性があるため、これは問題になると思います。少なくとも、この問題で私が見つけたのはそれです。

生成されたクラスは次のようになります(明らかに名前は異なり、発音できません)。

class GeneratedClass {
    Request _this;
    TaskCompletionSource tcs;

    public lambda1 (Object e, ElapsedEventArgs a) {
        tcs.SetException(new TimeoutException("Timeout at " + a.SignalTime));
    }

    public lambda2 () {
        tcs.SetResult(_this._response);
    }
}

TaskCompletionSourceコンパイラがこれを行う理由は、おそらく両方のラムダで使用されるため、効率であると思います。ただし、これらのラムダの1つへの参照が参照されている限り、Requestオブジェクトへの参照も維持されます。

しかし、私はまだこの問題を回避する方法を理解することに近づいていません。

編集:私がそれを書いていたとき、私は明らかにこれについて考えていませんでした。私は次のように方法を変更することで問題を解決しました:

    public Task<Response> TaskResponse
    {
        get
        {
            var tcs = new TaskCompletionSource<Response>();

            Timeout.Elapsed += (e, a) => tcs.SetException(new TimeoutException("Timeout at " + a.SignalTime));

            if (_response != null) tcs.SetResult(_response);
            else ResponseHandler += tcs.SetResult; //The event passes an object of type Response (derp) which is then assigned to the _response field.
            return tcs.Task;
        }
    }
于 2012-10-18T13:47:25.137 に答える
13

_responseあなたのクラスのフィールドのようです。

ラムダからの参照はクロージャーで_responseキャプチャされ、ラムダの実行時に読み取られます。thisthis._response

これを防ぐ_responseには、ローカル変数にコピーして代わりに使用します。これにより、最終的な値ではなく現在の値が使用されることに注意してください。_response

于 2012-10-18T11:49:42.183 に答える