Warning: file_get_contents(/data/phpspider/zhask/data//catemap/2/csharp/267.json): failed to open stream: No such file or directory in /data/phpspider/zhask/libs/function.php on line 167

Warning: Invalid argument supplied for foreach() in /data/phpspider/zhask/libs/tag.function.php on line 1116

Notice: Undefined index: in /data/phpspider/zhask/libs/function.php on line 180

Warning: array_chunk() expects parameter 1 to be array, null given in /data/phpspider/zhask/libs/function.php on line 181
C# 使用ExpressionTree指定属性_C#_Expression Trees - Fatal编程技术网

C# 使用ExpressionTree指定属性

C# 使用ExpressionTree指定属性,c#,expression-trees,C#,Expression Trees,我正在考虑将属性赋值作为表达式树传递给方法。该方法将调用表达式,以便正确分配属性,然后嗅出刚刚分配的属性名称,以便引发PropertyChanged事件。我的想法是,我希望能够在WPF ViewModels中使用slim自动属性,并且仍然能够触发PropertyChanged事件 我是一个有表情树的无知者,所以我希望有人能给我指出正确的方向: public class ViewModelBase { public event Action<string> PropertyCh

我正在考虑将属性赋值作为表达式树传递给方法。该方法将调用表达式,以便正确分配属性,然后嗅出刚刚分配的属性名称,以便引发PropertyChanged事件。我的想法是,我希望能够在WPF ViewModels中使用slim自动属性,并且仍然能够触发PropertyChanged事件

我是一个有表情树的无知者,所以我希望有人能给我指出正确的方向:

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()();