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


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 отличается для разных специализаций.


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
Используя наш сайт, вы подтверждаете, что прочитали и поняли нашу Политику в отношении файлов cookie и Политику конфиденциальности.
Licensed under cc by-sa 3.0 with attribution required.