Reputation: 3933
(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?
Upvotes: 1
Views: 214
Reputation: 76266
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
(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.
Upvotes: 2