使用ExpressionTree分配属性
我正在尝试将属性赋值作为表达式树传递给方法。 该方法将调用表达式以便正确分配属性,然后嗅出刚刚分配的属性名称,以便我可以引发PropertyChanged事件。 我的想法是,我希望能够在我的WPF ViewModel中使用超薄自动属性,并且仍然会触发PropertyChanged事件。
我是ExpressionTrees的无知者,所以我希望有人可以指出我正确的方向:
public class ViewModelBase { public event Action 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的完美答案。 我移动了一个小东西,并将其作为一种扩展方法。 以下是unit testing的完整代码示例:
[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(this TViewModel vm, Expression<Func> exp, TProperty value) where TViewModel : ViewModelBase { var propertyInfo = (PropertyInfo)((MemberExpression)exp.Body).Member; propertyInfo.SetValue(vm, value, null); vm.OnPropertyChanged(propertyInfo.Name); } }
你不能这样做。 首先,lambda表达式只能转换为委托类型或Expression
。
如果将方法的签名(现在忽略其实现)更改为public void RunAndRaise(Expression
,编译器会抱怨“表达式树可能不包含赋值运算符”。
您可以通过使用lambda指定属性以及要在另一个参数中将其设置为的值来实现。 另外,我没有找到从表达式访问 : 见编辑 vm
值的方法,所以你必须将它放在另一个参数中(你不能使用它,因为你需要在表达式中使用正确的inheritance类型)
public static void SetAndRaise( TViewModel vm, Expression> exp, TProperty value) where TViewModel : ViewModelBase { var propertyInfo = (PropertyInfo)((MemberExpression)exp.Body).Member; propertyInfo.SetValue(vm, value, 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 vm, Expression> exp) where TViewModel : ViewModelBase { var propertyInfo = (PropertyInfo)((MemberExpression)exp.Body).Member; vm.PropertyChanged(propertyInfo.Name); }
这样,您可以像往常一样使用属性,如果有的话,还可以为计算属性引发事件。
编辑:在阅读Matt Warren关于实现IQueryable
的系列文章时 ,我意识到我可以访问引用的值,这简化了RaisePropertyChanged()
的使用(虽然它对你的SetAndRaise()
没有多大帮助):
private int m_value; public int Value { get { return m_value; } set { m_value = value; RaisePropertyChanged(() => Value); } } static void RaisePropertyChanged(Expression> 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)); }
这是一个genraic解决方案,可以为您提供一个Action
用于指定表达式的左侧和赋值的值。
public Expression Assignment(Expression> lvalue, T rvalue) { var body = lvalue.Body; var c = Expression.Constant(rvalue, typeof(T)); var a = Expression.Assign(body, c); return Expression.Lambda(a); }
有了这个,问题中的代码就是这么简单
ViewModelBase vm = new ViewModelBase(); vm.RunAndRaise(Assignment(() => vm.Value, 1));
如果改变定义,如
public void RunAndRaise(Expression Exp) { Exp.Compile()(); PropertyChanged(Exp.Member.Name); }
我们也可以说
//Set the current thread name to "1234" Assignment(() => Thread.CurrentThread.Name, "1234")).Compile()();
很简单,不是吗?
expression tree may not contain an assignment operator
一般解决方案expression tree may not contain an assignment operator
问题,即创建本地setter Action
并通过Expression
调用它:
// raises "An expression tree may not contain an assignment operator" Expression> setterExpression1 = value => MyProperty = value; // works Action setter = value => MyProperty = value; Expression> setterExpression2 = value => setter(value);
这可能不适合您的确切问题,但我希望这有助于某人,因为这个问题是谷歌搜索该错误消息的最佳匹配。
我不确定为什么编译器不允许这样做。
可能是你的意思是在.net 4.0中加入的Expression.Assign ?
上述就是C#学习教程:使用ExpressionTree分配属性分享的全部内容,如果对大家有所用处且需要了解更多关于C#学习教程,希望大家多多关注—计算机技术网(www.ctvol.com)!
本文来自网络收集,不代表计算机技术网立场,如涉及侵权请联系管理员删除。
ctvol管理联系方式QQ:251552304
本文章地址:https://www.ctvol.com/cdevelopment/1002260.html