30

我正在使用 C# 开发一个项目。以前的程序员不了解面向对象编程,所以大部分代码都在巨大的文件中(我们谈论的是大约 4-5000 行),分布在数十个甚至数百个方法中,但只有一个类。重构这样一个项目是一项艰巨的任务,所以我现在已经半学会了忍受它。

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

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

4

8 回答 8

28

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

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

于 2008-10-14T21:02:11.023 回答
10

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

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

用户控制器、地理控制器、购物控制器...

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

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

IE

ShoppingController.ListPaymentMethods()

代替

new ShoppingController().ListPaymentMethods()

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

于 2008-12-25T03:16:51.060 回答
7

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

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

于 2008-10-15T03:08:49.147 回答
6

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

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

此外,如果您要遵循良好的 OO 原则(请参阅http://en.wikipedia.org/wiki/SOLID_%28object-orientated_design%29上的 SOLID )和/或使用设计模式,您肯定会做很多基于实例的,基于接口的开发,并没有使用很多静态方法。

至于这个建议:

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

我在 dot net 商店中经常看到这种情况,对我来说这违反了封装,这是一个关键的 OO 概念。我应该无法通过该方法是否为静态来判断该方法是否具有副作用。除了打破封装之外,这意味着如果/当您修改它们以产生副作用时,您将需要将方法从静态更改为实例。我建议您阅读此原则的开放/封闭原则,看看上面引用的建议方法是如何考虑到这一点的。

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

于 2012-01-17T12:38:12.437 回答
6

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

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

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);

在这种情况下,只要实例变量在大多数情况下被所有方法使用,实例方法就非常值得。实例与状态无关,它与共享有关,尽管公共状态是共享的自然形式,但它们并不相同。一般的经验法则是这样的:如果该方法与其他方法紧密耦合——它们非常相爱,以至于当一个被调用时,另一个也需要被调用,并且它们可能共享同一杯水—— - 应该是实例。将静态方法转换为实例方法并不难。您只需要获取共享参数并将它们作为实例变量。反过来更难。

或者您可以创建一个代理类来桥接静态方法。虽然它在理论上可能看起来效率更低,但实践却讲述了一个不同的故事。这是因为每当您需要调用一次 DrawSquare(或在循环中)时,您都会直接使用静态方法。但是,当您将它与 DrawCircle 一起反复使用时,您将使用实例代理。一个例子是 System.IO 类 FileInfo(实例)与 File(静态)。

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

于 2014-11-09T11:14:44.853 回答
3

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

如果惩罚不明显,您根本不需要做任何事情。(尽管毫无疑问,代码库将从逐渐重构为受人尊敬的 OO 模型中受益匪浅)。

我想我的意思是,只有当您注意到这是一个问题时,性能问题才是一个问题。

于 2008-10-14T21:08:04.210 回答
2

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

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

再说一次,可能会使用的方法是不同的;)。

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

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

于 2008-10-14T20:26:36.510 回答
0

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

于 2015-04-03T13:45:26.963 回答