Почему я не могу наследовать статические классы?

224

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

Но, похоже, я не могу объявить наследование для статических классов.

Что-то такое:

public static class Base
{
}

public static class Inherited : Base
{
}

не будет работать.

Почему разработчики языка закрыли эту возможность?

пользователь
источник
Возможный дубликат Как статический класс может быть получен из объекта?
Мусульманин Бен Дхау

Ответы:

174

Цитата отсюда :

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

Возможно, есть причина рассмотреть механизмы для непосредственного приведения статических членов в область действия (и мы на самом деле рассмотрим это после цикла продуктов Orcas), но статическое наследование классов - это не тот путь: это неправильный механизм для использования, и он работает только для статических членов, которые находятся в статическом классе.

(Мадс Торгерсен, C # Language PM)

Другие мнения от channel9

Наследование в .NET работает только на базе экземпляров. Статические методы определяются на уровне типа, а не на уровне экземпляра. Вот почему переопределение не работает со статическими методами / свойствами / событиями ...

Статические методы хранятся в памяти только один раз. Для них не существует виртуальной таблицы и т. Д.

Если вы вызываете метод экземпляра в .NET, вы всегда указываете ему текущий экземпляр. Это скрыто в среде выполнения .NET, но это происходит. Каждый метод экземпляра имеет в качестве первого аргумента указатель (ссылку) на объект, на котором запущен метод. Этого не происходит со статическими методами (так как они определены на уровне типа). Как компилятор должен выбрать метод для вызова?

(Littleguru)

И, как ценная идея, у littleguru есть частичный «обходной путь» для этой проблемы: шаблон Singleton .

БЯ
источник
92
Недостаток фантазии на стороне Торгерсена, правда. У меня была довольно веская причина хотеть сделать это :)
Thorarin
10
Как насчет этого? У вас есть DLL, которая не является открытым исходным кодом / у вас нет доступа к ее источнику, скажем, NUnit. Вы хотите расширить его класс Assert, чтобы иметь такой метод, как Throws <> (Action a) (да, он тоже есть, но в какой-то момент его не было.) Вы можете добавить в свой проект Assert: NUnit.Framework.Assert класс, а затем добавьте метод Throws. Задача решена. В коде тоже удобнее использовать ... вместо того, чтобы придумывать другое имя класса и запоминать это имя класса, просто введите Assert. и посмотреть доступные методы.
user420667
4
@KonradMorawski Невозможно написать методы расширения для статических классов. stackoverflow.com/questions/249222/…
Мартин Браун
2
@modiX Конечно. Вы неправильно поняли меня здесь. Я имел в виду, что функциональность, требуемая в сценарии, описанном user420667, может быть обеспечена ( в будущем ) с помощью всего лишь разрешения методов расширения для статических классов. Статическое наследование не требуется. Вот почему его сценарий не показался мне очень хорошим обоснованием для введения статического наследования. Если C # должен был быть изменен в этом аспекте, зачем переходить к серьезному редизайну, если второстепенный был бы столь же хорош на практике.
Конрад Моравский
5
@Learner Это не совсем честно. В .Net все наследуется object, и все, как ожидается, будут это знать. Таким образом, статические классы всегда наследуются objectнезависимо от того, указали вы это явно или нет.
RenniePet
71

Основная причина того, что вы не можете наследовать статический класс, состоит в том, что они абстрактны и запечатаны (это также предотвращает создание любого их экземпляра).

Итак, это:

static class Foo { }

компилируется в этот IL:

.class private abstract auto ansi sealed beforefieldinit Foo
  extends [mscorlib]System.Object
 {
 }
Эндрю Хэйр
источник
17
Вы говорите, что static был реализован как абстрактный + герметичный. Он хочет знать, почему это было сделано. Почему это запечатано?
Лукас
22
Это не отвечает на все вопросы; это просто повторяет проблему, о которой он спрашивал.
Игби Ларджман
28
Что ж, OP должен был спросить «Почему статические классы запечатаны», а не «Почему я не могу наследовать от статических классов?», Ответ на который, конечно, «потому что они запечатаны» . Этот ответ получает мой голос.
Алекс Будовски
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 (например, класс с такими членами должен определять защищенный нестатический класс, который содержит такие элементы экземпляра вместе со статическим полем держа экземпляр этого типа).
суперкат
Я оказался в ситуации, когда считаю, что наследование статического класса - это то, что нужно, хотя, очевидно, вы можете получить к ним доступ в любом случае. У меня есть статический класс для отправки потоков необработанных данных на принтер (для общения с промышленными принтерами этикеток). У этого есть куча объявлений Windows API. Теперь я пишу другой класс для работы с принтерами, которым нужны те же вызовы API. Объединить? Не хорошо. Объявить их дважды? Некрасиво. Наследование? Хорошо, но не разрешено.
Лорен Печтел
3

