2011-01-27 4 views
1

(C#3.0およびVS 2008)。Lambdaを返すメソッドにパラメータを渡すことに関する質問

:私はこのような方法を試し

[Test] 
public void MyPropertyRaisesPropertyChangedWhenChanged(){ 
    var mySUT = CreateSUT(); 

    bool eventRaised = false; 
    string propName = ""; 

    mySUT.PropertyChanged += 
     (s,e)=>{eventRaised = true;propName = e.PropertyName;}; 

    Assert.That(mySUT.MyProperty,Is.False(),"mySUT.MyProperty"); 

    mySUT.MyProperty = true; 

    Assert.That(eventRaised,"eventRaised"); 
    Assert.That(propName, Is.EqualTo("MyProperty"),"propName"); 

    // could check not raised when set same... 
} 

:私は、多くの場合のようなテストを書く終わる

TDDを行う
public bool MyProperty { 
    get{return _myProperty;} 
    set{ 
     if(_myProperty == value)return; 
     _myProperty = value; 
     RaisePropertyChanged("MyProperty"); 
    } 
} 

:あなたは、多くの場合、このような性質を記述しMVVM WPFのものをやって

public class MyTestMethods{ 

    public static PropertyChangedEventHandler MakePropertyChangedHandler(
     bool eventWasRaised, string propertyName){  

     return (s,e)=>{eventWasRaised = true; propertyName = e.PropertyName}; 

    } 
} 

私のテストを書くことができるように:

[Test] 
    public void MyPropertyRaisesPropertyChangedWhenChanged(){ 
     var mySUT = CreateSUT(); 

     bool eventRaised = false; 
     string propName = ""; 

     mySUT.PropertyChanged += 
      MyTestMethods.MakePropertyChangedHandler(eventRaised,propName); 

     // etc... 
} 

しかし、VS2008は私に、eventRaisedは常にfalseと言いました。

私は多分、REFパラメータを使用するMakePropertyChangedHandlerを変更することが

public static PropertyChangedEventHandler MakePropertyChangedHandler(
     ref bool eventWasRaised, ref string propertyName){ 

     return // lambda... 

    } 

を働くだろうと思ったが、VisualStudioをは「匿名メソッドの本体内の 『x』 refまたはoutパラメータを使用することはできません」と言われます。

MakePropertyChangedHandlerのような作業メソッドを書くことができるかどうか誰にでも教えてもらえますか?

+1

これが問題である理由については、http://stackoverflow.com/questions/1365689/cannot-use-ref-or-out-parameter-in-lambda-expressionsを参照してください。 – Justin

+0

@Justin、リンクありがとうございます。 – Grokodile

答えて

2

適切なライフサイクル管理が保証されないため、ラムダを参照することはできません。コンパイラはクロージャ(ラムダ外側のスコープ変数を使用して)に遭遇、それ

  1. ラップ匿名オブジェクト内のすべての捕捉のローカル変数、
  2. は、代わりにスタック上の変数を割り当てるの
  3. このオブジェクトのインスタンスを作成
  4. は(詳細は少し違うかもしれませんが、それは原則です)ラムダコードこのオブジェクトのメソッドと
  5. このオブジェクトにデリゲートを返す
  6. と方法

になります。このようにして、キャプチャされた変数はデリゲートが存在する限り存在します。

しかし、上位の関数をコンパイルするとき、コンパイラはこれを知らないので、スタックに変数を割り当てます。これは高速ですが、変数は関数が戻るまで存在します。クロージャはそれよりも長く続くことができるので(あなたのケースではコンパイラが知ることはできませんが)、クロージャはスタック変数を参照することはできません。

あなたができることは、参照意味論を持つオブジェクトを作成し、それをクロージャに渡すことです。作成したのであれば:あなたは外の変化を見ることができますよりも、ラムダへとラムダで

class BoolHolder { 
    public bool value; 
}; 

パスBoolHolderは

boolHolder.value = true; 

を行います。

+0

有用な説明と回避策の提案をありがとう。 – Grokodile