7 Stimmen

Wie man den Aufruf einer abgeleiteten C#-Methode erzwingt

Ich habe eine Klasse, die von einem Tool generiert wurde und die ich daher nicht ändern kann. Die generierte Klasse ist sehr einfach (keine Schnittstelle, keine virtuellen Methoden):

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

Im C#-Projekt wollen wir eine Möglichkeit schaffen, um eine andere Implementierung von MyFoo einzubinden. Ich denke also, dass MyFoo von GeneratedFoo abgeleitet werden soll

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

Dann habe ich eine CreateFoo-Methode, die entweder eine Instanz der Klasse GeneratedFoo oder MyFoo zurückgeben wird. Sie ruft jedoch immer die Methode in GeneratedFoo auf.

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

Dies wird erläutert, da es sich nicht um eine virtuelle Methode handelt. Aber ich frage mich, ob es eine Möglichkeit (ein Hack vielleicht), um es die abgeleitete Methode aufrufen.

Danke,
Ian

0 Stimmen

"public new void" ? Das ist eine Verwendung von "new", die ich noch nie gesehen habe. Ist das überhaupt gültig?

0 Stimmen

New' bedeutet hier, dass Sie die Methode explizit von der Basisklasse 'abgedeckt' haben, ohne sie zu überschreiben (da sie nicht virtuell ist) - ohne dies erhalten Sie eine Warnung vom Compiler.

0 Stimmen

Um genauer zu sein - wenn Sie Ihrer Methode in der abgeleiteten Klasse den gleichen Namen wie in der Basisklasse geben, und es ist nicht überschreiben Methode, Compiler gibt Ihnen eine Warnung, dass Sie Ihre Methode versteckt geerbten Mitglied (weil Sie wahrscheinlich nicht wollte, dies zu tun), aber wenn Sie sicher sind, dass es, Sie sagen, dass der Compiler mit 'new' Schlüsselwort.

22voto

Marcin Deptuła Punkte 11521

Adam hat Ihnen eine (richtige) Antwort gegeben. Jetzt ist es Zeit für den Hack, nach dem du gefragt hast :)

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);
}

Druckt aus:

Hello simple world
Hello complicated world

0 Stimmen

Es gibt keinen Grund, unsicher zu sein.

0 Stimmen

Aye, diese unsafe ist hier, weil ich einige "Test"-Projekte, die ich in VS öffnen, wenn ich einige wirklich seltsamen Code kochen müssen, so Main ist unsafe standardmäßig, und ich habe auch viele seltsame Usings dort hehe.

0 Stimmen

Wie auch immer, unsicher entfernt für Klarheit (da es einfach veraltet ist), danke für den Kommentar.

6voto

Adam Robinson Punkte 176996

Ohne die Möglichkeit, die Methode virtuell zu machen, nein. Eine nicht-virtuelle Methode ist zur Kompilierzeit statisch gelinkt und kann nicht geändert werden.

6voto

Tullo_x86 Punkte 2408

Schreiben Sie eine Erweiterungsmethode, die auf Ihren abgeleiteten Typ safecastet und die Methode gegen que stattdessen eine Referenz.

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);
     }
  }
}

und rufen ihn dann mit

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

NB : Das ist ein schmutziger Hack. Eine klarere Lösung wäre, sich zu fragen warum müssen Sie die new Modifikator in erster Linie. Die Überladung der Bedeutung von Write(p) macht Ihren Code für die Betreuer verwirrend. Sie könnten es genauso gut deklarieren public void WriteToTarget() oder etwas viel Spezifischeres, um diese Verwirrung von vornherein zu vermeiden.

3voto

Chris Chilvers Punkte 6359

Die Verwendung von "new" für eine Methode ist kein Overriding, sondern ein Verstecken der ursprünglichen Methode. Dies ist etwas völlig anderes als Polymorphismus und sollte vermieden werden. Sein einziger Zweck ist die Kovarianz/Kontravarianz von Methoden, die eine identische Funktion ausführen (z. B. public Foo Clone(); und public new Bar Clone();). Der andere Zweck ist für Legacy-Code, wenn eine identisch benannte Methode zu einer Basisklasse hinzugefügt wird, über die Sie keine Kontrolle haben und den Namen Ihrer Methode zu diesem Zeitpunkt nicht ändern können.

Obwohl die beiden Methoden denselben Namen tragen, handelt es sich um völlig unterschiedliche Methoden, die einen anderen Platz in der Methodentabelle der Klasse einnehmen, während das Überschreiben die vorhandene Methode in der Methodentabelle der Klasse ersetzt.

Meine Lösung dafür wäre, eine Schnittstelle wie IFoo zu verwenden und entweder die generierte Klasse zu bearbeiten oder eine Proxyklasse zu verwenden, die alle ihre Methodenaufrufe an eine Instanz von GeneratedFoo delegiert, um IFoo zu implementieren.

public DelegatingFoo : IFoo
{
  private GeneratedFoo foo;

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

1voto

softveda Punkte 10552

Wenn die generierte Klasse keine virtuelle Methode hat, ist dies nicht möglich. Wenn Sie das Tool so ändern könnten, dass die Write-Methode als virtuell generiert wird, dann verwenden Sie in MyFoo das Schlüsselwort override anstelle von new, um die Write-Methode zu qualifizieren. Eine zweite Möglichkeit wäre, ein Skript/Makro zu schreiben, um den Quellcode von GeneratedFoo zu ändern, nachdem das Tool gelaufen ist, um die Zeichenfolge "virtual" vor den gewünschten Methoden als Teil Ihres Build-Prozesses einzufügen.

CodeJaeger.com

CodeJaeger ist eine Gemeinschaft für Programmierer, die täglich Hilfe erhalten..
Wir haben viele Inhalte, und Sie können auch Ihre eigenen Fragen stellen oder die Fragen anderer Leute lösen.

Powered by:

X