web-dev-qa-db-ja.com

staticキーワードを理解する

Java、JavaScript、PHPでの開発経験があります。

Microsoft Visual C#2010を読んでいますが、C#言語を紹介する上で非常に良い本だと思います。

Staticキーワードの理解に問題があるようです。これまで私が理解していたことから、クラスが静的に宣言されている場合、すべてのメソッドと変数は静的である必要があります。メインメソッドは常に静的メソッドであるため、メインメソッドが存在するクラスでは、すべての変数とメソッドがメインメソッドで呼び出す必要がある場合に静的と宣言されます。また、別のクラスから静的メソッドを呼び出すために、クラス名を使用できるオブジェクトを作成する必要がないことにも気付きました。

しかし、静的キーワードの実際の目的は何ですか?静的変数とメソッドはいつ宣言する必要がありますか?

16

C#の 'static'キーワードは、クラスのすべてのインスタンス間で共有される、クラス内の何か、またはクラス自体を指します。たとえば、静的とマークされているフィールドには、クラス名を使用して、そのクラスのすべてのインスタンスからアクセスできます。

public class SomeObject
{
    //Static Field
    static int Foo = 3;

    //instance field
    private int _Foo2 = 4;

    //instance property
    public int Foo2{get{return _Foo2;}set{_Foo2 = value;}}


    //static factory method
    public static SomeObject CreateSomeObject(int fooValue)
    {
        SomeObject retVal = new SomeObject();
        retVal.Foo2 = fooValue;
        return retVal;
    }

    //Parameterless instance constructor
    public SomeObject()
    {
    }

    public static int Add(int x)
    {
        //Static methods can only deal with local variables, or fields that
        //  are also static in the class.  This one adds x to the static member foo
        return x + Foo;

        //Foo2 is not accessable here!
    }

      //Instance method
    public int AddSomething(int x)
    {
        //Add x to the property value of Foo2
        return x + this.Foo2;

        //Note that Foo *is* accessable here as 'SomeObject.Foo'
    }

}

正直に言うと、拡張メソッドの作成( 拡張メソッドのクイックチュートリアル )を除いて、静的としてマークされたクラスを使用したことはありません。

とにかく、 factory patternsingleton pattern など、静的メソッドを利用するための特定のデザインパターンがありますが、重要なのは、静的メソッドとコンストラクターが処理しないことです。クラスの特定のインスタンス(1つを渡さない限り)。通常、計算またはオブジェクト間の比較を行います。参照している「メイン」メソッドは常に静的ですが、別の観点から見ると、 この記事を参照 です。

これをフォローアップするために、静的メソッドとインスタンス化されたメソッド、フィールド、プロパティの違いを以下に示します。

public static void Main(string[] args)
{
    //This is a static method that starts a thread in an application
    // space.  At this point not everything actually has to be static...

    //Here is an instantiation with a parameterless contruction
    SomeObject obj = new SomeObject();

    //Here is an instantiation using a static factory method
    SomeObject obj2 = SomeObject.CreateSomeObject(3);

    //Getting field value from static field
    // Notice that this references the class name, not an instance
    int fooValue1 = SomeObject.Foo;

    //Getting property value from instance
    //  Note that this references an object instance
    int fooValue2 = obj2.Foo2;

    //Instance method must be called through an object
    obj2.AddSomething(4);  //if default constructor, would return 8

    //Static methods must be called through class name
    SomeObject.Add(4); //Returns 7
}

また、静的クラスの詳細については、 この投稿 を確認してください。

15
iMortalitySX

ジョシュア・ブロッホがそれを説明する方法は次のとおりです。私は彼が言うほとんどのように素晴らしいと思います(そう、私はジョシュア・ブロッホのファンです:))。これは記憶から引用されています。

クラスが家の青写真に相当すると想像してください。次に、クラスのインスタンスはクラス用であるため、家が設計図にあると想像してください。 1つのクラス(設計図)とそれから作成された複数のインスタンス(家)を持つことができます。

