Решарпер: скрытое закрытие: это

Я получаю это предупреждение («Захваченное закрытие Implicity: 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, который является преднамеренным. Это на самом деле проблема, и как бы я решил ее, если она есть?

РЕДАКТИРОВАТЬ: предупреждение находится на первой лямбда (событие тайм-аут).

65 голосов | спросил Aaron Maslen 18 +04002012-10-18T15:48:03+04:00312012bEurope/MoscowThu, 18 Oct 2012 15:48:03 +0400 2012, 15:48:03

2 ответа


0

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

Проблема в том, что у меня есть два лямбда-выражения, ссылающихся на поля в родительском объекте: компилятор создает класс с двумя методами и ссылкой на родительский класс (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 используется обеими лямбдами; но теперь, пока ссылка на одну из этих лямбд еще указана, ссылка на объект 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;
        }
    }
ответил Aaron Maslen 18 +04002012-10-18T17:47:25+04:00312012bEurope/MoscowThu, 18 Oct 2012 17:47:25 +0400 2012, 17:47:25
0

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

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

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

ответил SLaks 18 +04002012-10-18T15:49:42+04:00312012bEurope/MoscowThu, 18 Oct 2012 15:49:42 +0400 2012, 15:49:42

Похожие вопросы

Популярные теги

security × 330linux × 316macos × 2827 × 268performance × 244command-line × 241sql-server × 235joomla-3.x × 222java × 189c++ × 186windows × 180cisco × 168bash × 158c# × 142gmail × 139arduino-uno × 139javascript × 134ssh × 133seo × 132mysql × 132