Chcę wykorzystać odpowiedź Achratta . Aby uzyskać kompletność, różnica polega na tym, że OP oczekuje, że new
słowo kluczowe w metodzie klasy pochodnej zastąpi metodę klasy bazowej. Właściwie to się ukrywa metodę klasy bazowej.
W języku C #, jak wspomniano w innej odpowiedzi, zastępowanie tradycyjnej metody musi być jawne; metoda klasy bazowej musi być oznaczona jako, virtual
a klasa pochodna musi być konkretnieoverride
metodą klasy bazowej. Jeśli tak się stanie, nie ma znaczenia, czy obiekt jest traktowany jako instancja klasy bazowej czy pochodnej; metoda pochodna zostanie znaleziona i wywołana. Odbywa się to w podobny sposób jak w C ++; metoda oznaczona jako „wirtualna” lub „nadpisanie” podczas kompilacji jest rozwiązywana „późno” (w czasie wykonywania) przez określenie rzeczywistego typu obiektu, do którego się odwołuje, i przechodzenie hierarchii obiektów w dół wzdłuż drzewa od typu zmiennej do rzeczywistego typu obiektu, aby znaleźć najbardziej pochodną implementację metody zdefiniowanej przez typ zmiennej.
Różni się to od języka Java, który umożliwia „niejawne przesłonięcia”; na przykład metody (niestatyczne), po prostu zdefiniowanie metody o tej samej sygnaturze (nazwa i liczba / typ parametrów) spowoduje, że podklasa zastąpi nadklasę.
Ponieważ często warto rozszerzyć lub zastąpić funkcjonalność metody niewirtualnej, której nie kontrolujesz, C # zawiera również new
kontekstowe słowo kluczowe. Słowo new
kluczowe „ukrywa” metodę nadrzędną zamiast ją przesłonić. Każda dziedziczna metoda może zostać ukryta, niezależnie od tego, czy jest wirtualna, czy nie; pozwala to programistom na wykorzystanie członków, których chcesz odziedziczyć po rodzicach, bez konieczności obchodzenia się z tymi, których nie masz, a jednocześnie umożliwia prezentowanie tego samego „interfejsu” konsumentom Twojego kodu.
Ukrywanie działa podobnie do nadpisywania z perspektywy osoby korzystającej z Twojego obiektu na poziomie dziedziczenia, na którym zdefiniowana jest metoda ukrywania lub poniżej niego. Z przykładu pytania, programista tworzący Nauczyciela i przechowujący to odniesienie w zmiennej typu Nauczyciel zobaczy zachowanie implementacji ShowInfo () od Nauczyciela, która ukrywa to przed Person. Jednak osoba pracująca z Twoim obiektem w kolekcji rekordów Person (tak jak Ty) zobaczy zachowanie implementacji Person metody ShowInfo (); ponieważ metoda Nauczyciela nie przesłania swojego rodzica (co również wymagałoby wirtualizacji Person.ShowInfo ()), kod działający na poziomie abstrakcji Person nie znajdzie implementacji Nauczyciela i nie będzie jej używał.
Ponadto new
słowo kluczowe nie tylko zrobi to jawnie, ale C # umożliwia niejawne ukrywanie metod; po prostu zdefiniowanie metody z taką samą sygnaturą jak metoda klasy nadrzędnej, bez override
lub new
, spowoduje jej ukrycie (chociaż spowoduje to ostrzeżenie kompilatora lub skargę od niektórych asystentów refaktoryzacji, takich jak ReSharper lub CodeRush). Jest to kompromis, jaki wymyślili projektanci C #, między jawnymi przesłonięciami w C ++ a niejawnymi przesłonięciami Javy, i chociaż jest elegancki, nie zawsze daje takie zachowanie, jakiego można by się spodziewać, jeśli pochodzisz z tła w którymkolwiek ze starszych języków.
Oto nowe rzeczy: staje się to skomplikowane, gdy połączysz dwa słowa kluczowe w długim łańcuchu dziedziczenia. Rozważ następujące:
class Foo { public virtual void DoFoo() { Console.WriteLine("Foo"); } }
class Bar:Foo { public override sealed void DoFoo() { Console.WriteLine("Bar"); } }
class Baz:Bar { public virtual void DoFoo() { Console.WriteLine("Baz"); } }
class Bai:Baz { public override void DoFoo() { Console.WriteLine("Bai"); } }
class Bat:Bai { public new void DoFoo() { Console.WriteLine("Bat"); } }
class Bak:Bat { }
Foo foo = new Foo();
Bar bar = new Bar();
Baz baz = new Baz();
Bai bai = new Bai();
Bat bat = new Bat();
foo.DoFoo();
bar.DoFoo();
baz.DoFoo();
bai.DoFoo();
bat.DoFoo();
Console.WriteLine("---");
Foo foo2 = bar;
Bar bar2 = baz;
Baz baz2 = bai;
Bai bai2 = bat;
Bat bat2 = new Bak();
foo2.DoFoo();
bar2.DoFoo();
baz2.DoFoo();
bai2.DoFoo();
Console.WriteLine("---");
Foo foo3 = bak;
Bar bar3 = bak;
Baz baz3 = bak;
Bai bai3 = bak;
Bat bat3 = bak;
foo3.DoFoo();
bar3.DoFoo();
baz3.DoFoo();
bai3.DoFoo();
bat3.DoFoo();
Wynik:
Foo
Bar
Baz
Bai
Bat
---
Bar
Bar
Bai
Bai
Bat
---
Bar
Bar
Bai
Bai
Bat
Można się spodziewać pierwszego zestawu pięciu; ponieważ każdy poziom ma implementację i odwołuje się do obiektu tego samego typu, do którego została utworzona instancja, środowisko wykonawcze rozpoznaje każde wywołanie poziomu dziedziczenia, do którego odwołuje się typ zmiennej.
Drugi zestaw pięciu jest wynikiem przypisania każdej instancji do zmiennej bezpośredniego typu nadrzędnego. Otóż, pewne różnice w zachowaniu się trzęsą; foo2
, który w rzeczywistości jest Bar
rzutowaniem jako a Foo
, nadal znajdzie bardziej pochodną metodę rzeczywistego typu obiektu Bar. bar2
jest a Baz
, ale w przeciwieństwie do with foo2
, ponieważ Baz nie zastępuje jawnie implementacji Bara (nie może; Bar sealed
it), nie jest widziany przez środowisko wykonawcze, gdy wygląda „z góry na dół”, więc zamiast tego wywoływana jest implementacja Bar. Zauważ, że Baz nie musi używać new
słowa kluczowego; otrzymasz ostrzeżenie kompilatora, jeśli pominiesz słowo kluczowe, ale niejawnym zachowaniem w C # jest ukrycie metody nadrzędnej. baz2
jest a Bai
, który zastępujeBaz
snew
implementacja, więc jej zachowanie jest podobne do foo2
's; wywoływana jest rzeczywista implementacja typu obiektu w Bai. bai2
jest a Bat
, który ponownie ukrywa Bai
implementację metody swojego rodzica i zachowuje się tak samo, bar2
jakby implementacja Baia nie została zapieczętowana, więc teoretycznie Bat mógł przesłonić metodę zamiast ją ukryć. Wreszcie bat2
jest to a Bak
, które nie ma nadrzędnej implementacji żadnego rodzaju i po prostu używa implementacji swojego rodzica.
Trzeci zestaw pięciu ilustruje zachowanie pełnej rozdzielczości z góry na dół. Wszystko faktycznie odwołuje się do wystąpienia najbardziej pochodnej klasy w łańcuchu, Bak
ale rozpoznawanie na każdym poziomie typu zmiennej jest wykonywane przez rozpoczęcie od tego poziomu łańcucha dziedziczenia i drążenie w dół do najbardziej pochodnego jawnego zastąpienia metody, które są tych w Bar
, Bai
i Bat
. W ten sposób metoda ukrywania „przerywa” nadrzędny łańcuch dziedziczenia; musisz pracować z obiektem na poziomie dziedziczenia lub poniżej tego poziomu, który ukrywa metodę, aby można było użyć metody ukrywania. W przeciwnym razie metoda ukryta jest „odkrywana” i używana zamiast niej.