現在、常識では、家(インスタンス)が設計図で宣言されていても、家(インスタンス)が持つことができる/実行できるほとんどの機能/動作は、実際の家(インスタンス)がその青から作成されるまで使用できません。 -print(クラス)。同様に、設計図には、ライトスイッチと電球の配置場所が含まれている可能性がありますが、設計図でそれらを機能させる方法はなく、実際に家を建てる必要があります。ライトスイッチのオン/オフを切り替え、特定の電球をオン/オフにします。

ただし、ブループリントに直接適用できる動作があり、ブループリントから実際の家を作成する必要なくブループリントで直接使用/アクセスできる場合があります。あなたの設計図に、押すと、その設計図に含まれている家のフットプリントを表示するボタンがあるとします(壁の長さなどをすべて計算して)。当然、最初に家を建ててから、その設置面積を測定しますが、設計図だけでこれを行うことができるため、この動作を設計図に実装するとより効果的です。家の設置面積を計算するこのような青写真の埋め込みボタンは、クラスに静的関数を含めるのと同じです。

19
Shivan Dragon

このようにそれを見ると私に役立ちます:

  • すべてのタイプには静的インスタンスがあります。
  • 静的インスタンスは、静的インスタンスを通じて、または別のインスタンスを作成することによって、タイプに初めてアクセスしたときに作成されます。
  • 非静的インスタンスはいくつでも作成できますが、静的インスタンスは1つしかありません。
  • Staticとして宣言されたクラス内のすべてのものはstaticインスタンスに属しているため、作成した他のインスタンスにはアクセスできません。ただし、他のインスタンスは静的インスタンスにアクセスできます。
  • クラスが静的として宣言されている場合、他のインスタンスを作成することはできません。存在できるのは静的インスタンスのみです。
  • 静的インスタンスの静的コンストラクターは、通常のインスタンスのコンストラクターと同じように宣言できます(ただし静的に宣言することにより)。

Staticキーワードをいつ使用するかについて:

  • ローカルプロパティにアクセスする必要のないメソッドは、静的であると宣言できます。
  • 状態がまったくなく(とにかくまれであるはずです)、モックされないヘルパークラスは、静的と宣言できます。彼らがすべきかどうかは別の問題です。この機能は慎重に使用してください。
  • クラスのすべてのインスタンスからアクセスする必要があるプロパティとフィールドは、静的に宣言する必要があります。ただし、他に選択肢がない場合にのみ使用してください。
12
pdr

最も簡単な説明--- Static =>環境ごとに1つのコピーのみが存在します。

したがって、VMまたはCLR内では、静的クラスのコピーは1つだけであり、それを参照する他のクラスは、そのメソッドとデータを、それを参照する他のすべてのクラスと共有する必要があります。

静的変数の場合、静的変数を参照するときに所有するクラスのコピーがいくつ作成されても、ランタイム環境にはこの変数のインスタンスが1つだけ存在し、すべて同じストレージを参照します。

4
James Anderson

静的メンバーは、そのクラスのインスタンスではなく、クラスに関連付けられています。

.Netについて話しているので、Stringクラス、特にSplitおよびJoinメソッドを検討してください。

Splitはinstanceメソッドです。文字列変数を作成して値を指定すると、その変数/値に対してSplit()を呼び出して、「ビット」の配列を取得できます。

String s1 = "abc,def,ghi" ; 
String[] array2 = s1.Split( ',' ) ; 

したがって、インスタンスメソッドの場合、指定されたクラスインスタンス内に保持される値matters

結合はstaticメソッドです。 OK、デリミタとかみ砕く文字列配列が与えられると、文字列resultを生成するので、文字列クラスと「何かをする」しかし、それは、任意のStringインスタンス内の特定のvalueに関連付けられているnotです(実際に、インスタンス値は静的メソッドでは使用できません)。
他の言語では、JoinメソッドはArrayクラス(または、おそらくStringArrayクラス)に「スタック」されている可能性がありますが、Redmondの私たちの友人は、Stringクラスに「より関連性がある」と判断しました。彼らはそれをそこに置きました。

