web-dev-qa-db-fra.com

Comment appeler base.base.method ()?

// Cannot change source code
class Base
{
    public virtual void Say()
    {
        Console.WriteLine("Called from Base.");
    }
}

// Cannot change source code
class Derived : Base
{
    public override void Say()
    {
        Console.WriteLine("Called from Derived.");
        base.Say();
    }
}

class SpecialDerived : Derived
{
    public override void Say()
    {
        Console.WriteLine("Called from Special Derived.");
        base.Say();
    }
}

class Program
{
    static void Main(string[] args)
    {
        SpecialDerived sd = new SpecialDerived();
        sd.Say();
    }
}

Le résultat est:

Appelé de spécial dérivé.
Appelé de Derived./* ce n'est pas prévu * /
Appelé de la base.

Comment puis-je réécrire la classe SpecialDerived de sorte que la méthode de la classe moyenne "Derived" ne soit pas appelée?

UPDATE: .__ La raison pour laquelle je veux hériter de Derived au lieu de Base est dérivée, la classe Derived contient beaucoup d'autres implémentations. Puisque je ne peux pas faire base.base.method() ici, je suppose que le meilleur moyen est de procéder comme suit?

// Impossible de changer le code source

class Derived : Base
{
    public override void Say()
    {
        CustomSay();

        base.Say();
    }

    protected virtual void CustomSay()
    {
        Console.WriteLine("Called from Derived.");
    }
}

class SpecialDerived : Derived
{
    /*
    public override void Say()
    {
        Console.WriteLine("Called from Special Derived.");
        base.Say();
    }
    */

    protected override void CustomSay()
    {
        Console.WriteLine("Called from Special Derived.");
    }
}
98
AZ.

C'est une mauvaise pratique de programmation, et non autorisée en C #. C'est une mauvaise pratique de programmation parce que

  • Les détails de la base principale sont les détails d'implémentation de la base; vous ne devriez pas compter sur eux. La classe de base fournit une abstraction au-dessus de la base principale; vous devriez utiliser cette abstraction et non pas créer un contournement pour l'éviter.

  • Vous avez dérivé de votre base parce que vous aimez ce qu'elle fait et que vous voulez la réutiliser et l'étendre. Si vous n'aimez pas ce qu'il fait et que vous voulez le contourner plutôt que de le faire, pourquoi en avez-vous dérivé? Dérivez vous-même de la base grandbase si c'est la fonctionnalité que vous souhaitez utiliser et étendre.

  • La base peut nécessiter certains invariants à des fins de sécurité ou de cohérence sémantique, gérées par les détails de la manière dont la base utilise les méthodes de la base grandbase. Autoriser une classe dérivée de la base à ignorer le code qui gère ces invariants peut placer la base dans un état corrompu et incohérent.

87
Eric Lippert

Je veux juste ajouter ceci ici, car les gens reviennent encore sur cette question même après plusieurs fois. Bien sûr, c'est une mauvaise pratique, mais il est toujours possible (en principe) de faire ce que veut l'auteur avec:

class SpecialDerived : Derived
{
    public override void Say()
    {
        Console.WriteLine("Called from Special Derived.");
        var ptr = typeof(Base).GetMethod("Say").MethodHandle.GetFunctionPointer();            
        var baseSay = (Action)Activator.CreateInstance(typeof(Action), this, ptr);
        baseSay();            
    }
}
70
Evk

Vous ne pouvez pas de C #. À partir de IL, ceci est réellement supporté. Vous pouvez faire un appel non virtuel à n’importe laquelle de vos classes de parents ... :)

23
rh.

