0

我有一个基本的 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?
  }

...定义 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));
     }
  }

我能找到的所有样本都需要参数。我更喜欢设置器(SetValue 调用)尽可能简单 - 即,有没有办法将输入参数减少到 1?

4

2 回答 2

1

有各种比你所拥有的更简单的选项(这里有一些按我喜欢每个选项的粗略顺序排列):

以下是“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); }
  }
于 2013-04-23T02:45:37.550 回答
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); }
    }
}
于 2013-04-23T02:58:25.463 回答