c# - Expression.Lambda 无法自动转换类型
问题描述
我在许多类中都有一些属性,并希望将它们的 getter/setter 编译成 lambda 表达式,以便我可以使用具有更好性能的反射。
(Profiled,并且使用 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);
它会抛出一个异常:
类型的 ParameterExpression
__Main__+A
不能用于类型的委托参数System.Object
如果我将表达式更改为Exp.Lambda<Func<A, int>>(...)
它将适用于 A 类,但不适用于 B 类。
如果我Expression.Convert
用来转换类型,它会抛出 ArgumentException,告诉我该方法不能在System.Object
.
那么我该怎么做才能编译这个表达式,就像下面一样,它支持任何类型的对象和相应的方法?
lambda = (object obj, MethodInfo method, ...) => { method.Invoke(obj, ...) }
解决方案
有更好的表达式 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 委托,您必须缓存它以获得您正在寻找的性能提升。
推荐阅读
- javascript - 即使对于反应中的状态 200,API 也没有获取任何响应(Fetch API)
- javascript - 为什么 getExpectedRate() 返回 0?
- ios - 在 Xcode 13 UIButton setBackgroundImage 中不起作用
- go - 如何使用火星代理添加动态标头?
- python - 如何让我的程序知道用户输入的列表是字符串还是数字?
- flutter - 为什么Future返回类型的函数可以返回一个对象而不是Future类型?
- javascript - 在反应中使用 process.env
- click - Javascript AddEventListener 无法识别事件
- javascript - 打开图层:十进制格式的逗号坐标
- html - 当有换行符时,MathJax 2.7.7 无法在网页上正确居中公式