2009-08-13 9 views
7

Mam klasę, która jest generowana przez niektóre narzędzia, dlatego nie mogę go zmienić. Wygenerowany klasa jest bardzo proste (bez interfejsu, nie ma metody wirtualne):Jak wymusić wywołanie metody pochodnej C#

class GeneratedFoo 
{ 
    public void Write(string p) { /* do something */ } 
} 

W projekcie C#, chcemy zapewnić sposób, tak, że możemy podłączyć inną realizację MyFoo. Więc myślę, aby MyFoo pochodzący z GeneratedFoo

class MyFoo : GeneratedFoo 
{ 
    public new void Write(string p) { /* do different things */ } 
} 

Następnie Mam metodę CreateFoo że albo będzie zwracać instancję GeneratedFoo lub klasy MyFoo. Jednak zawsze wywołuje metodę w GeneratedFoo.

GeneratedFoo foo = CreateFoo(); // if this returns MyFoo, 
foo.Write("1"); // it stills calls GeneratedFoo.Write 

To jest rozszerzone, ponieważ nie jest to metoda wirtualna. Ale zastanawiam się, czy istnieje sposób (może to hack), aby wywołać metodę pochodną.

Dzięki,
Ian

+0

"publiczna nowa pustka"? Jest to użycie "nowego", którego nigdy wcześniej nie widziałem. Czy to jest nawet ważne? – abelenky

+0

"Nowy" tutaj oznacza, że ​​masz jawnie "zakryty" metodę z klasy bazowej, bez jej przesłonięcia (ponieważ nie jest to wirtualne) - bez niej faktycznie otrzymasz ostrzeżenie od kompilatora. –

+0

Aby być bardziej szczegółowym - gdy podasz metodę w klasie pochodnej o takiej samej nazwie jak w bazie, a nie jest to metoda nadpisywania, kompilatory dają ostrzeżenie, że twoja metoda ukrywa dziedziczonego członka (ponieważ prawdopodobnie nie chciałaś tego zrobić), ale jeśli jesteś tego pewien, mówisz to kompilatorowi, używając słowa kluczowego "new". –

Odpowiedz

6

nie będąc w stanie dokonać metody wirtualne, no. Metoda nie-wirtualna jest statycznie połączona w czasie kompilacji i nie można jej zmienić.

1

Jeśli wygenerowana klasa nie ma metody wirtualnej, wówczas nie jest to możliwe. Jeśli możesz zmodyfikować narzędzie, aby wygenerować metodę Write jako wirtualną, wówczas w MyFoo użyj zastąpienia słowa kluczowego zamiast nowego, aby zakwalifikować metodę Write. Druga opcja to napisanie skryptu/makra w celu zmiany kodu źródłowego GeneratedFoo po uruchomieniu narzędzia w celu wstrzyknięcia ciągu "wirtualnego" przed wybranymi metodami jako częścią procesu kompilacji.

22

Adam dał ci odpowiedź (poprawną). Teraz nadszedł czas na włamania pytałeś dla :)


class BaseFoo 
{ 
    public void Write() { Console.WriteLine("Hello simple world"); } 
} 

class DerFoo : BaseFoo 
{ 
    public void Write() { Console.WriteLine("Hello complicated world"); } 
} 

public static void Main() 
{ 
    BaseFoo bs = new DerFoo(); 
    bs.Write(); 

    bs.GetType().GetMethod("Write").Invoke(bs, null); 
} 

wypisuje:

 
Hello simple world 
Hello complicated world 
+0

Nie ma potrzeby, aby tam było niebezpieczne –

+0

Tak, ta niebezpieczna jest tutaj, ponieważ mam kilka "testowych" projektów, które otwieram w VS, kiedy muszę ugotować naprawdę dziwny kod, więc Main jest domyślnie niebezpieczny, a ja też mam dużo dziwne rzeczy tam hehe. –

+0

W każdym razie, niebezpieczne dla jasności usunięte (ponieważ jest to po prostu przestarzałe), dzięki za skomentowanie go. –

-1

Podejście, które można użyć byłoby właściwie zdefiniować kilka nowych klas wszystkim pochodzące z GeneratedFoo; Np .:

public class MyFooBase : GeneratedFoo 
{ 
    public virtual void MyMethod() { ... } 
} 

public class MyFoo1 : MyFooBase { ... } 

public class MyFoo2 : MyFooBase { ... } 

Pozwoliłoby to zasadniczo dodać wirtualne metody do wygenerowanej klasy.

+0

Ale nie wywołaj metody innej niż wirtualna z wygenerowanego foo, ponieważ byłaby wirtualna. –

+0

Ale można napisać nową metodę, która BYŁA wirtualna i zwana nie-wirtualną w klasie pochodnej, która chciała mieć to zachowanie i nie wywoływała funkcji nonvirtual w klasie pochodnej, która nie powinna mieć takiego zachowania. –

3

Użycie "nowego" metody nie przesłoni, ponieważ ukrywa oryginalną metodę. Jest to całkowicie odmienne od polimorfizmu i powinno się go unikać, jego jedynym celem jest kowariancja/przeciwstawność metod, które pełnią identyczną funkcję (np. Publiczny Foo Clone() i publiczny nowy Bar Clone();). Drugi cel dotyczy kodu starszego, gdy do klasy bazowej, która nie ma nad nim kontroli, jest dodawana metoda o identycznej nazwie i nie może w tym momencie zmienić nazwy tej metody.

Chociaż te dwie metody mają tę samą nazwę, są całkowicie odrębnymi metodami, to zajmują one inne miejsce w tabeli metod klasy, gdzie zastąpienie zastępuje istniejącą metodę w tabeli metod klasy.

Moim rozwiązaniem byłoby użycie interfejsu takiego jak IFoo i edycja wygenerowanej klasy lub użycie klasy proxy, która deleguje wszystkie wywołania metod do instancji GeneratedFoo w celu implementacji IFoo.


public DelegatingFoo : IFoo 
{ 
    private GeneratedFoo foo; 

    public DelegatingFoo(GeneratedFoo foo) { this.foo = foo; } 
    public void Write(string p) { foo.Write(p); } 
} 
6

Napisz metodę rozszerzenia że safecasts do rodzaju pochodnej, i wywołuje metodę przeciwko tym zamiast odniesienia.

public static class Extensions 
{ 
    public static void WriteFoo(this GeneratedFoo foo, string p) 
    { 
    MyFoo derived = foo as MyFoo; 
    if (derived != null) 
    { 
     derived.Write(p); 
    } 
    else 
    { 
     foo.Write(p); 
    } 
    } 
} 

następnie wywołać ją z

GeneratedFoo unknownFoo; 
unknownFoo.WriteFoo("win"); 

NB: to jest brudny Hack. Bardziej przejrzystym rozwiązaniem byłoby zadać sobie pytanie: dlaczego w pierwszej kolejności trzeba użyć modyfikatora. Przeciążenie znaczenia Write(p) spowoduje, że twój kod będzie mylący dla opiekunów. Równie łatwo można go ogłosić public void WriteToTarget() lub coś znacznie bardziej konkretnego, aby uniknąć tego zamieszania.

0

Co powiesz na niestandardową regułę policjanta FX dodaną do twojego systemu kompilacji?