创建一个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检查(我不确定我是否涵盖了所有内容):
我不是表达式树专家,但我不认为构建表达式树然后在这里调用 ( 编辑 :显然,这些已经在.NET 4中添加。这是一个难以发现的function,因为C#编译器似乎无法从lambdas构建它们)。 Compile
是可能的,因为表达式树不能包含赋值语句,据我所知。
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转换为预期的委托类型