Являются ли статические члены общего класса привязаны к конкретному экземпляру?

Это скорее документация, чем реальный вопрос. Это, похоже, не было адресовано на SO еще (если я не пропустил это), так вот:

Представьте общий класс, содержащий статический член:

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

Есть ли новый экземпляр элемента для каждого конкретного класса или существует только один экземпляр для всех классов типа Foo?

Это легко проверить с помощью кода:

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

Каков результат и где это поведение документировано?

Ответ 1

Поле

A static используется для всех экземпляров того же типа. Foo<int> и Foo<string> - два разных типа. Это может быть доказано следующей строкой кода:

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

Что касается того, где это задокументировано, в разделе 1.6.5 "Поля языка С#" (для С# 3) содержится следующее:

Статическое поле идентифицирует ровно один место хранения. Независимо от того, сколько экземпляры класса создаются, существует только одна копия статическое поле.

Как указано выше; Foo<int> и Foo<string> не являются тем же классом; они представляют собой два разных класса, построенных из одного и того же родового класса. Как это происходит, описано в разделе 4.4 вышеупомянутого документа:

Объявление типового типа, само по себе, обозначает несвязанный общий тип, который используется в качестве "плана" для формирования многих различных типов, путем применения аргументы типа.

Ответ 2

Проблема здесь в том, что "родовые классы" вообще не являются классами.

Определения общего класса - это просто шаблоны для классов, и до тех пор, пока не будут указаны их параметры типа, это всего лишь фрагмент текста (или несколько байтов).

Во время выполнения можно указать параметр типа для шаблона, тем самым оживив его и создав класс полностью заданного типа. Поэтому статические свойства не являются шаблонами, и поэтому вы не можете использовать между List<string> и List<int>.

Это отношение вида отражает отношения класса и объекта. Подобно тому, как классы не существуют * пока вы не создадите экземпляр объекта из них, общие классы не существуют, пока вы не создадите класс на основе шаблона.

P.S. Вполне возможно объявить

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

Из этого совершенно очевидно, что статические члены не могут быть разделены, поскольку T различен для разных специализаций.

Ответ 3

Они не разделяются. Не уверен, где он задокументирован, но аналитическое предупреждение CA1000 (не объявляйте статические члены в общих типах) предупреждает об этом только из-за риска сделать кода сложнее.

Ответ 4

Реализация генериков С# ближе к С++. В обоих этих языках MyClass<Foo> и MyClass<Bar> не разделяют статические члены, а в Java они делают. В С# и С++ MyClass<Foo> внутренне создает совершенно новый тип во время компиляции, как будто generics являются своего рода макросами. Обычно вы можете видеть их сгенерированные имена в трассировке стека, например MyClass'1 и MyClass'2. Вот почему они не разделяют статические переменные. В Java генерические средства реализуются более простым методом генерации кода компилятора с использованием не-генерических типов и добавлением типов. Таким образом, MyClass<Foo> и MyClass<Bar> не генерируют два совершенно новых класса в Java, вместо этого они оба являются одним и тем же классом MyClass под ним и поэтому разделяют статические переменные.

Ответ 5

Они действительно не разделены. Поскольку член не принадлежит экземпляру вообще. Статический член класса принадлежит самому классу. Итак, если у вас есть MyClass.Number, то он одинаковый для всех объектов MyClass.Number, потому что он даже не зависит от объекта. Вы даже можете вызвать или изменить MyClass.Number без какого-либо объекта.

Но поскольку Foo <int> не является тем же классом, что и Foo <string> эти два номера не разделяются.

Пример, чтобы показать это:

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

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

Ответ 6

IMO, вам нужно проверить его, но я думаю, что

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

выводит 1, потому что я считаю, что во время компиляции компилятор создает 1 класс для каждого используемого вами универсального класса (в вашем примере: Foo<int> и Foo<string>).

Но я не уверен на 100% =).

Примечание. Я считаю, что это не хороший дизайн и не хорошая практика использования таких статических атрибутов.