使用表达式树分配属性
我正在考虑将属性分配作为表达式树传递给方法的想法。该方法将调用表达式,以便正确分配属性,然后嗅出刚刚分配的属性名称,以便我可以引发 PropertyChanged 事件。我的想法是,我希望能够在 WPF ViewModel 中使用精简的自动属性,并且仍然触发 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);
}
}
I'm playing around with the idea of passing a property assignment to a method as an expression tree. The method would Invoke the expression so that the property gets assigned properly, and then sniff out the property name that was just assigned so I can raise the PropertyChanged event. The idea is that I'd like to be able to use slim auto-properties in my WPF ViewModels and still have the PropertyChanged event fired off.
I'm an ignoramus with ExpressionTrees, so I'm hoping someone can point me in the right direction:
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);
}
}
The problem is I'm not sure how to call this. This naive attempt was rejected by the compiler for reasons that I'm sure will be obvious to anyone who can answer this:
ViewModelBase vm = new ViewModelBase();
vm.RunAndRaise(() => vm.Value = 1);
EDIT
Thank you @svick for the perfect answer. I moved one little thing around and made it into an extension method. Here's the complete code sample with unit test:
[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);
}
}
如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。
绑定邮箱获取回复消息
由于您还没有绑定你的真实邮箱,如果其他用户或者作者回复了您的评论,将不能在第一时间通知您!
发布评论
评论(4)
你不能这样做。首先,lambda 表达式只能转换为委托类型或
Expression
。如果您将该方法的签名(暂时忽略其实现)更改为 public void RunAndRaise(ExpressionExp),编译器会抱怨“表达式树可能不包含赋值运算符”。
您可以通过使用 lambda 指定属性以及要在另一个参数中将其设置为的值来完成此操作。
此外,我没有找到从表达式访问vm
值的方法,因此您必须将其放入另一个参数中(您不能使用this< /code> 为此,因为您需要在表达式中使用正确的继承类型)
:参见编辑
另一种可能性(也是我更喜欢的一种)是专门使用 lambda 从 setter 引发事件像这样:
这样,您可以像往常一样使用属性,并且还可以引发计算属性的事件(如果有的话)。
编辑:在阅读Matt Warren 关于实现
IQueryable
的系列文章,我意识到我可以访问引用的值,这简化了RaisePropertyChanged()
的使用(尽管它赢得了对您的SetAndRaise()
没有多大帮助):You can't do it this way. First, lambda expressions can be converted only to delegate types or
Expression<T>
.If you change the signature of the method (for now ignoring its implementation) to
public void RunAndRaise(Expression<Action> Exp)
, the compiler complains that “An expression tree may not contain an assignment operator”.You could do it by specifying the property using lambda and the value you want to set it to in another parameter.
Also, I didn't figure out a way to access the value of:see editvm
from the expression, so you have to put that in another parameter (you can't usethis
for that, because you need the proper inherited type in the expression)Another possibility (and one I like more) is to raise the event from setter specifically using lambda like this:
This way, you can use the properties as usual, and you could also raise events for computed properties, if you had them.
EDIT: While reading through Matt Warren's series about implementing
IQueryable<T>
, I realized I can access the referenced value, which simplifies the usage ofRaisePropertyChanged()
(although it won't help much with yourSetAndRaise()
):这是一个通用解决方案,它可以为您提供一个从表达式进行赋值的
Action
来指定赋值的左侧,以及要赋值的值。有了这个,问题中的代码就是简单的
如果你改变定义,就像
我们也可以说
足够简单,不是吗?
Here is a generaic solution that can give you an
Action
for assignment from an expression to specify the left hand side of assignment, and a value to assign.with this, the code in the question is simply
If you change the definition like
We can also say
Simple enough, isn't it?
对于
表达式树可能不包含赋值运算符
问题的通用解决方案是创建一个本地setterAction
并通过Expression
调用它:这可能不适合您的具体问题,但我希望这对某人有帮助,因为这个问题是谷歌搜索该错误消息的最佳匹配。
我不确定为什么编译器不允许这样做。
A general solution for the
expression tree may not contain an assignment operator
issue would be to create a local setterAction
and call that one by theExpression
:This may not be suited to your exact problem, but I'm hoping this helps someone as this question is kind of the best match for googling that error message.
I'm unsure why exactly the compiler disallows this.
您可能是指 .net 4.0 中添加的 Expression.Assign 吗?
May be u mean Expression.Assign which was added in .net 4.0?