关于将参数传递给返回Lambda的方法的问题

时间:2011-01-27 14:09:43

标签: c# lambda parameter-passing

(使用C#3.0和VS 2008)。

做MVVM WPF的东西你经常写这样的属性:

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

做TDD我经常最终编写测试,例如:

[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...
}

我尝试了这样的方法:

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总是假的。

我想也许改变MakePropertyChangedHandler使用ref参数会起作用

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

        return // lambda...

    }

但VisualStudio告诉我'不能在匿名方法体内使用ref或out参数'x'。

有人能告诉我是否可以编写像MakePropertyChangedHandler这样的工作方法,如果没有,怎么回事?

1 个答案:

答案 0 :(得分:2)

不可能将ref赋予lambda,因为无法确保正确的生命周期管理。当编译器遇到闭包(使用外部作用域变量的lambda)时,它

  1. 将所有捕获的局部变量包装在匿名对象中,
  2. 创建此对象的实例,而不是在堆栈上分配变量,
  3. 使lambda代码成为此对象的方法和
  4. 将委托返回给此对象和方法
  5. (细节可能有点不同,但这是原则)。这样,只要代理人就可以存在捕获的变量。

    然而,当编译函数在堆栈的更高位置时,编译器不知道这一点,因此它在堆栈上分配变量。这更快,但变量只存在,直到函数返回。由于闭包可以活得比那个(在你的情况下它不会,但编译器无法知道),闭包不能引用堆栈变量。

    你可以做的是创建一个带引用语义的对象(只要它被引用就存在)并将它交给闭包。所以如果你创建:

    class BoolHolder {
        public bool value;
    };
    

    将BoolHolder传递给lambda并在lambda中传递

    boolHolder.value = true;
    

    比你看到外面的变化。