【问题标题】:Resharper: Implicitly captured closure: thisResharper:隐式捕获的闭包:this
【发布时间】:2012-10-08 20:48:54
【问题描述】:

我收到了来自 Resharper 的警告(“隐式捕获闭包: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,这是有意的。这实际上是一个问题吗?如果是,我将如何解决它?

编辑:警告出现在第一个 lambda(超时事件)上。

【问题讨论】:

标签: c# .net resharper


【解决方案1】:

看来问题不是我想的那条线。

问题是我有两个 lambdas 引用父对象中的字段:编译器生成一个具有两个方法和一个对父类 (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);
    }
}

我想编译器这样做的原因可能是效率,因为两个 lambdas 都使用了TaskCompletionSource;但现在只要仍然引用了对其中一个 lambda 的引用,对 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;
        }
    }

【讨论】:

  • 将 if 语句或 Timeout 事件行移动到单独的方法会消除警告,但我觉得这不是一个理想的解决方案。
【解决方案2】:

看起来_response 是您班级中的一个字段。

从 lambda 引用 _response 将在闭包中捕获 this,并在 lambda 执行时读取 this._response

为防止这种情况,您可以将_response 复制到一个局部变量并使用它。请注意,这将导致它使用_response当前 值,而不是其最终值。

【讨论】:

  • 我应该提到,警告是在第一个 lambda(超时事件)而不是第二个。此外,您所描述的是 _response 的捕获,而不是“this”引用
  • @user1447072:在答案中将_response 替换为Timeout :)
  • 它不应该捕获 Timeout 变量吗? (它没有在任何地方的 lambda 中引用)。
  • @AaronMaslen#1:不。Lambda 捕获变量或参数,例如this_response 真的是this._response,它捕获了this
  • 据我记得,我认为它不会捕获 this - 从某种意义上说,编译器生成的类将只包含一个类型为 Response 的字段,这是一个参考到this._response。不过,必须检查拆卸才能确认。无论如何,这是一个有争议的问题,因为这条线不是导致警告的线。
猜你喜欢
  • 1970-01-01
  • 2013-09-22
  • 2013-06-10
  • 2012-11-17
  • 2016-08-05
  • 1970-01-01
  • 2017-11-27
  • 1970-01-01
  • 2020-09-24
相关资源
最近更新 更多