我们的UI系统可以从MethodInfo生成表单。在System.Linq.Expressions之前,我们使用反射(方法1)获取MethodInfo:
MethodInfo info = typeof(ExtensionTestClass).GetMethod("InstanceMethod", BindingFlags.Public | BindingFlags.Instance, null, new Type[] { typeof(string), typeof(string) }, null);
关于这一点的不好的部分是,如果我们更改了InstanceMethod的签名或名称,代码仍然会编译。
输入表达式。现在我们这样做(方法2):
MethodInfo info = GetMethod<ExtensionTestClass>(x => x.InstanceMethod("defaultValue", "defaultValue"));
或者这(方法3):
MethodInfo info = GetMethod<ExtensionTestClass, string, string>(x => x.InstanceMethod);
语法是“更好”,我们得到intellisense,如果方法不存在或签名不匹配,我们会得到编译错误。然而,方法2和方法3比反射慢约10至20倍。
一些数字(使用StopWatch测量):
单一电话: 方法1:.0000565 方法2:.0004272 方法3:.0019222
100000电话: 方法1:.1171071 方法2:1.5648544 方法3:2.0602607
我们实际上并没有编译表达式或执行它,如果有人对性能差异有任何解释,我很感兴趣。
更新:GetMethod&lt;&gt;代码:
方法2:
public static MethodInfo GetMethod<T>(Expression<Action<T>> target)
{
MethodCallExpression exp = target.Body as MethodCallExpression;
if (exp != null)
{
return exp.Method;
}
return null;
}
方法3:
public static MethodInfo GetMethod<T, A1, A2>(Expression<Func<T, Action<A1, A2>>> expression)
{
var lambdaExpression = (LambdaExpression)expression;
var unaryExpression = (UnaryExpression)lambdaExpression.Body;
var methodCallExpression = (MethodCallExpression)unaryExpression.Operand;
var methodInfoExpression = (ConstantExpression)methodCallExpression.Arguments.Last();
return (MethodInfo)methodInfoExpression.Value;
}
答案 0 :(得分:1)
我的猜测是它的速度较慢,因为表达式版本执行相同的反射(尽管它们可能使用IL#快捷methodof
在C#中没有模拟),以便创建表达式树 为每次调用创建树本身的开销(我不认为它们是由编译器发出的代码缓存的);再加上你必须阅读这些树才能让方法退出。
反思可能“缓慢”,但事实上它反应很快;特别是因为我相信幕后的数据也被缓存了。因此,一旦你打电话给GetMethod
,第二次通话就会更快。这提供了另一个令人信服的证据,证明后续表达式树版本为何更慢 - 因为它们实际上做得更多。
如果您对IL感到满意,请编译一个包含所有三个版本的版本,然后用ILSpy或Reflector分析编译后的图像(在C#模式下,两者都会很聪明,并将表达式代码重新组合成C#,这是不好的;所以切换到IL模式) - 看一下发出的代码来生成表达式树,你就会明白我的意思。