2009-02-13 9 views
5

Ho finito con qualcosa di simile al seguente codice in un progetto su cui sto lavorando. Ho pensato che fosse davvero strano che mi fosse permesso farlo, ma ora sto iniziando a chiedermi che cosa è più probabile che una raffica architettonica da parte mia mi abbia portato a questo.Polimorfismo di interfaccia stranamente strano che utilizza la composizione di interfaccia

Le mie domande a voi sono:

  • Che cosa esattamente si chiama?
  • Quali sono alcuni usi del mondo reale di questo?
  • Perché qualcuno dovrebbe voler fare questo?

Qui sono i miei Interfacce:

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

    public interface ICanDoThat 
    { 
     string Do(); 
    } 

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

Ecco la mia classe concreta:

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!"; 
     } 
    } 
} 

E la mia superato le prove:

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()); 
     } 

    } 
} 

risposta

5

Non c'è assolutamente nulla di sbagliato in questo codice (purché non sia fonte di confusione per i vostri utenti), e non è un modello con un nome che conosco. CanDoThisAndThat implementa due interfacce, in modo che i client possano utilizzarle in entrambi i modi.

.NET consente alle interfacce di essere implementate in questo modo - nota come implementazione dell'interfaccia esplicita.

implementazione dell'interfaccia esplicita è utile quando:

  1. Due interfacce hanno la stessa definizione membro
  2. È necessario implementare un'interfaccia, ma non vogliono pubblicizzare che un membro particolare, è a disposizione di codice client che non ha dichiarato un riferimento utilizzando il tipo di interfaccia

Un esempio di caso 2 dal framework .NET è ICollection.SyncLock.List<T> implementa ICollection ancora il seguente codice non verrà compilato perché il membro è volutamente stato 'nascosto' come i progettisti della BCL non è più sostenere le collezioni di blocco in questo modo:

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

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

Qualsiasi codice eredità di questo formato sarà ancora lavorare , perché il riferimento è di tipo ICollection esplicitamente:

ICollection list = new List<object>(); 

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

Grazie, ti dà la risposta, perché hai fornito il nome esatto della cosa sta succedendo. –

+0

Downvoted come hai molti errori nella risposta. Innanzitutto, la classe 'CanDoThisAndThat' implementa TRE interfacce, non due. 'ICanDothisAndThat' afferma che, quando viene implementato, è necessario implementare anche le altre due interfacce. Inoltre, SyncRoot non è definito esplicitamente dalle implementazioni per nasconderlo per scoraggiare l'uso, fatto solitamente per tenere i membri raramente utilizzati fuori dal sito, o se il fatto che un'interfaccia sia implementata non ha importanza. Guardare http://msdn.microsoft.com/en-us/library/system.collections.icollection.syncroot.aspx e http://msdn.microsoft.com/en-us/library/bb356596.aspx. – Andy

+0

No, in quella documentazione si dice che non dovresti usare SyncLock, né sono contrassegnati come Obsoleto, che è ciò che è veramente fatto quando qualcosa non dovrebbe più essere usato. – Andy

4

Ogni tipo ha un interface mapping (che può essere recuperato con Type.GetInterfaceMap se si wa per vederlo con la riflessione). Questo in pratica dice "Quando viene invocato il metodo X sull'interfaccia Y, questo è il metodo da chiamare". Nota che anche se non è supportato in C#, è possibile che il metodo di destinazione della mappatura abbia un nome diverso dal nome del metodo dell'interfaccia! (VB supporta esplicitamente questo, credo.)

Nel tuo caso, hai tre metodi e ognuno dei tre metodi corrisponde a un metodo in una delle interfacce coinvolte.

Quando il compilatore invia una chiamata a un metodo virtuale tramite un'interfaccia, l'IL generato genera qualcosa come "chiama IFoo.Bar su questo oggetto" e IFoo.Bar viene quindi risolto utilizzando la mappa di interfaccia.

A volte potrebbe essere necessario utilizzarlo se si dispone di firme che differiscono solo per il tipo restituito o se si stanno implementando due interfacce eterogenee che hanno gli stessi nomi di metodo ma dovrebbero fare cose diverse. Ovunque tu sia il puoi evitare però, fallo! Rende il codice molto confusionario.