我在许多类中都有一些属性,希望将它们的getter / setter编译为lambda表达式,以便可以使用性能更好的反射。
(分析后,使用Reflection的getValue / setValue大约占总运行时间的78%...)
但是,似乎Expression.Lambda()仅支持一个参数集合,并且不会自动转换参数类型。
using Exp = System.Linq.Expressions.Expression;
...
public class A { public int propA { get; set; } }
public class B { public int propB { get; set; } }
static Func<object, int> BuildFunc(Type type, string propName)
{
var param = Exp.Parameter(prop.DeclaringType, "x");
var exBody = Exp.Call(param, prop.GetGetMethod());
return Exp.Lambda<Func<object, int>>(exBody, param).Compile();
}
...
var a = new A();
var b = new B();
var fA = BuildFunc(typeof(A).GetProperty("propA"));
var fB = BuildFunc(typeof(B).GetProperty("propB"));
fA(a);
fB(b);
它将阻止异常:
类型
__Main__+A
的ParametersExpression不能用于类型System.Object
的委托参数
如果我将表达式更改为Exp.Lambda<Func<A, int>>(...)
,它将适用于A类,但不适用于B类。
如果我使用Expression.Convert
来转换类型,它将抛出ArgumentException,告诉我该方法无法在System.Object
的实例上调用。
那么我该怎么做才能像下面这样编译该表达式,它支持任何类型的对象和相应的方法?
lambda = (object obj, MethodInfo method, ...) => { method.Invoke(obj, ...) }
答案 0 :(得分:0)
有更好的表达式API可用于调用属性getter / setter方法。您绝对不必诉诸MethodInfo.Invoke
。
从object
到Expression.Convert
的转换。您只需要将其插入正确的位置即可。
这是一个getter / setter委托编译示例,其中T
是容器的类型(在示例中为A
或B
)。
static Func<T, object> CompileGetter<T>(PropertyInfo property)
{
// Target expression: (T obj) => (object)obj.Property;
ParameterExpression objParam = Expression.Parameter(typeof(T), "obj");
Expression body = Expression.Convert(Expression.MakeMemberAccess(objParam, property), typeof(object));
return Expression
.Lambda<Func<T, object>>(body, objParam)
.Compile();
}
static Action<T, object> CompileSetter<T>(PropertyInfo property)
{
// Target expression: (T obj, object value) => obj.Property = (TProperty)value;
ParameterExpression objParam = Expression.Parameter(typeof(T), "obj");
ParameterExpression valueParam = Expression.Parameter(typeof(object), "value");
Expression body = Expression.Assign(
Expression.MakeMemberAccess(objParam, property),
Expression.Convert(valueParam, property.PropertyType)
);
return Expression
.Lambda<Action<T, object>>(body, objParam, valueParam)
.Compile();
}
证明:
class Dummy
{
public int Value { get; set; }
}
...
PropertyInfo prop = typeof(Dummy).GetProperty("Value");
Func<Dummy, object> getter = CompileGetter<Dummy>(prop);
Action<Dummy, object> setter = CompileSetter<Dummy>(prop);
Dummy d = new Dummy { Value = 123 };
Assert.AreEqual(123, getter(d));
setter(d, 321);
Assert.AreEqual(321, d.Value);
请注意:LambdaExpression.Compile
是一项占用大量CPU的操作,因此,一旦创建了getter / setter委托,就必须对其进行缓存,以提高性能。