2012-10-04 11 views
10

最近、ラムダ式と変数キャプチャで奇妙なことが起こりました。コードは、.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>を正しくキャプチャしないのはなぜですか?

+1

2つのアプローチで生成されたILの違いを確認しましたか?それはいくつかのヒントを与えるかもしれません。 – nicodemus13

+0

'SetMainContent'が' ViewModelBase'クラスにある場合、どのようにラムダで最後のコード例でそれを呼び出すのですか? – Pat

+0

SetMainContentは、(MVVMLightからの)メッセージを使用して、メインウィンドウのviewmodelにviewmodelインスタンスを送信します。それはUIにレンダリングされるContentプロパティにそれを割り当てます。私はこの問題を呈するより完全なコード例を考え出すつもりです –

答えて

2

私はあなたが「、その場合は次のコードでも

public MyViewModel(Func<ViewModelBase> viewModelCreator) 
{ 
    var action =() => { creator = viewModelCreator; SetMainContent(creator()); }; 
    MyCommand = new RelayCommand(action); 
} 

を働くだろう、それは最初の方法を働いていない理由は、あなたが実際にviewModelCreator変数の周りに閉じていないということです推測していますそれを呼び出す結果の周りに再閉鎖する。

私はまだLINQPadのコードで遊んでいますが、あなたと同じ問題が発生しているようには思えません。おそらく、それはRelayCommandに特有のものです。あなたのコードをもっと投稿できますか?

+0

答えをありがとう。 RelayCommandはMVVMの光です:http://bit.ly/QRLCES –

+1

うーん..私の独自の質問からコードを使用して別のコンピュータ(VS2010を使用して)を再現しようとしているが、私はそれを見ていない..私は行く必要があります私の元のコードに戻って、違いが何であるかを見てください(私の質問のコードは単純化されています - それは私があまり単純化して、プロセスの問題を取り除いたかもしれません) –

関連する問題