Question About Passing Parameters To Methods That Return Lambdas

て烟熏妆下的殇ゞ 提交于 2021-01-28 10:34:03

问题


(with C# 3.0 and VS 2008).

Doing MVVM WPF stuff you often write properties like this:

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

Doing TDD I often end up writing tests such as:

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

I experimented with a method like this:

public class MyTestMethods{

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

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

    }
} 

So that I could write my test:

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

        bool eventRaised = false;
        string propName = "";

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

        // etc...
}

But VS2008 told me that eventRaised would always be false.

I thought maybe changing MakePropertyChangedHandler to use ref parameters would work

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

        return // lambda...

    }

but VisualStudio tells me 'Cannot use ref or out parameter 'x' inside an anonymous method body'.

Can anyone tell me if it's possible to write a working method like MakePropertyChangedHandler and if not, how come?


回答1:


It's not possible to give ref to a lambda, because proper life cycle management can't be ensured. When the compiler encounters a closure (lambda using outer scope variable), it

  1. wraps all captured local variables in an anonymous object,
  2. creates instance of this object instead of allocating the variables on the stack,
  3. makes the lambda code a method of this object and
  4. returns a delegate to this object and method

(details might be a bit different, but that's the principle). This way the captured variables exist as long as the delegate does.

However when compiling the function higher up the stack, the compiler does not know this and so it allocates variables on the stack. That's faster, but the variables only exist until the function returns. Since the closure can live longer than that (in your case it won't, but the compiler can't know), the closure can't refer to the stack variable.

What you can do is create an object with reference semantics (which lives as long as it's refered to) and give it to the closure. So if you create:

class BoolHolder {
    public bool value;
};

pass BoolHolder to the lambda and in the lambda do

boolHolder.value = true;

than you'll see the change outside.



来源:https://stackoverflow.com/questions/4817352/question-about-passing-parameters-to-methods-that-return-lambdas

易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!