Связаны ли статические члены универсального класса с конкретным экземпляром?

85

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

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

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

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

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

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

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

мафу
источник
4
Краткий ответ: существует новый экземпляр для каждого фактического класса, то есть по одному для каждого Tиспользуемого типа ( Foo<int>и Foo<string>представляют два разных класса, и каждый будет иметь по одному экземпляру, но несколько экземпляров Foo<int>будут совместно использовать один экземпляр member). Для более подробного примера см .: stackoverflow.com/a/38369256/336648
Kjartan

Ответы:

92

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

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

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

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

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

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

Фредрик Мёрк
источник
26
Если вы разрабатываете как на C #, так и на Java, вот в чем проблема. Хотя Foo<int>и Foo<String>являются разными типами в C #, они являются одним и тем же типом в Java из-за того, как Java работает с универсальными шаблонами (трюки со стиранием типов / компилятором).
Powerlord
Что, если бы это было так: Foo <int> foo1 = new Foo <int> (); foo1.member = 10; Foo <int> foo2 = новый Foo <int> (); foo2.member = 20; Что здесь происходит?
Все
@Everyone значение member будет изменено для обоих экземпляров (и будет 20), потому что они имеют один и тот же тип Foo <int>.
Стас Иванов
1
Что, если вы наследуете неуниверсальный базовый класс, у которого есть статический член, в производный универсальный класс? Будет ли это по-прежнему правдой?
Brain2000
@StasIvanov, foo1.member останется 10, а foo2.member будет 20. Пожалуйста, подтвердите
SHRI
16

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

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

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

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

PS Вполне возможно заявить

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

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

SWeko
источник
4

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

Ханс Ольссон
источник
1
Вау, еще одно правило, с которым я бы не согласился на FX Cop.
Matthew Whited
Это задокументировано здесь
NtFreX
3

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

Шитал Шах
источник
1

На самом деле они не разделяются. Потому что член вообще не принадлежит экземпляру. Статический член класса принадлежит самому классу. Итак, если у вас есть 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
Метки
источник
-4

ИМО, вам нужно проверить это, но я думаю, что

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

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

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

Замечание: Я считаю, что использование статических атрибутов такого типа - плохой дизайн и плохая практика.

Клемент Херреман
источник
6
Если не уверен на 100% - не комментируйте
sll