序文:私はここでシナリオを非常に正確に説明しようとしています。TL;DR
バージョンは'ラムダがインスタンスメソッドまたはクロージャにコンパイルされるかどうかをどのように判断するか' ...
WPFプロジェクトでMvvmLightを使用していますが、そのライブラリは最近、WeakReference
に渡されるアクションを保持するためにインスタンスを使用するように変更されましたRelayCommand
。WeakReference
したがって、事実上、を保持しているオブジェクトがどこかにありますAction<T>
。
現在、最新バージョンにアップグレードしてから、一部のコマンドが機能しなくなりました。そして、次のようなコードがありました。
ctor(Guid token)
{
Command = new RelayCommand(x => Messenger.Default.Send(x, token));
}
これにより、クロージャー(正しい用語を使用していない場合は修正してください)クラスが生成されました-次のようになります:
[CompilerGenerated]
private sealed class <>c__DisplayClass4
{
public object token;
public void <.ctor>b__0(ReportType x)
{
Messenger.Default.Send<ReportTypeSelected>(new ReportTypeSelected(X), this.token);
}
}
アクションはインスタンス内に格納されRelayCommand
、インスタンスメソッドにコンパイルされた場合でもクロージャにコンパイルされた場合でも(つまり、「<> DisplayClass」構文を使用して)存続するため、これは以前は正常に機能していました。
ただし、現在はで保持されているため、WeakReference
指定されたラムダがインスタンスメソッドにコンパイルされている場合にのみコードが機能します。これは、クロージャクラスがインスタンス化され、渡され、RelayCommand
事実上即座にガベージコレクションされるためです。つまり、コマンドが使用されるようになったときに、実行するアクションがありませんでした。したがって、上記のコードを変更する必要があります。これを次のように変更すると、たとえば次のようになります。
Guid _token;
ctor(Guid token)
{
_token = token;
Command = new RelayCommand(x => Messenger.Default.Send(x, _token));
}
これにより、コンパイルされたコードは次のようなメンバーになります。
[CompilerGenerated]
private void <.ctor>b__0(ReportType x)
{
Messenger.Default.Send<ReportTypeSelected>(new ReportTypeSelected(X), this._token);
}
これで上記はすべて問題ありません。以前は機能しなかった理由と、変更すると機能するようになった理由を理解しています。しかし、私が残しているのは、私が今書いているコードは、私が知らないコンパイラーの決定に基づいて、スタイル的に異なっている必要があることを意味します。
だから、私の質問は-これはすべての状況で文書化された動作ですか-それともコンパイラの将来の実装に基づいて動作が変わる可能性がありますか?ラムダを使用することを忘れて、常にインスタンスメソッドを?に渡す必要がありますRelayCommand
か?または、アクションが常にインスタンスメンバーにキャッシュされるという規則が必要です。
Action<ReportTypeSelected> _commandAction;
ctor(Guid token)
{
_commandAction = x => Messenger.Default.Send(x, token);
Command = new RelayCommand(_commandAction);
}
どんな背景の読書ポインターもありがたいことに受け入れられます!