C # - użycie słowa kluczowego wirtualny + override vs. new

204

Jakie są różnice między zadeklarowaniem metody w typie podstawowym „ virtual”, a następnie nadpisaniem jej w typie podrzędnym za pomocą overridesłowa kluczowego „ ”, a nie po prostu za pomocą newsłowa kluczowego „ ” w przypadku zadeklarowania metody dopasowania w typie podrzędnym?

i3ensays
źródło
3
MSDN mówi: „Używanie newtworzy nowego członka o tej samej nazwie i powoduje, że oryginalny członek zostaje ukryty, a jednocześnie overriderozszerza implementację dla odziedziczonego członka”
AminM

Odpowiedzi:

181

Słowo kluczowe „new” nie zastępuje, oznacza nową metodę, która nie ma nic wspólnego z metodą klasy podstawowej.

public class Foo
{
     public bool DoSomething() { return false; }
}

public class Bar : Foo
{
     public new bool DoSomething() { return true; }
}

public class Test
{
    public static void Main ()
    {
        Foo test = new Bar ();
        Console.WriteLine (test.DoSomething ());
    }
}

Wypisuje to fałsz, jeśli użyjesz przesłonięcia, wydrukowałoby to prawda.

(Kod podstawowy pochodzi od Josepha Daigle'a)

Tak więc, jeśli robisz prawdziwy polimorfizm, POWINNO ZAWSZE BYĆ NADMIERNE . Jedynym miejscem, w którym należy użyć „nowego”, jest to, że metoda nie jest w żaden sposób powiązana z wersją klasy podstawowej.

albertein
źródło
Powinieneś poprawić swój kod, ustawiłem metody na statyczne (co jest ważne dla używania słowa kluczowego „new”). Uznałem jednak, że łatwiej jest stosować metody instancji.
Joseph Daigle
1
Dziękuję, brakowało mi części „statycznej”. Należy zwrócić większą uwagę na przyszłość
albertein
1
Zauważ, że linia Foo test = new Bar () jest tutaj kluczowa, słowo kluczowe new / override dla metod określa, która metoda jest wywoływana, gdy wstawiasz Bar do zmiennej Foo.
Thomas N
... tak to jest dokładnie taki sam jak po prostu nie mający virtualw podstawie i overridew pochodzić? Dlaczego to istnieje? Kod nadal będzie działał nawet bez new- więc czy jest to czysta czytelność?
Don Cheadle
Pańska odpowiedź, drogi panie, jest dość niejasna. new i virtual-override robią dokładnie to samo, jedyną różnicą jest to, że new HIDES metodę w klasie nadrzędnej i override… cóż, przesłania ją. Oczywiście wypisuje fałsz, ponieważ obiekt testowy jest typu Foo, jeśli byłby to typ Bar, wydrukowałby true. Jednak dość trudny przykład.
MrSilent,
228

Zawsze znajduję takie rzeczy łatwiejsze do zrozumienia na zdjęciach:

Ponownie, biorąc kod Josepha Daigle'a,

public class Foo
{
     public /*virtual*/ bool DoSomething() { return false; }
}

public class Bar : Foo
{
     public /*override or new*/ bool DoSomething() { return true; }
}

Jeśli następnie wywołasz kod w ten sposób:

Foo a = new Bar();
a.DoSomething();

UWAGA: Ważną rzeczą jest to, że nasz obiekt jest tak naprawdę Bar, ale przechowujemy go w zmiennej typuFoo (jest to podobne do rzutowania)

Wynik będzie następujący, w zależności od tego, czy używałeś virtual/ overridelub newdeklarując swoje zajęcia.

Obraz wyjaśnienia wirtualnego / zastąpienia

Orion Edwards
źródło
3
Dzięki ... ale czy mógłbyś wyjaśnić trochę powyższy obrazek w odniesieniu do obsady, o której mówiłeś?
odiseh
Ups, jeśli zapomniałem dodać te kilka wierszy do mojego poprzedniego. komentarz: czy masz na myśli wirtualny / przesłonięcie i nie-żywy / nowy są używane tylko do koncepcji polimorfizmu, a kiedy po prostu deklarujesz zmienną (nie używając rzutowania), nie ma to na myśli? Dzięki jeszcze raz.
odiseh
ta odpowiedź jest dokładnie tym, czego szukałem. Jedyny problem, jaki miałem, to to, że obrazy Flickr są zablokowane w moim miejscu pracy, więc musiałem uzyskać do nich dostęp przez telefon ...
mezoid
7
Prawdziwy wysiłek, aby odpowiedzieć na pytanie.
iMatoria
Głosowałbym za poprawą obrazu? Jest zablokowany za zaporą ogniową ...
Jeremy Thompson
43

Oto kod, aby zrozumieć różnicę w zachowaniu metod wirtualnych i nie-wirtualnych:

class A
{
    public void foo()
    {
        Console.WriteLine("A::foo()");
    }
    public virtual void bar()
    {
        Console.WriteLine("A::bar()");
    }
}

class B : A
{
    public new void foo()
    {
        Console.WriteLine("B::foo()");
    }
    public override void bar()
    {
        Console.WriteLine("B::bar()");
    }
}

class Program
{
    static int Main(string[] args)
    {
        B b = new B();
        A a = b;
        a.foo(); // Prints A::foo
        b.foo(); // Prints B::foo
        a.bar(); // Prints B::bar
        b.bar(); // Prints B::bar
        return 0;
    }
}
Franci Penov
źródło
dziękuję za to - ale newpo co „ukrywać” metodę podstawową, skoro po prostu nie zastępowanie wydaje się robić to samo?
Don Cheadle
1
Nie sądzę, że został stworzony, aby zapobiec zastąpieniu klasy podstawowej. Myślę, że został stworzony, aby uniknąć kolizji nazw, ponieważ nie można przesłonić metody, która nie jest, virtuala kompilator będzie narzekał, jeśli zobaczy tę samą nazwę funkcji w klasie „linia krwi” bez virtualpodpisu
mr5
19

Słowo newkluczowe faktycznie tworzy całkowicie nowego członka, który istnieje tylko dla tego określonego typu.

Na przykład

public class Foo
{
     public bool DoSomething() { return false; }
}

public class Bar : Foo
{
     public new bool DoSomething() { return true; }
}

Metoda istnieje na obu typach. Kiedy użyjesz refleksji i uzyskasz elementy typu Bar, w rzeczywistości znajdziesz 2 nazywane metody, DoSomething()które wyglądają dokładnie tak samo. Korzystając z niego new, skutecznie ukrywasz implementację w klasie bazowej, aby gdy klasy wywodzą się Bar(w moim przykładzie), wywołanie metody do base.DoSomething()idzie, Bara nie Foo.

Joseph Daigle
źródło
9

virtual / override informuje kompilator, że obie metody są ze sobą powiązane, i że w niektórych okolicznościach, gdy wydaje ci się, że wywołujesz pierwszą (wirtualną) metodę, to w rzeczywistości poprawne jest wywołanie drugiej (zastąpionej) metody. To podstawa polimorfizmu.

(new SubClass() as BaseClass).VirtualFoo()

Wywoła nadpisaną metodę VirtualFoo () podklasy.

new informuje kompilator, że dodajesz metodę do klasy pochodnej o takiej samej nazwie jak metoda w klasie bazowej, ale nie mają one ze sobą żadnego związku.

(new SubClass() as BaseClass).NewBar()

Wywoła metodę NewBar () klasy BaseClass, podczas gdy:

(new SubClass()).NewBar()

Wywoła metodę NewBar () SubClass.

Klin
źródło
naprawdę podoba mi się to zdanie „mówi kompilatorowi”
Mina Gabriel
„podstawa polimorfizmu” to kolejne klasyczne powiedzenie :)
Jeremy Thompson
8

