Когда и зачем запечатывать класс?

88

В C # и C ++ / CLI ключевое слово sealed(или NotInheritableв VB) используется для защиты класса от любого шанса наследования (класс не наследуется). Я знаю, что одна из особенностей объектно-ориентированного программирования - это наследование, и я чувствую, что использование sealedидет вразрез с этой функцией, она останавливает наследование. Есть ли пример, показывающий пользу sealedи когда это важно использовать?

Aan
источник

Ответы:

97
  1. В классе, который реализует функции безопасности, так что исходный объект не может быть «олицетворен».

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

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

Луи Коттманн
источник
3
Я имел в виду быть осторожным с запечатыванием классов в повторно используемых библиотеках, особенно если они будут повторно использоваться третьими сторонами, а затем повторно интегрированы (через MEF) в базу кода. Ваша кодовая база не может наследовать данный класс, но третьи стороны наследуют.
Луи Коттманн,
10
Причина №1 звучит расплывчато, но, если предположить, что мы не пишем «функции безопасности» большую часть времени, означает ли это, что причина №1 вряд ли применима? Причина №2 для настройки производительности. О какой разнице в производительности мы говорим? Достаточно ли они значительны, чтобы оправдать изменение определения класса, не связанного с безопасностью? Даже если бы ответ был «да», в идеале это был бы вариант компилятора, то есть «генерировать оптимизированный код для всех незапечатанных классов», вместо того, чтобы заставлять нас разработчиков изменять базу кода.
RayLuo
1
это становится дорогостоящим с точки зрения производительности, если его не лечить , измеримо ли это даже с помощью менее чем безумного количества сумасшедших тестов?
t3chb0t
4
Уплотнение - отстой. Это усложняет тестирование - я хотел бы поиздеваться над парой классов ASP.NET с помощью FakeItEasy, но не могу, потому что они запечатаны.
Warlike Chimpanzee
2
Не могу больше согласиться с @RayLuo. Я несколько раз отмечал, что люди закрывают свои классы там, где безопасность и производительность не являются проблемой. Их «запечатанный» просто лишил меня разумной потребности переопределить классы, значительно усложнив задачу. Как сказал воинственный шимпанзе, издевательство над классом - обычное дело при тестировании.
ZZY
15

Дополнение к отличному ответу Бабуина :

  1. Если класс не предназначен для наследования, подклассы могут нарушить инварианты класса . Это действительно применимо только в том случае, если вы создаете общедоступный API, но, как я знаю, я запечатываю любой класс, который явно не предназначен для создания подклассов.

В связи с этим, применимо только к незапечатанным классам: любой созданный метод virtualявляется точкой расширения или, по крайней мере, выглядит так, как будто он должен быть точкой расширения. Объявление методов также virtualдолжно быть осознанным решением. (В C # это осознанное решение; в Java - нет.)


РЕДАКТИРОВАТЬ : некоторые соответствующие ссылки:

Также обратите внимание, что Kotlin по умолчанию закрывает классы; его openключевое слово является противоположностью Java finalили sealedC # . (Безусловно, нет единого мнения, что это хорошо .)

Петтер Хессельберг
источник
26
Занятия запечатыванием вызывают больше головной боли, чем пользы. Я постоянно сталкивался с ситуациями, когда разработчики закрывали классы, что доставляло мне часы затруднений с тем, что должно быть простым. Прекратите запечатывать классы, вы не так остроумны, как думаете. Запечатывать классы только в том случае, если вы ДОЛЖНЫ, да и то, пересмотреть. Просто мое мнение как парня, которому приходится иметь дело с закрытыми классами других людей, которые я не могу редактировать / распечатывать.
Гант Лаборд,
9
Комментарий @GantMan на самом деле следует рассматривать как один из ответов на вопрос OP, потому что он, по сути, дает ответ как «Когда? Вряд ли. Почему? Это причина того, почему вы НЕ делаете этого». Вы должны повторно опубликовать свой комментарий как отдельный ответ, а затем собрать за него голоса. :-)
RayLuo 02
1
Относилось ли это к этому ответу: stackoverflow.com/a/7777674/3195477 ? Лучше дать ссылку на него, чем (только) назвать человека
UuDdLrLrSs
2

Пометка класса как Sealedпредотвращает вмешательство в важные классы, которые могут поставить под угрозу безопасность или повлиять на производительность.

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

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

Точно так же structuresin C#всегда неявно запечатаны. Следовательно, нельзя вывести одну структуру / класс из другой структуры. Причина этого в том, что structuresони используются для моделирования только автономных, атомарных, определяемых пользователем типов данных, которые мы не хотим изменять.

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

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

Акшай Хот
источник
Чем нежелательно расширение класса String? String по-прежнему будет работать точно так же, как сейчас, и при желании у вас может быть производный класс с дополнительной функциональностью, так о какой проблеме вы говорите?
Кевин Уэллс,
Также в чем будет смысл «ограничивать» вашу иерархию наследования? Это будет означать, что если вам когда-либо понадобится расширить эту иерархию, вам придется сначала распечатать родительский класс, что просто неэффективно,
Кевин Уэллс,
Посмотрите этот отличный пост от Эрика Липперта и этот ТАК вопрос .
Акшай Хот
1
Даже этот ответ в основном сводится к «Зачем вам наследовать String?», Затем упоминаются причины, по которым вы могли бы захотеть получить String (например, строки с завершающим нулем), и говорит, что вы должны просто обойти это без наследования. Так зачем усложнять это и нужно обойти это позже, когда вы можете просто оставить его незапечатанным и оставить свои варианты открытыми
Кевин Уэллс
Что касается второго вопроса, цель состоит в том, чтобы предотвратить нежелательное поведение (в зависимости от бизнес-логики). unsealПозже, если потребуется, это легче сделать классу, чем запечатать его и сломать все классы, которые от него зависят.
Акшай Хот
0

Я думаю, что в этом посте есть несколько хороших моментов, конкретный случай был при попытке привести незапечатанный класс к любому случайному интерфейсу, компилятор не выдает ошибку; но когда используется запечатанный, компилятор выдает ошибку, которую он не может преобразовать. Запечатанный класс обеспечивает дополнительную безопасность доступа кода.
https://www.codeproject.com/Articles/239939/Csharp-Tweaks-Why-to-use-the-sealed-keyword-on-cla

струйный свет
источник
1
Ссылка на решение приветствуется, но убедитесь, что ваш ответ полезен и без нее: добавьте контекст вокруг ссылки чтобы ваши друзья-пользователи имели некоторое представление, что это такое и почему оно есть, а затем процитируйте наиболее релевантную часть страницы, которую вы повторная ссылка на, если целевая страница недоступна. Ответы, которые представляют собой не более чем ссылку, могут быть удалены.
Baum mit Augen
Извините, я не собирался публиковать его в качестве ответа, но, похоже, он не связан с другими ответами, и я не знаю, где его поставить
strisunshine
1
Отредактировал пост по предложению. Изначально я просто хотел бы высказаться под другим углом (возможно), но я получил только отрицательный голос, и мы еще не говорили о содержании, не мог бы -1 сообщить причину?
strisunshine