2008-10-31 22 views
16

Aujourd'hui, il m'arrive de trouver qu'une classe C# peut hériter d'une interface de manière implicite et explicite. Cela me surprend. Si C# fonctionne de cette manière, une instance peut se comporter différemment lorsqu'elle est référencée de manière différente.Pourquoi une classe C# peut-elle hériter d'une même interface de manière implicite et explicite?

interface IFoo 
{ 
    void DoSomething(); 
} 

class Foo : IFoo 
{ 
    #region IFoo Members 
    public void DoSomething() 
    { 
     Console.WriteLine("do something implicitly"); 
    } 
    #endregion 

    #region IFoo Members 
    void IFoo.DoSomething() 
    { 
     Console.WriteLine("do something explicitly"); 
    } 
    #endregion 
} 


     Foo f = new Foo(); 
     f.DoSomething(); 

     ((IFoo)f).DoSomething(); 

Au-dessus des pistes de code et la sortie

do something implicitly 
do something explicitly 

Je crois que cette conception de # C font l'incohérence du comportement. Peut-être est-il obligatoire qu'une classe C# puisse hériter d'une interface de manière implicite ou explicite, mais pas les deux.

Y a-t-il une raison pour laquelle C# est conçu de cette manière?

+0

Je n'avais pas remarqué que vous pouviez faire les deux. Cela semble un peu stupide ... –

+0

Ce n'est pas idiot. Lisez les réponses. –

+0

Désolé pour le double-retag, j'essayais d'ajouter "explicit-interface-member-implementation" en tant que tag (c'est le nom de la fonctionnalité que vous utilisez), mais apparemment ce tag serait trop long. :) – Rytmis

Répondre

11

Votre exemple ne pas implémenter IFoo à la fois implicitement et explicitement. Vous implémentez uniquement IFoo.DoSometing() explicitement. Vous avez une nouvelle méthode sur votre classe appelée DoSomething(). Cela n'a rien à voir avec IFoo.DoSomething, sauf qu'il a le même nom et les mêmes paramètres.

+0

Et comment serait implicitement implémenté? –

+0

seulement s'il n'y avait pas déjà une implémentation explicite –

+0

Donc, "la classe C# peut hériter d'une interface à la fois de manière implicite et explicite en même temps" est en fait une illusion. Une classe peut hériter d'une interface une fois réellement. –

13

Chaque classe qui implémente une interface a un mappage entre les membres de cette classe et les membres de l'interface. Si la classe explicitement implémente un membre d'interface, alors l'implémentation explicite sera toujours être mappée à l'interface. S'il n'y a pas d'implémentation explicite, une implémentation implicite est attendue, et celle-ci sera mappée à l'interface.

Lorsqu'une classe a le même nom de membre et les types associés comme une interface mais elle aussi implémente explicitement le membre correspondant de l'interface, puis la mise en œuvre « implicite » de la classe n'est pas considérée comme une implémentation de l'interface (sauf si l'implémentation explicite l'appelle). En plus des significations différentes dans chaque cas où la classe implémente plusieurs interfaces avec le même nom/types de membres, même avec une seule interface, la classe elle-même est considérée comme ayant une interface implicite qui pourrait avoir le même membre/types comme l'interface unique, mais toujours quelque chose de différent.

+0

Est-ce que cette interface implicite est une interface réelle, ou simplement la classe exposant une interface, c'est-à-dire est-ce un type, bien que caché? – ProfK

+0

Je ne fais référence qu'à ce que la classe expose. Le CLR ne diffère pas entre l'implémentation d'une classe et son interface inhérente (c'est-à-dire que vous ne pouvez pas implémenter l'interface d'une autre classe et lui substituer sauf si l'interface est un type d'interface défini séparément). –

2

Héritage multiple: Et si vous dériviez de deux interfaces définissant la même méthode à des fins différentes?

interface IMoveable 
    { 
    public void Act(); 
    } 

    interface IRollable 
    { 
    public void Act(); 
    } 

    class Thing : IMoveable, IRollable 
    { 
    //TODO Roll/Move code here 

    void IRollable.Act() 
    { 
     Roll(); 
    } 

    void IMoveable.Act() 
    { 
     Move(); 
    } 
    } 
7

Ceci le rend plus flexible en cas de collision. En particulier, regardez IEnumerator et IEnumerator<T> - ils ont tous les deux une propriété Current, mais de types différents. Vous avez pour utiliser l'implémentation d'interface explicite afin d'implémenter les deux (et le formulaire générique étend le formulaire non générique).

+0

IIRC C'est l'exemple exact que Jeff Richter utilise pour les EIMI dans l'impressionnant CLR via C#. – Quibblesome

+0

C'est probablement celui qui revient le plus souvent par un facteur énorme :) –

+0

J'ai aussi eu la propriété "this [string]" dans ce scénario. J'ai eu une classe avec un indexeur de chaîne, et ensuite voulu implémenter IDataErrorInfo. Même problème - j'ai dû implémenter explicitement l'indexeur de chaîne pour l'erreur. –

0

Les gars, merci pour vos réponses.

Il s'avère que "la classe C# peut hériter d'une interface de manière implicite et explicite en même temps" est en fait une illusion. En fait, une classe peut hériter d'une interface pour une fois.

Dans la question d'origine, la méthode "DoSomething" semble implicitement "implémenter" l'interface IFoo (la méthode est réellement générée par VS2008), mais ce n'est en fait PAS. Avec l'implémentation explicite de l'interface IFoo, la méthode "DoSomething" devient une méthode normale qui n'a rien à voir avec IFoo sauf avec la même signature. Je crois toujours que c'est une conception délicate de C#, et il est facile de l'utiliser par erreur. Dites, j'ai un code comme celui-ci

 Foo f = new Foo(); 
     f.DoSomething(); 

Maintenant, je veux le refactoriser en dessous du code. Cela semble parfaitement correct, mais le résultat de l'exécution est différent.

 Action<IFoo> func = foo => foo.DoSomething(); 
     func(f); 
+1

C'est difficile parce qu'on pourrait penser que Foo est-un IFoo juste comme c'est un BaseFoo mais ce n'est pas le cas! Un Foo est mieux considéré comme jouant le * rôle * d'un IFoo et quand vous accédez à un Foo comme IFoo, vous changez son * rôle * et * éventuellement * son * comportement *. –

+0

Je vais effectivement prétendre que c'est une conception délicate de la classe Foo d'avoir deux méthodes différentes avec le même nom et les mêmes paramètres. Un autre exemple de chose que vous pouvez utiliser pour confondre les gens est d'utiliser le nouveau mot-clé comme modificateur de méthode. Je ne l'utilise jamais parce que c'est confus, mais je devrais peut-être un jour. – Hallgrim