String[] array3 = { ... } 
s1 = String.Join( array3, "," ) ; 

別の代替案は、instanceJoinメソッドを使用することでした。この場合、String [クラスインスタンス]内に保持されている値を、結合区切り文字として使用しました。 :

// Maybe one day ... 
String s4 = "," ; 
s1 = s4.Join( array3 ) ; 
2
Phill W.

staticキーワードは、初心者が理解するのが少し難しい場合があります。その主な目的は、クラスのメンバーをクラスの単一のインスタンスではなく、クラス自体に属するものとして識別することです。

C#(およびJava)は、詳細に触れずに、すべてのコードとデータがオブジェクトに属している必要があるため、スコープ、可視性、およびライフタイムが制限されているというオブジェクト指向の理想を厳密に適用します。これは一般に、現実世界のものを表すオブジェクトの基本的な原則が適用される場合は常にベストプラクティスです。ただし、常にそうとは限りません。必要なのは、コードでanywhereから取得できる関数または変数であり、それを含むオブジェクトへの参照を渡す必要がなく、探しているデータが確実であるatまたは変更はexactly他のすべての人が処理していることであり、オブジェクトの別のインスタンスに属するコピーではありません。

このような動作は、CおよびC++では、「グローバル」関数または変数の形で利用できましたが、オブジェクトにはカプセル化されていませんでした。したがって、妥協案として、C#とJavaは「静的スコープ」をサポートします。これは、親オブジェクトのない真にグローバルなコードと限定スコープのインスタンスメンバーの中間点です。

staticとして宣言された「コードメンバー」(関数、プロパティ、フィールド)は、プログラムのmain()関数の1行目からスコープに入り、main()関数は終了します。単純な英語では、静的メンバーが存在し、プログラムが実行されている限り使用できます。さらに、静的メンバーは、その型の1つのインスタンスのメンバーではなく、型自体のメンバーとして呼び出すことによって呼び出されます。

public class Foo
{
   public int MyInt {get;set;} //this is an "instance member"
   public static int MyStaticInt {get;set;} //this is a "static member"
}

...

var myFoo = new Foo();
myFoo.MyInt = 5; //valid
myFoo.MyStaticInt = 5; //invalid; MyStaticInt doesn't belong to any one Foo

Foo.MyInt = 5; //invalid; MyInt only has meaning in the context of an instance
Foo.MyStaticInt = 2; //valid

これにより、静的メンバーは、そのタイプの単一のインスタンスについて知っているかどうかに関係なく、そのタイプを知っているすべてのコードから見えるようになります。

あなたの質問に答えるために、何かを静的としてマークすることの主な利点は、使用するコードが包含オブジェクトのインスタンスを持っているかどうかを問わず、型自体がどこにあるかがわかるようになることです。 わずかのパフォーマンス上の利点もあります。メソッドは静的スコープにあるため、(同じクラスまたは他の)他の静的メンバー、およびパラメーターとして渡されたものにのみアクセスできます。したがって、コンテキストは、コンテキスト固有の状態情報を提供するためにインスタンスメソッドで通常必要となるので、ランタイムは包含オブジェクトの現在のインスタンスへの参照を解決する必要はありません。

クラス全体を静的としてマークすることもできます。そうすることで、クラス宣言が静的メンバーのみで構成され、インスタンス化できないことをコンパイラーに伝えます。これは、メモリにオブジェクトのコピーが1つだけ存在することを確認する簡単な方法です。クラスとその中のすべてを静的にします。ただし、これがそのようなニーズに対する最良のソリューションであることは非常にまれです。データセットのコピーが1つだけ必要な状況では、代わりに「シングルトン」が一般的に推奨されます。これは非静的クラスであり、静的アクセサーと非パブリックコンストラクターを使用して、それ自体の単一インスタンスへのアクセスを提供します。理論的には、シングルトンは完全に静的なクラスとほぼ同じ利点を提供しますが、インスタンスベースのオブジェクト指向の方法でクラスを使用する機能が追加されます。

2
KeithS