Resharperからこの警告(「Impplicityキャプチャされたクロージャ:これ」)を受け取っています。
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であり、これは意図的なものです。これは実際に問題なのでしょうか?それが解決した場合、どうすれば解決できますか?
編集:警告は最初のラムダ(タイムアウトイベント)にあります。
問題は、私がそう思うと思う線ではないようです。
問題は、親オブジェクトのフィールドを参照する2つのラムダがあることです。コンパイラは、2つのメソッドと親クラスへの参照(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
は両方のラムダで使用されるからです。しかし、これらのラムダの1つへの参照がまだ参照されている限り、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;
}
}
_response
はクラスのフィールドのようです。
ラムダから_response
を参照すると、クロージャーでthis
がキャプチャされ、ラムダの実行時にthis._response
が読み取られます。
これを防ぐには、_response
をローカル変数にコピーして、代わりに使用します。その結果、最終的な値ではなく_response
の-current値が使用されることに注意してください。