web-dev-qa-db-fra.com

Pourquoi l'appel d'une méthode dans ma classe dérivée appelle-t-il la méthode de la classe de base?

Considérons ce code:

class Program
{
    static void Main(string[] args)
    {
        Person person = new Teacher();
        person.ShowInfo();
        Console.ReadLine();
    }
}

public class Person
{
    public void ShowInfo()
    {
        Console.WriteLine("I am Person");
    }
}
public class Teacher : Person
{
    public new void ShowInfo()
    {
        Console.WriteLine("I am Teacher");
    }
}

Lorsque j'exécute ce code, les éléments suivants sont générés:

Je suis une personne

Cependant, vous pouvez voir qu'il s'agit d'une instance de Teacher et non de Person. Pourquoi le code fait-il cela?

143
user1968030

Il y a une différence entre new et virtual/override.

Vous pouvez imaginer qu'une classe, lorsqu'elle est instanciée, n'est rien d'autre qu'une table de pointeurs pointant vers la mise en œuvre réelle de ses méthodes. L'image suivante devrait bien le visualiser:

Illustration of method implementations

Il existe maintenant différentes manières de définir une méthode. Chacun se comporte différemment lorsqu'il est utilisé avec l'héritage. La méthode standard fonctionne toujours comme illustré ci-dessus. Si vous souhaitez modifier ce comportement, vous pouvez associer différents mots-clés à votre méthode.

1. Cours abstraits

Le premier est abstract. Les méthodes abstract ne font que pointer vers nulle part:

Illustration of abstract classes

Si votre classe contient des membres abstraits, il doit également être marqué comme suit: abstract, sinon le compilateur ne compilera pas votre application. Vous ne pouvez pas créer d'instances de classes abstract, mais vous pouvez en hériter, créer des instances de vos classes héritées et y accéder à l'aide de la définition de classe de base. Dans votre exemple, cela ressemblerait à ceci:

public abstract class Person
{
    public abstract void ShowInfo();
}

public class Teacher : Person
{
    public override void ShowInfo()
    {
        Console.WriteLine("I am a teacher!");
    }
}

public class Student : Person
{
    public override void ShowInfo()
    {
        Console.WriteLine("I am a student!");
    }
}

Si appelé, le comportement de ShowInfo varie en fonction de l'implémentation:

Person person = new Teacher();
person.ShowInfo();    // Shows 'I am a teacher!'

person = new Student();
person.ShowInfo();    // Shows 'I am a student!'

Students et Teachers sont tous deux Persons, mais ils se comportent différemment lorsqu'ils sont invités à demander des informations sur eux-mêmes. Cependant, la manière de leur demander de demander leurs informations est la même: utilisation de l'interface de classe Person.

Alors, que se passe-t-il dans les coulisses lorsque vous héritez de Person? Lors de l'implémentation de ShowInfo, le pointeur ne pointe plus vers nulle part , il pointe maintenant vers l'implémentation réelle! Lors de la création d'une instance Student, il pointe sur Students ShowInfo:

Illustration of inherited methods

2. Méthodes virtuelles

La deuxième méthode consiste à utiliser les méthodes virtual. Le comportement est identique, sauf que vous fournissez une implémentation par défaut facultative dans votre classe de base. Les classes avec les membres virtual peuvent être instanciées, cependant les classes héritées peuvent fournir différentes implémentations. Voici à quoi devrait ressembler votre code pour fonctionner:

public class Person
{
    public virtual void ShowInfo()
    {
        Console.WriteLine("I am a person!");
    }
}

public class Teacher : Person
{
    public override void ShowInfo()
    {
        Console.WriteLine("I am a teacher!");
    }
}

La principale différence est que le membre de base Person.ShowInfo ne pointe plus nulle part . C’est aussi la raison pour laquelle vous pouvez créer des instances de Person (et il n’est donc plus nécessaire de le marquer en tant que abstract):

Illustration of a virtual member inside a base class

Vous remarquerez que cela ne semble pas différent de la première image pour le moment. En effet, la méthode virtual pointe vers une implémentation " de manière standard ". En utilisant virtual, vous pouvez indiquer à Persons qu'ils peut (pas doit) fournit une implémentation différente pour ShowInfo. Si vous fournissez une implémentation différente (en utilisant override), comme je l'ai fait pour le Teacher ci-dessus, l'image sera identique à celle de abstract. Imaginez, nous n'avons pas fourni d'implémentation personnalisée pour Students:

