Перейти к содержимому

Как запретить наследование от класса c

  • автор:

Как запретить наследование класса в C#

Для предотвращения наследования от класса необходимо объявить его с модификатором sealed. Например, если мы не хотим чтобы от класса SomeClass можно было наследоваться, то объявляем его так:

sealed class SomeClass < // Объявление класса >

Отслеживать

ответ дан 21 окт 2010 в 21:13

Nicolas Chabanovsky Nicolas Chabanovsky

51.3k 87 87 золотых знаков 267 267 серебряных знаков 505 505 бронзовых знаков

sealed (Справочник по C#)

При применении к классу модификатор sealed запрещает другим классам наследовать от этого класса. В следующем примере класс B наследует от класса A , но никакие классы не могут наследовать от класса B .

class A <> sealed class B : A <> 

Модификатор sealed можно использовать для метода или свойства, которое переопределяет виртуальный метод или свойство в базовом классе. Это позволяет классам наследовать от вашего класса, запрещая им при этом переопределять определенные виртуальные методы или свойства.

Пример

В следующем примере класс Z наследует от класса Y , но Z не может переопределить виртуальную функцию F , которая объявлена в классе X и запечатана в классе Y .

class X < protected virtual void F() < Console.WriteLine("X.F"); >protected virtual void F2() < Console.WriteLine("X.F2"); >> class Y : X < sealed protected override void F() < Console.WriteLine("Y.F"); >protected override void F2() < Console.WriteLine("Y.F2"); >> class Z : Y < // Attempting to override F causes compiler error CS0239. // protected override void F() < Console.WriteLine("Z.F"); >// Overriding F2 is allowed. protected override void F2() < Console.WriteLine("Z.F2"); >> 

Чтобы предотвратить переопределение производных классов при определении новых методов или свойств, не назначайте их в качестве виртуальных (virtual).

Нельзя использовать модификатор abstract с запечатанным классом, поскольку абстрактный класс должен наследоваться классом, реализующим абстрактные методы или свойства.

При применении к методу или свойству модификатор sealed всегда должен использоваться с переопределением.

Поскольку структуры неявно запечатаны, их нельзя наследовать.

Дополнительные сведения см. в разделе Наследование.

sealed class SealedClass < public int x; public int y; >class SealedTest2 < static void Main() < var sc = new SealedClass(); sc.x = 110; sc.y = 150; Console.WriteLine($"x = , y = "); > > // Output: x = 110, y = 150 

Чтобы наследовать от запечатанного класса, можно применить следующую инструкцию в приведенном выше примере:

class MyDerivedC: SealedClass <> // Error

В результате выдается сообщение об ошибке:

‘MyDerivedC’: cannot derive from sealed type ‘SealedClass’

Комментарии

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

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

Спецификация языка C#

Дополнительные сведения см. в спецификации языка C#. Спецификация языка является предписывающим источником информации о синтаксисе и использовании языка C#.

См. также

  • Справочник по C#
  • Руководство по программированию на C#
  • Ключевые слова в C#
  • Статические классы и члены статических классов
  • Абстрактные и запечатанные классы и члены классов
  • Модификаторы доступа
  • Модификаторы
  • override
  • virtual

Совместная работа с нами на GitHub

Источник этого содержимого можно найти на GitHub, где также можно создавать и просматривать проблемы и запросы на вытягивание. Дополнительные сведения см. в нашем руководстве для участников.

Как запретить наследование от класса c

Тут кто-то спрашивал про то, как запретить наследование — у Страуструпа в FAQ есть ответ с комментариями. Здесь привожу свой перевод (простите за качество, переводил навскидку)

Могу ли я запретить наследоваться от моего класса?
Да, но зачем? На это может быть вообще говоря две причины:

для эффективности: чтобы не пользоваться виртуальными вызовами
для безопасности : чтобы быть уверенным в том, что класс не используется как базовый (к примеру, быть уверенным, что объекты можно безбоязненно копировать [slizing — не знаю что это])

Из моего опыта, боязнь потери эффективности обычно неуместна. В C++ вызовы виртуальных функций быстры настолько, что их использование в классах, спроектированных с виртуальными функциями не даёт заметных издержек в сравнении с альтернативными решениями, использующими обычные функции. Также заметьте, что использование механизма вирутальных вызовов обычно происходит только при вызовах через указатель или ссылку. При вызове функций напрямую для именованного объекта виртуальный вызов может быть довольно просто оптимизирован и не давать вообще никаких издержек.

С другой стороны, когда есть логичная причина на это, запретить дальнейшее наследование можно. К сожалению, решение не самое красивое. Оно опирается на то, что самый последний производный класс [most derived class] в иерархии обязан сконструировать виртуальную базу.

 class Usable; class Usable_lock < friend class Usable; private: Usable_lock() <> >; class Usable : public virtual Usable_lock < // . public: Usable(); Usable(char*); // . >; Usable a; class DD : public Usable < >; DD dd; // error: DD::DD() cannot access // Usable_lock::Usable_lock(): private member

Конец перевода, далее мои комментарии
На самом деле, «некрасивость» решения сводится к тому, что эта техника не может быть легко расширена шаблонами:

 templatetypename T> class nonderivable < friend class T; private: nonderivable() <> >; class my_class : public virtual nonderivable < public: my_class(); >;

Это работать не будет, потому что [7.1.5.3/2]

If the identifier resolves to a typedef-name or a template typeparameter, the elaborated-type-specifier is illformed. [Note: this implies that, within a class template with a template type-parameter T, the declaration friend class T; is illformed. ]

Поэтому для каждого класса, который хочет воспользоваться подобной техникой и стать финальным в иерархии придётся писать «friend class concrete-class-name-to-be-final».

Запрет наследования. Ключевое слово sealed . Особенности применения. Пример

В языке C# введено ключевое слово sealed , которое используется в двух случаях:

  • когда нужно запретить наследование от некоторого класса. В многих иерархиях, классы, которые размещены на нижних уровнях могут быть обозначены как sealed ;
  • когда нужно запретить переопределение некоторого метода в иерархии классов. Эта ситуация возможна, если в иерархии классов методы унаследованных классов переопределяют виртуальные методы базовых классов.
2. Применение ключевого слова sealed для класса. Общая форма. Пример

Общая форма использования ключевого слова sealed для класса, следующая:

sealed class ClassName < // . >
  • ClassName – имя класса, который нужно запретить наследовать.

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

Например, в следующем коде

// Класс, который запрещено наследовать sealed class A < // . > // Класс, который пробует унаследовать класс A class B : A < // . >

компилятор выдаст ошибку

'B': cannot derive from sealed type 'A'
3. Применение ключевого слова sealed для метода. Общая форма. Пример

Ключевое слово sealed может быть применено для метода также. Использование sealed для метода означает, что метод запрещено переопределять в унаследованных классах. Значит, такой метод должен быть объявлен с ключевым словом override . А это, в свою очередь, означает, что класс с sealed-методом должен наследовать другой базовый класс, который содержит метод с таким же именем.

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

  • класс должен быть унаследован от другого (базового) класса;
  • в базовом классе должен быть объявлен метод, который переопределяется в данном (производном) классе. Этот метод базового класса также должен быть объявлен с ключевыми словами virtual или override (если базовый класс также унаследован).

Общая форма использования ключевого слова sealed для метода следующая:

class ClassName : BaseClassName < // . access_modifier sealed override MethodName(parameters) < // . > >
  • ClassName – имя класса, в котором объявлен sealed-метод с именем MethodName ;
  • BaseClassName – имя класса, который есть базовым для класса ClassName . Запрет переопределения метода в классе предусматривает, что этот класс унаследован от другого базового класса, в котором объявлен одноименный переопределяемый метод. Поэтому, если для некоторого метода в классе нужно применить ключевое слово sealed , этот класс обязательно должен быть унаследован от другого класса;
  • access_modifier – один из модификаторов доступа public , protected , internal , protected internal . Модификатор доступа private не может быть применен к sealed-методу;
  • MethodName – имя метода, который объявляется как sealed . Данный метод должен быть объявлен с ключевым словом override . Это значит, что данный метод переопределяет одноименный метод базового класса BaseClassName .

Пример.

Объявляется иерархия классов A , B , C . В классе C объявляется метод, который запрещено переопределять.

// Базовый класс для классов B, C class A < // Метод, который переопределяется в производных классах public virtual void Method() < Console.WriteLine("MethodA()"); > > // Класс, который наследует класс A class B : A < // В этом классе объявляется метод, который не запрещено // переопределять в производных классах public override void Method() < Console.WriteLine("MethodB()"); > > // class C : B < // Объявляется метод, который запрещено переопределять // в производных классах - с ключевым словом sealed public override sealed void Method() < Console.WriteLine("MethodC()"); > >

Если из класса C попробовать унаследовать класс D , и в классе D объявить метод, который переопределяет метод Method() класса C как показано далее

. // Класс D наследует класс C class D : C < // Ошибка! Метод Method() нельзя переопределять public override void Method() // ошибка! < Console.WriteLine("MethodD()"); > > .

то компилятор выдаст ошибку

D.Method(): cannot override inherited member C.Method() because it is sealed

Чтобы поправить ситуацию нужно выполнить одно из двух действий (в зависимости от поставленной задачи):

  • убрать слово sealed перед именем метода в классе C ;
  • поменять слово override на слово new перед именем метода в классе D .
4. Можно ли в sealed -классе объявить sealed метод?

Да, можно. В приведенном ниже примере объявляется sealed-класс и sealed-метод в этом классе.

// Базовый класс class A < // . // виртуальный метод protected virtual void Method() < >> // Класс, который наследует класс A sealed class B : A < // . // метод, который переопределяет одноименный метод базового класса protected sealed override void Method() < >>

Хотя класс B не может быть унаследован, компилятор такой код не запрещает.

Связанные темы

  • Наследование. Базовые понятия. Преимущества и недостатки. Общая форма. Простейшие примеры. Модификатор доступа protected
  • Использование конструкторов в классах при наследовании. Ключевое слово base при наследовании
  • Доступ к элементам базового класса из унаследованного класса. Ключевые слова base , new . Примеры
  • Примеры создания иерархий классов с использованием наследования

Добавить комментарий

Ваш адрес email не будет опубликован. Обязательные поля помечены *