Kiedy i dlaczego zapieczętowałbyś klasę?


89

W C # i C ++ / CLI słowo kluczowe sealed(lub NotInheritablew VB) służy do ochrony klasy przed jakąkolwiek szansą na dziedziczenie (klasa nie będzie dziedziczona). Wiem, że jedną z cech programowania obiektowego jest dziedziczenie i czuję, że użycie sealedjest sprzeczne z tą funkcją, zatrzymuje dziedziczenie. Czy istnieje przykład, który pokazuje korzyści sealedi kiedy ważne jest, aby z niego korzystać?

Odpowiedzi:


100
  1. W klasie, która implementuje funkcje zabezpieczeń, tak że oryginalny obiekt nie może być „podszywany”.

  2. Mówiąc bardziej ogólnie, niedawno wymieniłem się z osobą w firmie Microsoft, która powiedziała mi, że próbowała ograniczyć dziedziczenie do miejsc, w których ma to naprawdę sens, ponieważ nieleczone staje się kosztowne pod względem wydajności.
    Sealed słowo kluczowe mówi CLR, że nie ma już klasy, która mogłaby szukać metod, a to przyspiesza działanie.

W większości narzędzi zwiększających wydajność dostępnych obecnie na rynku znajduje się pole wyboru, które zapieczętuje wszystkie klasy, które nie są dziedziczone.
Należy jednak zachować ostrożność, ponieważ jeśli chcesz zezwolić na wykrywanie wtyczek lub zestawu za pośrednictwem MEF, napotkasz problemy.


3
Chodziło mi o ostrożność z zapieczętowywaniem klas w ponownie używanych bibliotekach, zwłaszcza jeśli zostaną ponownie wykorzystane przez osoby trzecie, a następnie ponownie zintegrowane (przez MEF) z bazą kodu. Twoja baza kodów może nie dziedziczyć danej klasy, ale strony trzecie to zrobią.
Louis Kottmann,

10
Powód nr 1 brzmi niejasno, ale zakładając, że nie piszemy „funkcji bezpieczeństwa” przez większość czasu, czy oznacza to, że powód nr 1 prawie nie ma zastosowania? Powodem nr 2 jest dostrajanie wydajności. O jakiej różnicy w wydajności mówimy? Czy są one wystarczająco istotne, aby uzasadniać zmianę definicji klasy niebędącej zabezpieczeniem? Nawet jeśli odpowiedź brzmiałaby „tak”, byłaby to idealna opcja kompilatora, tj. „Generowanie zoptymalizowanego kodu dla wszystkich niezamkniętych klas”, zamiast zmuszania nas, programistów, do zmiany bazy kodu.
RayLuo

1
staje się kosztowne, jeśli nie jest leczone, czy jest to w ogóle możliwe do zmierzenia przy mniejszej niż szalona liczbie szalonych testów?
t3chb0t

4
Uszczelnianie jest do niczego. Utrudnia to testowanie - chciałbym wyśmiać kilka klas ASP.NET za pomocą FakeItEasy, ale nie mogę, ponieważ są one zapieczętowane.
Warlike Chimpanzee

2
Nie mogę się bardziej zgodzić z @RayLuo. Trafiłem kilka razy, że ludzie zapieczętowali swoje zajęcia, w których bezpieczeństwo i wydajność nie stanowią problemu. Ich „zapieczętowanie” po prostu uniemożliwiło mi rozsądną potrzebę zastępowania zajęć, znacznie utrudniło sprawę. Jak powiedział Warlike Chimpanzee, kpiny z klasy są tak powszechne w testowaniu.
ZZY

15

Dodatek do doskonałej odpowiedzi Baboon :

  1. Jeśli klasa nie jest przeznaczona do dziedziczenia, podklasy mogą złamać niezmienniki klas . Dotyczy to oczywiście tylko sytuacji, gdy tworzysz publiczny interfejs API, ale zgodnie z ogólną zasadą pieczętuję każdą klasę, która nie została wyraźnie zaprojektowana do podklasy.

W związku z tym, ma zastosowanie tylko do niezapieczętowanych klas: każda utworzona metoda virtualjest punktem rozszerzenia lub przynajmniej wygląda na to, że powinna być punktem rozszerzenia. Deklarowanie metod również virtualpowinno być świadomą decyzją. (W C # jest to świadoma decyzja; w Javie tak nie jest).


EDYCJA : Kilka odpowiednich linków:

Należy również zauważyć, że Kotlin domyślnie pieczętuje klasy; jego opensłowo kluczowe jest przeciwieństwem języka Java finallub sealedC # . (Z pewnością nie ma powszechnej zgody, że to dobra rzecz ).


26
Zajęcia uszczelniające powodują więcej bólu głowy niż korzyści. Ciągle znajdowałem sytuacje, w których programiści zapieczętowali klasy, sprawiając mi wiele godzin trudności z tym, co powinno być proste. Przestańcie zapieczętować zajęcia, nie jesteście tak dowcipni, jak myślicie. Zapieczętuj zajęcia tylko wtedy, gdy MUSISZ, a nawet wtedy, ponownie rozważyć. Tylko moja opinia, jako gościa, który ma do czynienia z zapieczętowanymi klasami innych ludzi, których nie mogę edytować / odpieczętować.
Gant Laborde

9
Komentarz @GantMan powinien być właściwie traktowany jako jedna z odpowiedzi na pytanie PO, ponieważ zasadniczo daje odpowiedź „Kiedy? Prawie. Dlaczego? To jest powód, dla którego tego NIE robisz”. Przyznaj, że powinieneś ponownie opublikować swój komentarz jako oddzielną odpowiedź, a następnie zebrać na nią głosy. :-)
RayLuo

