Ist es möglich, eine nicht virtuelle Methode zu überschreiben?

89

Gibt es eine Möglichkeit, eine nicht virtuelle Methode zu überschreiben? oder etwas, das ähnliche Ergebnisse liefert (außer eine neue Methode zum Aufrufen der gewünschten Methode zu erstellen)?

Ich möchte eine Methode Microsoft.Xna.Framework.Graphics.GraphicsDevicemit Blick auf Unit-Tests überschreiben .

zfedoran
quelle
8
Meinen Sie Überlastung oder Übersteuerung ? Überladung = Fügen Sie eine Methode mit demselben Namen, aber unterschiedlichen Parametern hinzu (z. B. unterschiedliche Überladungen von Console.WriteLine). Override = (grob) das Standardverhalten der Methode ändern (z. B. eine Shape.Draw-Methode, die sich für Circle, Rectangle usw. unterschiedlich verhält). Sie können eine Methode in einer abgeleiteten Klasse jederzeit überladen , das Überschreiben gilt jedoch nur für virtuelle Methoden.
Itowlson

Antworten:

110

Nein, Sie können eine nicht virtuelle Methode nicht überschreiben. Das Beste, was Sie tun können, ist, die Methode auszublenden, indem Sie eine newMethode mit demselben Namen erstellen. Dies ist jedoch nicht ratsam, da dies gegen gute Entwurfsprinzipien verstößt.

Aber selbst wenn Sie eine Methode ausblenden, erhalten Sie keine polymorphe Ausführung von Methodenaufrufen, wie dies bei einem echten virtuellen Methodenaufruf der Fall wäre. Betrachten Sie dieses Beispiel:

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

In diesem Beispiel rufen beide die MMethode print auf Foo.M. Wie Sie sehen können, können Sie mit diesem Ansatz eine neue Implementierung für eine Methode erstellen, solange der Verweis auf dieses Objekt vom richtigen abgeleiteten Typ ist. Das Ausblenden einer Basismethode unterbricht jedoch den Polymorphismus.

Ich würde empfehlen, dass Sie Basismethoden nicht auf diese Weise ausblenden.

Ich neige dazu, mich auf die Seite derjenigen zu stellen, die das Standardverhalten von C # bevorzugen, dass Methoden standardmäßig nicht virtuell sind (im Gegensatz zu Java). Ich würde noch weiter gehen und sagen, dass Klassen standardmäßig auch versiegelt werden sollten. Vererbung ist schwer richtig zu entwerfen und die Tatsache, dass es eine Methode gibt, die nicht als virtuell markiert ist, zeigt an, dass der Autor dieser Methode niemals beabsichtigt hat, die Methode zu überschreiben.

Edit: "Ausführungszeit polymorpher Versand" :

Damit meine ich das Standardverhalten, das zur Ausführungszeit auftritt, wenn Sie virtuelle Methoden aufrufen. Nehmen wir zum Beispiel an, dass ich in meinem vorherigen Codebeispiel anstelle einer nicht virtuellen Methode tatsächlich eine virtuelle Methode und eine echte überschriebene Methode definiert habe.

Wenn ich b.Fooin diesem Fall anrufen würde, würde die CLR den Objekttyp, auf den die bReferenz verweist, korrekt bestimmen Barund den Anruf Mentsprechend weiterleiten.

