我有一个实现INotifyPropertyChanged
的基本EF实体类。
基本属性,Id是我的例子:
/// <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?
}
…其中SetValue是定义的:
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));
}
}
我能找到的所有样本都是期望参数。我更喜欢setter (SetValue调用)尽可能简单-即,是否有方法将输入参数减少到1?
你应该改变
var propertyName = ((PropertyExpression)expr.Left).Name;
var propertyName = ((MemberExpression)expr.Left).Member.Name;
和你的代码编译,但是你所做的根本不是最优的和值得信任的。你会得到一个InvalidCastException
!
在每次调用时编译Expression<T>
不是最优的,并且,您如何知道用户将lambda传递给方法,如:
() => (Id != value)
和
() => (id != value) // using the field instead of property
或
() => (value != Id) // passing the property as the second operand
?
同样,表达式中的value
不是ConstantExpression
。value
本身只是属性的set
部分的一个局部变量,当传递给lambda表达式时,它被提升为一个类字段(该值被捕获-请参阅此处了解更多信息)。所以两边都有一个MemberExpression
如果你不能使用。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;
}
}
使用起来很简单:
public class Person : EntityBase
{
private int _id;
public int Id
{
get { return _id; }
set { ChangeAndNofity(ref _id, value, () => Id); }
}
}
有各种各样的选项比你所拥有的更简单(这里有一些我喜欢的大致顺序):
- Fody/PropertyChanged -这是一个免费的,自动的代码编织器,在编译时运行,自动实现
INotifyPropertyChanged
对你选择的类的属性。运行时不需要程序集。 - INotifyPropertyChanged, .NET 4.5方式-重新访问
- PostSharp自动实现INotifyPropertyChanged
- INotifyPropertyChanged Interface文档的代码示例
下面是来自" 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;
}
使用: /// <summary>
/// Entity Id
/// </summary>
public int Id {
get { return id; }
set { SetValue(ref id, value); }
}