Est-il possible de remplacer une méthode non virtuelle?

90

Existe-t-il un moyen de remplacer une méthode non virtuelle? ou quelque chose qui donne des résultats similaires (autre que la création d'une nouvelle méthode pour appeler la méthode souhaitée)?

Je voudrais remplacer une méthode de Microsoft.Xna.Framework.Graphics.GraphicsDeviceavec les tests unitaires à l'esprit.

zfedoran
la source
8
Voulez-vous dire surcharge ou dépassement ? Overload = ajouter une méthode avec le même nom mais des paramètres différents (par exemple différentes surcharges de Console.WriteLine). Override = modifie (grossièrement) le comportement par défaut de la méthode (par exemple une méthode Shape.Draw qui a un comportement différent pour Circle, Rectangle, etc.). Vous pouvez toujours surcharger une méthode dans une classe dérivée, mais la substitution ne s'applique qu'aux méthodes virtuelles.
itowlson

Réponses:

111

Non, vous ne pouvez pas remplacer une méthode non virtuelle. La chose la plus proche que vous pouvez faire est de cacher la méthode en créant une newméthode avec le même nom, mais ce n'est pas conseillé car cela enfreint les bons principes de conception.

Mais même le fait de cacher une méthode ne vous donnera pas le temps d'exécution d'une distribution polymorphe des appels de méthode comme le ferait un véritable appel de méthode virtuelle. Prenons cet exemple:

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

Dans cet exemple, les deux appels à la Mméthode print Foo.M. Comme vous pouvez le voir cette approche ne vous permet d'avoir une nouvelle mise en œuvre d'une méthode aussi longtemps que la référence à cet objet est du type dérivé correct , mais cachant une méthode de base ne polymorphisme de rupture.

Je vous recommande de ne pas masquer les méthodes de base de cette manière.

J'ai tendance à me rallier à ceux qui favorisent le comportement par défaut de C # selon lequel les méthodes ne sont pas virtuelles par défaut (par opposition à Java). J'irais même plus loin et dire que les classes devraient également être scellées par défaut. L'héritage est difficile à concevoir correctement et le fait qu'il existe une méthode qui n'est pas marquée comme virtuelle indique que l'auteur de cette méthode n'a jamais eu l'intention de remplacer la méthode.

Edit: "expédition polymorphe du temps d'exécution" :

Ce que je veux dire par là, c'est le comportement par défaut qui se produit au moment de l'exécution lorsque vous appelez des méthodes virtuelles. Disons par exemple que dans mon exemple de code précédent, plutôt que de définir une méthode non virtuelle, j'ai en fait défini une méthode virtuelle et une véritable méthode surchargée.

Si je devais appeler b.Foodans ce cas, le CLR déterminerait correctement le type d'objet vers lequel la bréférence pointe Baret enverrait l'appel de Mmanière appropriée.

Andrew Hare
la source
6
Bien que "expédition polymorphe du temps d'exécution" soit techniquement la bonne façon de le dire, j'imagine que cela dépasse probablement la tête de presque tout le monde!
Orion Edwards
3
S'il est vrai que l'auteur a pu avoir l'intention d'interdire le remplacement de méthode, il n'est pas vrai que c'était nécessairement la bonne chose à faire. Je pense que l'équipe XNA aurait dû implémenter une interface IGraphicsDevice pour permettre à l'utilisateur plus de flexibilité dans le débogage et les tests unitaires. Je suis obligé de faire des choses très laides et cela aurait dû être prévu par l'équipe. D'autres discussions peuvent être trouvées ici: forums.xna.com/forums/p/30645/226222.aspx
zfedoran
2
@Orion Je ne l'ai pas compris non plus mais après un rapide google j'ai apprécié de voir l'utilisation des termes "corrects".
zfedoran
9
Je n'achète pas du tout l'argument "l'auteur n'en avait pas l'intention". C'est comme dire que l'inventeur de la roue ne s'attendait pas à ce que les roues soient montées sur les voitures, donc nous ne pouvons pas les utiliser sur les voitures. Je sais comment fonctionne la roue / méthode et je veux faire quelque chose de légèrement différent avec. Je me fiche de savoir si le créateur le voulait ou non. Désolé d'essayer de raviver un fil mort. J'ai juste besoin de souffler un peu avant de trouver un moyen vraiment gênant de contourner ce problème dans
Jeff
2
Alors qu'en est-il lorsque vous héritez d'une base de code volumineuse et que vous devez ajouter un test pour de nouvelles fonctionnalités, mais pour tester cela, vous devez instancier un grand nombre de machines. En Java, je voudrais simplement étendre ces parties et remplacer les méthodes requises par les stubs. En C #, si les méthodes ne sont pas marquées comme virtuelles, je dois trouver un autre mécanisme (bibliothèque moqueuse ou autre, et même alors).
Adam Parkin
22

Non, tu ne peux pas.

Vous ne pouvez remplacer qu'une méthode virtuelle - voir le MSDN ici :

En C #, les classes dérivées peuvent contenir des méthodes portant le même nom que les méthodes de classe de base.

  • La méthode de classe de base doit être définie virtuelle.
ChrisF
la source
6

Si la classe de base n'est pas scellée, vous pouvez en hériter et écrire une nouvelle méthode qui cache celle de base (utilisez le mot-clé "new" dans la déclaration de méthode). Sinon, vous ne pouvez pas le remplacer car ce n'était jamais l'intention des auteurs d'origine de le remplacer, d'où la raison pour laquelle il n'est pas virtuel.

slugster
la source
3

Je pense que vous obtenez une surcharge et une substitution confuses, la surcharge signifie que vous avez deux méthodes ou plus avec le même nom mais des ensembles de paramètres différents tout en remplaçant signifie que vous avez une implémentation différente pour une méthode dans une classe dérivée (remplaçant ou modifiant ainsi le comportement dans sa classe de base).

Si une méthode est virtuelle, vous pouvez la remplacer à l'aide du mot clé override dans la classe dérivée. Cependant, les méthodes non virtuelles ne peuvent masquer l'implémentation de base qu'en utilisant le mot clé new à la place du mot clé override. La route non virtuelle est inutile si l'appelant accède à la méthode via une variable tapée comme type de base car le compilateur utiliserait une distribution statique à la méthode de base (ce qui signifie que le code de votre classe dérivée ne sera jamais appelé).

Il n'y a jamais rien qui vous empêche d'ajouter une surcharge à une classe existante, mais seul le code qui connaît votre classe pourra y accéder.

Rory
la source
2

Vous ne pouvez pas remplacer la méthode non virtuelle d'une classe en C # (sans pirater CLR), mais vous pouvez remplacer toute méthode d'interface implémentée par la classe. Considérez que nous avons non scellé

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

Et voici la démo

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

    }
}
Vyacheslav Napadovsky
la source
@Dan voici la démo en direct: dotnetfiddle.net/VgRwKK
Vyacheslav Napadovsky
0