Poza szczegółami technicznymi myślę, że użycie wirtualnego / zastępowania przekazuje wiele semantycznych informacji o projekcie. Deklarując metodę wirtualną, wskazujesz, że oczekujesz, że klasy implementujące mogą chcieć udostępnić własne, niestandardowe implementacje. Pominięcie tego w klasie podstawowej również deklaruje oczekiwanie, że domyślna metoda powinna wystarczyć dla wszystkich klas implementujących. Podobnie, można użyć deklaracji abstrakcyjnych, aby zmusić klasy implementujące do zapewnienia własnej implementacji. Ponownie myślę, że to dużo mówi o tym, jak programista oczekuje użycia kodu. Gdybym pisał zarówno klasę podstawową, jak i implementacyjną i znalazłem się przy użyciu nowych, poważnie przemyślałem decyzję, aby nie uczynić tej metody wirtualną w obiekcie nadrzędnym i konkretnie zadeklarować swój zamiar.

tvanfosson
źródło
Techniczne wyjaśnienia dotyczące zastąpienia nowego vs są trafne, ale wydaje się, że ta odpowiedź najbardziej pomaga w kierowaniu programistami, z których korzystać.
Sully,
4

Różnica między zastąpionym słowem kluczowym a nowym słowem kluczowym polega na tym, że pierwsze z nich zastępuje metodę, a później ukrywa metodę.

