Kiedy tworzymy klasę dziedziczącą po klasie abstrakcyjnej i kiedy implementujemy odziedziczoną klasę abstrakcyjną, dlaczego musimy używać słowa kluczowego zastępującego?
"Dlaczego?" na takie pytania może być trudno odpowiedzieć, ponieważ są niejasne. Założę, że twoje pytanie brzmi: „jakie argumenty można sformułować podczas projektowania języka, aby argumentować za tym, że overridesłowo kluczowe jest wymagane ?”
Zacznijmy od cofnięcia się. W niektórych językach, powiedzmy Java, metody są domyślnie wirtualne i zastępowane automatycznie. Projektanci C # byli tego świadomi i uważali to za niewielką wadę w Javie. C # nie jest „Javą z usuniętymi głupimi częściami”, jak niektórzy powiedzieli, ale projektanci C # chętnie uczyli się z problematycznych punktów projektowych w C, C ++ i Javie, a nie replikowali ich w C #.
Projektanci C # uznali przesłonięcie za możliwe źródło błędów; w końcu jest to sposób na zmianę zachowania istniejącego, przetestowanego kodu , co jest niebezpieczne. Zastąpienie nie jest czymś, co należy zrobić przypadkowo lub przypadkowo; powinien być zaprojektowany przez kogoś, kto się nad tym intensywnie zastanawia . Dlatego metody nie są domyślnie wirtualne i dlatego musisz powiedzieć, że zastępujesz metodę.
To podstawowe rozumowanie. Możemy teraz przejść do bardziej zaawansowanego rozumowania.
Odpowiedź StriplingWarrior daje dobre pierwsze cięcie w tworzeniu bardziej zaawansowanego argumentu. Autor klasy pochodnej może nie być poinformowany o klasie bazowej, może chcieć stworzyć nową metodę i nie powinniśmy pozwolić użytkownikowi na pomyłkę .
Chociaż ten punkt jest uzasadniony, istnieje wiele kontrargumentów, takich jak:
- Autor klasy pochodnej ma obowiązek wiedzieć wszystko o klasie bazowej! Ponownie wykorzystują ten kod i powinni dołożyć należytej staranności, aby dokładnie zrozumieć ten kod przed ponownym użyciem.
- W twoim konkretnym scenariuszu metoda wirtualna jest abstrakcyjna; błędem byłoby nie nadpisanie go, więc jest mało prawdopodobne, że autor stworzy implementację przez przypadek.
Zróbmy jeszcze bardziej zaawansowany argument na ten temat. W jakich okolicznościach autor klasy pochodnej może być usprawiedliwiony za to, że nie wie, co robi klasa podstawowa? Rozważ ten scenariusz:
- Autor klasy podstawowej tworzy abstrakcyjną klasę podstawową B.
- Autor klasy pochodnej, w innym zespole, tworzy klasę pochodną D metodą M.
- Autor klasy podstawowej zdaje sobie sprawę, że zespoły, które rozszerzają klasę podstawową B zawsze będą musiały podać metodę M, więc autor klasy podstawowej dodaje metodę abstrakcyjną M.
- Co się stanie po ponownej kompilacji klasy D.
Chcemy, aby autor D został poinformowany, że coś istotnego się zmieniło . Istotną rzeczą, która się zmieniła, jest to, że M jest teraz wymogiem i że ich implementacja musi być przeciążona. DM może potrzebować zmienić swoje zachowanie, gdy wiemy, że można go wywołać z klasy podstawowej. Właściwe jest, aby nie mówić po cichu „och, DM istnieje i rozszerza BM”. Prawidłowe działanie kompilatora to błąd i powiedz „hej, autor D, sprawdź to założenie, które nie jest już poprawne i popraw kod, jeśli to konieczne”.
W twoim przykładzie załóżmy, że opcjaoverride była włączona, ponieważ zastępuje metodę abstrakcyjną. Istnieją dwie możliwości: (1) autor kodu zamierza zastąpić metodę abstrakcyjną lub (2) metoda zastępująca jest nadpisywana przypadkowo, ponieważ ktoś inny zmienił klasę podstawową, a kod jest teraz w jakiś subtelny sposób niepoprawny. Nie możemy rozróżnić tych możliwości, jeśli jest to opcjonalne . SayHellooverride
Ale jeśli overridejest to wymagane , możemy wyróżnić trzy scenariusze. Jeśli istnieje możliwy błąd w kodzie, overrideto go brakuje . Jeśli jest to celowo nadpisywane, overridejest obecne . A jeśli celowo nie jest to nadrzędne, to newjest obecne . Projekt C # umożliwia nam dokonanie tych subtelnych rozróżnień.
Pamiętaj, że raportowanie błędów kompilatora wymaga przeczytania opinii programisty ; kompilator musi wywnioskować z niewłaściwego kodu, jaki prawidłowy kod prawdopodobnie miał na myśli autor , i podać błąd wskazujący właściwy kierunek. Im więcej wskazówek możemy pozostawić programistom w kodzie na temat tego, co myślą, tym lepiej kompilator może wykonać raportowanie błędów, a tym samym szybciej można znaleźć i naprawić swoje błędy.
Ale bardziej ogólnie, C # został zaprojektowany dla świata, w którym zmienia się kod . Istnieje wiele funkcji C #, które wydają się „nieparzyste”, ponieważ informują one programistę, gdy założenie, które było ważne, stało się nieważne z powodu zmiany klasy podstawowej. Ta klasa błędów nosi nazwę „kruchych awarii klasy bazowej”, a język C # ma wiele interesujących elementów łagodzących dla tej klasy błędów.