37 votes

Pourquoi ce code C# renvoie-t-il ce qu'il renvoie ?

Quelqu'un peut-il m'aider à comprendre pourquoi ce bout de code renvoie "Bar-Bar-Quux" ? J'ai du mal à comprendre, même après avoir lu des articles sur les interfaces.

interface IFoo
{ 
    string GetName();
}

class Bar : IFoo
{
    public string GetName() { return "Bar"; }
}

class Baz : Bar
{
    public new string GetName() { return "Baz"; }
}

class Quux : Bar, IFoo
{
    public new string GetName() { return "Quux"; }
}

class Program
{
    static void Main()
    {
        Bar f1 = new Baz();
        IFoo f2 = new Baz();
        IFoo f3 = new Quux();
        Console.WriteLine(f1.GetName() + "-" + f2.GetName() + "-" + f3.GetName());
    }
}

57voto

mike z Points 14365

Il y a deux choses qui se passent ici. La première est la dissimulation des membres. C'est assez bien connu et couvert ailleurs . L'autre fonctionnalité, moins connue, est la réimplémentation d'interfaces, couverte par la section 13.4.6 de la spécification C# 5. Je cite :

Une classe qui hérite de l'implémentation d'une interface est autorisée à réimplémenter l'interface en l'incluant dans la liste des classes de base. La réimplémentation d'une interface suit exactement les mêmes règles de mappage d'interface que l'implémentation initiale d'une interface. Ainsi, le mappage d'interface hérité n'a aucun effet sur le mappage d'interface établi pour la réimplémentation de l'interface.

et

Les déclarations de membres publics hérités et les déclarations de membres d'interface explicites héritées participent au processus de mappage d'interface pour les interfaces réimplémentées.

Le résultat pour f1.GetName() est "Bar" parce que la méthode Baz.GetName se cache Bar.GetName y f1 déclaré comme type Bar . Il n'y a pas de répartition vers l'implémentation du type d'exécution à moins qu'il ne soit explicitement déclaré comme virtuel et surchargé.

De même, pour f2.GetName() , Baz.GetName cache l'implémentation dans Bar Il n'est donc pas appelé lors de l'utilisation de la répartition par le biais d'une référence à l'interface. L'interface est "mappée" à la méthode déclarée dans le fichier Bar car c'est le type sur lequel l'interface a été déclarée. Il importe peu que Baz a une méthode compatible portant le même nom. Les règles de mappage des interfaces sont définies dans la section 13.4.4 de la spécification. Si GetName avait été déclarée virtuelle dans Bar il pourrait être surchargé, ce qui serait alors appelé par l'interface. Le résultat est donc également "Bar".

Para f3.GetName() , Quux réimplémente IFoo afin qu'il puisse définir sa propre correspondance avec GetName . Notez qu'il cache également l'implémentation héritée de Bar . Il n'est pas nécessaire d'utiliser new pour effectuer la réimplémentation, il suffit de supprimer l'avertissement concernant la dissimulation. Le résultat est donc "Quux".

Cela explique donc le résultat que vous voyez : "Bar-Bar-Quux"

Ce site poste par Eric Lippert discutent de quelques nuances supplémentaires dans cette fonctionnalité délicate.

8voto

Austin Mullins Points 2739

Par définition, les interfaces n'ont pas d'implémentation associée, c'est-à-dire que leurs méthodes sont toujours virtuelles et abstraites. En revanche, la classe Bar ci-dessus définit une implémentation concrète pour GetName . Cela permet de satisfaire le contrat nécessaire à la mise en œuvre de IFoo .

Classe Baz hérite désormais de Bar et déclare un new méthode GetName . C'est-à-dire que la classe mère Bar possède une méthode portant le même nom, mais elle est complètement ignorée lors de l'utilisation de la fonction Baz de manière explicite.

Toutefois, si un Baz est transformé en un objet Bar ou simplement affectée à une variable de type Bar o IFoo il fera ce qu'on lui dit et se comportera comme un agent de sécurité. Bar . En d'autres termes, le nom de la méthode GetName se réfère à Bar.GetName au lieu de Baz.GetName .

Maintenant, dans le troisième cas, Quux hérite à la fois de Bar et met en œuvre IFoo . Maintenant, lorsqu'il s'agit d'un IFoo il fournira sa propre implémentation (selon la spécification fournie dans la réponse de Mike Z).

Cependant, lorsqu'un Quux est utilisé comme un Bar, il renvoie "Bar", tout comme Baz.

4voto

Travis J Points 28588

La sortie est Bar-Bar-Quux, résultat de 3 appels à GetName() dans votre appel à la méthode Console.WriteLine.

Bar f1 = new Baz();
IFoo f2 = new Baz();
IFoo f3 = new Quux();
Console.WriteLine(f1.GetName() + "-" + f2.GetName() + "-" + f3.GetName());
//Bar-Bar-Quux

Examinons chaque appel afin de mieux comprendre ce qui se passe.

f1.GetName()

f1 est instancié en tant que Baz . Cependant c'est dactylographié como Bar . Parce que Bar expose GetName quand f1.GetName() est utilisée, c'est la méthode qui est appelée - indépendamment de du fait que Baz met également en œuvre GetName . La raison en est que f1 n'est pas typée comme Baz et si c'était le cas, il appellerait Baz 's GetName méthode. Un exemple de cela serait d'examiner la sortie de

Console.WriteLine(((Baz)f1).GetName() + "-" + f2.GetName() + "-" + f3.GetName());
//Baz-Bar-Quux

Cela est possible grâce à deux faits. Premièrement, f1 a été initialement instancié en tant que Baz il a été simplement tapé comme Bar . Deuxièmement, Baz fait ont un GetName et l'utilisation de la méthode new dans sa définition cache l'héritage Bar 's GetName méthode permettant Baz 's GetName pour être appelé.

f2.GetName()

Un typage très similaire se produit avec f2 qui est défini comme suit

IFoo f2 = new Baz();

Bien que la classe Baz implémente un GetName il ne met pas en œuvre la méthode IFoo 's GetName car Baz n'hérite pas de IFoo et donc la méthode n'est pas disponible. Bar met en œuvre IFoo et puisque Baz hérite de Bar , Bar 's GetName est la méthode qui est exposée lorsque f2 est tapé comme IFoo .

Encore une fois, puisque f2 a été initialement instancié en tant que Baz il peut toujours être transformé en Baz .

Console.WriteLine(f1.GetName() + "-" + ((Baz)f2).GetName() + "-" + f3.GetName());
//Bar-Baz-Quux

Et aura le même résultat de sortie pour la raison indiquée ci-dessus pour f1 ( f2 était à l'origine tapé comme Baz y Baz 's GetName cache la méthode héritée Bar 's GetName méthode).

f3.GetName()

C'est une histoire différente ici. Quux fait hériter et mettre en œuvre IFoo , et il se cache Bar La mise en œuvre de l IFoo en utilisant new . Le résultat est que Quux 's GetName est celle qui est appelée.

Prograide.com

Prograide est une communauté de développeurs qui cherche à élargir la connaissance de la programmation au-delà de l'anglais.
Pour cela nous avons les plus grands doutes résolus en français et vous pouvez aussi poser vos propres questions ou résoudre celles des autres.

Powered by:

X