How to decompose an expression to satisfy the general method of changing properties?

I have an EF entity base class that implements INotifyPropertyChanged .

The basic Id property is my example:

  /// <summary> /// Entity Id /// </summary> public int Id { get { return id; } set { SetValue<int>(() => (Id != value), (v) => id = v); } // < can this be simplified into a single call? } 

... where SetValue is defined:

  protected void SetValue<TValue>(Expression<Func<bool>> evalExpr, Action<TValue> set) { // Compile() returns a Func<bool> var doSetValue = evalExpr.Compile(); if (doSetValue()) { var expr = evalExpr.Body as BinaryExpression; // this is not compiling - how do I decompose the expression to get what I need? var propertyName = ((PropertyExpression)expr.Left).Name; var assignValue = (TValue)((ConstantExpression)expr.Right).Value; set(assignValue); _propertyChangedHandler(this, new PropertyChangedEventArgs(propertyName)); } } 

All samples that I can find are expecting parameters. I prefer the setter (calling SetValue) to be as simple as possible, i.e. Was there a way to reduce the input parameter to 1?

0
c # expression inotifypropertychanged
source share
2 answers

You have to change

 var propertyName = ((PropertyExpression)expr.Left).Name; 

to

 var propertyName = ((MemberExpression)expr.Left).Member.Name; 

and your code compiles, but what you do is not optimal and trusting at all. And you get an InvalidCastException !

Compiling Expression<T> for each call is not optimal, and as you can tell, the user passes the lambda to the method, for example:

 () => (Id != value) 

but not

 () => (id != value) // using the field instead of property 

or

 () => (value != Id) // passing the property as the second operand 

?

Also, the value in your expression is not ConstantExpression . value itself is a local variable in the set of the property, and when passed to a lambda expression, in the class field (the value is captured - see here for more information). So you have MemberExpression on both sides.

I highly recommend using this approach if you cannot use .NET 4.5 ( [CallerMemberName] ):

 public class EntityBase : INotifyPropertyChanged { protected virtual void OnPropertyChanged(string propName) { var h = PropertyChanged; if (h != null) h(this, new PropertyChangedEventArgs(propName)); } public event PropertyChangedEventHandler PropertyChanged; protected bool ChangeAndNofity<T>(ref T field, T value, Expression<Func<T>> memberExpression) { if (memberExpression == null) { throw new ArgumentNullException("memberExpression"); } var body = memberExpression.Body as MemberExpression; if (body == null) { throw new ArgumentException("Lambda must return a property."); } if (EqualityComparer<T>.Default.Equals(field, value)) { return false; } field = value; OnPropertyChanged(body.Member.Name); return true; } } 

Usage is simple:

 public class Person : EntityBase { private int _id; public int Id { get { return _id; } set { ChangeAndNofity(ref _id, value, () => Id); } } } 
+1
source share

There are various options that are simpler than what you have (here are a few in a rough order of how much I like each one):

Here's a snippet of kernel code from "The.NET 4.5 Way":

 protected bool SetProperty<T>(ref T storage, T value, [CallerMemberName] String propertyName = null) { if (object.Equals(storage, value)) return false; storage = value; this.OnPropertyChanged(propertyName); return true; } 

Used as:

  /// <summary> /// Entity Id /// </summary> public int Id { get { return id; } set { SetValue(ref id, value); } } 
+1
source share

All Articles