web-dev-qa-db-fra.com

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

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.GraphicsDevice en pensant aux tests unitaires.

82
zfedoran

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

Mais même cacher une méthode ne vous donnera pas de temps d'exécution polymorphe à l'envoi d'appels de méthode comme le ferait un véritable appel de méthode virtuelle. Considérez 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 méthode M print Foo.M. Comme vous pouvez le voir, cette approche vous permet d'avoir une nouvelle implémentation pour une méthode tant que la référence à cet objet est du type dérivé correct mais que le fait de cacher une méthode de base le fait briser le polymorphisme.

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

J'ai tendance à me ranger du côté de ceux qui préfèrent le comportement par défaut de C #, à savoir que les méthodes sont non virtuelles par défaut (par opposition à Java). J'irais même plus loin et dirais 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 voulu que la méthode soit remplacée.

Modifier: "répartition 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 vraie méthode surchargée également.

Si je devais appeler b.Foo dans ce cas, le CLR déterminerait correctement le type d'objet que la référence b pointe vers Bar et enverrait l'appel à M de manière appropriée.

102
Andrew Hare

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.
21
ChrisF

Si la classe de base n'est pas scellée, vous pouvez en hériter et écrire une nouvelle méthode qui masque celle de base (utilisez le mot-clé "new" dans la déclaration de méthode). Sinon non, vous ne pouvez pas le remplacer car il n'a jamais été prévu par les auteurs d'origine qu'il soit remplacé, d'où la raison pour laquelle il n'est pas virtuel.

5
slugster

Je pense que la surcharge et la surcharge sont 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 tandis que la surcharge 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 nouveau mot clé à la place du mot clé override. La route non virtuelle est inutile si l'appelant accède à la méthode via une variable typée comme type de base car le compilateur utiliserait une répartition statique vers la méthode de base (ce qui signifie que le code de votre classe dérivée ne serait jamais appelé).

Rien ne vous empêche d'ajouter une surcharge à une classe existante, mais seul le code connaissant votre classe pourrait y accéder.

2
Rory

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.

0
user3853059

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 mot clé modificateur new 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 de Class1 le mot clé new dans Class1 n'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());

... contre 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.

0
Aaron Thomas