public class Student : Person
{
}

Le code s'appellerait comme ceci:

Person person = new Teacher();
person.ShowInfo();    // Shows 'I am a teacher!'

person = new Student();
person.ShowInfo();    // Shows 'I am a person!'

Et l'image pour Student ressemblerait à ceci:

Illustration of the default implementation of a method, using virtual-keyword

3. La magie `new` mot clé aka" Shadowing "

new est plus un hack que ça. Vous pouvez fournir des méthodes dans des classes généralisées, qui portent les mêmes noms que des méthodes dans la classe/interface de base. Les deux désignent leur propre implémentation personnalisée:

Illustration of the "way around" using the new-keyword

L'implémentation ressemble à celle que vous avez fournie. Le comportement diffère selon la manière dont vous accédez à la méthode:

Teacher teacher = new Teacher();
Person person = (Person)teacher;

teacher.ShowInfo();    // Prints 'I am a teacher!'
person.ShowInfo();     // Prints 'I am a person!'

Ce comportement peut être voulu, mais dans votre cas, il est trompeur.

J'espère que cela rend les choses plus claires à comprendre pour vous!

364
Carsten

Le polymorphisme de sous-type en C # utilise la virtualité explicite, semblable au C++ mais différent de Java. Cela signifie que vous devez explicitement marquer les méthodes comme pouvant être remplacées (c'est-à-dire virtual). En C #, vous devez également marquer explicitement les méthodes de substitution comme telles (par exemple, override) pour éviter les fautes de frappe.

public class Person
{
    public virtual void ShowInfo()
    {
        Console.WriteLine("I am Person");
    }
}

public class Teacher : Person
{
    public override void ShowInfo()
    {
        Console.WriteLine("I am Teacher");
    }
}

Dans le code de votre question, vous utilisez new, ce qui fait observation au lieu de remplacer. L'observation affecte uniquement la sémantique au moment de la compilation plutôt que la sémantique à l'exécution, d'où la sortie non intentionnelle.

45
user142019

Vous devez définir la méthode virtual et redéfinir la fonction dans la classe enfant afin d'appeler la méthode de l'objet class que vous avez définie dans la référence de la classe parent.

public class Person
{
    public virtual void ShowInfo()
    {
        Console.WriteLine("I am Person");
    }
}
public class Teacher : Person
{
    public override void ShowInfo()
    {
        Console.WriteLine("I am Teacher");
    }
}

Méthodes virtuelles

Lorsqu'une méthode virtuelle est appelée, le type d'exécution de l'objet est vérifié pour un membre de substitution. Le membre substituant de la classe la plus dérivée est appelé, ce qui peut être le membre d'origine, si aucune classe dérivée n'a remplacé le membre. Par défaut, les méthodes ne sont pas virtuelles. Vous ne pouvez pas remplacer une méthode non virtuelle. Vous ne pouvez pas utiliser le modificateur virtuel avec les modificateurs static, abstract, private ou override, MSDN .

Utiliser du nouveau pour l'observation

Vous utilisez nouvelle clé Word au lieu de remplacer, c'est ce que fait nouvelle

  • Si la méthode de la classe dérivée n'est pas précédée de nouveaux mots-clés ou de mots-clés de substitution, le compilateur émet un avertissement et la méthode se comporte comme si le nouveau mot-clé était présent.

  • Si la méthode de la classe dérivée est précédée du nouveau mot-clé, la méthode est définie comme étant indépendante de la méthode de la classe de base . article MSDN l'explique très bien.

Liaison anticipée VS Liaison tardive

Nous avons une liaison précoce au moment de la compilation pour la méthode normale (non virtuelle) qui est le cas actuel du compilateur qui liera l’appel à la méthode de la classe de base qui est une méthode de type référence (classe de base) au lieu de l'objet est maintenu dans la référence de la classe de base, c'est-à-dire un objet de classe dérivée . En effet, ShowInfo n'est pas une méthode virtuelle. La liaison tardive est effectuée au moment de l'exécution pour (méthode virtuelle/remplacée) à l'aide de table de méthode virtuelle (vtable).

