web-dev-qa-db-ja.com

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

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

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

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

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

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

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

結果は何ですか?この動作はどこに文書化されていますか?

79
mafu

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

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

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

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

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

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

86
Fredrik Mörk

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

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

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

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

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

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

このことから、Tは特殊化によって異なるため、静的メンバーを共有できないことは明らかです。

15
SWeko

それらは共有されません。どこに文書化されているかは不明ですが、分析警告 CA10一般的な型の静的メンバーを宣言しないでください)は、これだけのために警告しますコードをより複雑にするリスク。

4
Hans Olsson

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

3
Shital Shah

それらは実際には共有されません。メンバーがインスタンスにまったく属していないためです。静的クラスメンバーはクラス自体に属します。したがって、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
1
Marks