1
Czy to odnosiło się do tej odpowiedzi: stackoverflow.com/a/7777674/3195477 ? Lepiej jest do niego linkować niż (tylko) nazwać osobę
UuDdLrLrSs

2

Oznaczenie klasy jako Sealedzapobiega manipulowaniu ważnymi klasami, które mogą zagrozić bezpieczeństwu lub wpłynąć na wydajność.

Często zapieczętowanie klasy ma również sens, gdy projektujemy klasę użytkową o stałym zachowaniu, którego nie chcemy zmieniać.

Na przykład Systemprzestrzeń nazw w C#zawiera wiele klas, które są zapieczętowane, takie jak String. Gdyby nie był zamknięty, możliwe byłoby rozszerzenie jego funkcjonalności, co mogłoby być niepożądane, ponieważ jest to typ podstawowy o danej funkcjonalności.

Podobnie, structuresw C#są zawsze domyślnie zapieczętowane. Dlatego nie można wyprowadzić jednej struktury / klasy z innej struktury. Powodem tego jest to, że structuressą one używane do modelowania tylko samodzielnych, niepodzielnych, zdefiniowanych przez użytkownika typów danych, których nie chcemy modyfikować.

Czasami podczas budowania hierarchii klas możesz chcieć ograniczyć określoną gałąź w łańcuchu dziedziczenia na podstawie modelu domeny lub reguł biznesowych.

Na przykład, a Manageri PartTimeEmployeesą oba Employee, ale nie pełnisz żadnej roli po pracownikach zatrudnionych w niepełnym wymiarze godzin w Twojej organizacji. W takim przypadku możesz chcieć zapieczętować, PartTimeEmployeeaby zapobiec dalszemu rozgałęzianiu. Z drugiej strony, jeśli masz pracowników zatrudnionych w niepełnym wymiarze godzinowym lub tygodniowym, warto odziedziczyć ich po PartTimeEmployee.


W jaki sposób rozszerzenie klasy String byłoby niepożądane? Ciąg nadal działałby dokładnie tak, jak obecnie, i możesz mieć klasę pochodną z dodatkową funkcjonalnością, gdy jest to pożądane, więc o czym mówisz?
Kevin Wells

Jaki byłby też sens „ograniczania” hierarchii dziedziczenia? Oznaczałoby to, że gdybyś kiedykolwiek musiał rozszerzyć tę hierarchię, musiałbyś najpierw odblokować klasę rodzicielską, co jest po prostu nieefektywne
Kevin Wells

Sprawdź ten doskonały post Erica Lipperta i to pytanie SO .
Akshay Khot

1
Nawet ta odpowiedź w zasadzie sprowadza się do „Dlaczego chcesz wyprowadzić String?”, A następnie wspomina o powodach, dla których możesz chcieć wyprowadzić String (na przykład ciągi zakończone znakiem null) i mówi, że powinieneś po prostu obejść to bez dziedziczenia. Po co więc komplikować to bardziej i trzeba to obejść później, kiedy można po prostu zostawić go niezamkniętego i zostawić otwarte opcje
Kevin Wells

W przypadku drugiego pytania celem byłoby zapobieganie niepożądanemu zachowaniu (w zależności od logiki biznesowej). unsealJeśli zajdzie taka potrzeba, łatwiej będzie później klasie niż zapieczętować ją i rozbić wszystkie klasy, które od niej zależą.
Akshay Khot

0

Myślę, że ten post ma dobry punkt, konkretny przypadek dotyczył próby rzutowania niezamykanej klasy na dowolny przypadkowy interfejs, kompilator nie zgłasza błędu; ale kiedy jest używany Seal, kompilator zgłasza błąd, którego nie może przekonwertować. Klasa Sealed zapewnia dodatkowe zabezpieczenie dostępu do kodu.
https://www.codeproject.com/Articles/239939/Csharp-Tweaks-Why-to-use-the-sealed-keyword-on-cla


1
Link do rozwiązania jest mile widziany, ale upewnij się, że Twoja odpowiedź jest przydatna bez niego: dodaj kontekst wokół linku, aby inni użytkownicy mieli pojęcie, co to jest i dlaczego się tam znajduje, a następnie zacytuj najbardziej odpowiednią część strony, którą podałeś. ponowne łącze w przypadku, gdy strona docelowa jest niedostępna. Odpowiedzi, które są niewiele więcej niż linkiem, mogą zostać usunięte.
Baum mit Augen

Przepraszam, że nie zamierzałem opublikować tego jako odpowiedzi, ale wydaje się, że nie ma to związku z innymi odpowiedziami i nie wiem, gdzie to umieścić
strisunshine

1
Post zredagowałem zgodnie z sugestią. Początkowo chciałbym przedstawić inny punkt widzenia (być może), ale dostałem tylko negatywną opinię i nie rozmawialiśmy jeszcze o treści, czy -1 może uprzejmie podać powód?
strisunshine
Korzystając z naszej strony potwierdzasz, że przeczytałeś(-aś) i rozumiesz nasze zasady używania plików cookie i zasady ochrony prywatności.
Licensed under cc by-sa 3.0 with attribution required.