Pour une fonction normale, le compilateur peut calculer son emplacement numérique en mémoire. Ensuite, lorsque la fonction est appelée, elle peut générer une instruction pour appeler la fonction à cette adresse.

Pour un objet ayant des méthodes virtuelles, le compilateur générera une v-table. C'est essentiellement un tableau qui contient les adresses des méthodes virtuelles. Chaque objet ayant une méthode virtuelle contiendra un membre masqué généré par le compilateur qui correspond à l'adresse de la v-table. Lorsqu'une fonction virtuelle est appelée, le compilateur détermine la position de la méthode appropriée dans la table virtuelle. Il va ensuite générer du code pour rechercher dans les objets v-table et appeler la méthode virtuelle à cette position, Référence .

25
Adil

Je veux construire à partir de réponse d'Achratt . Pour être complet, la différence est que l'OP s'attend à ce que le mot clé new de la méthode de la classe dérivée remplace la méthode de la classe de base. Ce qu'il fait réellement est cacher la méthode de la classe de base.

En C #, comme l’a mentionné une autre réponse, le remplacement de la méthode traditionnelle doit être explicite; la méthode de la classe de base doit être marquée comme virtual et la classe dérivée doit plus précisément override la méthode de la classe de base. Si cela est fait, peu importe si l'objet est traité comme une instance de la classe de base ou de la classe dérivée; la méthode dérivée est trouvée et appelée. Cela se fait de la même manière qu'en C++; une méthode marquée "virtual" ou "override", une fois compilée, est résolue "tard" (au moment de l'exécution) en déterminant le type réel de l'objet référencé et en parcourant la hiérarchie des objets vers le bas le long de l'arbre du type variable au type d'objet réel, pour trouver l'implémentation la plus dérivée de la méthode définie par le type de variable.

Cela diffère de Java, qui autorise les "remplacements implicites"; pour les méthodes d'instance (non statiques), la simple définition d'une méthode de la même signature (nom et nombre/type de paramètres) entraînera le remplacement de la super-classe par la sous-classe.

Comme il est souvent utile d'étendre ou de remplacer les fonctionnalités d'une méthode non virtuelle que vous ne contrôlez pas, C # inclut également le mot-clé contextuel new. Le mot clé new "masque" la méthode parente au lieu de la remplacer. Toute méthode pouvant être héritée peut être masquée, qu'elle soit virtuelle ou non. Cela vous permet, en tant que développeur, d'exploiter les membres que vous souhaitez hériter d'un parent, sans avoir à contourner ceux que vous n'aimez pas, tout en vous permettant de présenter la même "interface" aux utilisateurs de votre code.

Le masquage fonctionne de la même manière que le remplacement du point de vue d'une personne utilisant votre objet à un niveau inférieur ou égal au niveau d'héritage auquel la méthode de masquage est définie. À partir de l'exemple de la question, un codeur qui crée un enseignant et stocke cette référence dans une variable de type Enseignant verra le comportement de l'implémentation ShowInfo () de Enseignant, qui masque celle de Personne. Cependant, quelqu'un qui travaille avec votre objet dans une collection d'enregistrements Person (comme vous) verra le comportement de l'implémentation Person de ShowInfo (); Etant donné que la méthode de Teacher ne remplace pas son parent (ce qui exigerait également que Person.ShowInfo () soit virtuel), le code fonctionnant au niveau abstraction de Person ne trouvera pas l'implémentation de Teacher et ne l'utilisera pas.

De plus, non seulement le mot-clé new le fait explicitement, mais C # autorise le masquage de méthode implicite; Définir simplement une méthode avec la même signature qu'une méthode de classe parent, sans override ou new, le masquera (bien que cela produira un avertissement du compilateur ou une plainte de certains assistants de refactoring tels que ReSharper ou CodeRush) . C'est le compromis que les concepteurs de C # ont proposé entre les substitutions explicites de C++ et celles implicites de Java. Bien que élégant, il ne produit pas toujours le comportement attendu si vous venez d'un arrière-plan dans l'un des langages plus anciens.

Voici la nouveauté: Cela devient complexe lorsque vous combinez les deux mots-clés dans une longue chaîne d'héritage. Considérer ce qui suit:

class Foo { public virtual void DoFoo() { Console.WriteLine("Foo"); } }
class Bar:Foo { public override sealed void DoFoo() { Console.WriteLine("Bar"); } }
class Baz:Bar { public virtual void DoFoo() { Console.WriteLine("Baz"); } }
class Bai:Baz { public override void DoFoo() { Console.WriteLine("Bai"); } }
class Bat:Bai { public new void DoFoo() { Console.WriteLine("Bat"); } }
class Bak:Bat { }

Foo foo = new Foo();
Bar bar = new Bar();
Baz baz = new Baz();
Bai bai = new Bai();
Bat bat = new Bat();

foo.DoFoo();
bar.DoFoo();
baz.DoFoo();
bai.DoFoo();
bat.DoFoo();

Console.WriteLine("---");

Foo foo2 = bar;
Bar bar2 = baz;
Baz baz2 = bai;
Bai bai2 = bat;
Bat bat2 = new Bak();

foo2.DoFoo();
bar2.DoFoo();
baz2.DoFoo();
bai2.DoFoo();    

Console.WriteLine("---");

Foo foo3 = bak;
Bar bar3 = bak;
Baz baz3 = bak;
Bai bai3 = bak;
Bat bat3 = bak;

foo3.DoFoo();
bar3.DoFoo();
baz3.DoFoo();
bai3.DoFoo();    
bat3.DoFoo();

Sortie:

Foo
Bar
Baz
Bai
Bat
---
Bar
Bar
Bai
Bai
Bat
---
Bar
Bar
Bai
Bai
Bat

La première série de cinq est tout à fait à prévoir; étant donné que chaque niveau a une implémentation et qu'il est référencé en tant qu'objet du même type que celui qui a été instancié, le moteur d'exécution résout chaque appel au niveau d'héritage référencé par le type de variable.

Le deuxième ensemble de cinq est le résultat de l'affectation de chaque instance à une variable du type parent immédiat. Maintenant, certaines différences de comportement disparaissent; foo2, qui est en fait un Bar converti en tant que Foo, trouvera toujours la méthode la plus dérivée du type d'objet réel Bar. bar2 est un Baz, mais contrairement à foo2, parce que Baz ne substitue pas explicitement l'implémentation de Bar (il ne peut pas; Bar sealed il), il n'est pas vu par l'exécution en regardant "de haut en bas", donc l'implémentation de Bar est appelée à la place. Notez que Baz n'a pas à utiliser le mot clé new; vous obtiendrez un avertissement pour le compilateur si vous omettez le mot clé, mais le comportement implicite en C # consiste à masquer la méthode parent. baz2 est une Bai, qui annule l'implémentation de Baz 'new, de sorte que son comportement est similaire à celui de foo2; l'implémentation du type d'objet réel dans Bai est appelée. bai2 est une Bat, qui masque encore l'implémentation de la méthode de son parent Bai, et se comporte de la même manière que bar2 même si l'implémentation de Bai n'est pas scellée, Bat pourrait donc en théorie ont remplacé la méthode au lieu de la masquer. Enfin, bat2 est un Bak, qui ne possède aucune implémentation dominante, et utilise simplement celle de son parent.

La troisième série de cinq illustre le comportement de résolution descendante complet. Tout référence en réalité à une instance de la classe la plus dérivée de la chaîne, Bak, mais la résolution à chaque niveau de type de variable est effectuée en commençant à ce niveau de la chaîne d'héritage et en descendant jusqu'au plus dérivé explicite remplacement de la méthode, qui sont ceux de Bar, Bai et Bat. La méthode masquée "brise" ainsi la chaîne d'héritage prépondérante; vous devez travailler avec l'objet à un niveau égal ou inférieur au niveau d'héritage qui masque la méthode pour que la méthode de masquage soit utilisée. Sinon, la méthode cachée est "découverte" et utilisée à la place.

7
KeithS

Lisez à propos du polymorphisme en C #: Polymorphisme (Guide de programmation en C #)

Voici un exemple à partir de là:

Lorsque le nouveau mot-clé est utilisé, les nouveaux membres de la classe sont appelés à la place des membres de la classe de base qui ont été remplacés. Ces membres de la classe de base sont appelés membres cachés. Les membres de classe masqués peuvent toujours être appelés si une instance de la classe dérivée est convertie en instance de la classe de base. Par exemple:

