- android - RelativeLayout 背景可绘制重叠内容
- android - 如何链接 cpufeatures lib 以获取 native android 库?
- java - OnItemClickListener 不起作用,但 OnLongItemClickListener 在自定义 ListView 中起作用
- java - Android 文件转字符串
我正在研究将属性赋值作为表达式树传递给方法的想法。该方法将调用表达式,以便正确分配属性,然后嗅探出刚刚分配的属性名称,以便我可以引发 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 指定属性以及要在另一个参数中将其设置为的值来实现。 此外,我没有想出一种方法来访问 :see edit vm
的值。从表达式中,所以你必须把它放在另一个参数中(你不能为此使用 this
,因为你需要表达式中的正确继承类型)
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/
我需要创建一个方法来动态检查作为参数传递的方法委托(delegate)。但我不能强制表达式树接受任何方法,而不管它们的签名如何。 这是我的方法(不编译:error CS0030: Cannot con
我有一个将 lambda 添加到 IQueryable 的通用方法。下面的代码添加了一个 StartsWith lambda。我现在要做的是创建一个 NotStartsWith lambda。由于 N
我目前有以下代码,它允许我调用对象的 EmailAddress 属性所需的任何方法,并且效果很好: public static Expression> BuildEmailAddressLambda(
using System; using System.Linq.Expressions; class Program { static void Main() { Expression
我正在研究将属性赋值作为表达式树传递给方法的想法。该方法将调用表达式,以便正确分配属性,然后嗅探出刚刚分配的属性名称,以便我可以引发 PropertyChanged 事件。我的想法是,我希望能够在我的
我的代码是对此处示例的轻微修改: https://learn.microsoft.com/en-us/dotnet/csharp/programming-guide/concepts/expressi
我目前正在阅读 MSDN,Walkthrough: Creating an IQueryable LInQ Provider ExpressionVisitor 有很多用途。 这让我想知道,使用它是不
我正在尝试为表达式树做,并尝试让它返回一个简单的 int 值。但它不再工作了 var method = typeof(Console).GetMethod("WriteLine", n
让有: Expression> expression = c => c.Name == "John"; 现在我通过使用获得值(value): string myvalue = ((ConstantEx
模糊地与 previous question 相关 注意:我正在使用 ExpressionTree 访问者的派生,如 here 所解释的那样 在我的 VisitMemberAccess 方法中,我目前
以下程序的输出是: 首先:System.String。第二:System.String。 预期结果是:第一:你好1。第二个:hello2。 如果我将索引硬编码为 1 或 2Expression.Ass
看起来 ExpressionTrees 编译器在许多行为上应该接近 C# 规范,但与 C# 不同的是,它不支持从 decimal 到任何 enum-type 的转换: using System; us
假设我有: Func a = (c) => c.fullName == "John"; 现在我想通过任何方式转换为 expressiontree 吗? 我知道我可以从一开始就将它定义为表达式树,但我
我正在尝试使用表达式树,以便我可以选择使用 Entity Framework 映射到 DTO,其方式与 Include 指令在 DbSet(实现 OData 的开源项目的一部分)上的工作方式大致相同。
我正在手动构建一个谓词来过滤 CollectionView 中的数据,我想添加通过用户提供的 Regex 过滤特定字段的功能。直接编写谓词会给出如下内容: string userRegex = "ab
如果我在下面的代码中犯了任何错误/输入错误,请不要生气,只需在这里发表评论,我会立即修复 - 谢谢 目标 重新映射 Expression来自一个EntityA到 EntityB . 我怀疑以前有人做过
我有一个 gridview,我们可以在其中按不同的标准进行过滤。每个条件都是一个表达式。我有一个场景,在调用 Compile 方法时,我可以有超过一千个条件导致我的表达式抛出 StackOverflo
今天早上我看到了一个问题 (Query my model on a range of values),似乎由 (https://stackoverflow.com/a/1447926/195550)
我编写了一个 ToList(); 扩展方法来将 DataTable 转换为 List。这只在某些情况下有效,但我们有很多使用数据表的旧代码,有时需要它。我的问题是这种方法与反射一起工作是好的,但不是那
我正在尝试创建委托(delegate)来访问任何 Property Get 和 Set 方法。 我发现以下代码(在这篇文章中:driis about deep properties)非常有效: pub
我是一名优秀的程序员,十分优秀!