654

以下是MSDN 在When to Use Static Classes下所说的:

static class CompanyInfo
{
    public static string GetCompanyName() { return "CompanyName"; }
    public static string GetCompanyAddress() { return "CompanyAddress"; }
    //...
}

使用静态类作为与特定对象无关的方法的组织单元。此外,静态类可以使您的实现更简单、更快,因为您不必创建对象即可调用其方法。以有意义的方式组织类内部的方法很有用,例如 System 命名空间中的 Math 类的方法。

对我来说,这个例子似乎并没有涵盖静态类的很多可能的使用场景。过去,我曾将静态类用于相关函数的无状态套件,但仅此而已。那么,在什么情况下应该(也不应该)将一个类声明为静态的?

4

11 回答 11

763

我在早期的 Stack Overflow 答案中写下了我对静态类的想法: Class with single method -- best approach?

我曾经喜欢充满静态方法的实用程序类。他们对辅助方法进行了极大的整合,否则这些方法会导致冗余和维护地狱。它们非常易于使用,无需实例化,无需处置,只需一劳永逸。我想这是我第一次在不知情的情况下尝试创建面向服务的架构——许多无状态服务只是完成了它们的工作,没有别的。然而,随着系统的发展,巨龙即将到来。

多态性

假设我们有方法 UtilityClass.SomeMethod 愉快地嗡嗡作响。突然我们需要稍微改变一下功能。大多数功能是相同的,但我们仍然必须更改几个部分。如果它不是静态方法,我们可以创建一个派生类并根据需要更改方法内容。因为它是一个静态方法,我们不能。当然,如果我们只需要在旧方法之前或之后添加功能,我们可以创建一个新类并在其中调用旧类——但这很糟糕。

接口问题

由于逻辑原因,不能通过接口定义静态方法。而且由于我们不能覆盖静态方法,所以当我们需要通过接口传递静态类时,它们是无用的。这使我们无法使用静态类作为策略模式的一部分。我们可能会通过传递委托而不是接口来修补一些问题。

测试

这基本上与上面提到的界面问题密切相关。由于我们交换实现的能力非常有限,我们也很难用测试代码替换生产代码。同样,我们可以将它们包装起来,但这需要我们更改大部分代码才能接受包装器而不是实际的对象。

促进斑点

由于静态方法通常用作实用方法,而实用方法通常有不同的用途,我们很快就会得到一个充满不连贯功能的大型类——理想情况下,每个类在系统中应该有一个单一的用途。只要他们的目的明确,我宁愿有五倍的课程。

参数蠕变

首先,那个可爱又纯真的静态方法可能只需要一个参数。随着功能的增长,添加了几个新参数。很快就会添加更多可选参数,因此我们创建方法的重载(或仅添加默认值,使用支持它们的语言)。不久之后,我们就有了一个需要 10 个参数的方法。只有前三个是真正需要的,参数 4-7 是可选的。但是如果指定了参数 6,那么 7-9 也需要填写...如果我们创建一个类的目的只是为了做这个静态方法所做的事情,我们可以通过在构造函数,并允许用户通过属性设置可选值,或者同时设置多个相互依赖的值的方法。此外,如果一种方法已经发展到如此复杂的程度,

无缘无故要求消费者创建类的实例

最常见的论点之一是:为什么要求我们类的消费者创建一个实例来调用这个单一方法,而之后却没有使用该实例?在大多数语言中,创建类的实例是一项非常便宜的操作,因此速度不是问题。向消费者添加额外的代码行成本很低,可以为未来更易于维护的解决方案奠定基础。最后,如果您想避免创建实例,只需创建一个允许轻松重用的类的单例包装器 - 尽管这确实要求您的类是无状态的。如果它不是无状态的,您仍然可以创建处理所有内容的静态包装器方法,同时从长远来看仍然为您提供所有好处。最后,new MyClass();

只有西斯在绝对交易

当然,我不喜欢静态方法也有例外。不会造成任何膨胀风险的真正实用程序类是静态方法的极好案例 - 以 System.Convert 为例。如果您的项目是一次性的,对未来的维护没有要求,那么整体架构真的不是很重要 - 静态或非静态,并不重要 - 但是开发速度确实如此。

标准,标准,标准!

使用实例方法不会阻止您也使用静态方法,反之亦然。只要差异化背后有推理并且它是标准化的。没有什么比查看具有不同实现方法的业务层更糟糕的了。

于 2008-10-27T21:01:00.293 回答
162

在决定将类设为静态还是非静态时,您需要查看您尝试表示的信息。这需要一种更“自下而上”的编程风格,您首先关注您所代表的数据。你正在写的课程是像石头还是椅子这样的真实世界的对象?这些东西是物理的,并且具有诸如颜色、重量等物理属性,这告诉您您可能想要实例化具有不同属性的多个对象。我可能同时想要一把黑色椅子和一把红色椅子。如果您同时需要两个配置,那么您会立即知道您希望将其实例化为一个对象,这样每个对象都可以是唯一的并且可以同时存在。

