C# 表达式。Lambda can';t自动转换类型

C# 表达式。Lambda can';t自动转换类型,c#,.net,reflection,C#,.net,Reflection,我在许多类中都有一些属性,并且希望将它们的getter/setter编译成lambda表达式,以便使用性能更好的反射 (已分析,使用反射的getValue/setValue大约占总运行时间的78%。) 但是,Expression.Lambda()似乎只支持一个参数集合,不会自动转换参数类型 using Exp = System.Linq.Expressions.Expression; ... public class A { public int propA { get; set; } } pu

我在许多类中都有一些属性,并且希望将它们的getter/setter编译成lambda表达式,以便使用性能更好的反射

(已分析,使用反射的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);
使用Exp=System.Linq.Expressions.Expression;
...
公共类A{public int propA{get;set;}}
公共类B{public int propB{get;set;}}
静态Func BuildFunc(类型、字符串名称)
{
var param=Exp.Parameter(prop.DeclaringType,“x”);
var exBody=Exp.Call(param,prop.getMethod());
返回Exp.Lambda(exBody,param.Compile();
}
...
var a=新的a();
var b=新的b();
var fA=BuildFunc(typeof(A).GetProperty(“propA”);
var fB=BuildFunc(typeof(B).GetProperty(“propB”);
fA(a);
fB(b);
这将是一个例外:

类型为
\uuuu Main\uuuu+A
的参数表达式不能用于类型为
系统的委托参数。对象

如果我将表达式更改为
Exp.Lambda(…)
它将适用于类A,但不适用于类B

如果我使用
Expression.Convert
转换类型,它将抛出ArgumentException,告诉我该方法无法在
System.Object
的实例上调用

那么,我可以做什么来编译下面这样的表达式,它支持任何类型的对象和相应的方法?

lambda=(objectobj,MethodInfo method,…)=>{method.Invoke(obj,…)}

有更好的表达式API可用于调用属性getter/setter方法。您绝对不必求助于
MethodInfo.Invoke

从/到
对象的转换由
Expression.Convert
处理。你只需要把它插在正确的位置

下面是一个getter/setter委托编译示例,其中
T
是容器的类型(
a
B

static Func编译器etter(PropertyInfo属性)
{
//目标表达式:(T obj)=>(object)obj.Property;
ParameterExpression objParam=表达式参数(类型为(T),“obj”);
Expression body=Expression.Convert(Expression.MakeMemberAccess(objParam,property),typeof(object));
返回表达式
.Lambda(车身,objParam)
.Compile();
}
静态操作编译器设置(PropertyInfo属性)
{
//目标表达式:(T对象,对象值)=>obj.Property=(TProperty)值;
ParameterExpression objParam=表达式参数(类型为(T),“obj”);
ParameterExpression valueParam=表达式。参数(类型(对象),“值”);
Expression body=Expression.Assign(
表达式.MakeMemberAccess(对象参数,属性),
Expression.Convert(valueParam,property.PropertyType)
);
返回表达式
.Lambda(主体、对象参数、值参数)
.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);
类虚拟
{
公共int值{get;set;}
}
...
PropertyInfo prop=typeof(Dummy).GetProperty(“值”);
Func getter=CompileGetter(prop);
动作设定器=编译设定器(prop);
虚拟d=新虚拟{Value=123};
AreEqual(123,getter(d));
设定器(d,321);
断言为相等(321,d值);

请注意:
LambdaExpression.Compile
是一个非常占用CPU的操作,因此创建getter/setter委托后,必须对其进行缓存,以获得所需的性能提升。

LambdaExpression.Compile在较新的.NET上有一个可选参数“解释”表达式,而不是真正编译它。如果表达式很少使用,您将获得更好的速度。有没有一种方法不需要为容器类指定编译时类型?似乎我必须手动缓存每一个可能以这种方式使用的类。@Apliex-Ddr,当然了。您可以将lambda getter和setter代理分别键入为
Func
Action
。这需要通过
Expression.Convert(objParam,Type)
引入额外的强制转换,其中
Type
是在运行时解析的容器类型。然后,我假设您可以将这些委托缓存在
字典中
(以及用于setter的类似缓存)。但是,这并不是我在性能和API设计方面的首选。很好,这是我想要的,非常感谢。顺便说一下,我还对它进行了分析,两个主要过程的运行时间从(21ms,6ms)变为(18ms,28ms)在dotnet内核上。但是它带来了一个很大的统一优化,将运行时间从(180ms,400ms)更改为(60ms,100ms)。无论如何,这是最近可以接受的。。。
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);