使用静态方法与实例化包含方法的类的性能

时间:2008-10-14 20:18:32

标签: performance oop static methods instance

我正在使用C#开发一个项目。以前的程序员不知道面向对象的编程,所以大多数代码都是在大量的文件中(我们说的是4-5000行),分布在几十个,有时几百个方法上,但只有一个类。重构这样一个项目是一项艰巨的任务,所以我现在已经半熟悉了。

每当在其中一个代码文件中使用方法时,就会实例化该类,然后在对象实例上调用该方法。

我想知道这样做是否会有明显的性能损失?我应该“暂时”将所有方法设为静态,最重要的是,应用程序是否会以任何方式从中受益?

8 个答案:

答案 0 :(得分:25)

here开始,每次调用实例方法时,静态调用比构造实例快4到5倍。但是,我们仍然只是在谈论每次调用几十纳秒,所以你不太可能注意到任何好处,除非你有一个非常紧密的循环调用方法数百万次,并且你可以通过构建一个外部的单个实例获得相同的好处循环并重复使用它。

由于您必须更改每个呼叫站点以使用新的静态方法,因此您可能最好将时间花在逐步重构上。

答案 1 :(得分:9)

我处理过类似的问题。我之前的程序员创建了1个控制器类,其中所有BLL函数都被转储。

我们现在正在重新设计系统,并根据他们应该控制的内容创建了许多Controller类。

UserController,GeographyController,ShoppingController ......

在每个控制器类中,它们都有静态方法,可以使用单例模式调用缓存或DAL。

这给了我们两个主要优势。它稍微快一点(大约快2-3倍,但在这里说纳秒; P)。另一个是代码更清晰

ShoppingController.ListPaymentMethods()

而不是

new ShoppingController().ListPaymentMethods()

如果类不维护任何状态,我认为使用静态方法或类是有意义的。

答案 2 :(得分:7)

这取决于该对象包含的其他内容 - 如果“对象”只是一堆函数,那么它可能不是世界末日。但是如果对象包含一堆其他对象,那么实例化它就会调用它们的所有构造函数(以及析构函数,当它被删除时),你可能会得到内存碎片等等。

也就是说,现在看来性能不是你最大的问题。

答案 3 :(得分:6)

您必须确定重写的目标。如果你想要有可测试的,可扩展的&可维护的OO代码然后您可以尝试使用对象及其实例方法。毕竟这是面向对象的编程,我们在这里讨论,而不是面向类的编程。

在定义实现接口的类并执行实例方法时,伪造和/或模拟对象非常简单。这使得彻底的单元测试变得快速有效。

另外,如果你要遵循良好的面向对象原则(参见http://en.wikipedia.org/wiki/SOLID_%28object-oriented_design%29的SOLID)和/或使用设计模式,你肯定会做很多基于实例的,基于接口的开发,而不是使用很多静态方法

至于这个建议:

  
    

创建一个JUST对象似乎很愚蠢,所以你可以调用一个方法     看似对对象没有任何副作用(根据你的描述我假设这个)。

  

我在点网商店看到了很多,对我来说这违反了封装,这是一个关键的OO概念。我不应该通过方法是否是静态来判断方法是否有副作用。除了打破封装之外,这意味着如果/当您修改它们以产生副作用时,您将需要将方法从静态更改为实例。我建议您阅读本文的开放/封闭原则,并了解上面引用的建议方法如何与此相关。

记住,老栗子,“过早优化是万恶之源”。我认为在这种情况下,这意味着不要使用不适当的技术(即面向类的编程)跳过箍,直到你知道你有性能问题。即使这样调试问题并寻找最合适的。

答案 4 :(得分:4)

静态方法速度更快,使用的内存更少。有一种误解,它只是快一点。只要你不把它放在循环上,它就会快一点。顺便说一下,有些循环看起来很小但实际上并不是因为包含循环的方法调用也是另一个循环。您可以区分执行渲染功能的代码。遗憾的是,在很多情况下,内存很少。实例允许使用姐妹方法轻松共享信息。静态方法会在需要时询问信息。

但就像驾驶汽车一样,速度带来了责任感。静态方法通常具有比其实例对应物更多的参数。因为实例会处理缓存共享变量,所以您的实例方法看起来更漂亮。

ShapeUtils.DrawCircle(stroke, pen, origin, radius);

ShapeUtils.DrawSquare(stroke, pen, x, y, width, length);

VS

ShapeUtils utils = new ShapeUtils(stroke,pen);

util.DrawCircle(origin,radius);

util.DrawSquare(x,y,width,length);

在这种情况下,只要大多数时候所有方法都使用实例变量,实例方法就非常值得。实例不是关于状态,而是关于共享虽然COMMON STATE是一种自然形式的共享,但它们并不相同。一般的经验法则是:如果方法与其他方法紧密结合 - 他们彼此相爱,以至于当一个人被召唤时,另一个人也需要被召唤,他们可能共享同一杯水 - - ,它应该是实例。将静态方法转换为实例方法并不难。您只需要获取共享参数并将它们作为实例变量。另一种方式更难。

或者您可以创建一个桥接静态方法的代理类。虽然理论上似乎效率更低,但实践却讲述了一个不同的故事。这是因为无论何时需要调用DrawSquare(或在循环中),您都可以直接使用静态方法。但是无论何时你要使用DrawCircle一遍又一遍地使用它,你将使用实例代理。一个例子是System.IO类FileInfo(实例)vs File(静态)。

静态方法是可测试的。事实上,比实例一次更可测试。方法GetSum(x,y)不仅可以测试单元测试,还可以测试负载测试,集成测试和使用测试。实例方法适用于单元测试,但对于其他所有测试都是可怕的(这比单元测试BTW更重要),这就是为什么我们这些天会遇到很多错误。使所有方法不可测试的事情是像(Sender s,EventArgs e)或像DateTime.Now这样的全局状态没有意义的参数。事实上,静态方法在可测试性方面非常好,你可以看到新Linux发行版的C代码中的错误少于普通的OO程序员(他知道我已经知道了)。

答案 5 :(得分:3)

我认为您已经按照您提出的方式部分回答了这个问题:您的代码中是否有明显的性能损失?

如果处罚不明显,您根本不需要做任何事情。 (虽然不言而喻,代码库会从渐进的重构中大大受益,成为一个值得尊敬的OO模型)。

我想我所说的是,当你发现这是一个问题时,性能问题只是一个问题。

答案 6 :(得分:2)

创建一个JUST对象似乎很愚蠢,所以你可以调用一个看似对对象没有副作用的方法(从你的描述我假设这个)。在我看来,更好的折衷方案是拥有几个全局对象并使用它们。这样,您可以将通常为全局的变量放入适当的类中,以使它们的范围略小。

从那里你可以慢慢地将这些物体的范围移动得越来越小,直到你有一个像样的OOP设计。

然后, I 可能会使用的方法不同;)。

就个人而言,我可能会专注于对它们进行操作的结构和函数,并尝试将这些转换为成员一点一点的类。

至于问题的性能方面,静态方法应该稍快(但不多),因为它们不涉及构造,传递和解构对象。

答案 7 :(得分:0)

它在PHP中无效,
对象方法更快:
http://www.vanylla.it/tests/static-method-vs-object.php