C# 使用ExpressionTree指定属性
我正在考虑将属性赋值作为表达式树传递给方法。该方法将调用表达式,以便正确分配属性,然后嗅出刚刚分配的属性名称,以便引发PropertyChanged事件。我的想法是,我希望能够在WPF ViewModels中使用slim自动属性,并且仍然能够触发PropertyChanged事件 我是一个有表情树的无知者,所以我希望有人能给我指出正确的方向:C# 使用ExpressionTree指定属性,c#,expression-trees,C#,Expression Trees,我正在考虑将属性赋值作为表达式树传递给方法。该方法将调用表达式,以便正确分配属性,然后嗅出刚刚分配的属性名称,以便引发PropertyChanged事件。我的想法是,我希望能够在WPF ViewModels中使用slim自动属性,并且仍然能够触发PropertyChanged事件 我是一个有表情树的无知者,所以我希望有人能给我指出正确的方向: public class ViewModelBase { public event Action<string> PropertyCh
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);
}
}
编辑
谢谢@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);
}
}
[TestClass]
公共类UnitTest1{
[测试方法]
公共void TestMethod1(){
MyViewModel vm=新的MyViewModel();
bool ValuePropertyRaised=假;
vm.PropertyChanged+=(s,e)=>ValuePropertyRaised=e.PropertyName==“Value”;
设置值(v=>v.Value,1);
Assert.AreEqual(1,vm.Value);
Assert.IsTrue(ValuePropertyRaised);
}
}
公共类ViewModelBase:INotifyPropertyChanged{
公共事件PropertyChangedEventHandler PropertyChanged=委托{};
公共void OnPropertyChanged(字符串propertyName){
PropertyChanged(这是新的PropertyChangedEventArgs(propertyName));
}
}
公共类MyViewModel:ViewModelBase{
公共int值{get;set;}
}
公共静态类ViewModelBaseExtension{
公共静态void SetValue(此TViewModel vm、Expression、TProperty值),其中TViewModel:ViewModelBase{
var propertyInfo=(propertyInfo)((MemberExpression)exp.Body).Member;
propertyInfo.SetValue(vm,value,null);
vm.OnPropertyChanged(propertyInfo.Name);
}
}
可能是您在.net 4.0中添加的意思?您不能这样做。首先,lambda表达式只能转换为委托类型或表达式
如果将方法的签名(现在忽略其实现)更改为public void RunAndRaise(Expression Exp)
,编译器会抱怨“表达式树可能不包含赋值运算符”
可以通过使用lambda和要在另一个参数中设置的值来指定属性。另外,我没有找到从表达式中访问vm
值的方法,因此必须将其放入另一个参数中(不能使用this
,因为表达式中需要正确的继承类型):请参见编辑
表达式树的一般解决方案可能不包含赋值运算符
问题将是创建一个本地setter操作
,并通过表达式调用它:
// raises "An expression tree may not contain an assignment operator"
Expression<Action<string>> setterExpression1 = value => MyProperty = value;
// works
Action<string> setter = value => MyProperty = value;
Expression<Action<string>> setterExpression2 = value => setter(value);
//引发“表达式树不能包含赋值运算符”
表达式setterExpression1=value=>MyProperty=value;
//工作
动作设置器=value=>MyProperty=value;
表达式setterExpression2=value=>setter(value);
这可能不适合你的具体问题,但我希望这能帮助别人,因为这个问题是谷歌搜索错误消息的最佳匹配
我不确定编译器为什么不允许这样做。这里有一个通用的解决方案,可以从表达式中为赋值提供一个操作
,以指定赋值的左侧,以及一个要赋值的值
public Expression<Action> Assignment<T>(Expression<Func<T>> lvalue, T rvalue)
{
var body = lvalue.Body;
var c = Expression.Constant(rvalue, typeof(T));
var a = Expression.Assign(body, c);
return Expression.Lambda<Action>(a);
}
如果你像这样改变定义
public void RunAndRaise(Expression<Action> Exp) {
Exp.Compile()();
PropertyChanged(Exp.Member.Name);
}
很简单,不是吗?你不能用lambda符号创建分配实例。已经+1e了,现在接受-感谢你的回答顺便说一下-关于你的第二个答案,避免像这样庞大的代码是我一开始想要避免的。我接受了您的答案,并将其转换为一个扩展方法,该方法应该允许我设置aotu属性并自动引发propertyChanged事件。再次感谢。
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));
}
// raises "An expression tree may not contain an assignment operator"
Expression<Action<string>> setterExpression1 = value => MyProperty = value;
// works
Action<string> setter = value => MyProperty = value;
Expression<Action<string>> setterExpression2 = value => setter(value);
public Expression<Action> Assignment<T>(Expression<Func<T>> lvalue, T rvalue)
{
var body = lvalue.Body;
var c = Expression.Constant(rvalue, typeof(T));
var a = Expression.Assign(body, c);
return Expression.Lambda<Action>(a);
}
ViewModelBase vm = new ViewModelBase();
vm.RunAndRaise(Assignment(() => vm.Value, 1));
public void RunAndRaise(Expression<Action> Exp) {
Exp.Compile()();
PropertyChanged(Exp.Member.Name);
}
//Set the current thread name to "1234"
Assignment(() => Thread.CurrentThread.Name, "1234")).Compile()();