Коли і чому ви б печатали клас?


87

У C # та C ++ / CLI ключове слово sealed(або NotInheritableу VB) використовується для захисту класу від будь-якого шансу успадкування (клас буде не успадковуватися). Я знаю, що однією особливістю об’єктно-орієнтованого програмування є успадкування, і я відчуваю, що використання “ sealedсуперечить” цій функції, це зупиняє успадкування. Чи є приклад, який показує користь sealedі коли важливо його використовувати?

Відповіді:


96
  1. У класі, який реалізує функції захисту, так що оригінальний об'єкт не може бути "виданим за себе".

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

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


3
Я мав на увазі бути обережним із запечатуванням класів у багаторазово використовуваних бібліотеках, особливо якщо вони повторно використовуються третіми сторонами, а потім реінтегруються (через MEF) у кодову базу. Ваша кодова база може не успадковувати даний клас, але сторонні сторони.
Луїс Коттманн

10
Причина №1 звучить розпливчасто, але, припускаючи, що ми не пишемо "функції безпеки" більшу частину часу, чи означає це, що причина №1 навряд чи застосовується? Причина №2 - налаштування продуктивності. Про яку різницю в продуктивності ми говоримо? Чи достатньо вони значущі, щоб виправдати зміну визначення класу, що не є безпекою? Навіть якщо відповідь буде "так", в ідеалі це буде варіант компілятора, тобто "генерувати оптимізований код для всіх негерметичних класів", а не вимагати від нас, розробників, змінювати базу коду.
RayLuo,

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

4
Ущільнення відстій. Це ускладнює тестування - я хотів би знущатися з кількома класами ASP.NET з FakeItEasy, але я не можу, оскільки вони запечатані.
Войовниче шимпанзе

2
Не можу більше погодитися з @RayLuo. Я кілька разів довідався, що люди запечатали свої класи, де безпека та продуктивність насправді не є проблемою. Їх "опечатування" просто перешкоджало моїй розумній потребі замінити класи, значно ускладнювало справи. Як сказав войовничий шимпанзе, знущання над класом так часто зустрічаються під час тестування.
ZZY

15

Додаток до чудової відповіді Бабуїна :

  1. Якщо клас не призначений для успадкування, підкласи можуть порушити інваріанти класу . Це дійсно стосується лише тих випадків, коли ви, звичайно, створюєте загальнодоступний API, але, як правило, я герметизую будь-який клас, який явно не призначений для підкласу.

У відповідній примітці, застосовній лише до незапечатаних класів: будь-який створений метод virtualє точкою розширення або, принаймні, схоже, він повинен бути точкою розширення. Методи декларування також virtualповинні бути свідомим рішенням. (У C # це свідоме рішення; у Java це не так.)


EDIT : Деякі відповідні посилання:

Також зверніть увагу, що Котлін за замовчуванням герметизує класи; його openключове слово протилежне Java finalабо sealedC # . (Звичайно, немає універсальної згоди, що це добре .)


25
Запечатування класів викликає більше головного болю, ніж користі. Я постійно знаходив ситуації, коли розробники закривали класи, викликаючи у мене години труднощів у тому, що повинно бути простим. Припиніть ущільнення класів, ви не такі дотепні, як думаєте. Запечатувати класи, лише якщо ви ПОВИННІ, та навіть тоді, переглянути. Просто моя думка, як хлопця, котрий повинен мати справу із закритими класами інших людей, які я не можу редагувати / розпечатувати.
Gant Laborde

9
Коментар @GantMan насправді слід розглядати як один із відповідей на питання ОП, оскільки він, по суті, дає відповідь "Коли? Навряд чи. Чому? Це причина того, чому ви цього НЕ робите". Зверніть увагу, що ви повинні повторно розмістити свій коментар як окрему відповідь, а потім зібрати за нього голоси. :-)
RayLuo,

1
Чи було це посиланням на цю відповідь: stackoverflow.com/a/7777674/3195477 ? Краще посилатися на нього, ніж (лише) називати особу
UuDdLrLrSs

2

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

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

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

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

Іноді, коли ви будуєте ієрархію класів, вам може знадобитися обмежити певну гілку в ланцюжку успадкування на основі вашої моделі домену або бізнес-правил.

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


Як розширення класу String було б небажаним? Рядок все одно працюватиме саме так, як зараз, і ви можете мати похідний клас із додатковою функціональністю, коли це потрібно, то про яку проблему ви говорите?
Кевін Уеллс,

Також який сенс матиме "обмеження" вашої ієрархії успадкування? Це означало б, що якщо вам коли-небудь знадобиться розширити цю ієрархію, вам доведеться спочатку розпечатати батьківський клас, що просто неефективно
Кевін Уеллс,

Ознайомтеся з цим чудовим дописом Еріка Ліпперта та цим ТАКИМ запитанням .
Akshay Khot

1
Навіть ця відповідь в основному зводиться до "Чому ви хочете вивести String?", Далі згадує причини, чому ви можете отримати String (наприклад, рядки з нульовим закінченням), і каже, що вам слід просто обійти це без успадкування. Тож навіщо це ускладнювати, і доводиться
Кевін Уеллс,

Для другого питання метою було б запобігти небажаній поведінці (залежно від бізнес-логіки). Пізніше unsealкласу легше , якщо це потрібно, а не запечатувати його та ламати всі класи, які від цього залежать.
Akshay Khot

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