为什么以及何时使课程“静态”?类上的“静态”关键字的目的是什么?


47

static多种语言在成员上使用关键字表示您不应创建该类的实例才能访问该成员。但是,我认为没有理由进行整个课堂学习static。我为什么以及何时上课static

上课能给我带来什么好处static?我的意思是,在声明静态类之后,仍然应该将他/她希望不带实例化即可访问的所有成员也声明为静态。

例如,这意味着Math可以将类声明为普通(非静态),而不会影响开发人员的编码方式。换句话说,将类设为静态或普通对开发人员而言是透明的。


如果您使用的是Rich Fickey风格的Func Prog编码,这可能会很有用。
工作

1
在我看来,静态类只是C#试图掩盖该语言中大量设计缺陷的拐杖。为什么要围绕这种荒谬的一切(要成为一类思想)来构造一种语言,然后才引入其他语法,以便您可以欺骗并解决这种不必要的限制。如果C#刚开始允许使用自由函数,那么就不需要静态类。
2013年

Answers:


32

它使用户明显知道如何使用该类。例如,编写以下代码完全是胡说八道:

Math m = new Math();

C#不必须禁止这一点,但因为它没有任何意义,还不如告诉用户。某些人(包括我在内)恪守这样的理念:编程语言(和API…)应尽可能地严格限制,以使它们难以被错误使用:只有允许的操作才是有意义且(希望)正确的操作。


3
我不同意这种哲学。我的问题是需要改变。因此,尽管在现在之前对其进行限制可能很有意义,但这种限制阻碍了我们对未来需求的发展。而且,我们不得不使用旧版库与旧系统接口(您编写的旧版库,并密封并静态化所有类),它无法为我们提供任何扩展或修改您编写的代码的方法。仅仅因为您没有理由实例化您的课程并不意味着将来我将不再需要它。虽然将实用程序类静态化是有意义的,但也要考虑未来。
SoylentGray

19
@Chad然后库设计不正确。并非为扩展而设计的扩展类最终将无法正常工作,因此最好首先创建该类sealed。我承认并非所有人都同意这种观点,但评论并不是讨论此事的好地方。但在的情况下,static这个问题甚至不构成本身:实例System.Math永远是有意义的。
康拉德·鲁道夫

1
@Konard,如果您将Math类的所有成员都声明Math为静态,而没有将类声明为静态,则仍然可以使用它,而无需实例化。
2011年

11
@Saeed-是的,但是您也可以实例化该类,它没有任何作用。我如何处理Math类的实例?为了明确显示类的意图(不需要或不需要实例化),将其标记为static
科宾2011年3

3
@Saeed让我们扭转一下:我建议您再次阅读我的答案,因为您似乎误解了:您的评论与我的答案无关。我从没说过你需要写信new Math()
康拉德·鲁道夫

24

StackOverflow对此主题进行了精彩的讨论。为了便于参考,我将代表作者Mark S. Rasmussen复制并粘贴到此处:

我在较早的线程中写了关于静态类的想法:

我曾经喜欢用静态方法填充的实用程序类。他们对辅助方法进行了很好的合并,否则将导致冗余和维护麻烦。它们非常易于使用,无需实例化,无需处理,只是忘了忘了。我想这是我首次尝试创建面向服务的体系结构-许多无状态服务只是完成了工作,而没有其他。但是,随着系统的发展,巨龙将会来临。

多态性

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

界面问题

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

测试中

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

福斯特斑点

由于静态方法通常用作实用程序方法,而实用程序方法通常具有不同的用途,因此我们很快就会得到一个充满非一致性功能的大型类-理想情况下,每个类在系统中应具有一个单一的用途。只要他们的目的得到明确定义,我宁愿选择五倍的课程。

参数蠕变

首先,这个可爱又天真的静态方法可能只需要一个参数。随着功能的增长,添加了两个新参数。不久,添加了其他可选参数,因此我们创建了该方法的重载(或仅添加默认值(使用支持它们的语言))。不久,我们有了一个采用10个参数的方法。实际上只需要前三个,参数4-7是可选的。但是,如果指定了参数6,则还必须填写7-9 ...如果我们仅出于执行此静态方法的目的而创建了一个类,则可以通过在构造函数,并允许用户通过属性或方法设置可选值,以同时设置多个相互依赖的值。也,

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

最常见的论据之一是,为什么要求我们的类的使用者创建一个实例来调用该单一方法,而之后再不使用该实例呢?在大多数语言中,创建类的实例是非常便宜的操作,因此速度不是问题。向消费者添加额外的代码行是为将来可维护性更高的解决方案奠定基础的低成本。最后,如果要避免创建实例,只需创建类的单例包装即可,以方便重用-尽管这确实要求类是无状态的。如果不是无状态的,您仍然可以创建可处理所有内容的静态包装器方法,而从长远来看仍将为您带来所有好处。最后,

只有一个西斯处理绝对

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

标准,标准,标准!

使用实例方法并不妨碍您也使用静态方法,反之亦然。只要差异化背后有其合理性并被标准化即可。没有什么比查看遍布着不同实现方法的业务层更糟糕的了。


12
嗯,不知道我会在此处复制并粘贴整个答案。也许链接和释义包含自您提到“讨论”以来的另一种观点,并分享您自己的经验?
科宾2011年3

2
“只有西斯能处理绝对数字”-我以前从未见过像那样使用……。但这很完美。让我笑了。
布鲁克

4
@Corbin:马克回答得很好。我给了他荣誉,我复制/粘贴以方便参考。我个人对此事的看法与马克的观点相符。除了开始辩论之外,我看不到您的评论的重点。
里克(Rick)

1
@Rick:复制的答案很长,将其改写肯定会有所帮助。就像这样的句子:“不仅如此,它们并没有真正有用,甚至可能会造成伤害,正如本SO帖子所显示的那样:”将让我快速跳过它,因为我看到的不是我在寻找的信息。
blubb

@Simon:大声笑我1+编辑了您的帖子。为了不再争论如此愚蠢的事情,我同意。:)希望原始张贴者发现我的回复/复制和粘贴有用。
里克(Rick)

16

我很惊讶没有其他人提到static类允许扩展方法 -安全地扩展您不拥有的现有类型(包括向接口添加方法定义)。例如,在Scala中是

trait MyFoo {
  def foo: Int
  def plusFoo(a: Int) = foo + a
}

可以用C#表示为

public interface IMyFoo {
  int Foo();
}

public static class MyFooExtensions {
  public static int PlusFoo(this IMyFoo f, int a) {
    return f.Foo() + a;
  }
}

您在干草堆中发现了针。+1
Saeed Neamati

3

对我来说,静态类(在C#中)有点像调用一个函数。

例如

public static string DoSomething(string DoSomethingWithThisString){}

我传入一个字符串,然后返回一个字符串。一切都包含在该方法中。无法访问成员变量等

老实说,对我而言,它的主要用途是减少代码行:

为什么:

MyClass class = new MyClass();
String s = class.DoSomething("test");

当我可以这样做时:

String s = MyClass.DoSomething("test");

因此,在那些情况下,当我想要做一些不需要类的事情并且意味着更少的键入时,我将使用静态类。

Mark S.点是有效的,但是对我来说,如果我有一些实用方法,就更容易假装我要调用一个函数以将它们引用为一个衬里。

那可能很简单,但这主要是我使用的方式static


1
新的MyClass()。DoSomething(“ test”)仍然有效。我在测试数据构建器中经常使用它。
JoanComasFdz 2014年
By using our site, you acknowledge that you have read and understand our Cookie Policy and Privacy Policy.
Licensed under cc by-sa 3.0 with attribution required.