与ExpressionTree财产分配 [英] Assign Property with an ExpressionTree

查看:130
本文介绍了与ExpressionTree财产分配的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

我在玩弄传递一个属性赋值的方法作为表达式树的主意。该方法将调用,使财产得到妥善分配表达式,然后嗅出刚分配的,所以我可以提高PropertyChanged事件属性名称。这个想法是,我希望能在我的WPF的ViewModels使用超薄自动性能,仍然有PropertyChanged事件发射了。



我是一个无知的人与ExpressionTrees,所以我希望有人能指出我在正确的方向:

 公共类ViewModelBase {
公事件动作<串GT; PropertyChanged的=委托{};

公共int值{搞定;组; }

公共无效RunAndRaise(MemberAssignment EXP){
Expression.Invoke(Exp.Expression);
的PropertyChanged(Exp.Member.Name);
}
}



但问题是我不知道如何调用这个。这个天真的尝试被编译器拒绝了,我敢肯定将是显而易见的人谁可以回答这个问题的原因:

  ViewModelBase VM =新ViewModelBase(); 

vm.RunAndRaise(()=> vm.Value = 1);

修改



感谢您@svick了完美的答案。我搬来搬去一个小东西,并把它做成一个扩展方法。下面是与单元测试的完整代码示例:

  [TestClass中] 
公共类UnitTest1 {
[TestMethod的]
公共无效TestMethod1(){
MyViewModel VM =新MyViewModel();
布尔ValuePropertyRaised = FALSE;
vm.PropertyChanged + =(S,E)=> ValuePropertyRaised = e.PropertyName ==值;

vm.SetValue(V => v.Value,1);

Assert.AreEqual(1,vm.Value);
Assert.IsTrue(ValuePropertyRaised);
}
}


公共类ViewModelBase:INotifyPropertyChanged的{
公共事件PropertyChangedEventHandler的PropertyChanged =委托{};

公共无效OnPropertyChanged(字符串propertyName的){
的PropertyChanged(这一点,新PropertyChangedEventArgs(propertyName的));
}
}

公共类MyViewModel:ViewModelBase {
公共int值{搞定;组; }
}

公共静态类ViewModelBaseExtension {
公共静态无效的SetValue< TViewModel,TProperty>(这TViewModel VM,表达< Func键< TViewModel,TProperty>> EXP,TProperty值),其中TViewModel:ViewModelBase {
VAR的PropertyInfo =(的PropertyInfo)((MemberExpression)exp.Body)。成员;
propertyInfo.SetValue(VM,价值,NULL);
vm.OnPropertyChanged(propertyInfo.Name);
}
}


解决方案

您不能做这种方式。首先,lambda表达式只能转换为委托类型或表达式来; T>



如果您更改签名方法(现在忽略了它的实现)到公共无效RunAndRaise(表达<作用> EXP),编译器会抱怨说:表达式树不能包含赋值运算符 。



您可以通过指定使用lambda,你想用另一种参数设置为值的属性做到这一点。 另外,我也没有想出一个办法,从表情访问 VM 的价值,所以你必须把在其他参数(你不能使用这个的,因为你需要在表达正确的继承型)看到修改

 公共静态无效SetAndRaise< TViewModel,TProperty>(
TViewModel VM,表达< Func键< TViewModel,TProperty>> EXP,TProperty值)
其中TViewModel:ViewModelBase
{
VAR的PropertyInfo =(的PropertyInfo)((MemberExpression)exp.Body)。成员;
propertyInfo.SetValue(VM,价值,NULL);
vm.PropertyChanged(propertyInfo.Name);
}



另一种可能性(和一个我喜欢更多)是提高从二传手事件特别是使用lambda是这样的:

 私人诠释m_value; 
公共int值
{
{返回m_value; }

{
m_value =价值;
RaisePropertyChanged(这一点,VM = GT; vm.Value);
}
}

静态无效RaisePropertyChanged< TViewModel,TProperty>(
TViewModel VM,表达< Func键< TViewModel,TProperty>> EXP)
其中,TViewModel:ViewModelBase
{
VAR的PropertyInfo =(的PropertyInfo)((MemberExpression)exp.Body)。成员;
vm.PropertyChanged(propertyInfo.Name);
}



这样,您就可以使用属性和往常一样,你也可以提高对于计算性能的事件,如果你有他们



编辑:在阅读通过的马特·沃伦的一系列关于实施的IQueryable< T> ,我才意识到我可以访问的参考价值,简化的使用 RaisePropertyChanged()(虽然它不会帮助很多与你的 SetAndRaise ()):

 私人诠释m_value; 
公共int值
{
{返回m_value; }

{
m_value =价值;
RaisePropertyChanged(()=>值);
}
}

静态无效RaisePropertyChanged< TProperty>(表达式来; Func键< TProperty>> EXP)
{
VAR体=(MemberExpression) exp.Body;
VAR的PropertyInfo =(的PropertyInfo)body.Member;
VAR VM =(ViewModelBase)((常量表达式)body.Expression).value的;
vm.PropertyChanged(VM,新PropertyChangedEventArgs(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);
    }
}

解决方案

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 vm from the expression, so you have to put that in another parameter (you can't use this for that, because you need the proper inherited type in the expression):see edit

public static void SetAndRaise<TViewModel, TProperty>(
    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.PropertyChanged(propertyInfo.Name);
}

Another possibility (and one I like more) is to raise the event from setter specifically using lambda like this:

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>(
    TViewModel vm, Expression<Func<TViewModel, TProperty>> exp)
    where TViewModel : ViewModelBase
{
    var propertyInfo = (PropertyInfo)((MemberExpression)exp.Body).Member;
    vm.PropertyChanged(propertyInfo.Name);
}

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 of RaisePropertyChanged() (although it won't help much with your SetAndRaise()):

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

这篇关于与ExpressionTree财产分配的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