2009-03-06 8 views
30

In Programmazione C# di Jesse Liberty (p.142) fornisce un esempio in cui inserisce un oggetto in un'interfaccia.Perché trasmettere a un'interfaccia?

interface IStorable 
{ 
    ... 
} 

public class Document : IStorable 
{ 
    ... 
} 

... 
IStorable isDoc = (IStorable) doc; 
... 

Qual è il punto di questo, in particolare se la classe dell'oggetto implementa comunque l'intefaccia?

Edit1: Per chiarire, io sono interessato al motivo per il cast (se presente), non la ragione per le interfacce di attuazione. Inoltre, il libro è la sua prima edizione del 2001 (basata su C# 1, quindi l'esempio potrebbe non essere pertinente per le versioni successive di C#).

EDIT2: ho aggiunto qualche contesto al codice

+0

La mia prima risposta è stata una conversione definita dall'utente esplicito, ma in realtà in base alle (6.4. 1) non è possibile definire un cast esplicito definito dall'utente su un'interfaccia. –

+0

Ho trovato la sezione equivalente nell'ultima versione, ma sembra che sia un po 'diversa. –

+0

@Jon - grazie per averlo visto su – eft

risposta

15

C'è solo una ragione, quando è effettivamente necessario un cast: Quando doc è di un tipo di base di un vero e proprio oggetto che implementa IStoreable. Lasciatemi spiegare:

public class DocBase 
{ 
    public virtual void DoSomething() 
    { 

    } 
} 

public class Document : DocBase, IStorable 
{ 
    public override void DoSomething() 
    { 
    // Some implementation 
    base.DoSomething(); 
    } 

    #region IStorable Members 

    public void Store() 
    { 
    // Implement this one aswell.. 
    throw new NotImplementedException(); 
    } 

    #endregion 
} 

public class Program 
{ 
    static void Main() 
    { 
    DocBase doc = new Document(); 
    // Now you will need a cast to reach IStorable members 
    IStorable storable = (IStorable)doc; 
    } 
} 

public interface IStorable 
{ 
    void Store(); 
} 
+1

Grazie, ma questo mi confonde un po '. Perché dovresti usare il tipo base DocBase quando crei l'oggetto Document? – eft

+0

Un motivo potrebbe essere che si sta utilizzando una fabbrica per costruire oggetti e restituisce oggetti come tipi di base. –

+2

Ci possono essere un numero qualsiasi di ragioni. Quello che vedo principalmente è l'implementazione di classi base che vengono spedite da terze parti e che non hanno un'interfaccia specificata. In questo caso la classe "DocBase" esce da una libreria diversa e viene implementata con un'interfaccia nel proprio codice. –

15

Se l'oggetto implementa l'interfaccia in modo esplicito (public void IStorable.StoreThis(...)) che casting è il modo più semplice per raggiungere realmente i membri di interfaccia.

+0

c'è un altro modo per raggiungere i membri dell'interfaccia esplicita? – Svish

+3

È l'UNICO modo per raggiungere membri dell'interfaccia esplicitamente implementati. Il motivo per l'implementazione esplicita è che si desidera mantenere la classe più pulita, in quanto forse l'implementazione viene utilizzata raramente. –

19

Perché si desidera limitare solo i metodi forniti dall'interfaccia. Se si utilizza la classe, si rischia di chiamare un metodo (inavvertitamente) che non fa parte dell'interfaccia.

+1

Non puoi fare IStoreable isDoc = doc; se doc è un'istanza di una classe di implementazione di IStorable? Il cast sembra superfluo, a meno che non sto mescolando Java e C#. (Non ho fatto C# in un momento, purtroppo). –

+1

@Devin: Sì, il cast è superfluo finché Doc è dichiarato come un tipo che implementa l'interfaccia. –

+0

Il riferimento stesso non ti impedisce di chiamare qualsiasi metodo al di fuori dell'interfaccia? –

10

È piuttosto difficile da dire senza più del contesto. Se la variabile doc è dichiarata come un tipo che implementa l'interfaccia, il cast è ridondante.

Quale versione del libro stai leggendo? Se è "Programmazione C# 3.0", avrò un'occhiata stasera quando sono a casa.

EDIT: Come abbiamo visto nelle risposte finora, ci sono tre domande potenziali qui:

  • Perché fuso nella dichiarazione indicata nella questione? (Risposta: non è necessario se doc è di tipo appropriato in fase di compilazione)
  • Perché è sempre opportuno eseguire il cast esplicito su un'interfaccia o una classe di base implementate? (Risposta: implementazione dell'interfaccia esplicita come mostrato in un'altra risposta, e anche per il gusto di scegliere un sovraccarico meno specifico quando si passa il valore di cast come argomento.)
  • Perché utilizzare l'interfaccia? (Risposta: lavorare con il tipo di interfaccia significa che siete meno suscettibile a cambiamenti nel tipo di cemento in seguito.)
+0

Anche il cast "(IStorable)" facilita la leggibilità affermando esplicitamente che "doc" NON è un oggetto "IStorable", ma piuttosto qualcosa che può essere lanciato come "IStorable". Anche se superfluo, aggiunge quell'informazione per chiunque stia leggendo il codice in seguito. – DevinB

+3

Non vedo come sia un utile bit di informazione - in effetti, implica (per me) che la variabile doc sia di un tipo che * richiede * un controllo del tempo di esecuzione (che sembra non sia il caso qui) . Questo è fuorviante. –

+0

"(Risposta: non è necessario se il documento IS è di tipo appropriato in fase di compilazione)". Solo una lettera ma fa un mondo di differenza. – BKSpurgeon

3

L'oggetto doc potrebbe essere di un tipo che implementa i membri del IStorable esplicitamente, non li aggiungendo al classi interfaccia primaria (cioè, possono essere chiamate solo tramite l'interfaccia).

