Чому я не можу успадкувати статичні класи?


224

У мене є кілька класів, які не дуже потребують жодної держави. З організаційної точки зору, я хотів би поставити їх в ієрархію.

Але, схоже, я не можу оголосити спадщину для статичних класів.

Щось схоже:

public static class Base
{
}

public static class Inherited : Base
{
}

не вийде.

Чому дизайнери мови закрили таку можливість?


Відповіді:


174

Цитування звідси :

Це насправді задумом. Здається, немає вагомих причин успадкувати статичний клас. У ньому є публічні статичні члени, до яких завжди можна отримати доступ через саме ім’я класу. Єдині причини, які я бачив у спадщині статичних речей, були поганими, наприклад, збереження пари символів набору тексту.

Можливо, є причина розглянути механізми для прямого введення статичних членів у сферу застосування (і ми насправді розглянемо це після циклу продуктів Orcas), але успадкування статичного класу - це не шлях: це неправильний механізм використання та працює лише для статичних членів, які трапляються у статичному класі.

(Mads Torgersen, C # Language PM)

Інші думки з каналу9

Спадкування в .NET працює лише на екземплярі. Статичні методи визначаються на рівні типу, а не на рівні примірника. Ось чому перевизначення не працює зі статичними методами / властивостями / подіями ...

Статичні методи зберігаються лише один раз в пам'яті. Для них немає віртуальної таблиці тощо.

Якщо ви викликаєте метод екземпляра в .NET, ви завжди надаєте йому поточний екземпляр. Це приховано під час виконання .NET, але це трапляється. Кожен метод примірника має в якості першого аргументу вказівник (посилання) на об'єкт, над яким працює метод. Це не відбувається зі статичними методами (так як вони визначені на рівні типу). Як компілятор повинен вирішити вибрати метод для виклику?

(маленький гуру)

І як цінна ідея, маленький гуру має часткове «вирішення» цього питання: шаблон Сінглтона .


92
Не вистачає фантазії на стороні Торгерсена, дійсно. У мене був досить вагомий привід, що я хотів це зробити :)
Торарін

10
Як щодо цього? У вас є dll, який не є відкритим кодом / у вас немає доступу до його джерела, скажімо, NUnit. Ви хочете розширити свій клас Assert, щоб мати такий метод, як Throw <> (Дія a) (так, це теж є, але в один момент цього не було.) Ви можете додати до свого проекту Assert: NUnit.Framework.Assert клас, а потім додайте метод Throw. Проблема вирішена. Чистіше використовувати і в коді ... замість того, щоб придумати інше ім’я класу та запам’ятати це ім’я класу, просто введіть Assert. і переглянути доступні методи.
user420667

4
@KonradMorawski Неможливо написати методи розширення для статичних класів. stackoverflow.com/questions/249222/…
Мартін Браун

2
@modiX Звичайно. Ви тут мене зрозуміли неправильно. Я мав на увазі те, що функціонал, необхідний у сценарії, описаному користувачем420667, може надаватися ( у майбутньому ) настільки ж мало, як дозволяти методи розширення для статичних класів. Не потрібно статичного успадкування. Ось чому його сценарій не сприйняв мене як дуже гарне обґрунтування введення статичної спадщини. Якщо C # має бути змінено в цьому аспекті, навіщо йти на капітальний редизайн, коли неповнолітній буде настільки ж хорошим на практиці.
Конрад Моравський

5
@Learner Це не дуже справедливо. У .Net все успадковується object, і всі, як очікується, знають це. Тож статичні класи завжди успадковують object, незалежно від того, вказуєте ви це чи ні.
RenniePet

71

Основна причина того, що ви не можете успадкувати статичний клас, полягає в тому, що вони абстрактні та запечатані (це також заважає створювати будь-який їх примірник).

Отже це:

static class Foo { }

компілює до цієї ІЛ:

.class private abstract auto ansi sealed beforefieldinit Foo
  extends [mscorlib]System.Object
 {
 }

17
Ви говорите, що статичне значення було реалізовано як абстрактне + запечатане. Він хоче знати, чому це було зроблено. Чому вона герметична?
Лукас

22
Це не відповідає на все питання; він просто відновлює питання, про яке він запитував.
Ігбі Ларгеман

28
Ну, в ОП повинні були запитати "Чому статичні класи запечатані", а не "Чому я не можу успадковувати статичні класи?", Відповідь на які, звичайно, "тому що вони запечатані" . Ця відповідь отримує мій голос.
Олексій Будовський

6
дякую за те, що статичні класи автоматично складаються у вигляді запечатаних класів - мені було цікаво, як / коли це сталося!
Марк

