ジェネリッククラスの静的メンバーは特定のインスタンスに関連付けられていますか?


84

これは、実際の質問というよりもドキュメントです。これはまだSOで対処されていないようです(私がそれを見逃していない限り)ので、ここに行きます:

静的メンバーを含むジェネリッククラスを想像してみてください。

class Foo<T> {
    public static int member;
}

特定のクラスごとにメンバーの新しいインスタンスがありますか、それともすべてのFooタイプのクラスに1つのインスタンスしかありませんか?

次のようなコードで簡単に確認できます。

Foo<int>.member = 1;
Foo<string>.member = 2;
Console.WriteLine (Foo<int>.member);

結果はどうなりますか?また、この動作はどこに文書化されていますか?


4
簡単な答え:実際のクラスごとに新しいインスタンスがあります。つまり、T使用されるタイプごとに1つです(2つの異なるクラスFoo<int>Foo<string>表し、それぞれに1つのインスタンスがありますが、のいくつかのFoo<int>インスタンスはの単一のインスタンスを共有しますmember)。より詳細な例については、stackoverflow.com
Kjartan 2016

回答:


91

staticフィールドは、すべてのインスタンス間で共有され、同じタイプのFoo<int>Foo<string>は2つの異なるタイプです。これは、次のコード行で証明できます。

// this prints "False"
Console.WriteLine(typeof(Foo<int>) == typeof(Foo<string>));

これが文書化されている場所については、セクション1.6.5 C#言語仕様のフィールド(C#3の場合)に次の内容があります。

静的フィールドは、1つの保管場所を正確に識別します。クラスのインスタンスがいくつ作成されても、静的フィールドのコピーは1つだけです。

前に述べたように; Foo<int>Foo<string>同じクラスではありません。これらは、同じジェネリッククラスから構築された2つの異なるクラスです。これがどのように発生するかは、上記のドキュメントのセクション4.4に概説されています。

ジェネリック型宣言は、それ自体が、型引数を適用することにより、多くの異なる型を形成するための「青写真」として使用される、バインドされていないジェネリック型を示します。


26
C#とJavaの両方で開発する場合の落とし穴があります。一方でFoo<int>Foo<String>C#の異なる種類があり、それらは同じため、Javaがジェネリック(タイプ消去/コンパイラトリック)を扱う方法をJavaでタイプ。
パワーロード2010年

この場合はどうなりますか:Foo <int> foo1 = new Foo <int>(); foo1.member = 10; Foo <int> foo2 = new Foo <int>(); foo2.member = 20; ここでは何が起きるのですか?
全員

@Everyoneは、同じタイプのFoo <int>を共有するため、両方のインスタンスでメンバーの値が変更されます(20になります)。
スタースイワノフ

1
静的メンバーを持つ非ジェネリック基本クラスを派生ジェネリッククラスに継承するとどうなりますか?これはまだ本当ですか?
Brain2000 2017年

@ StasIvanov、foo1.memberは10のままで、foo2.memberは20になります。確認してください
SHRI

16

ここでの問題は、実際には「ジェネリッククラス」はクラスではないという事実です。

ジェネリッククラス定義はクラスの単なるテンプレートであり、それらの型パラメーターが指定されるまで、それらは単なるテキスト(または数バイト)です。

実行時に、テンプレートの型パラメーターを指定して、テンプレートを実現し、現在は完全に指定されている型のクラスを作成できます。そのため、静的プロパティはテンプレート全体ではなく、との間List<string>でキャストできないのはそのためですList<int>

その関係は、クラスとオブジェクトの関係を反映しています。クラスからオブジェクトをインスタンス化するまでクラスが存在しない*のと同様に、テンプレートに基づいてクラスを作成するまで、ジェネリッククラスは存在しません。

PS宣言することはかなり可能です

class Foo<T> {
    public static T Member;
}

このことから、Tは専門分野ごとに異なるため、静的メンバーを共有できないことは明らかです。



3

ジェネリックスのC#実装は、C ++にさらに近いものです。これらの言語の両方でMyClass<Foo>MyClass<Bar>Javaの静的メンバを共有することはありませんが、中に彼らはありません。C#およびC ++ではMyClass<Foo>、ジェネリックが一種のマクロであるかのように、コンパイル時に内部的にまったく新しい型を作成します。あなたは、通常のように、スタックトレースにその生成された名前を見ることができるMyClass'1MyClass'2。これが、静的変数を共有しない理由です。Javaでは、ジェネリックは、非ジェネリック型を使用してコードを生成し、型キャストを全体に追加するコンパイラのより単純な方法によって実装されます。したがって、Javaで2つのまったく新しいクラスを生成しないMyClass<Foo>MyClass<Bar>ください。代わりに、両方とも同じクラスのMyClass下にあり、静的変数を共有するのはそのためです。


1

それらは実際には共有されていません。メンバーはインスタンスにまったく属していないためです。静的クラスメンバーは、クラス自体に属します。したがって、MyClass.Numberがある場合、それはオブジェクトに依存しないため、すべてのMyClass.Numberオブジェクトで同じです。オブジェクトなしでMyClass.Numberを呼び出したり変更したりすることもできます。

ただし、Foo <int>はFoo <string>と同じクラスではないため、これら2つの数値は共有されません。

これを示す例:

TestClass<string>.Number = 5;
TestClass<int>.Number = 3;

Console.WriteLine(TestClass<string>.Number);  //prints 5
Console.WriteLine(TestClass<int>.Number);     //prints 3

-4

IMO、テストする必要がありますが、

Foo<int>.member = 1;
Foo<string>.member = 2;
Console.WriteLine (Foo<int>.member);

1コンパイル中に、コンパイラーが使用するジェネリッククラスごとに1つのクラスを作成すると思うので(例では:Foo<int>Foo<string>)が出力されます。

しかし、私は100%確信が持てません=)。

備考:このような静的属性を使用することは、良い設計でも良い習慣でもないと思います。


6
そうでない場合は100%を確認してください-ないコメントください
SLL
弊社のサイトを使用することにより、あなたは弊社のクッキーポリシーおよびプライバシーポリシーを読み、理解したものとみなされます。
Licensed under cc by-sa 3.0 with attribution required.