C# 如何分解表达式以满足泛型属性更改方法?

C# 如何分解表达式以满足泛型属性更改方法?,c#,expression,inotifypropertychanged,C#,Expression,Inotifypropertychanged,我有一个基本EF实体类,它实现了INotifyPropertyChanged 基本属性Id是我的示例: /// <summary> /// Entity Id /// </summary> public int Id { get { return id; } set { SetValue<int>(() => (Id != value), (v) => id = v); } // < can this be

我有一个基本EF实体类,它实现了
INotifyPropertyChanged

基本属性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?
  }
//
///实体Id
/// 
公共整数Id{
获取{return id;}
set{SetValue(()=>(Id!=value),(v)=>Id=v);}/<这可以简化为单个调用吗?
}
…其中定义了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));
     }
  }
protectedvoid SetValue(表达式evalExpr,动作集){
//Compile()返回一个Func
var doSetValue=evalExpr.Compile();
if(doSetValue()){
var expr=evalExpr.Body作为二进制表达式;
//这不是编译-如何分解表达式以获得所需内容?
var propertyName=((PropertyExpression)expr.Left).Name;
var assignValue=(TValue)((ConstantExpression)expr.Right)值;
设定(赋值);
_propertyChangedHandler(这是新的PropertyChangedEventArgs(propertyName));
}
}

我能找到的所有样本都需要参数。我希望setter(SetValue调用)尽可能简单,也就是说,有没有办法将输入参数减少到1?

有各种选项比您得到的选项更简单(以下是我对每个选项的喜好程度的大致顺序):

  • -这是一个免费的自动代码编织器,在编译时运行,以自动实现对所选类的属性的
    INotifyPropertyChanged
    。运行时不需要任何程序集
下面是“the.NET 4.5 Way”的核心代码片段:

protected bool SetProperty(ref T storage,T value,[CallerMemberName]String propertyName=null)
{
if(object.Equals(storage,value))返回false;
储存=价值;
此.OnPropertyChanged(propertyName);
返回true;
}
用法如下:

  /// <summary>
  /// Entity Id
  /// </summary>
  public int Id {
     get { return id; }
     set { SetValue(ref id, value); }
  }
//
///实体Id
/// 
公共整数Id{
获取{return id;}
set{SetValue(ref id,value);}
}
您应该更改

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

您的代码可以编译,但您所做的根本不是最佳的和可信的。您将得到一个
无效的castexception

在每次调用时编译
表达式
不是最优的,您如何判断用户是否将lambda传递给如下方法:

() => (Id != value)
而不是

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

?

此外,表达式中的
不是
常量表达式
本身只是属性的
集合
部分的局部变量,当传递到lambda表达式时,会提升到类字段(捕获值-有关详细信息,请参阅)。因此,两边都有一个
MemberExpression

如果您不能使用.NET 4.5(
[CallerMemberName]
),我强烈建议您使用这种方法:


嗯……还不能完全使用.NET4.5。我将仔细阅读参考资料,寻找与.NET4.0兼容的解决方案。谢谢,关于信任的观点很好。我目前正在使用更多的参数来确保正确遵守SetValue方法的意图。谢谢。奖金…更好的通知。谢谢你,先生!
() => (Id != value)
() => (id != value) // using the field instead of property
() => (value != Id) // passing the property as the second operand
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); }
    }
}