23
@AlexBudovski: Ця відповідь правильна, але настільки ж марна, як казати загубленій людині "ти переді мною", коли вони запитують тебе "де я".
DeepSpace101

24

Подумайте про це так: ви отримуєте доступ до статичних членів через ім'я типу, наприклад:

MyStaticType.MyStaticMember();

Якщо ви успадкували цей клас, вам доведеться отримати доступ до нього через нове ім'я типу:

MyNewType.MyStaticMember();

Таким чином, новий елемент не має відношення до оригіналу при використанні в коді. Не було б можливості скористатися будь-якими відносинами спадкування для таких речей, як поліморфізм.

Можливо, ви думаєте, що просто хочете розширити деякі предмети в початковому класі. У такому випадку нічого не заважає просто використовувати член оригіналу абсолютно нового типу.

Можливо, ви хочете додати методи до існуючого статичного типу. Це можна зробити вже методами розширення.

Можливо, ви хочете мати можливість передавати статику Typeфункції під час виконання та викликати метод цього типу, не знаючи, що саме робить метод. У цьому випадку ви можете використовувати інтерфейс.

Отже, зрештою, ви насправді нічого не отримуєте від успадкування статичних класів.


5
Ви не можете додати методи до існуючого статичного типу за допомогою методу розширення. Будь ласка, дивіться мій коментар до прийнятої відповіді для прикладу бажаного використання. (В основному ви хочете просто перейменувати те, що ви назвали MyNewType як MyStaticType, тому MyStaticType: OldProject.MyStaticType)
user420667

2
Можна було б визначити відносини успадковування зі статичними типами та віртуальними статичними членами таким чином, щоб a SomeClass<T> where T:Fooмав доступ до членів Foo, а якщо Tбув клас, який перекривав деякі віртуальні статичні члени Foo, родовий клас використовував би ці зміни. Можливо, навіть було б можливо визначити конвенцію, за допомогою якої мови могли би це зробити, сумісно з поточним CLR (наприклад, клас з такими членами повинен визначати захищений нестатичний клас, який містить такі члени екземпляра, а також статичне поле тримає екземпляр цього типу).
supercat

Я опинився у випадку, коли я вважаю, що наслідування статичного класу - це правильно зробити, хоча очевидно, ви можете отримати доступ до них у будь-якому випадку. У мене є статичний клас для надсилання необроблених потоків даних на принтер (для розмови з промисловими принтерами етикеток). Він має купу оголошень Windows API. Зараз я вважаю, що пишу ще один клас для возитися з принтерами, яким потрібні ті ж дзвінки API. Об’єднати? Не добре. Декларуйте їх двічі? Некрасивий. Спадщина? Добре, але не дозволено.
Лорен Печтел

3

