最近、ラムダ式と変数キャプチャで奇妙なことが起こりました。コードは、.NET 4.5(VS2012)を使用するWPF/MVVMアプリケーションでした。コンストラクタへの呼び出しでラムダをフィールド変数にキャプチャする必要があります
public class MyViewModel : ViewModelBase
{
public MyViewModel(Action menuCallback)
{
MyCommand = new RelayCommand(menuCallback);
}
public MyViewModel(Func<ViewModelBase> viewModelCreator)
// I also tried calling the other constructor, but the result was the same
// : this(() => SetMainContent(viewModelCreator())
{
Action action =() => SetMainContent(viewModelCreator());
MyCommand = new RelayCommand(action);
}
public ICommand MyCommand { get; private set; }
}
:私は本質的には(このコマンドはその後、私の見解では、メニュー項目にバインドされます)のセットアップに
をRelayCommand
ためのコールバックを私のviewmodelの異なるコンストラクタを使用していた、私は次のコードを持っていましたその後、使用して上記のインスタンスを作成:
// From some other viewmodel's code:
new MyViewModel(() => new SomeViewModel());
new MyViewModel(() => new SomeOtherViewModel());
次いで、これらは、WPFのメニューに結合させた - 各メニュー項目は、そのデータコンテキストとしてMyViewModelインスタンスを持っていました。奇妙なことは、メニューは一度しか働かないということでした。私が試した項目のどれに関係なく、それは適切なFunc<ViewModelBase>
を呼ぶでしょう - しかし、1回だけ。別のメニュー項目または同じ項目を再度選択しようとすると、は単に機能しませんでした。何も呼び出されず、VSデバッグ出力にエラーがないと出力されません。
私は、ループ内の変数キャプチャの問題を認識してるので、私はので、私のVMに変更し、この問題が関係していたことを推測をした:
public class MyViewModel : ViewModelBase
{
public MyViewModel(Action buttonCallback)
{
MyCommand = new RelayCommand(buttonCallback);
}
private Func<ViewModelBase> _creator;
public MyViewModel(Func<ViewModelBase> viewModelCreator)
{
// Store the Func<> to a field and use that in the Action lambda
_creator = viewModelCreator;
var action =() => SetMainContent(_creator());
MyCommand = new RelayCommand(action);
}
public ICommand MyCommand { get; private set; }
}
をし、同じように呼ばれます。今はすべてが正常に動作します。
は楽しみのためだけに、私もMyViewModel
コンストラクタの適切なAction
外作成することによって、全体Func<ViewModelBase>
コンストラクタ周り働い:
// This code also works, even without the _creator field in MyViewModel
new MyViewModel(() => SetMainContent(new SomeViewModel()));
new MyViewModel(() => SetMainContent(new SomeOtherViewModel()));
をだから私は、それが働いて得ることができたが、なぜそれ私はまだ興味このように動作します。コンパイラがコンストラクタ内のFunc<ViewModelBase>
を正しくキャプチャしないのはなぜですか?
2つのアプローチで生成されたILの違いを確認しましたか?それはいくつかのヒントを与えるかもしれません。 – nicodemus13
'SetMainContent'が' ViewModelBase'クラスにある場合、どのようにラムダで最後のコード例でそれを呼び出すのですか? – Pat
SetMainContentは、(MVVMLightからの)メッセージを使用して、メインウィンドウのviewmodelにviewmodelインスタンスを送信します。それはUIにレンダリングされるContentプロパティにそれを割り当てます。私はこの問題を呈するより完全なコード例を考え出すつもりです –