Dans le cas où vous héritez d'une classe non dérivée, vous pouvez simplement créer une super-classe abstraite et en hériter en aval à la place.

user3853059
la source
0

Existe-t-il un moyen de remplacer une méthode non virtuelle? ou quelque chose qui donne des résultats similaires (autre que la création d'une nouvelle méthode pour appeler la méthode souhaitée)?

Vous ne pouvez pas remplacer une méthode non virtuelle. Cependant vous pouvez utiliser le newmot-clé modificateur pour obtenir des résultats similaires:

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

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

Vous voudrez également vous assurer que le modificateur d'accès est également le même, sinon vous n'obtiendrez pas d'héritage sur toute la ligne. Si une autre classe hérite du mot Class1- newclé in Class1n'affectera pas les objets qui en héritent, sauf si le modificateur d'accès est le même.

Si le modificateur d'accès n'est pas le même:

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

... versus si le modificateur d'accès est le même:

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

Comme indiqué dans une réponse précédente, ce n'est pas un bon principe de conception.

Aaron Thomas
la source
-5

Il existe un moyen d'y parvenir en utilisant une classe abstraite et une méthode abstraite.

Considérer

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

     void Method1()
     {
     }

     void Method2()
     {
     }

     ...
}

Maintenant, si vous souhaitez avoir différentes versions de la méthode MethodToBeTested (), changez Class Base en une classe abstraite et la méthode MethodToBeTested () en tant que méthode abstraite

abstract Class Base
{

     abstract void MethodToBeTested();

     void Method1()
     {
     }

     void Method2()
     {
     }

     ...
}

Avec abstract void MethodToBeTested () vient un problème; la mise en œuvre a disparu.

Par conséquent, créez un class DefaultBaseImplementation : Base pour avoir l'implémentation par défaut.

Et créer un autre class UnitTestImplementation : Base pour implémenter le test unitaire.

Avec ces 2 nouvelles classes, la fonctionnalité de classe de base peut être remplacée.

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

}

Class UnitTestImplementation : Base
{

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

}

Maintenant, vous avez 2 classes implémentées (remplacement) MethodToBeTested() .

Vous pouvez instancier la classe (dérivée) selon les besoins (c'est-à-dire avec l'implémentation de base ou avec l'implémentation de test unitaire).

ShivanandSK
la source
@slavoo: Salut slavoo. Merci pour la mise à jour du code. Mais pourriez-vous s'il vous plaît me dire la raison de votre vote négatif?
ShivanandSK
6
Parce que ça ne répond pas à la question. Il vous demande si vous pouvez remplacer les membres non marqués virtuels. Vous avez démontré que vous devez implémenter des membres marqués abstract.
Lee Louviere