提高从MethodCallExpression获取MethodInfo的性能

Joe*_*ger 5 c# reflection expression

我们的UI系统可以从MethodInfo生成表单.在System.Linq.Expressions之前,我们使用反射获得MethodInfo(方法1):

MethodInfo info = typeof(ExtensionTestClass).GetMethod("InstanceMethod", BindingFlags.Public | BindingFlags.Instance, null, new Type[] { typeof(string), typeof(string) }, null);
Run Code Online (Sandbox Code Playgroud)

关于这一点的不好的部分是,如果我们更改了InstanceMethod的签名或名称,代码仍然会编译.

输入表达式.现在我们这样做(方法2):

 MethodInfo info = GetMethod<ExtensionTestClass>(x => x.InstanceMethod("defaultValue", "defaultValue"));
Run Code Online (Sandbox Code Playgroud)

或者这个(方法3):

MethodInfo info = GetMethod<ExtensionTestClass, string, string>(x => x.InstanceMethod);
Run Code Online (Sandbox Code Playgroud)

语法是"更好",我们得到intellisense,如果方法不存在或签名不匹配,我们会得到编译错误.然而,方法2和方法3比反射慢约10至20倍.

一些数字(用StopWatch测量):

单次呼叫:方法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;
}
Run Code Online (Sandbox Code Playgroud)

方法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;
}
Run Code Online (Sandbox Code Playgroud)

And*_*tan 1

我的猜测是,它速度较慢,因为表达式版本执行相同的反射(尽管它们可能使用methodof在 C# 中没有类似功能的 IL 快捷方式)来创建表达式树,此外还需要为每次调用创建树本身的开销(我不认为它们是由编译器发出的代码缓存的);另外,您还必须阅读这些树才能恢复该方法。

反射可能很“慢”,但实际上它非常快;特别是因为我相信幕后的数据也被缓存了。因此,一旦您拨打了GetMethod电话,第二个电话就会更快。这为为什么后续表达式树版本变慢提供了另一个令人信服的证据 - 因为它们实际上做了更多的工作。

如果您对 IL 感到满意,请使用所有三个版本编译一个版本,然后使用 ILSpy 或 Reflector 分析编译后的图像(在 C# 模式下,两者都会很聪明,并将表达式代码重新构造回 C#,这不好;所以切换IL 模式) - 看一下生成表达式树的代码,您就会明白我的意思。