Sealed c что это
Перейти к содержимому

Sealed c что это

  • автор:

Абстрактные и запечатанные классы и члены классов (Руководство по программированию на C#)

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

Ключевое слово sealed позволяет предотвратить наследование класса или определенных членов класса, помеченных ранее как virtual.

Абстрактные классы и члены классов

Классы могут быть объявлены абстрактными путем помещения ключевого слова abstract перед определением класса. Пример:

public abstract class A < // Class members here. >

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

Абстрактные классы могут определять абстрактные методы. Для этого перед типом возвращаемого значения метода необходимо поместить ключевое слово abstract . Пример:

public abstract class A

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

// compile with: -target:library public class D < public virtual void DoWork(int i) < // Original implementation. >> public abstract class E : D < public abstract override void DoWork(int i); >public class F : E < public override void DoWork(int i) < // New implementation. >> 

Если метод virtual объявляется как abstract , он все равно считается виртуальным по отношению к любому классу, наследующему от абстрактного класса. Класс, наследующий абстрактный метод, не может получать доступ к исходной реализации метода (так, в предыдущем примере DoWork для класса F не может вызывать DoWork для класса D). Таким образом, абстрактный класс может принудительно задавать необходимость предоставлять новые реализации виртуальных методов в производных классах.

Запечатанные классы и члены классов

Классы могут быть объявлены как запечатанные путем помещения ключевого слова sealed перед определением класса. Пример:

public sealed class D < // Class members here. >

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

Метод, индексатор, свойство или событие для производного класса, переопределяющего виртуальный член базового класса, может объявлять этот член как запечатанный. Это делает бесполезным виртуальный аспект члена для каждого последующего производного класса. Для этого в объявлении члена класса необходимо перед ключевым словом override поместить ключевое слово sealed . Пример:

public class D : C < public sealed override void DoWork() < >> 

См. также раздел

  • Руководство по программированию на C#
  • Система типов C#
  • Наследование
  • Методы
  • Поля
  • Определение абстрактных свойств

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

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

Запечатанные классы (sealed)

Добавление модификатора sealed к суперклассу ограничивает возможность создания подклассов. Все прямые подклассы должны быть вложены в суперкласс. Запечатанный класс не может иметь наследников, объявленных вне класса.

 sealed class SealedClass < class One(val value: Int) : SealedClass() class Two(val x: Int, val y: Int) : SealedClass() fun eval(e: SealedClass): Int = when (e) < is SealedClass.One ->e.value is SealedClass.Two -> e.x + e.y > > 

В методе eval() при использовании when не пришлось использовать ветку else, так как sealed позволяет указать все доступные варианты и значение по умолчанию не требуется.

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

По умолчанию запечатанный класс открыт и модификатор open не требуется. Запечатанные классы немного напоминают enum.

Пример: Продам кота дёшево

Вася Ложкин

Создадим запечатанный класс AcceptedCurrency и три подкласса на его основе. Обратите внимание, что сейчас Kotlin разрешает объявлять подклассы не внутри запечатанного класса, а на одном уровне (для сравнения смотри старые примеры выше).

 package ru.alexanderklimov.sealed sealed class AcceptedCurrency class Rubel : AcceptedCurrency() class Dollar : AcceptedCurrency() class Tugrik : AcceptedCurrency() 

В классе активности создадим список принимаемых валют для покупки котят и применим его к адаптеру выпадающего списка.

 package ru.alexanderklimov.sealed import android.os.Bundle import android.widget.ArrayAdapter import androidx.appcompat.app.AppCompatActivity import kotlinx.android.synthetic.main.activity_main.* class MainActivity : AppCompatActivity() < private val currencies = listOf(Rubel(), Dollar(), Tugrik()) override fun onCreate(savedInstanceState: Bundle?) < super.onCreate(savedInstanceState) setContentView(R.layout.activity_main) val adapter = ArrayAdapter(this, android.R.layout.simple_spinner_item, currencies) adapter.setDropDownViewResource(android.R.layout.simple_spinner_dropdown_item) currencySpinner.adapter = adapter > > 

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

Sealed class

Внесём изменения в запечатанный класс, чтобы у него появилось новое свойство.

 sealed class AcceptedCurrency < val name: String get() = when (this) < is Rubel ->"Рубль" is Dollar -> "Доллар" is Tugrik -> "Тугрик" > > 

Если вы пропустите какой-то подкласс в выражении when, то компилятор будет ругаться. Это удобно, когда вы будете вносить изменения в код.

Поменяем код для адаптера.

 val adapter = ArrayAdapter(this, android.R.layout.simple_spinner_item, currencies.map < it.name >) 

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

Sealed class

Установим зависимость валют от рубля. Создадим в запечатанном классе абстрактное свойство valueInRubels. После этого студия потребует дополнить код у всех подклассов.

 package ru.alexanderklimov.sealed sealed class AcceptedCurrency < abstract val valueInRubels: Double var amount: Double = 0.0 val name: String get() = when (this) < is Rubel ->"Рубль" is Dollar -> "Доллар" is Tugrik -> "Тугрик" > > class Rubel : AcceptedCurrency() < override val valueInRubels = 1.00 >class Dollar : AcceptedCurrency() < override val valueInRubels = 70.0 >class Tugrik : AcceptedCurrency()

Добавим в класс ещё одну переменную ammount и функцию для подсчёта общей суммы.

 sealed class AcceptedCurrency < abstract val valueInRubels: Double var amount: Double = 0.0 val name: String get() = when (this) < is Rubel ->"Рубль" is Dollar -> "Доллар" is Tugrik -> "Тугрик" > fun totalValueInRubels(): Double > 

Напишем код для щелчка кнопки. Вам нужно ввести минимальную и максимальную цену в любой валюте для одного котёнка, а кнопка покажет цену в рублях. Если вы увидите, что покупатель из Америки, то выставляете ценник в долларах. Если покупатель из непонятной страны, то ставьте тугрики (какая вам разница?).

 convertButton.setOnClickListener < val low = currencyFromSelection() val high = currencyFromSelection() low.amount = lowAmountEditText.text.toString().toDouble() high.amount = highAmountEditText.text.toString().toDouble() lowAmountInRubelsTextView.text = String.format("%.2f руб.", low.totalValueInRubels()) highAmountInRubelsTextView.text = String.format("%.2f руб.", high.totalValueInRubels()) >private fun currencyFromSelection() = when (currencies[currencySpinner.selectedItemPosition]) < is Dollar ->Dollar() is Rubel -> Rubel() is Tugrik -> Tugrik() > 

В примере мы выставили цену от 2 до 3 долларов за котёнка (что-то мы продешевили) и сразу видим, сколько заработаем в рублях.

Для чего нужен модификатор sealed в языке C#?

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

class A <>
sealed class B : A <>

Здесь класс B наследует от класса A, но ни какие классы не могут наследовать от класса B.

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

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("X.F3"); >
>
class Z : Y
// Attempting to override F causes compiler error CS0239.
// protected override void F() < Console.WriteLine("C.F"); >

// Overriding F2 is allowed.
protected override void F2() < Console.WriteLine("Z.F2"); >
>

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

Несколько правил при использовании модификатора sealed:

  1. Чтобы предотвратить переопределение производных классов при определении новых методов или свойств, не назначайте их в качестве виртуальных (virtual). В противном случае программист будет видеть данный метод в списке методов, которые можно переопределить, но на этапе компиляции получит ошибку.
  2. Нельзя использовать модификатор abstract с sealed классом, поскольку абстрактный класс должен наследоваться классом, реализующим абстрактные методы или свойства. Это понятно, ведь основное назначение абстрактного класса в том, чтобы от него наследовались другие классы.
  3. При применении sealed модификатора к методу или свойству его необходимо всегда использовать с override.

Рассмотрим еще одну особенность на следующем примере:
public class A

 public virtual int Sum(int a, int b) 
return a + b;
>
>

public class B: A
public sealed override int Sum(int a, int b)
return base.Sum (a, b);
>
>

public class C: B
public override int Sum(int a, int b)
return base.Sum(a, b);
>
>

Здесь мы получим ошибку компиляции по понятным уже причинам.

public class C: B
public new int Sum(int a, int b)
return base.Sum(a, b);
>
>

А здесь все будет работать. Почему? Ведь мы переопределили метод, который переопределять то нельзя. Этому есть объяснение. Дело в том, что посредством модификатора new мы говорим, что не хотим переопределять унаследованный метод, а хотим создать свой собственный (новый метод) с таким же именем, который прячет одноименный метод предка. То есть в этом месте у нас перестает работать виртуальный механизм и появляется обычный метод.

Дополнительные материалы:

  • sealed (Справочник по C#)
  • Наследование (Руководство по программированию в C#)
  • Расширенные возможности полиморфизма в языке C#

Используете ли вы модификатор «sealed»

Только зарегистрированные пользователи могут участвовать в опросе. Войдите, пожалуйста.

Используете ли вы модификатор «sealed»

10.65% Да 36

9.76% Только на классах 33

0.89% Только на методах 3

26.92% Нет 91

51.78% А что это? 175

Проголосовали 338 пользователей. Воздержались 96 пользователей.

Голосование

Голосование

Добавить в закладки 3

Александр Фомин @Sane

Публикации

Лучшие за сутки Похожие

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

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