另一方面,静态函数倾向于更多地用于不属于现实世界对象或您可以轻松表示的对象的动作。请记住,C# 的前身是 C++ 和 C,您可以在其中定义类中不存在的全局函数。这有助于“自上而下”的编程。静态方法可用于“对象”执行任务没有意义的情况。通过强制您使用类,这使得对相关功能进行分组变得更加容易,从而帮助您创建更可维护的代码。

大多数类都可以用静态或非静态来表示,但是当您有疑问时,只需回到您的 OOP 根源并尝试考虑您所表示的内容。这是一个正在执行动作的对象(一辆可以加速、减速、转弯的汽车)还是更抽象的东西(比如显示输出)。

与你内心的 OOP 联系,你永远不会出错!

于 2012-02-01T02:50:11.207 回答
37

对于 C# 3.0,扩展方法可能只存在于顶级静态类中。

于 2008-10-27T20:57:06.323 回答
30

如果您使用代码分析工具(例如FxCopstatic ),如果该方法不访问实例数据,它会建议您标记该方法。理由是有性能提升。MSDN:CA1822 - 将成员标记为 static

与其说是规则,不如说是指导方针,真的……

于 2008-10-27T21:39:12.730 回答
13

我确实倾向于对工厂使用静态类。例如,这是我的一个项目中的日志记录类:

public static class Log
{
   private static readonly ILoggerFactory _loggerFactory =
      IoC.Resolve<ILoggerFactory>();

   public static ILogger For<T>(T instance)
   {
      return For(typeof(T));
   }

   public static ILogger For(Type type)
   {
      return _loggerFactory.GetLoggerFor(type);
   }
}

您甚至可能已经注意到 IoC 是使用静态访问器调用的。 对我来说,大多数时候,如果你可以在一个类上调用静态方法,那就是你所能做的,所以为了更加清晰,我将这个类标记为静态。

于 2008-10-27T21:11:55.670 回答
12

静态类非常有用并且占有一席之地,例如库。

我能提供的最好的例子是 .Net Math 类,它是一个包含数学函数库的系统命名空间静态类。

就像其他任何事情一样,使用正确的工具来完成工作,否则任何事情都可能被滥用。

盲目地将静态类视为错误,不要使用它们,或者说“只能有一个”或没有,与过度使用它们一样错误。

C#.Net 包含许多与 Math 类一样使用的静态类。

因此,如果实现正确,它们将非常有用。

我们有一个包含许多与业务相关的时区函数的静态 TimeZone 类,不需要像 Math 类那样创建该类的多个实例,它在静态类中包含一组全局可访问的 TimeZone 相关函数(方法) .

于 2013-01-04T23:47:16.640 回答
10

当我希望使用函数而不是类作为我的重用单元时,我已经开始使用静态类。以前,我都是关于静态类的邪恶的。然而,学习F#让我对它们有了新的认识。

我这是什么意思?好吧,假设在处理一些超级DRY代码时,我最终得到了一堆单一方法类。我可能只是将这些方法拉入一个静态类,然后使用委托将它们注入到依赖项中。这也与我选择 Autofac 的依赖注入(DI) 容器配合得很好。

当然,直接依赖静态方法通常仍然是邪恶的(有一些非邪恶的用途)。

于 2011-07-01T09:34:51.860 回答
9

我使用静态类来定义给定类型的对象可以在特定上下文中使用的“额外功能”。通常它们是实用程序类。

除此之外,我认为“使用静态类作为与特定对象无关的方法的组织单元”。很好地描述了它们的预期用途。

于 2008-10-27T21:05:24.577 回答
7

这是自 OOP 开始以来另一个古老但非常热门的问题。当然,使用(或不使用)静态类的原因有很多,其中大部分已经在众多答案中涵盖。

我将为此添加我的 2 美分,说,我将一个类设为静态,当这个类在系统中是唯一的并且在程序中拥有它的任何实例时真的没有意义。但是,我将这种用法保留给大班。我从不将 MSDN 示例中的小类声明为“静态”,当然,也不会声明将成为其他类成员的类。

我还想指出,静态方法和静态是需要考虑的两件不同的事情。接受的答案中提到的主要缺点是静态方法。静态提供与普通类相同的灵活性(涉及属性和参数),其中使用的所有方法都应与类存在的目的相关。

在我看来,静态类候选的一个很好的例子是“FileProcessing”类,它将包含与程序的各种对象相关的所有方法和属性,以执行复杂的 FileProcessing 操作。拥有多个此类的实例几乎没有任何意义,并且静态将使程序中的所有内容都可以轻松使用它。

于 2014-10-17T07:46:33.183 回答
3

我只对辅助方法使用静态类,但随着 C# 3.0 的出现,我宁愿使用扩展方法。

我很少使用静态类方法,原因与我很少使用单例“设计模式”的原因相同。

于 2008-10-27T21:00:33.883 回答
0

基于MSDN

  1. 您不能为静态类创建实例
  2. 如果类声明为静态,则该类的成员变量应该是静态的
  3. 封印【不可继承】
  4. 不能包含实例构造函数
  5. 内存管理

示例:数学计算(数学值)不会改变 [STANDARD CALCULATION FOR DEFINED VALUES]

于 2014-12-12T06:57:49.827 回答