提高从 MethodCallExpression 获取 MethodInfo 的性能
本文关键字:MethodInfo 性能 获取 MethodCallExpression | 更新日期: 2023-09-27 18:32:12
我们的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);
语法是"更好的",我们得到智能感知,如果方法不存在或签名不匹配,我们就会得到编译错误。 然而,方法2和方法3比反射慢约10到20倍。
一些数字(用秒表测量):
单次通话:方法 1: .0000565方法 2: .0004272方法3:.0019222
100000 次调用:方法 1: .1171071方法2:1.5648544方法3:2.0602607
我们实际上并没有编译或执行表达式,如果有人对性能差异有解释,我很感兴趣。
更新:GetMethod<>代码:
方法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;
}
我的猜测是它更慢,因为表达式版本执行相同的反射(尽管它们可能使用 IL 快捷方式methodof
在 C# 中没有类似物)来创建表达式树,除了为每个调用创建树本身的开销(我不认为它们被编译器发出的代码缓存); 另外,您必须读取这些树才能取回该方法外。
反射可能很"慢",但实际上它非常快;特别是因为我相信幕后的数据也被缓存了。 因此,一旦您拨打了GetMethod
第二次呼叫就会更快。 这为为什么后续表达式树版本更慢提供了另一个令人信服的证据 - 因为它们实际上正在做更多的工作。
如果您熟悉 IL,请使用所有三个版本编译一个版本,然后使用 ILSpy 或 Reflector 分析编译的图像(在 C# 模式下,两者都会很聪明,并将表达式代码重新构建回 C#,这不好;所以切换到 IL 模式) - 看看为生成表达式树而发出的代码,你就会明白我的意思。