c# – 使用ExpressionTree分配属性
我正在玩一个将属性分配传递给方法作为表达式树的想法.该方法将调用表达式,以便正确分配属性,然后嗅出刚分配的属性名称,以便可以引发PropertyChanged事件.这个想法是,我希望能够在我的
WPF ViewModels中使用超薄的自动属性,并且仍然关闭PropertyChanged事件.
我是ExpressionTrees的无知,所以我希望有人能指出我正确的方向: public class ViewModelBase { public event Action<string> PropertyChanged = delegate { }; public int Value { get; set; } public void RunAndRaise(MemberAssignment Exp) { Expression.Invoke(Exp.Expression); PropertyChanged(Exp.Member.Name); } } 问题是我不知道如何调用这个.这个天真的尝试被编译器拒绝了,因为我肯定会对任何可以回答这个问题的人显而易见的: ViewModelBase vm = new ViewModelBase(); vm.RunAndRaise(() => vm.Value = 1); 编辑 谢谢@svick的完美答案.我移动了一个小东西,把它变成一个扩展方法.以下是单元测试的完整代码示例: [TestClass] public class UnitTest1 { [TestMethod] public void TestMethod1() { MyViewModel vm = new MyViewModel(); bool ValuePropertyRaised = false; vm.PropertyChanged += (s,e) => ValuePropertyRaised = e.PropertyName == "Value"; vm.SetValue(v => v.Value,1); Assert.AreEqual(1,vm.Value); Assert.IsTrue(ValuePropertyRaised); } } public class ViewModelBase : INotifyPropertyChanged { public event PropertyChangedEventHandler PropertyChanged = delegate { }; public void OnPropertyChanged(string propertyName) { PropertyChanged(this,new PropertyChangedEventArgs(propertyName)); } } public class MyViewModel : ViewModelBase { public int Value { get; set; } } public static class ViewModelBaseExtension { public static void SetValue<TViewModel,TProperty>(this TViewModel vm,Expression<Func<TViewModel,TProperty>> exp,TProperty value) where TViewModel : ViewModelBase { var propertyInfo = (PropertyInfo)((MemberExpression)exp.Body).Member; propertyInfo.SetValue(vm,value,null); vm.OnPropertyChanged(propertyInfo.Name); } } 解决方法
你不能这样做.首先,lambda表达式只能转换为委托类型或表达式.
如果将方法的签名(现在忽略其实现)更改为public void RunAndRaise(Expression< Action> Exp),则编译器会抱怨“表达式树可能不包含赋值运算符”. 您可以通过使用lambda指定属性并将其设置为另一个参数的值来执行此操作.此外,我没有找到一种方法从表达式访问vm的值,所以你必须把它放在另一个参数(你不能使用这个,因为你需要正确的继承类型在表达式中) :看编辑 public static void SetAndRaise<TViewModel,TProperty>( TViewModel vm,TProperty value) where TViewModel : ViewModelBase { var propertyInfo = (PropertyInfo)((MemberExpression)exp.Body).Member; propertyInfo.SetValue(vm,null); vm.PropertyChanged(propertyInfo.Name); } 另一个可能性(和我更喜欢的一个)是使用这样的lambda来特别提出来自setter的事件: private int m_value; public int Value { get { return m_value; } set { m_value = value; RaisePropertyChanged(this,vm => vm.Value); } } static void RaisePropertyChanged<TViewModel,TProperty>> exp) where TViewModel : ViewModelBase { var propertyInfo = (PropertyInfo)((MemberExpression)exp.Body).Member; vm.PropertyChanged(propertyInfo.Name); } 这样,您可以像往常一样使用属性,如果有的话,您还可以为计算属性引发事件. 编辑:在阅读Matt Warren’s series about implementing private int m_value; public int Value { get { return m_value; } set { m_value = value; RaisePropertyChanged(() => Value); } } static void RaisePropertyChanged<TProperty>(Expression<Func<TProperty>> exp) { var body = (MemberExpression)exp.Body; var propertyInfo = (PropertyInfo)body.Member; var vm = (ViewModelBase)((ConstantExpression)body.Expression).Value; vm.PropertyChanged(vm,new PropertyChangedEventArgs(propertyInfo.Name)); } (编辑:李大同) 【声明】本站内容均来自网络,其相关言论仅代表作者个人观点,不代表本站立场。若无意侵犯到您的权利,请及时与联系站长删除相关内容! |