In realtà "casting" (utilizzando la sintassi (T)) non ha alcun senso poiché C# gestisce gli upcast (cast sul tipo padre) automaticamente (a differenza di F # per esempio).

0

Il punto è, l'oggetto (dove l'hai ottenuto?) Può non implementare l'interfaccia, nel qual caso viene generata un'eccezione che può essere rilevata e gestita. Ovviamente puoi usare l'operatore "is" per controllare, e l'operatore "as" per lanciare invece del cast in stile C.

+0

Qualcuno potrebbe spiegare perché hai votato? Grazie. – royatl

13

Non sono sicuro in quale contesto l'esempio è stato fornito nel libro. Ma, in genere, puoi digitare il cast di un oggetto da interfacciare per ottenere un'ereditarietà multipla. Ho dato l'esempio qui sotto.

public interface IFoo 
{ 
    void Display(); 
} 
public interface IBar 
{ 
    void Display(); 
} 

public class MyClass : IFoo, IBar 
{ 
    void IBar.Display() 
    { 
     Console.WriteLine("IBar implementation"); 
    } 
    void IFoo.Display() 
    { 
     Console.WriteLine("IFoo implementation"); 
    } 
} 

public static void Main() 
{ 
    MyClass c = new MyClass(); 
    IBar b = c as IBar; 
    IFoo f = c as IFoo; 
    b.Display(); 
    f.Display(); 
    Console.ReadLine(); 
} 

Ciò visualizzare

IBar implementazione
IFoo implementazione

1

Come è stato notato, il getto è superfluo e non necessario. Tuttavia, è una forma più esplicita di codifica che sarebbe utile ai principianti per aiutarli a capire.

In un libro di testo introduttivo, è meglio agire esplicitamente, piuttosto che lasciare che il complatore faccia le cose implicitamente, il che sarebbe più confuso per i principianti.

Il "documento" non è di tipo "IStorable", quindi sarebbe confuso per i principianti vedere che è stato assegnato a un isDoc. Con la fusione esplicita, l'autore (del libro e del codice) sta dicendo che un documento può essere castato su un oggetto IStorable, ma NON È LO STESSO come oggetto IStorable.

2

Ci sono molte buone risposte qui, ma non credo che rispondano PERCHÉ VUOI davvero usare l'interfaccia più restrittiva possibile.

Le ragioni non riguardano la codifica iniziale, coinvolgono la prossima volta che si visita o refactoring il codice - o quando qualcun altro lo fa.

Diciamo che vuoi un pulsante e lo stai posizionando sullo schermo. Hai trovato il pulsante sia passata o da un'altra funzione, in questo modo:

Button x=otherObject.getVisibleThingy(); 
frame.add(x); 

Vi capita di sapere che VisibleThingy un pulsante, esso restituisce un pulsante, quindi tutto è fresco qui (senza getto richiesto).