То, чего вы хотите достичь с помощью иерархии классов, может быть достигнуто просто с помощью пространства имен. Поэтому языки, которые поддерживают пространства имен (например, C #), не будут использовать реализацию иерархии классов статических классов. Поскольку вы не можете создать экземпляр какого-либо из классов, все, что вам нужно, - это иерархическая организация определений классов, которую вы можете получить, используя пространства имен

Amit
источник
У меня есть общий загрузчик, который получает класс в качестве типа. Этот класс имеет 5 вспомогательных методов и 2 метода, которые возвращают строку (имя и описание). Возможно, я ошибся (я так думаю), но единственное решение, которое я нашел, было создать экземпляр класса в универсальном загрузчике, чтобы получить доступ к методам ... ме.
ANeves
Я думаю, альтернативой будет гигантский словарь. Кроме того, когда вы говорите «чего хотите достичь», вы должны вместо этого сказать «к чему вы стремитесь» или что-то подобное.
ANeves
3

Вместо этого вы можете использовать композицию ... это позволит вам получить доступ к объектам класса из статического типа. Но все еще не могу реализовать интерфейсы или абстрактные классы

Йогеш Карекар
источник
2

Хотя вы можете получить доступ к «унаследованным» статическим членам через имя унаследованных классов, статические члены на самом деле не наследуются. Это частично, почему они не могут быть виртуальными или абстрактными и не могут быть переопределены. В вашем примере, если вы объявили Base.Method (), компилятор все равно отобразит вызов Inherited.Method () обратно в Base.Method (). Вы также можете явно вызвать Base.Method (). Вы можете написать небольшой тест и увидеть результат с помощью Reflector.

Итак ... если вы не можете наследовать статические члены, и если статические классы могут содержать только статические члены, что хорошего в наследовании статического класса?

Лукас
источник
1

Хммм ... было бы очень по-другому, если бы у вас были нестатические классы, заполненные статическими методами ..?

CookieOfFortune
источник
2
Вот что мне осталось. Я делаю это так. И мне это не нравится.
Пользователь
Я также сделал это таким образом, но по какой-то причине это не кажется эстетичным, когда вы знаете, что никогда не создадите объект. Я всегда мучаюсь такими деталями, несмотря на то, что это не требует материальных затрат.
gdbj
В нестатический класс, заполненный статическими методами, нельзя
добавлять
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>();

К сожалению, из-за языкового ограничения "by-design" мы не можем:

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

public static class TestClass : TestClass<double>
{
}
FocusedWolf
источник
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, но статическое поле только для чтения с его безопасностью потока было достаточно хорошим.

Konard
источник
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

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

Класс может быть объявлен статическим, что означает, что он содержит только статические члены. Невозможно использовать ключевое слово new для создания экземпляров статического класса. Статические классы загружаются автоматически общеязыковой средой (CLR) .NET Framework, когда загружается программа или пространство имен, содержащее класс.

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

Ниже приведены основные характеристики статического класса:

  1. Они содержат только статические члены.

  2. Они не могут быть созданы.

  3. Они запечатаны.

  4. Они не могут содержать конструкторы экземпляров (Руководство по программированию в C #).

Поэтому создание статического класса в основном аналогично созданию класса, который содержит только статические члены и закрытый конструктор. Закрытый конструктор предотвращает создание экземпляра класса.

Преимущество использования статического класса заключается в том, что компилятор может проверить, чтобы убедиться, что никакие члены экземпляра не были добавлены случайно. Компилятор гарантирует, что экземпляры этого класса не могут быть созданы.

Статические классы запечатаны и поэтому не могут быть унаследованы. Они не могут наследовать ни от какого класса, кроме Object. Статические классы не могут содержать конструктор экземпляра; однако они могут иметь статический конструктор. Для получения дополнительной информации см. Статические конструкторы (Руководство по программированию в C #).

зона
источник
-1

Когда мы создаем статический класс, который содержит только статические члены и закрытый конструктор. Единственная причина в том, что статический конструктор предотвращает создание экземпляра класса, для этого мы не можем наследовать статический класс. Единственный способ получить доступ к члену класса. статический класс с использованием самого имени класса. Попытка наследовать статический класс не очень хорошая идея.

Сумен Чакраборты
источник