Те, що ви хочете досягти за допомогою ієрархії класів, можна досягти лише за допомогою простору імен. Тож мови, які підтримують простори імен (наприклад, C #), не матимуть ніякої користі від реалізації ієрархії класів статичних класів. Оскільки ви не можете інстанціювати жоден із класів, все, що вам потрібно, - це ієрархічна організація визначень класів, яку ви можете отримати за допомогою просторів імен


У мене є загальний навантажувач, який отримує клас як тип. У цьому класі є 5 допоміжних методів та 2 методи, які повертають рядок (ім'я та опис). Я, можливо, вибрав це неправильно (я думаю, що так), але єдине знайдене нами рішення - інстанціювати клас у загальному завантажувачі, отримати доступ до методів ... Мех.
ANeves

Альтернативою був би гігантський словник, я думаю. Крім того, коли ви говорите "чого ви хочете досягти", вам слід сказати "те, на що ви прагнете", або щось подібне.
ANeves

3

Ви можете використовувати композицію замість цього ... це дозволить отримати доступ до об'єктів класу зі статичного типу. Але все ще не реалізує інтерфейси або абстрактні класи


2

Хоча ви можете отримати доступ до "успадкованих" статичних членів через успадковану назву класів, статичні члени насправді не успадковуються. Це частково тому, що вони не можуть бути віртуальними або абстрактними і їх неможливо змінити У вашому прикладі, якщо ви оголосили Base.Method (), компілятор буде все-таки відображати виклик Inherited.Method () назад до Base.Method (). Ви можете також чітко викликати Base.Method (). Ви можете написати невеликий тест і побачити результат за допомогою Reflector.

Отже ... якщо ви не можете успадкувати статичні члени, і якщо статичні класи можуть містити лише статичні члени, яке б користь отримав би у спадок статичний клас?


1

Гммм ... чи було б набагато інакше, якби у вас просто були нестатичні класи, заповнені статичними методами ..?


2
Ось що мені залишається. Я роблю це так. І мені це не подобається.
Користувач

Я це зробив і таким чином, але чомусь це не відчуває естетичного вигляду, коли ти знаєш, що ти ніколи не будеш створювати об'єкт. Я завжди агонізую подібні деталі, незважаючи на те, що матеріальної вартості на це немає.
gdbj

У нестатичному класі, заповненому статичними методами, ви не можете застосовувати методи розширення :)
Jakub Szułakiewicz

1

Ви можете вирішити не використовувати статичні класи, а приховати конструктор, щоб статичні члени класів були єдиним доступом поза класом. В результаті отримується спадковий "статичний" клас по суті:

public class TestClass<T>
{
    protected TestClass()
    { }

    public static T Add(T x, T y)
    {
        return (dynamic)x + (dynamic)y;
    }
}

public class TestClass : TestClass<double>
{
    // Inherited classes will also need to have protected constructors to prevent people from creating instances of them.
    protected TestClass()
    { }
}

TestClass.Add(3.0, 4.0)
TestClass<int>.Add(3, 4)

// Creating a class instance is not allowed because the constructors are inaccessible.
// new TestClass();
// new TestClass<int>();

На жаль, через обмеження мови "під задумом" ми не можемо:

public static class TestClass<T>
{
    public static T Add(T x, T y)
    {
        return (dynamic)x + (dynamic)y;
    }
}

public static class TestClass : TestClass<double>
{
}

1

Можна зробити щось, що буде схоже на статичну спадщину.

Ось хитрість:

public abstract class StaticBase<TSuccessor>
    where TSuccessor : StaticBase<TSuccessor>, new()
{
    protected static readonly TSuccessor Instance = new TSuccessor();
}

Тоді ви можете зробити це:

public class Base : StaticBase<Base>
{
    public Base()
    {
    }

    public void MethodA()
    {
    }
}

public class Inherited : Base
{
    private Inherited()
    {
    }

    public new static void MethodA()
    {
        Instance.MethodA();
    }
}

InheritedКлас не статичний сам, але не дозволяє створювати його. Він фактично успадкував статичний конструктор, який будує Base, і всі властивості та методи Baseдоступні як статичні. Тепер залишається лише зробити статичні обгортки для кожного методу та властивості, які потрібно виставити на ваш статичний контекст.

Є такі мінуси, як потреба в ручному створенні статичних методів обгортки та newключових слів. Але такий підхід допомагає підтримувати щось, що насправді схоже на статичне успадкування.

PS Ми використовували це для створення складених запитів, і це насправді можна замінити ConcurrentDictionary, але статичне поле для читання лише з безпекою його потоку було досить хорошим.


1

Моя відповідь: поганий вибір дизайну. ;-)

Це цікава дискусія, зосереджена на синтаксичному впливі. Ядро аргументу, на мою думку, полягає в тому, що проектне рішення призвело до герметичних статичних класів. Зосередження уваги на прозорості імен статичного класу, що з’являються на верхньому рівні замість того, щоб ховатися («плутати») за дочірніми іменами? Можна зобразити мовну реалізацію, яка могла б отримати доступ до основи або дитини безпосередньо, збиваючи з пантелику.

Псевдо-приклад, припускаючи статичне успадкування, було визначено певним чином.

public static class MyStaticBase
{
    SomeType AttributeBase;
}

public static class MyStaticChild : MyStaticBase
{
    SomeType AttributeChild;
}

призведе до:

 // ...
 DoSomethingTo(MyStaticBase.AttributeBase);
// ...

що може (впливатиме?) на той самий накопичувач

// ...
DoSomethingTo(MyStaticChild.AttributeBase);
// ...

Дуже заплутано!

Але зачекайте! Як компілятор матиме справу з MyStaticBase та MyStaticChild, які мають однаковий підпис у обох? Якщо дитина переорієнтовано, ніж мій приклад вище, НЕ змінить ту саму сховище, можливо? Це призводить до ще більшої плутанини.

Я вважаю, що існує сильне обґрунтування інформаційного простору для обмеженого статичного успадкування. Більше про обмеження найближчим часом. Цей псевдокод показує значення:

public static class MyStaticBase<T>
{
   public static T Payload;
   public static void Load(StorageSpecs);
   public static void Save(StorageSpecs);
   public static SomeType AttributeBase
   public static SomeType MethodBase(){/*...*/};
}

Тоді ви отримуєте:

public static class MyStaticChild : MyStaticBase<MyChildPlayloadType>
{
   public static SomeType AttributeChild;
   public static SomeType SomeChildMethod(){/*...*/};
   // No need to create the PlayLoad, Load(), and Save().
   // You, 'should' be prevented from creating them, more on this in a sec...
} 