Ora, diciamo che si refactoring VisibleThingy per restituire un pulsante di attivazione invece. Ora devi refactoring il tuo metodo perché sapevi troppo sull'implementazione.

in quanto è necessario solo i metodi di Component (un genitore sia di bottone e Toggle, che avrebbe potuto essere un'interfaccia - stessa cosa più o meno per i nostri scopi), se avessi scritto che prima linea come questa:

Component x=(Component)otherObject.getVisibleThingy(); 

Non avresti dovuto refactoring nulla - avrebbe appena funzionato.

Questo è un caso molto semplice, ma può essere molto più complesso.

Quindi immagino che il riepilogo sia che un'interfaccia è un modo specifico per "Visualizzare" il tuo oggetto - come guardarlo attraverso un filtro ... puoi vedere solo alcune parti. Se riesci a limitare la tua vista a sufficienza, l'oggetto può "Morph" dietro la tua vista particolare e non influisce su nulla nel tuo mondo attuale - un trucco molto potente di astrazione.

2

Il motivo migliore per cui si eseguirebbe il cast sulle interfacce sarebbe se si scrive codice contro gli oggetti e non si conosce il tipo concreto che sono e non si desidera.

Se si sa che si potrebbe imbattersi in un oggetto che implementa un'interfaccia specifica, è possibile estrarre i valori dall'oggetto senza conoscere la classe concreta di questo oggetto. Inoltre, se si sa che un oggetto implementa una determinata interfaccia, tale interfaccia potrebbe definire metodi che è possibile eseguire per eseguire determinate azioni sull'oggetto.

Ecco un semplice esempio:

public interface IText 
{ 
    string Text { get; } 
} 

public interface ISuperDooper 
{ 
    string WhyAmISuperDooper { get; } 
} 

public class Control 
{ 
    public int ID { get; set; } 
} 

public class TextControl : Control, IText 
{ 
    public string Text { get; set; } 
} 

public class AnotherTextControl : Control, IText 
{ 
    public string Text { get; set; } 
} 

public class SuperDooperControl : Control, ISuperDooper 
{ 
    public string WhyAmISuperDooper { get; set; } 
} 

public class TestProgram 
{ 
    static void Main(string[] args) 
    { 
     List<Control> controls = new List<Control> 
       { 
        new TextControl 
         { 
          ID = 1, 
          Text = "I'm a text control" 
         }, 
        new AnotherTextControl 
         { 
          ID = 2, 
          Text = "I'm another text control" 
         }, 
        new SuperDooperControl 
         { 
          ID = 3, 
          WhyAmISuperDooper = "Just Because" 
         } 
       }; 

     DoSomething(controls); 
    } 

    static void DoSomething(List<Control> controls) 
    { 
     foreach(Control control in controls) 
     { 
     // write out the ID of the control 
     Console.WriteLine("ID: {0}", control.ID); 

     // if this control is a Text control, get the text value from it. 
     if (control is IText) 
      Console.WriteLine("Text: {0}", ((IText)control).Text); 

     // if this control is a SuperDooperControl control, get why 
     if (control is ISuperDooper) 
      Console.WriteLine("Text: {0}", 
       ((ISuperDooper)control).WhyAmISuperDooper); 
     } 
    } 
} 

l'esecuzione di questo piccolo programma vi darebbe il seguente output:

ID: 1

Testo: Sono un controllo di testo

ID: 2

Testo: io sono un altro controllo di testo

ID: 3

Testo: Just Because

Si noti che non ho avuto di scrivere qualsiasi codice nel metodo DoSomething che mi ha richiesto di sapere qualsiasi cosa su tutti gli oggetti su cui stavo lavorando essendo tipi di oggetti concreti. L'unica cosa che so è che sto lavorando su oggetti che sono almeno un'istanza della classe Control. Posso quindi usare l'interfaccia per scoprire cos'altro potrebbero avere.

C'è un milione di motivi diversi per adottare questo approccio con le interfacce sugli oggetti, ma ti dà un modo libero per accedere agli oggetti senza dover sapere esattamente di cosa si tratta.

Pensate a tutte le carte di credito del mondo, ogni azienda fa le proprie, l'interfaccia è la stessa però, così ogni lettore di carte può avere una carta passata attraverso di esso che segue lo standard. Simile all'utilizzo di interfacce.

0

Per consentire la più disaccoppiamento tra pezzi di codice ...

Vedere il seguente articolo per ulteriori: Interfaces