gpt4 book ai didi

c# - 使用 ExpressionTree 分配属性

转载 作者:可可西里 更新时间:2023-11-01 08:10:12 27 4
gpt4 key购买 nike

我正在研究将属性赋值作为表达式树传递给方法的想法。该方法将调用表达式,以便正确分配属性,然后嗅探出刚刚分配的属性名称,以便我可以引发 PropertyChanged 事件。我的想法是,我希望能够在我的 WPF ViewModel 中使用 slim 自动属性,并且仍然触发 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 表达式只能转换为委托(delegate)类型或 Expression<T> .

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

您可以通过使用 lambda 指定属性以及要在另一个参数中将其设置为的值来实现。 此外,我没有想出一种方法来访问 vm 的值。从表达式中,所以你必须把它放在另一个参数中(你不能为此使用 this,因为你需要表达式中的正确继承类型):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);
}

另一种可能性(也是我更喜欢的一种)是专门使用 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>(
TViewModel vm, Expression<Func<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));
}

关于c# - 使用 ExpressionTree 分配属性,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/5780232/

27 4 0
Copyright 2021 - 2024 cfsdn All Rights Reserved 蜀ICP备2022000587号
广告合作:1813099741@qq.com 6ren.com