クラスを「静的」にする理由と時期 クラスの「静的」キーワードの目的は何ですか?


47

static多くの言語のメンバーのキーワードは、そのメンバーにアクセスできるようにするためにそのクラスのインスタンスを作成しないことを意味します。ただし、クラス全体を作成する理由はありませんstatic。なぜ、私はクラスを作る必要があるときstatic

クラスを作るとどんなメリットがありstaticますか?つまり、静的クラスを宣言した後も、インスタンス化せずにアクセスしたいすべてのメンバーを静的として宣言する必要があります。

これは、たとえば、Math開発者のコ​​ーディング方法に影響を与えることなく、クラスを(静的ではなく)通常と宣言できることを意味します。言い換えれば、クラスを静的または通常にすることは、開発者にとっては透過的です。


Rich HickeyのようなFunc Progスタイルでコーディングしている場合、これは便利です。
ジョブ

1
私の意見では、静的クラスは単にC#が言語の大規模な設計上の欠陥を隠そうとする松葉杖です。この無意味なすべてのクラスイデオロギーを中心に言語を構築し、追加の構文を導入して、この不必要な制限をごまかして回避できるようにするのはなぜですか。C#がget-goからの無料の関数を許可した場合、静的クラスは必要ありません。
antred

回答:


32

クラスがどのように使用されるかをユーザーに明らかにします。たとえば、次のコードを記述することはまったくナンセンスです。

Math m = new Math();

C#はこれ禁止する必要はありません、目的を果たさないため、ユーザーにそれを伝えることもできます。特定の人々(私を含む)は、プログラミング言語(およびAPI…)を可能な限り制限して間違った使用を困難にするという哲学を固守しています。


3
私はその哲学に同意しません。私が抱えている問題は、変化が必要なことです。そのため、これまで制限することは理にかなっているかもしれませんが、その制限は将来のニーズに対する開発を妨げます。そして、古いシステム(すべてのクラスを作成して封印し、静的にしたもの)とのインターフェースに使用することを余儀なくされたレガシーライブラリは、作成したコードを拡張または変更する方法を提供しません。クラスをインスタンス化する理由がなかったからといって、将来そのクラスを必要としないわけではありません。ユーティリティクラスの静的化は理にかなっていますが、将来も考慮してください。
-SoylentGray

19
@Chad次に、ライブラリの設計が不適切です。拡張用に設計されていないクラスの拡張は最終的には機能しないためsealed、最初にクラスを作成することをお勧めします。すべての人々がこの感情に同意するわけではないが、コメントはこれを議論するのに適した場所ではないことを認める。しかし、staticこの問題の場合、それ自体を引き起こすことさえありません。インスタンス化System.Mathは意味をなしません
コンラッドルドルフ

1
@Konard、Mathクラスを静的として宣言せずにクラスのすべてのメンバーを静的として宣言したMath場合でも、インスタンス化を必要とせずに使用できます。
サイードネアマティ

11
@Saeed-これは事実ですが、クラスをインスタンス化することもできますが、これは何の目的にも役立ちません。Mathクラスのインスタンスで何をしますか?クラスの意図を明示的に示すため(インスタンス化は不要または必要ではありません)、マークされていstaticます。
コービン

3
@Saeedこれを好転させましょう:あなたはそれを誤解しているように見えるので、私はあなたにもう一度私の答えを読むことを提案します:あなたのコメントは私の答えとは無関係です。あなたが書く必要があると言ったことはありませんnew Math()
コンラッドルドルフ

24

StackOverflowには、このトピックに関する素晴らしい議論があります。参照しやすくするために、著者のMark S. Rasmussenを代表して、ここにコピーして貼り付けます。

以前のスレッドで静的クラスの考えを書きまし

以前は、静的メソッドで満たされたユーティリティクラスが大好きでした。彼らは、そうでなければ冗長性と保守の地獄を引き起こすことを避けなければならないであろうヘルパーメソッドの大きな統合を行いました。それらは非常に使いやすく、インスタンス化も廃棄も不要で、ただの消火も忘れません。これは、サービス指向アーキテクチャを作成しようとする最初の無意識の試みだったと思います。多くのステートレスサービスが仕事をしただけで、他には何もありませんでした。しかし、システムが成長するにつれて、ドラゴンが登場します。

多型

たとえば、幸運にも話題になっているUtilityClass.SomeMethodメソッドがあるとします。突然、機能をわずかに変更する必要があります。ほとんどの機能は同じですが、それでもいくつかの部分を変更する必要があります。静的メソッドではなかった場合、派生クラスを作成し、必要に応じてメソッドの内容を変更できます。静的メソッドなので、できません。もちろん、古いメソッドの前または後に機能を追加する必要がある場合は、新しいクラスを作成して、その中にある古いクラスを呼び出すことができますが、それは大雑把です。

インターフェースの問題

静的なメソッドは、ロジック上の理由により、インターフェースを介して定義できません。また、静的メソッドをオーバーライドすることはできないため、静的クラスは、インターフェイスで渡す必要がある場合は役に立ちません。これにより、戦略パターンの一部として静的クラスを使用できなくなります。インターフェイスの代わりにデリゲートを渡すことにより、いくつかの問題を修正することができます。

