Resharper: неявно захваченное закрытие: это


Я получаю это предупреждение ("Implicity captured closure: this") от Resharper: означает ли это, что каким-то образом этот код захватывает весь охватывающий объект?

    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, который является преднамеренным. Это действительно проблема, и как бы я ее решил, если это так?

EDIT: предупреждение находится на первой лямбде (событие тайм-аута).

2 65

2 ответа:

кажется, что проблема не в линии, я думаю.

проблема в том, что у меня есть два лямбда-поля ссылки в Родительском объекте: компилятор генерирует класс с двумя методами и ссылкой на родительский класс (this).

Я думаю, что это будет проблемой, потому что ссылка на this потенциально может остаться в объекте TaskCompletionSource, не позволяя ему быть GCed. По крайней мере, это то, что я нашел по этому вопросу предлагает.

сгенерированный класс будет выглядеть примерно так (очевидно, имена будут разными и непроизносимыми):

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 используется обеими лямбдами; но теперь, пока ссылка на одну из этих лямбд все еще ссылается на ссылку на

похоже _response поля в вашем классе.

ссылка _response от лямбда захвата this в закрытии, и будет читать this._response когда лямбда-выражения.

чтобы предотвратить это, вы можете скопировать _response к локальной переменной и использовать ее вместо этого. Обратите внимание, что это заставит его использовать настоящее стоимостью _response, а не его конечной стоимости.