c# – 使用ExpressionTree分配属性

前端之家收集整理的这篇文章主要介绍了c# – 使用ExpressionTree分配属性前端之家小编觉得挺不错的,现在分享给大家,也给大家做个参考。
我正在玩一个将属性分配传递给方法作为表达式树的想法.该方法调用表达式,以便正确分配属性,然后嗅出刚分配的属性名称,以便可以引发PropertyChanged事件.这个想法是,我希望能够在我的 WPF viewmodels中使用超薄的自动属性,并且仍然关闭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);
    }
}

解决方法

你不能这样做.首先,lambda表达式只能转换为委托类型或表达式.

如果将方法的签名(现在忽略其实现)更改为public void RunAndRaise(Expression< Action> Exp),则编译器会抱怨“表达式树可能不包含赋值运算符”.

您可以通过使用lambda指定属性并将其设置为另一个参数的值来执行此操作.此外,我没有找到一种方法从表达式访问vm的值,所以你必须把它放在另一个参数(你不能使用这个,因为你需要正确的继承类型在表达式中) :看编辑

public static void SetAndRaise<Tviewmodel,TProperty>(
    Tviewmodel vm,TProperty value)
    where Tviewmodel : viewmodelBase
{
    var propertyInfo = (PropertyInfo)((MemberExpression)exp.Body).Member;
    propertyInfo.SetValue(vm,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,TProperty>> exp)
    where Tviewmodel : viewmodelBase
{
    var propertyInfo = (PropertyInfo)((MemberExpression)exp.Body).Member;
    vm.PropertyChanged(propertyInfo.Name);
}

这样,您可以像往常一样使用属性,如果有的话,您还可以为计算属性引发事件.

编辑:在阅读Matt Warren’s series about implementing IQueryable<T>时,我意识到我可以访问引用的值,这简化了RaisePropertyChanged()的使用(尽管它不会对您的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));
}
原文链接:/csharp/97429.html

猜你在找的C#相关文章