静态反射性能

9

我正在尝试使用Joel Abrahamsson的博客和Daniel Cazzulino的博客关于静态反射代码的玩法。但是我发现它们的性能有点慢,即使与使用“魔术字符串”的反射相比。

int iterations = 1000000; 
watch.Start();

for (int i = 0; i < iterations; i++)
{
    var propertyOfName = Reflect<Employee>.GetProperty(c => c.Name); 
}

watch.Stop();
Console.WriteLine("[Reflector]: " + watch.ElapsedMilliseconds.ToString());
watch.Reset();
watch.Start();

for (int i = 0; i < iterations; i++)
{
    var propertyName = typeof (Employee).GetProperty("Name"); 
}

watch.Stop();
Console.WriteLine("[Regular Reflection]: " + watch.ElapsedMilliseconds.ToString());
watch.Reset();
watch.Start();

for (int i = 0; i < iterations; i++)
{
    var propertyName = StaticReflection.GetMemberName<Employee>(c => c.Name);
}

watch.Stop();
Console.WriteLine("[StaticReflection]: " + watch.ElapsedMilliseconds.ToString());

以下是结果:

  • [反射器]:37823
  • [常规反射]:780
  • [静态反射]:24362

那么为什么我们应该更喜欢静态反射呢?只是为了去除“魔法字符串”吗?还是我们应该添加一些缓存来提高静态反射的性能?


1
缓存StaticReflection的结果。如果您请求Employee.Name,它在该应用程序域的生命周期内永远不会更改,因此缓存是安全的。 - Just another metaprogrammer
这不是静态反射。 - user90843
2个回答

4
"prefer"这种写法的主要原因是编译器的静态类型检查,以确保您不会搞砸它(并确保它在混淆时工作,例如)。然而,在我看来,这种错误很少会导致一个显著的错误(这意味着:我不包括在开发/单元测试期间发现和修复的愚蠢的打字错误); 因此(由于我是性能追求者),我通常建议使用最简单的选项(string)。一个特别的例子是,当人们使用像这样的技巧实现INotifyPropertyChanged接口时。只需传递一个string ;p


谢谢,马克!有趣的是,几年前Ayende也使用了.NET 2.0进行了类似的操作。请参见http://ayende.com/blog/779/static-reflection。他的结论还包括通过使用静态反射来提高性能。由于他的演示代码无法再下载,我无法复制它。 - Liang Wu
@Liang 可能直接检查了 byte[] 实现以获取方法句柄 - 这可能比 Expression 快得多。 - Marc Gravell
不幸的是,构建表达式树非常缓慢,.NET 不会自动缓存表达式树。有趣的是,在 C# 中构建动态表达式的开销(例如使用 DynamicObject)比使用 Expression<> 更低。 - Just another metaprogrammer
2
@Fule 我想这是能够任意参数化的功能。这意味着他们可以缓存一个带有“捕获”类参数的表达式。然后可以为所有调用者重复使用它。在常规LINQ中,签名是预定义的,因此必须每次使用捕获实例的ConstantExpression来构建表达式树。 - Marc Gravell
@Fule 进一步说:这里做(或不做)表达式树缓存的是 C# 编译器,而不是 .NET(但通过 dynamic,.NET 会为每个已解析类型缓存 IL)。 - Marc Gravell
@Marc 是的,我已经考虑到需要一个捕获表达式来将“变量”从叶子移动到根。我真的很希望C#4能够实现这一点,但不幸的是没有。在我看来,当前的实现限制了表达式树的全部潜力。 - Just another metaprogrammer

2
你看过 http://ayende.com/blog/779/static-reflection 吗?他只使用了委托(而不是表达式树),并且改进了与常规反射相比的性能。
以下是其实现例子。
 public static MethodInfo MethodInfo<TRet, A0>(Func<TRet, A0> func0)
{
        return func0.Method;
}

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接