我从Resharper得到这个警告(“Implicity capture closure: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(Timeout事件)上。
答案 0 :(得分:27)
似乎问题不在于我认为的问题。
问题是我有两个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
对象的引用。
尽管如此,我仍然无法确定如何避免这个问题。
编辑:当我写作时,我显然没有想到这一点。我通过改变这样的方法解决了这个问题: 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;
}
}
答案 1 :(得分:11)
看起来_response
是您班级中的一个字段。
从lambda引用_response
将捕获闭包中的this
,并在lambda执行时读取this._response
。
为了防止这种情况,您可以将_response
复制到本地变量并使用它。请注意,这将导致它使用_response
的当前值,而不是其最终值。