テスト中

これは基本的に、上記のインターフェイスの問題と密接に関連しています。実装を交換する能力は非常に限られているため、実稼働コードをテストコードに置き換えるのも困難です。繰り返しますが、それらをラップすることはできますが、実際のオブジェクトの代わりにラッパーを受け入れることができるようにするために、コードの大部分を変更する必要があります。

塊を育てる

通常、静的メソッドはユーティリティメソッドとして使用され、ユーティリティメソッドは通常異なる目的を持つため、すぐに非一貫性のある機能で満たされた大きなクラスになります-理想的には、各クラスはシステム内で単一の目的を持つ必要があります。目的が明確に定義されている限り、5倍のクラスが必要です。

パラメータクリープ

まず、その小さく可愛くて無邪気な静的メソッドは、単一のパラメーターを取ることができます。機能が大きくなると、いくつかの新しいパラメーターが追加されます。すぐにオプションのパラメーターが追加されるため、メソッドのオーバーロードを作成します(または、それらをサポートする言語でデフォルト値を追加します)。やがて、10個のパラメーターを取るメソッドがあります。最初の3つのみが実際に必要であり、パラメーター4〜7はオプションです。しかし、パラメーター6が指定されている場合、7-9も入力する必要があります。コンストラクタ、およびユーザーがプロパティを通じてオプションの値を設定できるようにするか、メソッドを使用して複数の相互依存する値を同時に設定できます。また、

理由もなくクラスのインスタンスを作成することを消費者に要求する

最も一般的な議論の1つは、クラスのコンシューマーがこの単一のメソッドを呼び出すためのインスタンスを作成し、その後インスタンスを使用しないように要求することです。クラスのインスタンスの作成は、ほとんどの言語で非常に安価な操作であるため、速度は問題になりません。消費者に追加のコード行を追加することは、将来、ずっと保守しやすいソリューションの基盤を築くための低コストです。最後に、インスタンスの作成を避けたい場合は、簡単に再利用できるクラスのシングルトンラッパーを作成するだけです。ただし、これにより、クラスがステートレスであるという要件が作成されます。ステートレスでない場合でも、すべてを処理する静的ラッパーメソッドを作成できますが、長期的にはすべての利点が得られます。最後に、

シスだけが絶対を扱う

もちろん、静的メソッドの嫌いには例外があります。肥大化のリスクをもたらさない真のユーティリティクラスは、静的メソッドの優れた例です-例としてSystem.Convert。ただし、プロジェクトが将来のメンテナンスの必要がない1回限りのプロジェクトである場合、全体的なアーキテクチャはそれほど重要ではありません-静的であっても非静的であっても、実際には重要ではありません-開発速度は重要です。

標準、標準、標準!

インスタンスメソッドを使用しても、静的メソッドを使用することを妨げることはありません。差別化の背後に理由があり、標準化されている限り。さまざまな実装方法で広がっているビジネス層を見渡すことほど悪いことはありません。


12
うーん、ここに回答全体をコピーして貼り付けることを知らない。リンクと言い換えかもしれませんが、「議論」について言及しているので、別の視点を含めて、あなた自身の経験を共有してください。
コービン

2
「シスだけがアブソリュートを扱う」-以前はそのように使われていたのを見たことがなかった....しかしそれは完璧だ。私を笑わせた。
ブルック

4
@コービン:マークは素晴らしい答えを持っていました。私は彼にクレジットを与え、参照しやすいようにコピー/ペーストしました。この問題に関する私自身の見解は、マークの見解と一致しています。議論を始める以外に、あなたのコメントの要点はわかりません。
リック

1
@Rick:コピーされた答えは非常に長く、言い換えれば間違いなく役立つでしょう。「このSO投稿が示すように、単に有用ではないというだけでなく、害を及ぼす可能性さえあります」というような文だけでも、探している情報ではないので、すぐにスキップできます。
-blubb

@サイモン:私はあなたの投稿を1+ 'しました。馬鹿げたことを議論するのをやめるために、私は同意します。:)うまくいけば、元のポスターが私の返信/コピーと貼り付けに役立つことを発見しました。
リック

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
サイードネアマティ

3

私にとって、静的クラス(C#)は、関数を呼び出すようなものです。

例えば

public static string DoSomething(string DoSomethingWithThisString){}

文字列を渡して、文字列を取得します。すべてがそのメソッドに含まれています。メンバー変数などへのアクセスなし

正直なところ、ほとんどの場合、その使用はコード行を減らすことです。

なぜか:

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

これができるとき:

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

そのため、クラスを必要とせずに何かをしたいときは、静的クラスを使用します。これにより、入力が少なくなります。

マークS.ポイントは有効ですが、私にとっていくつかのユーティリティメソッドがある場合、それらを1つのライナーとして参照する関数を呼び出しているふりをする方が簡単です。

それは単純なことかもしれませんが、それは私が使用している方法staticです。


1
new MyClass()。DoSomething( "test")はまだ有効です。これはテストデータビルダーによく使用します。
JoanComasFdz 14年
弊社のサイトを使用することにより、あなたは弊社のクッキーポリシーおよびプライバシーポリシーを読み、理解したものとみなされます。
Licensed under cc by-sa 3.0 with attribution required.