Являются ли статические члены общего класса привязаны к конкретному экземпляру?
Это скорее документация, чем реальный вопрос. Это, похоже, не было адресовано на 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% =).
Примечание. Я считаю, что это не хороший дизайн и не хорошая практика использования таких статических атрибутов.