Today I had a look at expression trees. There are some really cool solutions using expression trees, which make the world of developers a better place.
I focused on the usage in combination with PropertyChanged where we have a lot to do with constant ("magic") strings which refer to the calling property name. In standard WPF projects we put this RaisePropertyChanged - boiler plate code like in the example http://msdn.microsoft.com/en-us/library/system.componentmodel.inotifypropertychanged(v=vs.110).aspx in every setter of a property.
A new and modern possible solution as used in
The difference is that you will get a
You see we use machinename as parametername and push this expression tree into method PrintStaticInfo.
This function inserts the value for a given parameter by name. The parameter name if mistyped breaks exactly the same way as PropertyChanged does if the wrong property name was inserted. Ugly code, different ways :-).
kind regards, Daniel
I focused on the usage in combination with PropertyChanged where we have a lot to do with constant ("magic") strings which refer to the calling property name. In standard WPF projects we put this RaisePropertyChanged - boiler plate code like in the example http://msdn.microsoft.com/en-us/library/system.componentmodel.inotifypropertychanged(v=vs.110).aspx in every setter of a property.
- When calling RaisPropertyChanged the property name has to be handed in as parameter
- hard coded (till version 4.0 of .net) -> #mistype
- using [CallerMemberName] - Attribute -> works only for the calling setter. If dependent notifying is necessary, the mistyping problem is still there (e.g.: if firstname changes call PropertyChanged for firstname and for fullname, too)
- using aspect oriented programming like with fody (intermediate language code will be manipulated as post-build action and the aspect of notification will be injected)
- wrapped like in http://crazorsharp.blogspot.co.at/2010/05/inotifypropertychanged-without-magic.html
- ... on-top solutions:
- Dependency Mapping Dictionaries ( https://github.com/StephenCleary/CalculatedProperties/wiki/Alternatives )
- Notification Chaining ( https://github.com/philchuang/MvvmNotificationChainer )
- Property Dependency Frameworks ( https://pdfx.codeplex.com/ )
A new and modern possible solution as used in
- MVVM light and
- the on-top extension "mvvm light dependentrelaycommand (PCL)"
(see http://www.mutzl.com/2014/02/a-smart-mvvm-command/ )
The concept is:
- still use caller member name if possible
- make a lambda function which has only 1 expression tree leaf: the property.
- push the lambda function as parameter inside (as expression tree)
- In the expression tree read the name of the property which is handed in and use its name as string like in the original way
The function to read the described expression tree is quite simple (as shown in http://stackoverflow.com/questions/671968/retrieving-property-name-from-lambda-expression/2916344#2916344 ) . The following example code shows how it works:
1 2 3 4 5 6 | private string GetMagicString(Expression<Func<object>> exp) { return (exp.Body as MemberExpression ?? ((UnaryExpression)exp.Body).Operand as MemberExpression).Member.Name; } |
Important here is:
UnaryExpression
if your expression represents a value type whereas you will get a MemberExpression
if your expression represents a reference type.
So you can call GetMagicString(() => MyProp) and get back MyProp as string.
This is a good idea, because now you get a compile error if you mistype your input. Quite cool.
Attention! You can implement magic strings with expression trees too and have the same problem as mentioned before.
1 2 3 | MessageBox.Show( PrintStaticInfo( (machinename) => string.Format("{0} is the machine name", machinename))); |
You see we use machinename as parametername and push this expression tree into method PrintStaticInfo.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 | private string PrintStaticInfo(Expression<Func<string,string>> expression) { string value = string.Empty; switch (expression.Parameters[0].Name.ToLower()) { case "username": value = Environment.UserName; break; case "machinename": value = Environment.MachineName; break; } return expression.Compile()(value); } |
This function inserts the value for a given parameter by name. The parameter name if mistyped breaks exactly the same way as PropertyChanged does if the wrong property name was inserted. Ugly code, different ways :-).
kind regards, Daniel
No comments:
Post a Comment