DerivedClass B = new DerivedClass();
B.DoWork();  // Calls the new method.

BaseClass A = (BaseClass)B;
A.DoWork();  // Calls the old method.
4
Emil

Vous devez le rendre virtual, puis remplacer cette fonction dans Teacher. Lorsque vous héritez et utilisez le pointeur de base pour faire référence à une classe dérivée, vous devez la remplacer à l'aide de virtual. new est destiné à masquer la méthode de classe base sur une référence de classe dérivée et non une référence de classe base.

3
Jay Patel

Je voudrais ajouter quelques exemples supplémentaires pour développer les informations autour de cela. J'espère que cela aide aussi:

Voici un exemple de code qui purifie l'air autour de ce qui se passe lorsqu'un type dérivé est affecté à un type de base. Quelles méthodes sont disponibles et la différence entre les méthodes substituées et cachées dans ce contexte.

namespace TestApp
{
    class Program
    {
        static void Main(string[] args)
        {
            A a = new A();
            a.foo();        // A.foo()
            a.foo2();       // A.foo2()

            a = new B();    
            a.foo();        // B.foo()
            a.foo2();       // A.foo2()
            //a.novel() is not available here

            a = new C();
            a.foo();        // C.foo()
            a.foo2();       // A.foo2()

            B b1 = (B)a;    
            b1.foo();       // C.foo()
            b1.foo2();      // B.foo2()
            b1.novel();     // B.novel()

            Console.ReadLine();
        }
    }


    class A
    {
        public virtual void foo()
        {
            Console.WriteLine("A.foo()");
        }

        public void foo2()
        {
            Console.WriteLine("A.foo2()");
        }
    }

    class B : A
    {
        public override void foo()
        {
            // This is an override
            Console.WriteLine("B.foo()");
        }

        public new void foo2()      // Using the 'new' keyword doesn't make a difference
        {
            Console.WriteLine("B.foo2()");
        }

        public void novel()
        {
            Console.WriteLine("B.novel()");
        }
    }

    class C : B
    {
        public override void foo()
        {
            Console.WriteLine("C.foo()");
        }

        public new void foo2()
        {
            Console.WriteLine("C.foo2()");
        }
    }
}

Une autre petite anomalie est que, pour la ligne de code suivante:

A a = new B();    
a.foo(); 

Le compilateur VS (intellisense) affichera a.foo () comme étant A.foo ().

Par conséquent, il est clair que lorsqu'un type plus dérivé est affecté à un type de base, la variable 'type de base' agit en tant que type de base jusqu'à ce qu'une méthode substituée dans un type dérivé soit référencée. Cela peut devenir un peu contre-intuitif avec des méthodes cachées ou des méthodes portant le même nom (mais non substituées) entre les types parent et enfant.

Cet exemple de code devrait aider à définir ces mises en garde!

3
Vaibhav

Le mot-clé nouvea indique que la méthode de la classe en cours ne fonctionnera que si une instance de la classe Teacher est stockée dans une variable de type Teacher. Ou vous pouvez le déclencher en utilisant des castings: ((Teacher) Person) .ShowInfo ()

2
Miguel

C # est différent de Java dans le comportement de substitution de classe parent/enfant. Par défaut, dans Java, toutes les méthodes sont virtuelles. Le comportement souhaité est donc pris en charge immédiatement.

En C #, vous devez marquer une méthode comme virtuelle dans la classe de base, vous obtiendrez alors ce que vous voulez.

2
Adrian Salazar

Peut-être trop tard ... Mais la question est simple et la réponse devrait avoir le même niveau de complexité.

Dans votre code, personne variable ne sait rien de Teacher.ShowInfo (). Il n’existe aucun moyen d’appeler la dernière méthode à partir de la référence de la classe de base, car elle n’est pas virtuelle.

Il existe une approche utile de l'héritage - essayez d'imaginer ce que vous voulez dire avec votre hiérarchie de code. Essayez également d'imaginer ce que l'un ou l'autre outil dit à propos de lui-même. Par exemple. Si vous ajoutez une fonction virtuelle à une classe de base, vous supposez que: 1. elle peut avoir une implémentation par défaut; 2. il peut être réimplémenté en classe dérivée. Si vous ajoutez une fonction abstraite, cela signifie seulement une chose - la sous-classe doit créer une implémentation. Mais si vous avez une fonction simple, vous ne vous attendez pas à ce que quelqu'un modifie son implémentation.

