Подтвердить что ты не робот

Resharper: неявно зафиксированное закрытие: это

Я получаю это предупреждение ( "Implicity capture closed: 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: предупреждение находится на первой лямбда (событие ожидания).

4b9b3361

Ответ 1

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

Проблема заключается в том, что у меня есть два поля привязки lambdas в родительском объекте: компилятор генерирует класс с двумя методами и ссылку на родительский класс (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 используется как lambdas; но теперь, пока ссылка на один из этих lambdas по-прежнему ссылается на ссылку на объект Request, также поддерживается.

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

EDIT: Я, очевидно, не думал об этом, когда писал. Я решил проблему, изменив метод следующим образом:

    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;
        }
    }

Ответ 2

Похоже, что _response - это поле в вашем классе.

Ссылка _response на lambda запишет this в закрытии и будет читать this._response, когда выполняется лямбда.

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