50980

How to decompose expression to satisfy generic property change method?

Question:

I have a base EF entity class which implements INotifyPropertyChanged.

The base property, Id 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 I can find are expecting parameters. I prefer that the setter (SetValue call) is as simple as possible - i.e., is there a way to reduce the input parameter to 1?

Answer1:

You should change

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

to

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

and your code compiles, but what you are doing is not optimal and trustful at all. And you'll get an InvalidCastException!

Compiling an Expression<T> on every call is not optimal, and, how can you tell that the user passes the lambda to the method like:

() => (Id != value)

and not

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

or

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

?

Also, value in your expression is not a ConstantExpression. The value itself is just a local variable to the set part of the property, and when passed to a lambda expression, is promoted to a class field (the value is captured - see <a href="http://msdn.microsoft.com/en-us/library/bb397687.aspx" rel="nofollow">here</a> for more information). So what you have is a MemberExpression on both sides.

I highly recommend using this approach if you can't 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; } }

Using it is simple:

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

Answer2:

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

<ul><li><a href="https://github.com/Fody/PropertyChanged" rel="nofollow">Fody/PropertyChanged</a> - This is a free, automatic code weaver that runs at compile time to automagically implement INotifyPropertyChanged on the properties of the classes you choose. No assemblies required at runtime.</li> <li><a href="http://danrigby.com/2012/04/01/inotifypropertychanged-the-net-4-5-way-revisited/" rel="nofollow">INotifyPropertyChanged, The .NET 4.5 Way – Revisited</a></li> <li><a href="http://www.postsharp.net/model/inotifypropertychanged" rel="nofollow">PostSharp - Automatically implementing INotifyPropertyChanged</a></li> <li><a href="http://msdn.microsoft.com/en-us/library/system.componentmodel.inotifypropertychanged.aspx" rel="nofollow">INotifyPropertyChanged Interface documentation's code sample</a></li> </ul>

Here's the core code snippet 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 like:

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

Recommend

  • Deploying a sample Spring Web MVC project
  • How to store pointer to S3 objects in Amazon SimpleDB?
  • Best style for iterating over a small number of items in Python?
  • ASP Net Core - Mixing External Identity Provider with Individual User Accounts for Audit Tracking
  • Dart HTTP server and Futures
  • How can we extract the main verb from a sentence?
  • Executing a function that adds columns and populates them dependig on other columns in Pandas
  • Plotly and ggplot with facet_grid in R: How to to get yaxis labels to use ticktext value instead of
  • Imagemagick set interline spacing?
  • Create ranking for vector of double
  • How to insert an Image in WORD after a bookmark using OpenXML
  • Hadoop shuffle uses which protocol?
  • “Complex Header” not responsive in current DataTables.net build?
  • Finding number of samples in a .wav header
  • finding greatest prime factor using recursion in c
  • Excel VBA Intersect
  • Variant from android-autofittextview library : scaling makes strange behaviour
  • Using multiple input pipelines in TensorFlow
  • How to get the index of element in the List in c#
  • Imageloader not loading image on real device
  • Is mp4 stream able with ffserver?
  • how to set to NULL all the filestream varbinary(max) fields?
  • in batch how do i use taskkill properly
  • Local Development, Apache vs Developer - file permissions
  • Not able to aggregate on nested fields in elasticsearch
  • Unable to install Git-core+svn by MacPorts
  • Django simple Captcha “No module named fields” error
  • Could not find rake using whenever rails
  • Yii2: Config params vs. const/define
  • NetLogo BehaviorSpace - Measure runs using reporters
  • Spring security and special characters
  • JSON with duplicate key names losing information when parsed
  • Acquiring multiple attributes from .xml file in c#
  • C# - Getting references of reference
  • Can Visual Studio XAML designer handle font family names with spaces as a resource?
  • need help with bizarre java.net.HttpURLConnection behavior
  • How can I remove ASP.NET Designer.cs files?
  • python draw pie shapes with colour filled
  • Is there any way to bind data to data.frame by some index?
  • How can i traverse a binary tree from right to left in java?