.NET反射的成本是多少?
我不断地听到要使用多么糟糕的反射。虽然我通常会避免反思,很少会找到没有它无法解决我的问题的情况,但我想知道...
对于那些在应用程序中使用过反射的人,我们是否衡量了性能方面的影响,真的那么糟糕吗?
解决方案
回答
它是。但这取决于我们要执行的操作。
我使用反射来动态加载程序集(插件),并且它的性能"惩罚"不是问题,因为该操作是我在应用程序启动期间执行的操作。
但是,如果我们要在一系列嵌套循环中进行反射,并在每个循环上进行反射调用,那么我想我们应该重新访问代码:)
对于"几次"操作,反射是完全可以接受的,并且我们不会注意到任何延迟或者问题。这是一种非常强大的机制,甚至.NET都在使用它,所以我不明白为什么不应该尝试一下。
回答
一切都与评估情况有关。在DotNetNuke中,有一个相当核心的组件" FillObject",该组件使用反射来填充数据行中的对象。
这是一个相当普遍的场景,并且在MSDN上有一篇文章,《使用反射将业务对象绑定到ASP.NET表单控件》涵盖了性能问题。
除了性能之外,我不喜欢在特定情况下使用反射的一件事是,它倾向于降低快速了解代码的能力,对于我来说,当我们认为自己也失去了编译能力时,这似乎不值得付出努力。时间安全性,而不是强类型数据集或者LINQtoSQL之类的方法。
回答
如果我们经常使用反射创建对象,则反射会对性能产生显着影响。我已经开发了基于Composite UI Application Block的应用程序,它非常依赖反射。与通过反射创建对象有关的性能显着下降。
但是,在大多数情况下,反射的使用没有问题。如果我们唯一需要检查的组件,我建议我们使用Mono.Cecil,它非常轻巧,快速
回答
就像编程中的所有事情一样,我们必须在性能成本与所获得的任何收益之间进行权衡。谨慎使用反思是一种无价的工具。我在C中创建了一个O / R映射库,该库使用反射来进行绑定。这非常好用。大部分反射代码仅执行一次,因此对性能的影响很小,但是好处却是巨大的。如果我正在编写一个新的fandangled排序算法,则可能不会使用反射,因为它的伸缩性可能很差。
感谢我们在这里没有完全回答问题。我的观点是,这并不重要。在适当的地方使用反射。这只是我们需要学习如何以及何时使用的另一种语言功能。
回答
杰夫·里希特(Jeff Richter)在他的演讲《日常事物的性能》中指出,通过反射调用方法比正常调用要慢大约1000倍。
Jeff的技巧:如果我们需要多次调用该方法,请使用一次反射来查找它,然后将其分配给一个委托,然后调用该委托。
回答
这非常糟糕,即使对于性能关键代码的.NET库在内部进行的反射,我们也必须担心。
以下示例在当时(2008)时已过时,但很早以前已在较新的CLR版本中修复。但是,总体而言,反思仍然是一件昂贵的事情!
恰当的例子:永远不要在高性能代码的锁(C#)/ SyncLock(VB.NET)语句中使用声明为"对象"的成员。为什么?因为CLR无法锁定值类型,所以这意味着它必须进行运行时反射类型检查,以查看Object是否实际上是值类型而不是引用类型。
回答
我最相关的经验是编写代码,以比较大型对象模型的属性来比较同一类型的任何两个数据实体。可以正常工作,尝试一下,显然像狗一样奔跑。
我很沮丧,然后一夜之间意识到,如果不改变逻辑,我可以使用相同的算法自动生成用于进行比较但静态访问属性的方法。完全不需要花任何时间就可以为此目的修改代码,并且我能够使用静态代码对实体进行深入的属性比较,只要对象模型发生更改,单击按钮即可更新静态代码。
我的观点是:自从我多次与同事交谈以来,我就指出他们对反射的使用可能是为了自动生成代码进行编译,而不是执行运行时操作,这通常值得考虑。
回答
没有大规模。我从来没有在桌面开发中遇到任何问题,除非像Martin指出的那样,除非我们在一个愚蠢的位置使用它。我听说很多人对它在桌面开发中的性能表现出完全非理性的恐惧。
不过,在紧凑型框架(通常是在紧凑型框架中)中,这简直是令人厌恶,在大多数情况下,应像瘟疫一样避免使用。我仍然可以不经常使用它,但是我必须非常小心它的应用程序,因为它的乐趣不那么严重。 :(
回答
反射不会大大降低应用程序的性能。通过不使用反射,我们也许可以更快地完成某些事情,但是如果反射是实现某些功能的最简单方法,请使用它。如果变成了性能问题,我们总是可以将代码重构为远离反射。
回答
我认为我们会发现答案取决于它。如果要将其放入任务列表应用程序中,这没什么大不了的。如果我们想将其放入Facebook的持久性库中,这很重要。
回答
如果我们不在循环中,请不必担心。
回答
反射的成本很高,因为在我们请求与参数列表匹配的方法时,运行时必须进行许多检查。在内部的某个深处,存在代码,该代码遍历某个类型的所有方法,验证其可见性,检查返回类型,还检查每个参数的类型。所有这些东西都花费时间。
当我们在内部执行该方法时,会有一些代码在执行实际的目标方法之前执行一些检查,例如检查我们是否传递了兼容的参数列表。
如果可能的话,如果将来要继续重用它,总是建议人们缓存该方法句柄。像所有好的编程技巧一样,避免重复自己通常很有意义。在这种情况下,连续查找具有某些参数的方法然后每次都执行将是浪费的。
在源头上戳一下,看看正在做什么。
回答
反射性能将取决于实现(重复调用应被缓存,例如:" entity.GetType()。GetProperty(" PropName")`)。由于我每天看到的大部分反射都是用来填充数据读取器或者其他存储库类型结构中的实体的,因此我决定在使用反射来获取或者设置对象属性时专门针对反射进行基准测试。
我设计了一个我认为很公平的测试,因为它可以缓存所有重复的调用,并且只对实际的SetValue或者GetValue调用进行一次。性能测试的所有源代码都位于以下位置的bitbucket中:https://bitbucket.org/grenade/accessortest。欢迎并鼓励进行审查。
我得出的结论是,它不切实际,并且没有提供显着的性能改进来消除数据访问层中的反射,该数据访问层在反射实现完成时一次返回的行数少于100,000。
上图显示了我的小基准测试的输出,并显示了优于反射的机制,只有在100,000个周期后才明显地表现出来。大多数DAL一次仅返回数百行,甚至数千行,在这些级别上,反射性能很好。