Використання виглядає так:

// ...
MyStaticChild.Load(FileNamePath);
MyStaticChild.Save(FileNamePath);
doSomeThing(MyStaticChild.Payload.Attribute);
doSomething(MyStaticChild.AttributeBase);
doSomeThing(MyStaticChild.AttributeChild);
// ...

Людині, яка створює статичну дитину, не потрібно думати про процес серіалізації, доки вони розуміють будь-які обмеження, які можуть бути поставлені на механізмі серіалізації платформи чи середовища.

Статистика (одиночні та інші форми "глобалів") часто виникає навколо сховища конфігурації. Статичне успадкування дозволило б подібному розподілу відповідальності чітко представити в синтаксисі, щоб відповідати ієрархії конфігурацій. Хоча, як я показав, є великий потенціал для масової неоднозначності, якщо реалізувати основні концепції статичного успадкування.

Я вважаю, що правильним вибором дизайну було б дозволити статичне успадкування з певними обмеженнями:

  1. Ніякої переоцінки нічого. Дитина не може замінити базові атрибути, поля чи методи, ... Перевантаження повинно бути нормальним, якщо існує різниця в підписі, що дозволяє компілятору розбирати дочір та базу.
  2. Дозволити лише загальні статичні бази, ви не можете успадковувати негенеричну статичну базу.

Ви все ще можете змінити той самий магазин через загальну посилання MyStaticBase<ChildPayload>.SomeBaseField. Але вас не відволікає, оскільки загальний тип повинен був би бути вказаний. У той час як еталонний дитина буде чистіше: MyStaticChild.SomeBaseField.

Я не письменник-компілятор, тому я не впевнений, чи пропускаю щось про труднощі реалізації цих обмежень у компіляторі. З цього приводу я переконаний, що в інформаційному просторі потрібна обмежена статична спадщина, і основна відповідь полягає в тому, що ви не можете через поганий (або надто простий) вибір дизайну.


0

Статичні класи та члени класу використовуються для створення даних та функцій, до яких можна отримати доступ, не створюючи примірник класу. Статичні члени класу можуть використовуватися для розділення даних і поведінки, незалежної від будь-якої ідентичності об'єкта: дані та функції не змінюються незалежно від того, що відбувається з об'єктом. Статичні класи можуть використовуватися, коли в класі немає даних або поведінки, які залежать від ідентичності об'єкта.

Клас можна оголосити статичним, що означає, що він містить лише статичних членів. Неможливо використовувати нове ключове слово для створення примірників статичного класу. Статичні класи завантажуються автоматично за допомогою загальної мови виконання (CLR) .NET Framework при завантаженні програми або простору імен, що містить клас.

Використовуйте статичний клас, щоб містити методи, не пов'язані з певним об'єктом. Наприклад, загальною вимогою є створення набору методів, які не діють на дані екземпляра і не пов'язані з певним об'єктом у вашому коді. Ви можете використовувати статичний клас для утримання цих методів.

Нижче наведено основні особливості статичного класу:

  1. Вони містять лише статичні члени.

  2. Їх неможливо встановити.

  3. Вони запечатані.

  4. Вони не можуть містити конструктори екземплярів (Посібник з програмування C #).

Тому створення статичного класу в основному те саме, що створити клас, який містить лише статичні члени та приватний конструктор. Приватний конструктор запобігає екземпляру класу.

Перевага використання статичного класу полягає в тому, що компілятор може перевірити, чи не випадково додано жодного члена екземпляра. Компілятор гарантує, що екземпляри цього класу не можуть бути створені.

Статичні класи запечатані і тому не можуть бути успадковані. Вони не можуть успадковувати жоден клас, окрім Object. Статичні класи не можуть містити конструктор екземплярів; однак вони можуть мати статичний конструктор. Для отримання додаткової інформації див. Статичні конструктори (Посібник з програмування C #).


-1

Коли ми створюємо статичний клас, який містить лише статичні члени та приватний конструктор. Єдина причина полягає в тому, що статичний конструктор перешкоджає інстанціюванню класу для того, що ми не можемо успадкувати статичний клас. Єдиний спосіб отримати доступ до члена статичний клас, використовуючи саме ім'я класу. Спробуйте успадкувати статичний клас - це не дуже гарна ідея.

Використовуючи наш веб-сайт, ви визнаєте, що прочитали та зрозуміли наші Політику щодо файлів cookie та Політику конфіденційності.
Licensed under cc by-sa 3.0 with attribution required.