创建一个Action 来“设置”一个属性,当我为“get”提供LINQ表达式时

我希望能够生成一个已编译的表达式来设置属性,给定lambda表达式为属性提供“get”方法。

这是我正在寻找的:

public Action CreateSetter(Expression<Func> getter) { // returns a compiled action using the details of the getter expression tree, or null // if the write property is not defined. } 

我仍然试图理解各种类型的表达式类,所以如果你能指出我正确的方向,这将是伟大的。

使用@ Ani的答案作为起点,您可以使用以下内容生成已编译的表达式

 [TestMethod] public void CreateSetterFromGetter() { Action ageSetter = InitializeSet((Person p) => p.Age); Action nameSetter = InitializeSet((Person p) => p.Name); Person p1 = new Person(); ageSetter(p1, 29); nameSetter(p1, "John"); Assert.IsTrue(p1.Name == "John"); Assert.IsTrue(p1.Age == 29); } public class Person { public int Age { get; set; } public string Name { get; set; } } public static Action InitializeSet(Expression> getter) { PropertyInfo propertyInfo = (getter.Body as MemberExpression).Member as PropertyInfo; ParameterExpression instance = Expression.Parameter(typeof(TContainer), "instance"); ParameterExpression parameter = Expression.Parameter(typeof(TProperty), "param"); return Expression.Lambda>( Expression.Call(instance, propertyInfo.GetSetMethod(), parameter), new ParameterExpression[] { instance, parameter }).Compile(); } 

您应该缓存已编译的表达式,以便将其用于多种用途。

您当然可以遍历表达式树,然后使用Delegate.CreateDelegate创建相应的Action<,> 。 这很简单,除了所有的validation检查(我不确定我是否涵盖了所有内容):

我不是表达式树专家,但我不认为构建表达式树然后在这里调用Compile是可能的,因为表达式树不能包含赋值语句,据我所知。编辑 :显然,这些已经在.NET 4中添加。这是一个难以发现的function,因为C#编译器似乎无法从lambdas构建它们)。

 public static Action CreateSetter (Expression> getter) { if (getter == null) throw new ArgumentNullException("getter"); var memberEx = getter.Body as MemberExpression; if (memberEx == null) throw new ArgumentException("Body is not a member-expression."); var property = memberEx.Member as PropertyInfo; if (property == null) throw new ArgumentException("Member is not a property."); if(!property.CanWrite) throw new ArgumentException("Property is not writable."); return (Action) Delegate.CreateDelegate(typeof(Action), property.GetSetMethod()); } 

用法

 public class Person { public int Age { get; set; } } ... static void Main(string[] args) { var setter = CreateSetter((Person p) => p.Age); var person = new Person(); setter(person, 25); Console.WriteLine(person.Age); // 25 } 

请注意,这会创建一个打开的实例委托,这意味着它不会绑定到任何特定的TContaining实例。 修改它以绑定到特定实例很简单; 你必须将TContaining传递给方法,然后使用Delegate.CreateDelegate的不同重载。 该方法的签名将类似于:

 public static Action CreateSetter (Expression> getter, TContaining obj) 

指针只是我害怕(我不是电脑) – 但是;

  • lambda的.Body很可能是MemberExpression
  • 做一个安全演员( as等)并访问.Member
  • 因为你相信这个属性,这应该是一个PropertyInfo,所以test / cast等
  • 从PropertyInfo中,调用GetSetMethod()来获取相应的MethodInfo
  • 使用Delegate.CreateDelegate将其作为委托(传递操作类型)
  • 最后,将Delegate转换为预期的委托类型