2009-02-13 12 views
5

Je me suis retrouvé avec quelque chose comme le code suivant dans un projet sur lequel je travaille. Je pensais que c'était vraiment étrange que je sois autorisé à le faire, mais maintenant je commence à me demander ce qui est probablement une gaffe architecturale de ma part m'a conduit à cela.Polymorphisme d'interface étrangement bizarre à l'aide de la composition d'interface

Mes questions à vous sont:

  • Qu'est-ce que ce appelle exactement?
  • Quelles sont les utilisations dans le monde réel?
  • Pourquoi quelqu'un voudrait-il faire cela?

Voici mes Interfaces:

namespace ThisAndThat 
{ 
    public interface ICanDoThis 
    { 
     string Do(); 
    } 

    public interface ICanDoThat 
    { 
     string Do(); 
    } 

    public interface ICanDoThisAndThat : ICanDoThis, ICanDoThat 
    { 
     new string Do(); 
    } 
} 

Voici ma classe concrète:

namespace ThisAndThat 
{ 
    public class CanDoThisAndThat : ICanDoThisAndThat 
    { 
     public string Do() 
     { 
      return "I Can Do This And That!"; 
     } 

     string ICanDoThis.Do() 
     { 
      return "I Can Do This!"; 
     } 

     string ICanDoThat.Do() 
     { 
      return "I Can Do That!"; 
     } 
    } 
} 

Et mes essais décisifs:

using Xunit; 

namespace ThisAndThat.Tests 
{ 
    public class ThisAndThatTests 
    { 
     [Fact] 
     public void I_Can_Do_This_And_That() 
     { 
      ICanDoThisAndThat sut = new CanDoThisAndThat(); 

      Assert.Equal("I Can Do This And That!", sut.Do()); 
     } 

     [Fact] 
     public void I_Can_Do_This() 
     { 
      ICanDoThis sut = new CanDoThisAndThat(); 

      Assert.Equal("I Can Do This!", sut.Do()); 
     } 

     [Fact] 
     public void I_Can_Do_That() 
     { 
      ICanDoThat sut = new CanDoThisAndThat(); 

      Assert.Equal("I Can Do That!", sut.Do()); 
     } 

    } 
} 

Répondre

5

Il n'y a absolument rien de mal avec ce code (à condition qu'il ne soit pas déroutant pour vos utilisateurs), et ce n'est pas un modèle avec un nom que je connais. CanDoThisAndThat implémente deux interfaces, de sorte que les clients peuvent l'utiliser dans les deux cas.

.NET permet de mettre en œuvre des interfaces de cette façon - connu sous le nom d'implémentation d'interface explicite .

implémentation d'interface explicite est utile lorsque:

  1. Deux interfaces ont la même définition de membre
  2. Vous devez implémenter une interface, mais ne veulent pas connaître qu'un membre particulier est disponible au code client n'a pas déclaré de référence en utilisant le type d'interface

Un exemple de cas 2 du framework .NET est ICollection.SyncLock.List<T> implémente ICollection encore le code suivant ne compile pas parce que le membre a volontairement été « cachée » que les concepteurs de la BCL ne préconise plus de verrouillage collections de cette façon:

List<object> list = new List<object>(); 

lock (list.SyncRoot) // compiler fails here 
{ 
    // ... 
} 

Tout code héritage de ce format fonctionne toujours , parce que la référence est de type ICollection explicitement:

ICollection list = new List<object>(); 

lock (list.SyncRoot) // no problem 
{ 
    // ... 
} 
+0

Merci, en vous donnant la réponse parce que vous avez fourni le nom exact de ce qui se passe. –

+0

Downvoted que vous avez beaucoup d'erreurs dans votre réponse. Tout d'abord, la classe 'CanDoThisAndThat' implémente TROIS interfaces, pas deux. 'ICanDothisAndThat' indique que, lorsqu'il est implémemnted, vous devez également implémenter les deux autres interfaces. En outre, SyncRoot n'est pas explicitement défini par des implémentations pour le cacher afin de décourager l'utilisation, il est généralement fait pour garder les membres rarement utilisés hors du site, ou si le fait qu'une interface soit implémentée n'a pas d'importance. Regardez http://msdn.microsoft.com/en-us/library/system.collections.icollection.syncroot.aspx et http://msdn.microsoft.com/en-us/library/bb356596.aspx. – Andy

+0

Non, dans cette documentation, il est dit que vous ne devriez pas utiliser SyncLock, et qu'ils ne sont pas marqués comme obsolètes, ce qui est vraiment le cas lorsque quelque chose ne devrait plus être utilisé. – Andy

4

Chaque type a un interface mapping (qui peut être récupéré avec Type.GetInterfaceMap si vous étiez nt de le regarder avec réflexion). Cela dit essentiellement, "Lorsque la méthode X sur l'interface Y est invoquée, cette méthode Z est celle à appeler." Notez que même s'il n'est pas supporté en C#, il est possible que la méthode cible de mapping ait un nom différent du nom de la méthode d'interface! (VB soutient explicitement ceci, je crois.)

Dans votre cas, vous avez trois méthodes et chacune des trois méthodes correspond à une méthode dans l'une des interfaces impliquées. Lorsque le compilateur lance un appel à une méthode virtuelle via une interface, l'IL généré dit quelque chose comme "IFoo.Bar appel sur cet objet" - et IFoo.Bar est ensuite résolu en utilisant la carte d'interface.

Vous pouvez parfois avoir besoin de l'utiliser si vous avez des signatures qui ne diffèrent que par le type de retour, ou si vous implémentez deux interfaces hétérogènes qui ont le même nom de méthode mais doivent faire des choses différentes. Où que vous pouvez l'éviter cependant, faites! Cela rend le code très confus.