.NET反射的成本有多高?

232

我经常听到使用反射很糟糕。虽然我通常避免使用反射,并且很少发现没有反射无法解决问题的情况,但我想知道...

对于那些在应用程序中使用过反射的人,您是否测量了性能损失?它真的很糟糕吗?


您可能还想查看这个问题。https://dev59.com/w3VC5IYBdhLWcg3wpi58 - smaclell
1
使用 fasterflect.codeplex.com 上的 API。它可以将 getter / setter / invoker 和其他一些东西的反射加速约 500 倍。如果您需要扩展它,那里也有源代码和信息说明如何工作。 - Brandon Moore
3
这些信息在2014年是否准确?这4年有什么变化吗? - Arnthor
1
将值分配给实例属性的简单任务,使用反射(PropertyInfo.SetValue(instance, value))大约比直接编码(instance.property = value)慢150倍。这是在.NET 4.0中。 - Thanasis Ioannidis
13个回答

169
在他的演讲中日常事物的表现, Jeff Richter 表示,通过反射调用方法比正常调用慢1000倍
Jeff的建议:如果需要多次调用该方法,请使用反射一次找到它,然后将其分配给一个委托,然后再调用该委托。

20
我也参加了Devscovery活动,对于.NET 3.5的结果表示同意。重新编译Devscovery性能基准程序针对.NET 4版本展示了巨大的改进!成本降低到慢100倍。在.NET 3.5和.NET 4之间,使用typeof()查找的反射方式没有改变。 - John Wigger

144

是的,但这取决于您要做什么。

我使用反射来动态加载程序集(插件),其性能“惩罚”不是问题,因为这个操作是我在应用程序启动期间执行的。

但是,如果您在一系列嵌套循环中进行反射调用,我建议您重新检查代码 :)

对于“几次”的操作,反射是完全可接受的,您不会注意到任何延迟或问题。它是一个非常强大的机制,甚至被.NET使用,所以我不明白为什么您不应该试一试。


我一直在使用反射来获取当前方法的方法和类名,以便在try-catch中记录错误,基本上是为了避免在记录错误时硬编码函数名称。我需要担心吗? - Sangram Nandkhile
@Sangram 不,那没问题。 - Karthik AMR
@Sangram 不需要,除非你有很多需要不断捕获的错误,那就应该是另一个问题了 :) - Martin Marconcini
6
@Sangram,虽然在你的情况下反射性能不应该是问题,但似乎你正在试图以一种更优雅的方式通过重新实现普通异常来提供相同的功能... - Jacek Gorgoń

65

反射性能将取决于实现方法(重复调用应缓存,例如:entity.GetType().GetProperty("PropName"))。由于我每天看到的大部分反射都用于从数据读取器或其他存储库类型结构中填充实体,因此我决定专门针对反射进行基准测试,特别是当它用于获取或设置对象属性时。

我设计了一个测试,我认为这是公平的,因为它缓存所有重复调用,并仅计时实际的SetValue或GetValue调用。性能测试的所有源代码都在bitbucket上: https://bitbucket.org/grenade/accessortest。欢迎并鼓励审查。

我得出的结论是,当反射实现良好且数据访问层一次返回少于100,000行时,删除反射不是实际可行且不会提供明显的性能改进。

时间(y)与实体数量(x)图表

上面的图表展示了我的小型基准测试的输出结果,并显示出在反射之外表现更好的机制,只有在超过100,000个周期后才能明显看到。大多数数据访问层一次只返回几百个或几千个行,在这些水平上,反射表现良好。


9
不一定。你的DAL转换可能只涉及几千个项目,但如果使用您的应用程序的并发用户(如果它是Web应用程序),那么乘以此数量可能会像转换了数百万个项目一样累加。如果某种方法慢100倍,在小型和大型数据集上也同样慢。慢就是慢。 - Robert Koritnik
@RobertKoritnik 假设您的服务器上的Web方法不是异步的。 - Kurren
1
@kurren 异步并不影响反射,而是影响服务器资源。异步Web方法当然能够服务更多的用户,但反射仍然会很慢。我所知道的,反射本身就是一个同步过程。另一方面,数据获取将是唯一能够与异步设计良好协作的部分。 - Robert Koritnik
3
“Hyper method” 在图表中是什么?它与“Reflector”有何不同? - Bryan Legend
我应该引用这个@LoneCoder:http://www.codeproject.com/Articles/18450/HyperDescriptor-Accelerated-dynamic-property-acces,由http://stackoverflow.com/users/23354/marc-gravell。 - grenade

13
如果您不在循环中,不要担心它。