Więcej informacji znajdziesz w poniższych linkach ...

MSDN i inne

Nescio
źródło
3
  • newsłowo kluczowe służy do ukrywania. - oznacza, że ​​ukrywasz swoją metodę w czasie wykonywania. Dane wyjściowe będą oparte na metodzie klasy bazowej.
  • overridedo zastąpienia. - oznacza, że ​​wywołujesz metodę klasy pochodnej z referencją do klasy bazowej. Dane wyjściowe będą oparte na metodzie klasy pochodnej.
Chetan
źródło
1

Moja wersja wyjaśnień pochodzi z używania właściwości, które pomagają zrozumieć różnice.

overridejest dość proste, prawda? Typ podstawowy zastępuje typ rodzica.

newjest może mylące (dla mnie tak było). Dzięki właściwościom łatwiej jest zrozumieć:

public class Foo
{
    public bool GetSomething => false;
}

public class Bar : Foo
{
    public new bool GetSomething => true;
}

public static void Main(string[] args)
{
    Foo foo = new Bar();
    Console.WriteLine(foo.GetSomething);

    Bar bar = new Bar();
    Console.WriteLine(bar.GetSomething);
}

Za pomocą debugera można zauważyć, że Foo fooma 2 GetSomething właściwości, ponieważ tak naprawdę ma 2 wersje właściwości, FooiBar , i aby wiedzieć, który z nich użyć, c # „wybiera” właściwość dla bieżącego typu.

Jeśli chcesz użyć wersji paska, użyłbyś zastąpienia lub użycia Foo foo zamiast tego.

Bar barma tylko 1 , ponieważ chce zupełnie nowego zachowania GetSomething.

Dror Weiss
źródło
0

Brak oznaczenia metody w jakikolwiek sposób oznacza: Powiąż tę metodę, używając typu kompilacji obiektu, a nie typu wykonawczego (wiązanie statyczne).

Oznaczanie metody za pomocą virtual pomocą środków: Powiąż tę metodę, używając typu środowiska wykonawczego obiektu, a nie typu czasu kompilacji (dynamiczne wiązanie).

Oznaczenie virtualmetody klasy bazowej za pomocą overridew klasie pochodnej oznacza: Jest to metoda, którą należy powiązać przy użyciu typu środowiska wykonawczego obiektu (powiązanie dynamiczne).

Oznaczenie virtualmetody klasy bazowej w klasie newpochodnej oznacza: Jest to nowa metoda, która nie ma związku z tą o tej samej nazwie w klasie bazowej i powinna być powiązana przy użyciu typu czasu kompilacji obiektu (wiązanie statyczne).

Brak oznaczenia virtualmetody klasy bazowej w klasie pochodnej oznacza: Ta metoda jest oznaczona jako new(wiązanie statyczne).

Oznaczenie metody abstractoznacza: Ta metoda jest wirtualna, ale nie zadeklaruję dla niej treści, a jej klasa jest również abstrakcyjna (wiązanie dynamiczne).

Emre Tapcı
źródło