Andrew Hare
quelle
6
Obwohl "Ausführungszeit polymorpher Versand" technisch die richtige Art zu sagen ist, stelle ich mir vor, dass dies wahrscheinlich über die Köpfe fast aller geht!
Orion Edwards
3
Es ist zwar richtig, dass der Autor beabsichtigt hat, das Überschreiben von Methoden zu verbieten, aber es ist nicht wahr, dass dies notwendigerweise das Richtige war. Ich bin der Meinung, dass das XNA-Team eine IGraphicsDevice-Schnittstelle hätte implementieren sollen, um dem Benutzer mehr Flexibilität beim Debuggen und Testen von Einheiten zu ermöglichen. Ich bin gezwungen, einige sehr hässliche Dinge zu tun, und dies hätte vom Team vorausgesehen werden müssen. Weitere Diskussionen finden Sie hier: forums.xna.com/forums/p/30645/226222.aspx
zfedoran
2
@Orion Ich habe es auch nicht verstanden, aber nach einem kurzen Google habe ich es sehr geschätzt, die Verwendung der "richtigen" Begriffe zu sehen.
Zfedoran
9
Ich kaufe das Argument "Der Autor hat es nicht beabsichtigt" überhaupt nicht. Das ist so, als hätte der Erfinder des Rades nicht damit gerechnet, dass Räder auf Autos montiert werden, also können wir sie nicht auf Autos verwenden. Ich weiß, wie das Rad / die Methode funktioniert und ich möchte etwas anderes damit machen. Es ist mir egal, ob der Schöpfer es wollte oder nicht. Entschuldigen Sie, dass Sie versucht haben, einen toten Thread wiederzubeleben. Ich muss nur etwas Dampf ablassen, bevor ich einen wirklich unbequemen Weg finde, um dieses Problem zu umgehen, in dem ich mich befinde :)
Jeff
2
Was ist also, wenn Sie eine große Codebasis erben und einen Test für neue Funktionen hinzufügen müssen, aber um dies zu testen, müssen Sie eine ganze Reihe von Maschinen instanziieren. In Java würde ich nur diese Teile erweitern und die für Stubs erforderlichen Methoden überschreiben. Wenn die Methoden in C # nicht als virtuell markiert sind, muss ich einen anderen Mechanismus finden (Verspottungsbibliothek oder ähnliches und selbst dann).
Adam Parkin
22

Nein, kannst du nicht.

Sie können nur eine virtuelle Methode überschreiben - siehe MSDN hier :

In C # können abgeleitete Klassen Methoden mit demselben Namen wie Basisklassenmethoden enthalten.

  • Die Basisklassenmethode muss virtuell definiert sein.
ChrisF
quelle
6

Wenn die Basisklasse nicht versiegelt ist, können Sie davon erben und eine neue Methode schreiben, die die Basisklasse verbirgt (verwenden Sie das Schlüsselwort "new" in der Methodendeklaration). Andernfalls können Sie es nicht überschreiben, da es nie die Absicht des ursprünglichen Autors war, es zu überschreiben, weshalb es nicht virtuell ist.

Slugster
quelle
3

Ich denke, Sie werden verwirrt und überschrieben. Überladen bedeutet, dass Sie zwei oder mehr Methoden mit demselben Namen, aber unterschiedlichen Parametersätzen haben. Überschreiben bedeutet, dass Sie eine andere Implementierung für eine Methode in einer abgeleiteten Klasse haben (wodurch das Verhalten ersetzt oder geändert wird in seiner Basisklasse).

Wenn eine Methode virtuell ist, können Sie sie mit dem Schlüsselwort override in der abgeleiteten Klasse überschreiben. Nicht virtuelle Methoden können die Basisimplementierung jedoch nur ausblenden, indem das neue Schlüsselwort anstelle des Überschreibungsschlüsselworts verwendet wird. Die nicht virtuelle Route ist nutzlos, wenn der Aufrufer über eine als Basistyp eingegebene Variable auf die Methode zugreift, da der Compiler einen statischen Versand an die Basismethode verwenden würde (was bedeutet, dass der Code in Ihrer abgeleiteten Klasse niemals aufgerufen würde).

Nichts hindert Sie daran, einer vorhandenen Klasse eine Überladung hinzuzufügen, aber nur Code, der über Ihre Klasse Bescheid weiß, kann darauf zugreifen.

Rory
quelle
2

Sie können nicht-virtuelle Methoden einer Klasse in C # nicht überschreiben (ohne CLR zu hacken), aber Sie können jede Methode der Schnittstelle überschreiben, die die Klasse implementiert. Bedenken Sie, wir haben nicht versiegelt

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

