65

我从 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 (超时事件)上。

4

2 回答 2

27

似乎问题不是我认为的问题。

问题是我有两个 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);
    }
}

我想编译器这样做的原因可能是效率,因为TaskCompletionSource两个 lambdas 都使用了;但是现在只要仍然引用了对其中一个 lambda 的引用,对Requestobject 的引用也将保持不变。

不过,我仍然没有更接近弄清楚如何避免这个问题。

编辑:当我写它时,我显然没有考虑到这一点。我通过改变这样的方法解决了这个问题:

    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从 lambda引用将this在闭包中捕获,并this._response在 lambda 执行时读取。

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

于 2012-10-18T11:49:42.183 回答