Czy istnieje sposób na zastąpienie metody niewirtualnej? lub coś, co daje podobne wyniki (inne niż tworzenie nowej metody wywoływania żądanej metody)?
Chciałbym zastąpić metodę z Microsoft.Xna.Framework.Graphics.GraphicsDevice
mając na uwadze testy jednostkowe.
c#
overriding
overloading
zfedoran
źródło
źródło
Odpowiedzi:
Nie, nie możesz zastąpić metody niewirtualnej. Najbliższą rzeczą, jaką możesz zrobić, jest ukrycie metody, tworząc
new
metodę o tej samej nazwie, ale nie jest to zalecane, ponieważ łamie dobre zasady projektowania.Ale nawet ukrycie metody nie zapewni polimorficznego wysyłania wywołań metod w czasie wykonywania, jak w przypadku prawdziwego wywołania metody wirtualnej. Rozważmy ten przykład:
using System; class Example { static void Main() { Foo f = new Foo(); f.M(); Foo b = new Bar(); b.M(); } } class Foo { public void M() { Console.WriteLine("Foo.M"); } } class Bar : Foo { public new void M() { Console.WriteLine("Bar.M"); } }
W tym przykładzie oba wywołania
M
metody printFoo.M
. Jak widać to podejście nie pozwala mieć nową implementację metody tak długo jak odniesienie do tego obiektu jest z odpowiedniego typu pochodzącego ale ukrywa metodę bazowy ma polimorfizm przerwy.Zalecałbym, abyś nie ukrywał w ten sposób podstawowych metod.
Zwykle jestem po stronie tych, którzy preferują domyślne zachowanie C #, polegające na tym, że metody są domyślnie niewirtualne (w przeciwieństwie do Javy). Poszedłbym jeszcze dalej i powiedziałbym, że klasy powinny być również domyślnie zapieczętowane. Dziedziczenie jest trudne do prawidłowego zaprojektowania, a fakt, że istnieje metoda, która nie jest oznaczona jako wirtualna, wskazuje, że autor tej metody nigdy nie zamierzał nadpisywać metody.
Edycja: "wysyłka polimorficzna czasu wykonania" :
Rozumiem przez to domyślne zachowanie, które ma miejsce w czasie wykonywania, gdy wywołujesz metody wirtualne. Powiedzmy na przykład, że w moim poprzednim przykładzie kodu, zamiast definiować metodę niewirtualną, w rzeczywistości zdefiniowałem metodę wirtualną, a także metodę true overridden.
Gdybym miał wywołać
b.Foo
w takim przypadku, CLR poprawnie określiłby typ obiektu, na któryb
wskazuje odwołanie,Bar
i odpowiednio wysłałby wywołanieM
.źródło
Nie, nie możesz.
Możesz zastąpić tylko metodę wirtualną - zobacz MSDN tutaj :
źródło
Jeśli klasa bazowa nie jest zapieczętowana, możesz dziedziczyć po niej i napisać nową metodę, która ukrywa podstawową (użyj słowa kluczowego „new” w deklaracji metody). W przeciwnym razie nie, nie można go zastąpić, ponieważ pierwotny autor nigdy nie zamierzał go zastąpić, dlatego nie jest wirtualny.
źródło
Myślę, że przeciążasz i zastępujesz zdezorientowany, przeciążenie oznacza, że masz dwie lub więcej metod o tej samej nazwie, ale różne zestawy parametrów, podczas gdy zastępowanie oznacza, że masz inną implementację metody w klasie pochodnej (tym samym zastępując lub modyfikując zachowanie w swojej klasie bazowej).
Jeśli metoda jest wirtualna, można ją przesłonić za pomocą słowa kluczowego override w klasie derrived. Jednak metody niewirtualne mogą ukryć podstawową implementację tylko za pomocą słowa kluczowego new zamiast słowa kluczowego override. Trasa niewirtualna jest bezużyteczna, jeśli obiekt wywołujący uzyskuje dostęp do metody za pośrednictwem zmiennej wpisanej jako typ podstawowy, ponieważ kompilator użyłby statycznej wysyłki do metody bazowej (co oznacza, że kod w Twojej klasie derrived nigdy nie zostałby wywołany).
Nigdy nic nie stoi na przeszkodzie, aby dodać przeciążenie do istniejącej klasy, ale tylko kod, który wie o Twojej klasie, będzie mógł uzyskać do niej dostęp.
źródło
Nie można zastąpić niewirtualnej metody dowolnej klasy w C # (bez hakowania CLR), ale można zastąpić dowolną metodę interfejsu implementowaną przez klasę. Rozważ, że mamy niezamknięte
class GraphicsDevice: IGraphicsDevice { public void DoWork() { Console.WriteLine("GraphicsDevice.DoWork()"); } } // with its interface interface IGraphicsDevice { void DoWork(); } // You can't just override DoWork in a child class, // but if you replace usage of GraphicsDevice to IGraphicsDevice, // then you can override this method (and, actually, the whole interface). class MyDevice: GraphicsDevice, IGraphicsDevice { public new void DoWork() { Console.WriteLine("MyDevice.DoWork()"); base.DoWork(); } }
A oto demo
class Program { static void Main(string[] args) { IGraphicsDevice real = new GraphicsDevice(); var myObj = new MyDevice(); // demo that interface override works GraphicsDevice myCastedToBase = myObj; IGraphicsDevice my = myCastedToBase; // obvious Console.WriteLine("Using real GraphicsDevice:"); real.DoWork(); // override Console.WriteLine("Using overriden GraphicsDevice:"); my.DoWork(); } }
źródło
W przypadku dziedziczenia z klasy niepochodzącej, możesz po prostu utworzyć abstrakcyjną superklasę i dziedziczyć po niej niżej.
źródło
Nie można zastąpić metody niewirtualnej. Jakkolwiek można użyć
new
słowa kluczowego modyfikujący aby uzyskać podobne wyniki:class Class0 { public int Test() { return 0; } } class Class1 : Class0 { public new int Test() { return 1; } } . . . // result of 1 Console.WriteLine(new Class1().Test());
Będziesz także chciał się upewnić, że modyfikator dostępu jest również taki sam, w przeciwnym razie nie uzyskasz dziedziczenia w dół. Jeśli inny klasa dziedziczy z
Class1
tymnew
słów kluczowych wClass1
nie wpłynie obiekty dziedziczenie z nim, chyba że modyfikator dostępu jest taka sama.Jeśli modyfikator dostępu nie jest taki sam:
class Class0 { protected int Test() { return 0; } } class Class1 : Class0 { // different access modifier new int Test() { return 1; } } class Class2 : Class1 { public int Result() { return Test(); } } . . . // result of 0 Console.WriteLine(new Class2().Result());
... a jeśli modyfikator dostępu jest taki sam:
class Class0 { protected int Test() { return 0; } } class Class1 : Class0 { // same access modifier protected new int Test() { return 1; } } class Class2 : Class1 { public int Result() { return Test(); } } . . . // result of 1 Console.WriteLine(new Class2().Result());
Jak wskazano w poprzedniej odpowiedzi, nie jest to dobra zasada projektowa.
źródło
Można to osiągnąć za pomocą klasy abstrakcyjnej i metody abstrakcyjnej.
Rozważać
Class Base { void MethodToBeTested() { ... } void Method1() { } void Method2() { } ... }
Teraz, jeśli chcesz mieć różne wersje metody MethodToBeTested (), zmień Class Base na klasę abstrakcyjną i metodę MethodToBeTested () jako metodę abstrakcyjną
abstract Class Base { abstract void MethodToBeTested(); void Method1() { } void Method2() { } ... }
Z abstrakcyjnym void MethodToBeTested () pojawia się problem; wdrożenie zostało zakończone.
Stąd utwórz,
class DefaultBaseImplementation : Base
aby mieć domyślną implementację.I stwórz kolejną
class UnitTestImplementation : Base
implementację testów jednostkowych.Te dwie nowe klasy umożliwiają przesłonięcie funkcji klas bazowych.
Class DefaultBaseImplementation : Base { override void MethodToBeTested() { //Base (default) implementation goes here } } Class UnitTestImplementation : Base { override void MethodToBeTested() { //Unit test implementation goes here } }
Teraz masz implementację 2 klas (nadpisywanie)
MethodToBeTested()
.Możesz utworzyć wystąpienie (pochodnej) klasy zgodnie z wymaganiami (tj. Za pomocą implementacji podstawowej lub implementacji testów jednostkowych).
źródło