1
Illia Levandovskyi

Le type de variable 'enseignant' ici est typeof(Person) et ce type ne connaît rien de la classe Enseignant et n'essaie pas de rechercher une méthode dans les types dérivés. Pour appeler la méthode de classe Teacher, vous devez convertir votre variable: (person as Teacher).ShowInfo().

Pour appeler une méthode spécifique en fonction du type de valeur, vous devez utiliser le mot clé 'virtual' dans votre classe de base et remplacer les méthodes virtuelles dans les classes dérivées. Cette approche permet d'implémenter des classes dérivées avec ou sans substitution de méthodes virtuelles. Les méthodes de la classe de base seront appelées pour les types sans virtuals surchargés.

public class Program
{
    private static void Main(string[] args)
    {
        Person teacher = new Teacher();
        teacher.ShowInfo();

        Person incognito = new IncognitoPerson ();
        incognito.ShowInfo();

        Console.ReadLine();
    }
}

public class Person
{
    public virtual void ShowInfo()
    {
        Console.WriteLine("I am Person");
    }
}

public class Teacher : Person
{
    public override void ShowInfo()
    {
        Console.WriteLine("I am Teacher");
    }
}

public class IncognitoPerson : Person
{

}
1
Vi Ki

S'appuyant sur l'excellente démonstration de Keith S. et sur les réponses de qualité fournies par tous les autres, et dans un souci de complète complétude, allons de l'avant et intégrons des implémentations d'interface explicites pour montrer comment cela fonctionne. Considérez ce qui suit:

espace de noms LinqConsoleApp {

class Program
{

    static void Main(string[] args)
    {


        Person person = new Teacher();
        Console.Write(GetMemberName(() => person) + ": ");
        person.ShowInfo();

        Teacher teacher = new Teacher();
        Console.Write(GetMemberName(() => teacher) + ": ");
        teacher.ShowInfo();

        IPerson person1 = new Teacher();
        Console.Write(GetMemberName(() => person1) + ": ");
        person1.ShowInfo();

        IPerson person2 = (IPerson)teacher;
        Console.Write(GetMemberName(() => person2) + ": ");
        person2.ShowInfo();

        Teacher teacher1 = (Teacher)person1;
        Console.Write(GetMemberName(() => teacher1) + ": ");
        teacher1.ShowInfo();

        Person person4 = new Person();
        Console.Write(GetMemberName(() => person4) + ": ");
        person4.ShowInfo();

        IPerson person3 = new Person();
        Console.Write(GetMemberName(() => person3) + ": ");
        person3.ShowInfo();

        Console.WriteLine();

        Console.ReadLine();

    }

    private static string GetMemberName<T>(Expression<Func<T>> memberExpression)
    {
        MemberExpression expressionBody = (MemberExpression)memberExpression.Body;
        return expressionBody.Member.Name;
    }

}
interface IPerson
{
    void ShowInfo();
}
public class Person : IPerson
{
    public void ShowInfo()
    {
        Console.WriteLine("I am Person == " + this.GetType());
    }
    void IPerson.ShowInfo()
    {
        Console.WriteLine("I am interface Person == " + this.GetType());
    }
}
public class Teacher : Person, IPerson
{
    public void ShowInfo()
    {
        Console.WriteLine("I am Teacher == " + this.GetType());
    }
}

}

Voici la sortie:

personne: Je suis Personne == LinqConsoleApp.Teacher

professeur: je suis professeur == LinqConsoleApp.Teacher

person1: Je suis professeur == LinqConsoleApp.Teacher

person2: Je suis professeur == LinqConsoleApp.Teacher

teacher1: Je suis un enseignant == LinqConsoleApp.Teacher

person4: Je suis une personne == LinqConsoleApp.Person

person3: Je suis l'interface Person == LinqConsoleApp.Person

Deux choses à noter:
La méthode Teacher.ShowInfo () omet le nouveau mot clé. Lorsque new est omis, le comportement de la méthode est le même que si le nouveau mot-clé était explicitement défini.