13

并不是非常大的问题。除非像Martin所说,你在一个愚蠢的位置使用它,在桌面开发中我从未遇到过这样的问题。我听说很多人对它在桌面开发中的性能有彻底的非理性担忧。

然而,在我通常使用的Compact Framework中,它几乎是忌讳的,在大多数情况下应该像瘟疫一样避免使用。虽然我偶尔仍可以轻松使用它,但我必须非常小心地应用它,这是一件不太有趣的事情。:(


8
谢谢教我一个新词:“anathema”。还提到了无理恐惧。我害怕那些无理恐惧的程序员——这表明他们不真正知道自己在做什么,只是根据别人告诉他们的来做。咳嗽装载文化咳嗽 - bsneeze
2
啊,货物崇拜。这是一个好奇人类行为的典型例子。 - Quibblesome

12

我的最有用的经验是编写代码来逐个属性地比较大型对象模型中任意两个相同类型的数据实体。我让它运行起来了,尝试了一下,但它运行得非常慢。

我感到沮丧,然后在一夜之间意识到,在不改变逻辑的情况下,我可以使用相同的算法来自动生成用于进行比较的方法,但是静态地访问属性。很快就能够将代码调整为此目的,并具备使用静态代码进行深度属性比较的功能,而且可以在对象模型更改时轻松更新代码。

我的观点是:在与同事交谈时,我已经多次指出他们使用反射的方式可以自动生成代码以进行编译,而不是执行运行时操作,这通常值得考虑。


考虑到Visual Studio具有如此出色的模板支持,使用代码生成是一种实用的方式。 - Sebastian

9

即使是由.NET库内部执行的反射,对于性能至关重要的代码也需要担心。

以下示例已过时 - 在更近期的CLR版本中已经修复。总体而言,反射仍然是一项有一定成本的操作!

举个例子:在高性能代码中,您永远不应该使用声明为“Object”的成员来进行锁定(C#)/ SyncLock(VB.NET)语句。为什么?因为CLR无法锁定值类型,这意味着它必须进行运行时反射类型检查,以查看您的对象实际上是值类型还是引用类型。


13
公平地说,反射类型检查速度较快。 - Jimmy
1
对于这样的“性能关键代码”,你真的应该从一开始就使用.NET吗? - Seph
1
@Seph:关于.NET的动态/反射部分,不行。但是对于通常的C#/.NET,为什么不呢?在应用层面上,C++与C#的速度提升微不足道(C++在密集数学例程方面仍然比C#快几个百分点)。我猜你不是在建议使用汇编语言吧... - DeepSpace101
一个装箱的值类型(即对象)可以被锁定。@BryceWagner 是正确的。 - Fowl
1
公正地说(对我来说),更准确地说答案是“过时”,而不是“纯粹的胡说八道”。我关于lock(obj)行为的评论在写下时是准确的,但CLR的特定实现行为早已不存在。 - McKenzieG1
显示剩余2条评论

5
与编程中的所有事物一样,您必须在性能成本和获得任何好处之间取得平衡。当小心使用时,反射是一种非常有价值的工具。我在C#中创建了一个O/R映射库,它使用反射进行绑定。这个库运行得非常出色。大多数反射代码只执行一次,因此任何性能损失都很小,但是好处很大。如果我要编写一个新的高级排序算法,我可能不会使用反射,因为它可能不会很好地扩展。
我知道我在这里并没有完全回答您的问题。我的观点是,这并不重要。在适当的情况下使用反射。它只是另一个语言特性,您需要学习如何以及何时使用它。

4

如果您经常使用反射来创建对象,那么反射可能会对性能产生明显的影响。我开发了一个基于Composite UI Application Block的应用程序,它大量依赖反射。由于通过反射创建对象,导致了明显的性能下降。

然而,在大多数情况下,反射的使用没有问题。如果您只需要检查某个程序集,我建议使用Mono.Cecil,它非常轻量级和快速


3
反射操作是昂贵的,因为每当您请求匹配参数列表的方法时,运行时都必须进行许多检查。在代码的深处,存在循环遍历类型的所有方法的代码,验证其可见性,检查返回类型以及检查每个参数的类型。所有这些都需要时间成本。

当您在内部执行该方法时,会有一些代码来检查您是否传递了兼容的参数列表,然后才执行实际目标方法。

如果可能的话,建议将方法句柄缓存起来,以便将来持续重复使用它。像所有良好的编程技巧一样,避免重复通常是有意义的。在这种情况下,不断查找具有特定参数的方法,然后每次执行它都是浪费的。

深入源代码并查看正在执行的内容。


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