Und hier ist eine 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();

    }
}
Wjatscheslaw Napadowski
quelle
@ Napadovsky Funktioniert das wirklich?
Dan
@Dan hier ist die Live-Demo: dotnetfiddle.net/VgRwKK
Vyacheslav Napadovsky
0

Wenn Sie von einer nicht abgeleiteten Klasse erben, können Sie einfach eine abstrakte Superklasse erstellen und stattdessen stromabwärts davon erben.

user3853059
quelle
0

Gibt es eine Möglichkeit, eine nicht virtuelle Methode zu überschreiben? oder etwas, das ähnliche Ergebnisse liefert (außer eine neue Methode zum Aufrufen der gewünschten Methode zu erstellen)?

Sie können eine nicht virtuelle Methode nicht überschreiben. Sie können jedoch das newSchlüsselwort modifier verwenden, um ähnliche Ergebnisse zu erzielen:

class Class0
{
    public int Test()
    {
        return 0;
    }
}

class Class1 : Class0
{
    public new int Test()
    {
        return 1;
    }
}
. . .
// result of 1
Console.WriteLine(new Class1().Test());

Sie sollten auch sicherstellen, dass der Zugriffsmodifikator auch derselbe ist, da Sie sonst später keine Vererbung erhalten. Wenn eine andere Klasse vom Schlüsselwort in erbt, wirkt sich dies nicht auf Objekte aus, die von Class1diesem newSchlüsselwort Class1erben, es sei denn, der Zugriffsmodifikator ist derselbe.

Wenn der Zugriffsmodifikator nicht identisch ist:

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

... im Vergleich zu, wenn der Zugriffsmodifikator ist das gleiche:

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

Wie bereits in einer früheren Antwort erwähnt, ist dies kein gutes Konstruktionsprinzip.

Aaron Thomas
quelle
-5

Es gibt eine Möglichkeit, dies mit einer abstrakten Klasse und einer abstrakten Methode zu erreichen.

Erwägen

Class Base
{
     void MethodToBeTested()
     {
        ...
     }

     void Method1()
     {
     }

     void Method2()
     {
     }

     ...
}

Wenn Sie nun verschiedene Versionen der Methode MethodToBeTested () haben möchten, ändern Sie die Klassenbasis in eine abstrakte Klasse und die Methode MethodToBeTested () als abstrakte Methode

abstract Class Base
{

     abstract void MethodToBeTested();

     void Method1()
     {
     }

     void Method2()
     {
     }

     ...
}

Mit der abstrakten Leere MethodToBeTested () kommt ein Problem; Die Implementierung ist weg.

Erstellen Sie daher eine class DefaultBaseImplementation : Base, um die Standardimplementierung zu erhalten.

Erstellen Sie eine weitere, class UnitTestImplementation : Baseum eine Unit-Test-Implementierung durchzuführen.

Mit diesen 2 neuen Klassen kann die Basisklassenfunktionalität überschrieben werden.

Class DefaultBaseImplementation : Base    
{
    override void MethodToBeTested()    
    {    
        //Base (default) implementation goes here    
    }

}

Class UnitTestImplementation : Base
{

    override void MethodToBeTested()    
    {    
        //Unit test implementation goes here    
    }

}

Jetzt haben Sie 2 Klassen implementiert (überschreiben) MethodToBeTested().

Sie können die (abgeleitete) Klasse nach Bedarf instanziieren (dh entweder mit Basisimplementierung oder mit Unit-Test-Implementierung).

ShivanandSK
quelle
@ Slaoo: Hallo Slavoo. Danke für das Code-Update. Aber können Sie mir bitte den Grund für die Ablehnung mitteilen?
ShivanandSK
6
Weil es die Frage nicht beantwortet. Er fragt, ob Sie Mitglieder überschreiben können, die nicht als virtuell markiert sind. Sie haben gezeigt, dass Sie als abstrakt gekennzeichnete Mitglieder implementieren müssen.
Lee Louviere