Kiedy tworzymy klasę dziedziczącą po klasie abstrakcyjnej i kiedy implementujemy odziedziczoną klasę abstrakcyjną, dlaczego musimy używać słowa kluczowego zastępującego?
public abstract class Person
{
public Person()
{
}
protected virtual void Greet()
{
// code
}
protected abstract void SayHello();
}
public class Employee : Person
{
protected override void SayHello() // Why is override keyword necessary here?
{
throw new NotImplementedException();
}
protected override void Greet()
{
base.Greet();
}
}
Ponieważ metoda jest zadeklarowana jako abstrakcyjna w swojej klasie nadrzędnej, nie ma żadnej implementacji w klasie nadrzędnej, więc dlaczego tutaj jest konieczne zastąpienie słowa kluczowego?
new
kluczowego”… Otrzymasz również błąd „żadna metoda nie zastępuje metody klasy bazowej”.Odpowiedzi:
"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
override
sł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:
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:
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 opcja
override
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 .SayHello
override
Ale jeśli
override
jest to wymagane , możemy wyróżnić trzy scenariusze. Jeśli istnieje możliwy błąd w kodzie,override
to go brakuje . Jeśli jest to celowo nadpisywane,override
jest obecne . A jeśli celowo nie jest to nadrzędne, tonew
jest 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.
źródło
Określa, czy próbujesz zastąpić inną metodę w klasie nadrzędnej, czy utworzyć nową implementację unikalną dla tego poziomu hierarchii klas. Można sobie wyobrazić, że programista może nie być świadomy istnienia metody w klasie nadrzędnej z dokładnie taką samą sygnaturą, jak ta, którą tworzą w swojej klasie, co może prowadzić do przykrych niespodzianek.
Chociaż prawdą jest, że metoda abstrakcyjna musi zostać zastąpiona w nieabstrakcyjnej klasie potomnej, twórcy C # prawdopodobnie uznali, że lepiej jest wyraźnie powiedzieć o tym, co próbujesz zrobić.
źródło
Ponieważ
abstract
metoda jest metodą wirtualną bez implementacji, zgodnie ze specyfikacją języka C # , oznacza, że metoda abstrakcyjna jest niejawnie metodą wirtualną. Ioverride
jest stosowany w celu rozszerzenia lub modyfikacji abstrakcyjnego lub wirtualnego wdrożenia, jak można zobaczyć tutajAby sformułować to nieco inaczej - używasz metod wirtualnych do implementacji pewnego rodzaju późnego wiązania, podczas gdy metody abstrakcyjne zmuszają podklasy tego typu do jawnego zastąpienia metody. To jest punkt, gdy metoda jest
virtual
, że mogą być pominięte, gdy jest toabstract
- to musi być nadpisaneźródło
Aby dodać do odpowiedzi @ StriplingWarrior, myślę, że zrobiono to również, aby mieć składnię zgodną z przesłonięciem metody wirtualnej w klasie bazowej.
Tak więc C # można było zaprojektować tak, aby nie trzeba było słowa kluczowego zastępującego w celu zastąpienia metod abstrakcyjnych, ale myślę, że projektanci zdecydowali, że byłoby to mylące, ponieważ nie byłoby spójne z zastąpieniem metody wirtualnej.
źródło
override
? Myślę, że większość ludzi zgodzi się, że absurdalne jest zmuszanie autora D do wprowadzenia niepotrzebnej zmiany kodu; ich klasa jest w porządku!