Вопрос о передаче параметров в методы, которые возвращают лямбда-выражения - PullRequest
1 голос
/ 27 января 2011

(с 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 Ответ

2 голосов
/ 27 января 2011

Невозможно дать ссылку на лямбду, потому что правильное управление жизненным циклом не может быть обеспечено. Когда компилятор встречает замыкание (лямбда-выражение, использующее внешнюю переменную области видимости), оно

  1. упаковывает все захваченные локальные переменные в анонимный объект,
  2. создает экземпляр этого объекта вместо размещения переменных в стеке,
  3. делает лямбда-код методом этого объекта, а
  4. возвращает делегата этому объекту и методу

(детали могут немного отличаться, но это принцип). Таким образом, захваченные переменные существуют до тех пор, пока существует делегат.

Однако при компиляции функции выше стека компилятор не знает этого и поэтому выделяет переменные в стеке. Это быстрее, но переменные существуют только до возврата функции. Поскольку замыкание может жить дольше, чем это (в вашем случае это не так, но компилятор не может знать), замыкание не может ссылаться на переменную стека.

Что вы можете сделать, это создать объект со ссылочной семантикой (которая живет столько времени, на которую он ссылается) и передать его закрытию. Итак, если вы создадите:

class BoolHolder {
    public bool value;
};

передать BoolHolder в лямбду и в лямбду сделать

boolHolder.value = true;

чем вы увидите изменения снаружи.

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...