La réponse (ce que je sais n'est pas ce que vous cherchez) est la suivante:

class SpecialDerived : Base
{
    public override void Say()
    {
        Console.WriteLine("Called from Special Derived.");
        base.Say();
    }
}

La vérité est que vous n’avez qu’une interaction directe avec la classe dont vous héritez. Pensez à cette classe en tant que couche, fournissant autant de fonctionnalités que celles de son parent à ses classes dérivées.

MODIFIER:

Votre montage fonctionne, mais je pense utiliser quelque chose comme ceci:

class Derived : Base
{
    protected bool _useBaseSay = false;

    public override void Say()
    {
        if(this._useBaseSay)
            base.Say();
        else
            Console.WriteLine("Called from Derived");
    }
}

Bien sûr, dans une implémentation réelle, vous pourriez faire quelque chose de plus semblable à celui-ci pour l'extensibilité et la maintenabilité:

class Derived : Base
{
    protected enum Mode
    {
        Standard,
        BaseFunctionality,
        Verbose
        //etc
    }

    protected Mode Mode
    {
        get; set;
    }

    public override void Say()
    {
        if(this.Mode == Mode.BaseFunctionality)
            base.Say();
        else
            Console.WriteLine("Called from Derived");
    }
}

Ensuite, les classes dérivées peuvent contrôler l'état de leurs parents de manière appropriée.

10
JoshJordan

Pourquoi ne pas simplement transtyper la classe enfant en une classe parent spécifique et invoquer alors l'implémentation spécifique? Il s’agit d’une situation particulière et une solution spécifique doit être utilisée. Vous devrez cependant utiliser le mot clé new dans les méthodes children.

public class SuperBase
{
    public string Speak() { return "Blah in SuperBase"; }
}

public class Base : SuperBase
{
    public new string Speak() { return "Blah in Base"; }
}

public class Child : Base
{
    public new string Speak() { return "Blah in Child"; }
}

public partial class MainWindow : Window
{
    public MainWindow()
    {
        InitializeComponent();

        Child childObj = new Child();

        Console.WriteLine(childObj.Speak());

        // casting the child to parent first and then calling Speak()
        Console.WriteLine((childObj as Base).Speak()); 

        Console.WriteLine((childObj as SuperBase).Speak());
    }
}
6
Kruczkowski

Vous pouvez également créer une fonction simple dans la classe dérivée de premier niveau, pour appeler une fonction grand base.

3
Rajesh

Mon objectif 2c consiste à implémenter la fonctionnalité dont vous avez besoin pour être appelée dans une classe de boîte à outils et à l'appeler de n'importe où vous avez besoin:

// Util.cs
static class Util 
{
    static void DoSomething( FooBase foo ) {}
}

// FooBase.cs
class FooBase
{
    virtual void Do() { Util.DoSomething( this ); }
}


// FooDerived.cs
class FooDerived : FooBase
{
    override void Do() { ... }
}

// FooDerived2.cs
class FooDerived2 : FooDerived
{
    override void Do() { Util.DoSomething( this ); }
}

Cela nécessite une certaine réflexion sur les privilèges d'accès, vous devrez peut-être ajouter des méthodes d'accès internal pour faciliter les fonctionnalités.

2
Julian Gold
public class A
{
    public int i = 0;
    internal virtual void test()
    {
        Console.WriteLine("A test");
    }
}

public class B : A
{
    public new int i = 1;
    public new void test()
    {
        Console.WriteLine("B test");
    }
}

public class C : B
{
    public new int i = 2;
    public new void test()
    {
        Console.WriteLine("C test - ");
        (this as A).test(); 
    }
}
2
Pavel

Dans les cas où vous n'avez pas accès à la source de la classe dérivée, mais avez besoin de toute la source de la classe dérivée en plus de la méthode actuelle, je vous recommande de créer une classe dérivée et d'appeler l'implémentation de la classe dérivée.

Voici un exemple:

//No access to the source of the following classes
public class Base
{
     public virtual void method1(){ Console.WriteLine("In Base");}
}
public class Derived : Base
{
     public override void method1(){ Console.WriteLine("In Derived");}
     public void method2(){ Console.WriteLine("Some important method in Derived");}
}

//Here should go your classes
//First do your own derived class
public class MyDerived : Base
{         
}

//Then derive from the derived class 
//and call the bass class implementation via your derived class
public class specialDerived : Derived
{
     public override void method1()
     { 
          MyDerived md = new MyDerived();
          //This is actually the base.base class implementation
          MyDerived.method1();  
     }         
}
1
yoel halb

Il semble y avoir beaucoup de questions concernant l'héritage d'une méthode membre d'une classe de grands-parents, son remplacement dans une seconde classe, puis son appel de nouveau à partir d'une classe de petits-enfants. Pourquoi ne pas simplement hériter des membres des grands-parents jusqu'aux petits-enfants? 

class A
{
    private string mystring = "A";    
    public string Method1()
    {
        return mystring;
    }
}

class B : A
{
    // this inherits Method1() naturally
}

class C : B
{
    // this inherits Method1() naturally
}


string newstring = "";
A a = new A();
B b = new B();
C c = new C();
newstring = a.Method1();// returns "A"
newstring = b.Method1();// returns "A"
newstring = c.Method1();// returns "A"

Cela semble simple ... le petit-fils hérite de la méthode des grands-parents. Pensez-y ..... c'est comme ça que "Object" et ses membres comme ToString () sont hérités de toutes les classes en C #. Je pense que Microsoft n'a pas bien expliqué le principe de base de l'héritage. L'accent est mis trop sur le polymorphisme et la mise en œuvre. Lorsque je fouille dans leur documentation, il n’existe aucun exemple de cette idée de base. :(

0
Stokely

Comme on peut le voir dans les publications précédentes, on peut affirmer que si la fonctionnalité de classe doit être contournée, alors quelque chose ne va pas dans l’architecture de la classe. Cela peut être vrai, mais on ne peut pas toujours restructurer ou refactoriser la structure de classe sur un grand projet mature. Les différents niveaux de gestion des modifications peuvent constituer un problème, mais conserver la même fonctionnalité existante après le refactoring n’est pas toujours une tâche facile, surtout si les contraintes de temps l’appliquent. Sur un projet mûr, il peut être assez difficile de laisser passer plusieurs tests de régression après une restructuration du code; il y a souvent des "bizarreries" obscures qui apparaissent. Nous avons eu un problème similaire dans certains cas, les fonctionnalités héritées ne doivent pas s'exécuter (ou doivent effectuer autre chose). L'approche que nous avons suivie ci-dessous consistait à placer le code de base à exclure dans une fonction virtuelle distincte. Cette fonction peut ensuite être remplacée dans la classe dérivée et la fonctionnalité exclue ou modifiée. Dans cet exemple, il est possible d'empêcher la sortie de "Texte 2" dans la classe dérivée. 

public class Base
{
    public virtual void Foo()
    {
        Console.WriteLine("Hello from Base");
    }
}

public class Derived : Base
{
    public override void Foo()
    {
        base.Foo();
        Console.WriteLine("Text 1");
        WriteText2Func();
        Console.WriteLine("Text 3");
    }

    protected virtual void WriteText2Func()
    {  
        Console.WriteLine("Text 2");  
    }
}

public class Special : Derived
{
    public override void WriteText2Func()
    {
        //WriteText2Func will write nothing when 
        //method Foo is called from class Special.
        //Also it can be modified to do something else.
    }
}
0
Pierre