【问题标题】:Question About Passing Parameters To Methods That Return Lambdas关于将参数传递给返回 Lambda 的方法的问题
【发布时间】:2011-06-16 14:01:43
【问题描述】:

(使用 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 这样的工作方法,如果没有,怎么会?

【问题讨论】:

标签: c# lambda parameter-passing


【解决方案1】:

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

  1. 将所有捕获的局部变量包装在一个匿名对象中,
  2. 创建此对象的实例,而不是在堆栈上分配变量,
  3. 使 lambda 代码成为此对象的方法并
  4. 返回一个委托给这个对象和方法

(细节可能有点不同,但这是原则)。这样,只要委托存在,捕获的变量就存在。

然而,当编译函数在栈的上层时,编译器不知道这一点,所以它在栈上分配变量。这更快,但变量只存在于函数返回之前。由于闭包可以比这更长的寿命(在你的情况下它不会,但编译器不知道),闭包不能引用堆栈变量。

您可以做的是创建一个具有引用语义的对象(只要它被引用就一直存在)并将其提供给闭包。因此,如果您创建:

class BoolHolder {
    public bool value;
};

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

boolHolder.value = true;

你会看到外面的变化。

【讨论】:

  • 感谢您的有用解释和解决方法建议。
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2015-10-27
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多