我经常听到使用反射很糟糕。虽然我通常避免使用反射,并且很少发现没有反射无法解决问题的情况,但我想知道...
对于那些在应用程序中使用过反射的人,您是否测量了性能损失?它真的很糟糕吗?
我经常听到使用反射很糟糕。虽然我通常避免使用反射,并且很少发现没有反射无法解决问题的情况,但我想知道...
对于那些在应用程序中使用过反射的人,您是否测量了性能损失?它真的很糟糕吗?
是的,但这取决于您要做什么。
我使用反射来动态加载程序集(插件),其性能“惩罚”不是问题,因为这个操作是我在应用程序启动期间执行的。
但是,如果您在一系列嵌套循环中进行反射调用,我建议您重新检查代码 :)
对于“几次”的操作,反射是完全可接受的,您不会注意到任何延迟或问题。它是一个非常强大的机制,甚至被.NET使用,所以我不明白为什么您不应该试一试。
反射性能将取决于实现方法(重复调用应缓存,例如:entity.GetType().GetProperty("PropName")
)。由于我每天看到的大部分反射都用于从数据读取器或其他存储库类型结构中填充实体,因此我决定专门针对反射进行基准测试,特别是当它用于获取或设置对象属性时。
我设计了一个测试,我认为这是公平的,因为它缓存所有重复调用,并仅计时实际的SetValue或GetValue调用。性能测试的所有源代码都在bitbucket上: https://bitbucket.org/grenade/accessortest。欢迎并鼓励审查。
我得出的结论是,当反射实现良好且数据访问层一次返回少于100,000行时,删除反射不是实际可行且不会提供明显的性能改进。
上面的图表展示了我的小型基准测试的输出结果,并显示出在反射之外表现更好的机制,只有在超过100,000个周期后才能明显看到。大多数数据访问层一次只返回几百个或几千个行,在这些水平上,反射表现良好。
并不是非常大的问题。除非像Martin所说,你在一个愚蠢的位置使用它,在桌面开发中我从未遇到过这样的问题。我听说很多人对它在桌面开发中的性能有彻底的非理性担忧。
然而,在我通常使用的Compact Framework中,它几乎是忌讳的,在大多数情况下应该像瘟疫一样避免使用。虽然我偶尔仍可以轻松使用它,但我必须非常小心地应用它,这是一件不太有趣的事情。:(
我的最有用的经验是编写代码来逐个属性地比较大型对象模型中任意两个相同类型的数据实体。我让它运行起来了,尝试了一下,但它运行得非常慢。
我感到沮丧,然后在一夜之间意识到,在不改变逻辑的情况下,我可以使用相同的算法来自动生成用于进行比较的方法,但是静态地访问属性。很快就能够将代码调整为此目的,并具备使用静态代码进行深度属性比较的功能,而且可以在对象模型更改时轻松更新代码。
我的观点是:在与同事交谈时,我已经多次指出他们使用反射的方式可以自动生成代码以进行编译,而不是执行运行时操作,这通常值得考虑。
即使是由.NET库内部执行的反射,对于性能至关重要的代码也需要担心。
以下示例已过时 - 在更近期的CLR版本中已经修复。总体而言,反射仍然是一项有一定成本的操作!
举个例子:在高性能代码中,您永远不应该使用声明为“Object”的成员来进行锁定(C#)/ SyncLock(VB.NET)语句。为什么?因为CLR无法锁定值类型,这意味着它必须进行运行时反射类型检查,以查看您的对象实际上是值类型还是引用类型。
如果您经常使用反射来创建对象,那么反射可能会对性能产生明显的影响。我开发了一个基于Composite UI Application Block的应用程序,它大量依赖反射。由于通过反射创建对象,导致了明显的性能下降。
然而,在大多数情况下,反射的使用没有问题。如果您只需要检查某个程序集,我建议使用Mono.Cecil,它非常轻量级和快速。
当您在内部执行该方法时,会有一些代码来检查您是否传递了兼容的参数列表,然后才执行实际目标方法。
如果可能的话,建议将方法句柄缓存起来,以便将来持续重复使用它。像所有良好的编程技巧一样,避免重复通常是有意义的。在这种情况下,不断查找具有特定参数的方法,然后每次执行它都是浪费的。
深入源代码并查看正在执行的内容。