Чи прив'язані статичні члени загального класу до конкретного екземпляра?


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; Що тут відбувається?
Усі

@ Кожному значення члена буде змінено для обох примірників (і буде 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.