静的キーワードを理解する


15

Java、Javascript、およびPHPを使用した開発の経験があります。

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

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

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


4
C#のstaticは、Javaのstaticとほぼ同じです。Javaでそれを理解していれば、C#
-superM

Javaは私の最初のプログラミング言語だったと私は短期間だけのJavaを使用していたがeither.Iこの概念を理解していなかった
Nistorアレクサンドル・

要するに、オブジェクトの向きを必要としない場合、たとえばいくつかのスタンドアロンのメソッドや変数など、「静的」を使用します。クラスを静的であると宣言することは、これらの非オブジェクト指向の関数と変数をクラス名である共通名(スペース)に入れることを意味します。
ドックブラウン

回答:


14

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'
    }

}

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

とにかく、ファクトリー・パターンシングルトン・パターンなどの静的メソッドを使用するための特定の設計パターンがありますが、覚えておくべき重要なことは、静的メソッドとコンストラクターがクラスの特定のインスタンスを処理しないことです(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
}

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


18

ジョシュアブロッホの説明の仕方は次のとおりです。彼の言うことのほとんどと同じように素晴らしいと思います(はい、私はジョシュアブロッホのファンの少年です:))。これはメモリから引用されます。

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

現在、常識では、家(インスタンス)がブループリントで宣言されていても、家(インスタンス)が持つことができる/行うことができるほとんどの機能/動作は、実際の家(インスタンス)がその青から作られるまで使用できないことを規定しています-print(クラス)。たとえば、あなたの青写真には、光のスイッチと電球が行くべき場所が含まれているかもしれませんが、青写真でそれらを機能させる方法はありません。実際に家を建てる必要がありますライトスイッチのオンとオフを切り替え、特定の電球をオンまたはオフにします。

ただし、ブループリントに直接適用できる動作があり、ブループリントから実際の家を作る必要なく、ブループリントで直接使用/アクセスできます。あなたの青写真には、押すとその青写真に含まれる家のフットプリントを表示するボタンがあります(壁の長さなどをすべて計算することによって)。明らかに、最初に家を建ててからそのフットプリントを測定することはできますが、ブループリントだけでこれを行うことができますので、この動作をブループリントに実装する方が便利です。家のフットプリントを計算するこのようなブルー​​プリントの埋め込みボタンは、クラスに静的関数を持たせることに相当します。


またはBlueprint、設計図で表現された家のフットプリントを計算する機能など、設計図の機能を実装するクラスがあります。次に、このブループリントインスタンスはBuilder(それ自体がインスタンスである可能性が高い)に供給さBuildingれ、ブループリントに基づいて潜在的に任意の数のインスタンスを構築および出力するために必要な処理を行います。
CVn

11

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

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

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

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

+1、良い要約のために、私はすべてのタイプが1つの静的インスタンスを持っていることを知りませんでした、そして、私はあなたに真実を言うのがおかしいと思います。
-NoChance

2
@EmmadKareemそれは、PDRが使用しているメンタルモデルにすぎません"Looking at it this way helps"。正確ではないため、奇妙に感じますが、必要に応じてそのように考えることができます。ボーアモデルを知っていますか?それは、原子と電子がどのように相互作用するかという一連のルールとアイデアを提示します。モデルはあなたが何をすべきかに応じて動作しますが、それは現実ではありません。
phant0m

@ phant0m、説明のおかげで、私はそれがモデルではなく本物であるという印象を受け、そのために驚きました。
-NoChance

実際、staticローカルプロパティを使用しない場合でも、メソッドを作成したくない場合があります。物事staticを作成すると、クライアントがクラスに直接対処する必要があるため、クライアントに結合を追加できます。たとえば、これにより、モックを使用した単体テストがより困難になる場合があります。
アラン14年

@Allan:おそらく、クラスのインスタンスの状態に影響を与えないクラスでパブリックメソッドを呼び出す場合、クライアント開発者にそれを明確にするために、静的である必要があります。そのメソッドがモックを必要とするほど多くの場合、それはさまざまな方法で解決できる別の問題です。
pdr 14年

3

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

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

静的変数の場合、すべてのストレージを参照する静的変数を参照する所有クラスのコピーがいくつ作成されても、ランタイム環境にこの変数のインスタンスは1つしかありません。


1

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

.Netについて説明しているため、Stringクラス、特にSplitメソッドとJoinメソッドを検討してください。

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

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

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

結合は静的メソッドです。OK、区切り文字と噛む文字列配列を指定すると文字列結果が得られるので、文字列クラスと「何か」がありますが、特定のことに関連付けられていませんますが、Stringインスタンスのに(実際、インスタンス値は静的メソッドでは使用できません)。
他の言語では、JoinメソッドがArrayクラス(または、おそらくStringArrayクラス)に「スタック」している場合がありますが、RedmondのOur Friendsは、それをStringクラスに「関連性がある」と判断し、そこに配置しました。

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

もう1つの代替方法は、インスタンス Joinメソッドを使用することです。このメソッドでは、String [クラスインスタンス]内に保持されている値が、次のような結合区切り文字として使用されます。

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

1

static初心者が把握するためのキーワードは、少し難しいことができます。その主な目的は、クラスの特定のインスタンスではなく、クラス自体に属するものとしてクラスメンバを識別することです。

詳細を説明しすぎることなく、C#(およびJava)は、すべてのコードとデータがオブジェクトに属している必要があるというオブジェクト指向の理想を厳密に実施します。これは、一般に、現実世界のものを表すオブジェクトの基本的な信条が適用される場合は常にベストプラクティスです。ただし、常にそうとは限りません。必要なのは、コード内のどこからでもアクセスできる関数または変数であり、それを含むオブジェクトへの参照を渡す必要がなく、見たり変更したりするデータが正確であることを保証するものです何を誰もそれ以外の場合は、オブジェクトの別のインスタンスに属しているコピーではなく、処理しています。

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

として宣言されたすべての「コードメンバー」(関数、プロパティ、フィールド)staticは、プログラムのmain()関数の最初の行からスコープに入り、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つだけ必要とする状況では、通常、代わりに「シングルトン」が推奨されます。これは非静的クラスであり、静的アクセサーと非パブリックコンストラクターを使用して、自身の単一インスタンスへのアクセスを提供します。理論的には、シングルトンは完全に静的なクラスとほぼ同じ利点を提供しますが、インスタンスベースのオブジェクト指向の方法でクラスを使用する機能が追加されています。

弊社のサイトを使用することにより、あなたは弊社のクッキーポリシーおよびプライバシーポリシーを読み、理解したものとみなされます。
Licensed under cc by-sa 3.0 with attribution required.