Vous ne pouvez utiliser le mot clé de substitution qu'en liaison avec la clé virtuelle Word. La méthode de la classe de base doit être virtuelle. Ou abstrait, auquel cas la classe doit aussi être abstraite.

personne obtient l'implémentation de base de ShowInfo car la classe Enseignant ne peut pas remplacer l'implémentation de base (pas de déclaration virtuelle) et personne est .GetType (Enseignant), de sorte qu'elle cache l'implémentation de la classe Enseignant.

enseignant obtient l'implémentation dérivée de ShowInfo de l'enseignant, car il s'agit de Typeof (Enseignant) et que ce n'est pas au niveau de l'héritage Personne.

person1 obtient l'implémentation dérivée de l'enseignant car il s'agit de .GetType (Enseignant) et le nouveau mot clé implicite masque l'implémentation de base.

person2 obtient également l'implémentation dérivée de l'enseignant même si elle implémente IPerson et obtient une conversion explicite vers IPerson. C'est encore parce que la classe Enseignant n'implémente pas explicitement la méthode IPerson.ShowInfo ().

teacher1 obtient également l'implémentation dérivée de Teacher, car il s'agit de .GetType (Teacher).

Seul person3 obtient l'implémentation IPerson de ShowInfo car seule la classe Person implémente explicitement la méthode et person3 est une instance du type IPerson.

Pour implémenter explicitement une interface, vous devez déclarer une instance var du type d'interface cible et une classe doit explicitement implémenter (qualifier complètement) le ou les membres de l'interface.

Notez que même personne4 n’obtient l’implémentation IPerson.ShowInfo. En effet, même si personne4 est .GetType (Personne) et même si Personne implémente IPerson, personne4 n'est pas une instance de IPerson.

0
steely

Je voulais juste donner une réponse brève -

Vous devez utiliser virtual et override dans les classes susceptibles d'être remplacées. Utilisez virtual pour les méthodes pouvant être remplacées par des classes enfant et utilisez override pour les méthodes devant remplacer ces méthodes virtual.

0
Shruti Kapoor

Le compilateur fait cela parce qu'il ne sait pas que c'est un Teacher. Tout ce qu'il sait, c'est qu'il s'agit d'une Person ou de quelque chose qui en dérive. Donc, tout ce qu'il peut faire est d'appeler la méthode Person.ShowInfo().

0
Cole Johnson

J'ai écrit le même code que vous avez mentionné ci-dessus dans Java à l'exception de quelques modifications et il a bien fonctionné, sauf exception. La méthode de la classe de base est remplacée et le résultat affiché est "Je suis enseignant".

Reason: Comme nous créons une référence de la classe de base (qui est capable d'avoir une instance de référence de la classe dérivée) qui contient en réalité la référence de la classe dérivée. Et comme nous savons que l'instance regarde toujours ses méthodes en premier si elle la trouve là, elle l'exécute, et si elle n'y trouve pas la définition, elle monte dans la hiérarchie.

public class inheritance{

    public static void main(String[] args){

        Person person = new Teacher();
        person.ShowInfo();
    }
}

class Person{

    public void ShowInfo(){
        System.out.println("I am Person");
    }
}

class Teacher extends Person{

    public void ShowInfo(){
        System.out.println("I am Teacher");
    }
}
0
Sanjeev Chauhan

LinQPad exemple à lancer à l'aveugle et réduire la duplication de code Ce que je pense est ce que vous essayiez de faire.

void Main()
{
    IEngineAction Test1 = new Test1Action();
    IEngineAction Test2 = new Test2Action();
    Test1.Execute("Test1");
    Test2.Execute("Test2");
}

public interface IEngineAction
{
    void Execute(string Parameter);
}

public abstract class EngineAction : IEngineAction
{
    protected abstract void PerformAction();
    protected string ForChildren;
    public void Execute(string Parameter)
    {  // Pretend this method encapsulates a 
       // lot of code you don't want to duplicate 
      ForChildren = Parameter;
      PerformAction();
    }
}

public class Test1Action : EngineAction
{
    protected override void PerformAction()
    {
        ("Performed: " + ForChildren).Dump();
    }
}

public class Test2Action : EngineAction
{
    protected override void PerformAction()
    {
        ("Actioned: " + ForChildren).Dump();
    }
}
0
Yrd