【问题标题】:When to use static classes in C# [duplicate]何时在 C# 中使用静态类 [重复]
【发布时间】:2010-09-19 11:20:37
【问题描述】:

这是MSDN has to say under When to Use Static Classes

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

以静态类为单位 方法组织不 与特定对象相关联。 此外,静态类可以使您的 实现更简单更快 因为您不必创建一个 对象以调用其方法。 组织方法很有用 在课堂上以一种有意义的方式, 比如 Math 类的方法 在 System 命名空间中。

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

【问题讨论】:

  • 作为 C# 的新手,解释一下为什么这被标记为 singleton vs static class 的重复问题以及这两者之间如何相互关联会有所帮助。
  • mr5,单例和静态类基本上是一回事。单例是其他语言用来模拟静态类的设计模式,因为其他语言(如Java)没有内置静态类,所以你必须依赖单例设计模式来创建这样的类。 Static类是不能实例化的类,可以直接使用(比如Console类)。 tutorialspoint.com/design_pattern/singleton_pattern.htm 如果你检查这个,你会看到当你使用 Singleton 时,你并没有创建一个新的实例......
  • ...您正在使用已在 Singleton 类中创建的那个,并且您可以通过 .getInstance() 方法访问它。 C# 通过一个简单的关键字“static”解决了所有这些问题。
  • 单例类和静态类本质上是完全相反的东西。一个可以实例化,另一个禁止实例化。
  • 恕我直言,在为对象设计属性时,请考虑盒子内部的实例化和开箱即用的静态类。

标签: c# class static


【解决方案1】:

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

【讨论】:

    【解决方案2】:

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

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

    【讨论】:

    • 我宁愿不使用辅助方法的扩展方法。扩展方法很混乱,让其他开发人员感到困惑,并且在其他语言中通常不直观。扩展方法有其目的,但不是作为通用辅助方法恕我直言。
    • 只是为了澄清,通过辅助方法我的意思是:string.ToSentence() 或 string.Camelize()。基本上任何会存在于像 StringHelpers 这样的类中的东西。
    • 扩展方法让你陷入了困境...我认为如果你发现自己为给定类型添加了超过 2 个扩展方法,那么可能是时候检查一下原因了。
    • 这两个例子,我特别不喜欢。类型越通用,就越不适合扩展方法。最糟糕的例子是开始将 ToInt32() 方法添加到 Object、ToDateTime() 等。我宁愿将它们放在单独的类中。
    【解决方案3】:

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

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

    多态性

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

    界面问题

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

    测试

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

    促进 blobs

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

    参数蠕变

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

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

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

    的属性

    只有西斯在做绝对交易

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

    标准、标准、标准!

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

    【讨论】:

    • Steve Yegge 也写过它。但是他的帖子比在这里发布的要长。如果您仍然不相信,请尝试steve.yegge.googlepages.com/singleton-considered-stupid
    • 有没有人注意到“只有西斯在绝对地交易”这句话是绝对的?对不起。没办法。
    • 就像我竭力想表达我的观点一样,耶格也是如此。他的一些观点归结为正常的编程考虑 - 如果单例保持有价值的资源开放,那当然可能是一个问题。仔细考虑后,单例和静态也有类似的地方。
    • @John Kraft:这显然是西斯写的。
    • 他的问题是“何时使用静态类”——但您似乎已经回答了“何时不使用静态类”的问题——很微妙。但是我仍然不知道你什么时候应该使用它们。一些适当使用的例子会很好+如果两个线程非常接近地调用静态类或方法怎么办 - 什么时候好/坏?
    【解决方案4】:

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

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

    【讨论】:

      【解决方案5】:

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

      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 是使用静态访问器调用的。 在大多数情况下对我来说,如果你可以在一个类上调用静态方法,那么你能做的就是这些,所以为了更加清晰,我将这个类标记为静态。

      【讨论】:

      • 为什么要在泛型方法中定义一个不用的参数?
      • @JohnClearZ 可能是因为您可以使用现有对象调用For 并获取它的记录器,而无需考虑对象的实际类型。只是猜测。
      【解决方案6】:

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

      这更像是一个准则而不是规则,真的......

      【讨论】:

      • 此外,实例类中私有方法上的此类静态声明向开发人员传达了有用的附加信息。它通知这样的私有静态方法不会改变实例状态。即该方法是类中的实用方法。
      【解决方案7】:

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

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

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

      【讨论】:

        【解决方案8】:

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

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

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

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

        【讨论】:

        • “你一定是无形的,无形的,像水一样。当你把水倒进杯子里时,它就变成了杯子。当你把水倒进瓶子里时,它就变成了瓶子。当你把水倒进茶壶里,它就变成了茶壶。水可以滴落,也可以崩溃。变得像水一样,我的朋友。” - 李小龙
        • @Chef_Code 抱歉,我无法理解 - 如果您不介意我问的话,您的报价与给定问题有什么关系?
        • 你可以在 C++ 中创建类,和 C# 差不多,这是 C++ 和 C 的主要区别。
        • 很好的解释——就简单性和简洁性而言,这是我见过的最好的解释。我会向这个主题的任何人推荐这一个加上马克拉斯穆森的更多细节!
        【解决方案9】:

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

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

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

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

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

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

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

        【讨论】:

        • 答案说“只能有一个”不是在谈论静态类的数量,而是很可爱(虽然相当不正确)暗示只能有一个实例(错误,因为实际上只能有零个实例)
        • 谢谢柯克,所以我很困惑;无论如何,在我看来,简单地忽略静态或笼统地说单例模式更好(正如我所见)是从工具箱中取出一个非常有价值的工具。
        • “只能有一个”是指现实世界中可以用静态类表示的概念或事物。与 System.Math 一样,您可能只需要一种方法来获取特定输入并离开并获得正确答案。只有一种方法可以进行基本数学 - 其他任何东西都太专业而无法与标准库相关联。另一个例子可能是一个只有一个房间或一个运动场的简单视频游戏。
        【解决方案10】:

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

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

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

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

        【讨论】:

          【解决方案11】:

          基于MSDN:

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

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

          【讨论】:

          • 您只是在描述什么是静态类,而不是描述它们何时实用,或者它们的用途是什么。
          • 问题是关于什么时候使用静态类而不是你可以用静态类做什么。
          猜你喜欢
          • 1970-01-01
          • 1970-01-01
          • 1970-01-01
          • 2014-07-13
          • 2020-08-06
          • 1970-01-01
          • 2018-08-28
          • 2011-06-08
          • 1970-01-01
          相关资源
          最近更新 更多