Domanda

Nella programmazione C # di Jesse Liberty (p.142) fornisce un esempio in cui lancia un oggetto su un'interfaccia.

 interface IStorable
 {
    ...
 }

 public class Document : IStorable
 {
    ...
 }

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

Qual è il punto di ciò, in particolare se la classe dell'oggetto implementa comunque l'interfaccia?

EDIT1: per chiarire, sono interessato al motivo del cast (se presente) , non il motivo dell'implementazione delle interfacce. Inoltre, il libro è la sua prima edizione del 2001 (basata su C # 1, quindi l'esempio potrebbe non essere germano per le versioni successive di C #).

EDIT2: ho aggiunto un po 'di contesto al codice

È stato utile?

Soluzione

C'è solo una ragione per cui hai effettivamente bisogno di un cast: quando doc è di un tipo base di un oggetto reale che implementa IStorable. Lasciami 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();
}

Altri suggerimenti

Perché vuoi limitarti ai soli metodi forniti dall'interfaccia. Se usi la classe, corri il rischio di chiamare un metodo (involontariamente) che non fa parte dell'interfaccia.

Se l'oggetto implementa esplicitamente l'interfaccia ( public void IStorable.StoreThis (...) ), il casting è il modo più semplice per raggiungere effettivamente i membri dell'interfaccia.

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

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

Questo verrebbe visualizzato

  

Implementazione IBar
  Implementazione IFoo

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

Quale versione del libro stai leggendo? Se è " Programmazione C # 3.0 " Stasera darò un'occhiata quando sarò a casa.

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

  • Perché trasmettere nella dichiarazione mostrata nella domanda? (Risposta: non è necessario se doc è del tipo di tempo di compilazione appropriato)
  • Perché è mai appropriato eseguire il cast esplicito su un'interfaccia o una classe base implementata? (Risposta: implementazione esplicita dell'interfaccia come mostrato in un'altra risposta, e anche per il bene di scegliere un sovraccarico meno specifico quando si passa il valore del cast come argomento.)
  • Perché usare l'interfaccia? (Risposta: lavorare con il tipo di interfaccia significa che sarai meno suscettibile ai cambiamenti nel tipo concreto in seguito.)

L'oggetto doc potrebbe essere di un tipo che implementa esplicitamente i membri di IStorable , non aggiungendoli all'interfaccia primaria delle classi (cioè, possono essere chiamati solo tramite il l'interfaccia).

In realtà " casting " (usando la sintassi (T)) non ha alcun senso dal momento che C # gestisce automaticamente gli upcast (trasmetti al tipo genitore) (diversamente da F # per esempio).

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

Le ragioni non implicano la tua codifica iniziale, ma coinvolgono la prossima volta che visiti o refactoring il codice - o quando qualcun altro lo fa.

Supponiamo che tu voglia un pulsante e lo stai posizionando sullo schermo. Stai ricevendo il pulsante o passato o da un'altra funzione, in questo modo:

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

Ti capita di sapere che VisibleThingy è un pulsante, restituisce un pulsante, quindi qui tutto è bello (non è richiesto il cast).

Ora, supponiamo che tu rifletti VisibleThingy per restituire invece un pulsante di attivazione / disattivazione. Ora devi riformattare il tuo metodo perché sapevi troppo dell'implementazione.

Poiché hai solo BISOGNO dei metodi in Component (un genitore di entrambi i pulsanti e Toggle, che avrebbe potuto essere un'interfaccia - la stessa cosa praticamente per i nostri scopi), se avessi scritto quella prima riga in questo modo:

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

Non avresti dovuto refactificare nulla: avrebbe semplicemente funzionato.

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

Quindi suppongo che il riassunto sarebbe che un'interfaccia è un modo specifico per " Visualizza " il tuo oggetto - come guardarlo attraverso un filtro ... puoi vedere solo alcune parti. Se riesci a limitare abbastanza la vista, l'oggetto può " Morph " dietro la tua visione particolare e non influenzare nulla nel tuo mondo attuale - un trucco molto potente di astrazione.

Il miglior motivo per cui dovresti eseguire il cast su interfacce sarebbe se stai scrivendo codice contro oggetti e non sai che tipo concreto sono e non vuoi.

Se sai che potresti imbatterti in un oggetto che implementa un'interfaccia specifica, potresti ottenere i valori dall'oggetto senza dover conoscere la classe concreta che è questo oggetto. Inoltre, se sai che un oggetto implementa una determinata interfaccia, quell'interfaccia potrebbe definire metodi che puoi 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 ti darebbe il seguente output:

  

ID: 1

     

Testo: sono un controllo di testo

     

ID: 2

     

Testo: sono un altro controllo di testo

     

ID: 3

     

Testo: solo perché

Nota che non ho dovuto scrivere alcun codice nel metodo DoSomething che mi richiedesse di sapere qualcosa su tutti gli oggetti su cui stavo lavorando come tipi di oggetti concreti. L'unica cosa che so è che sto lavorando su oggetti che sono almeno un'istanza della classe Control. Posso quindi utilizzare l'interfaccia per scoprire cos'altro potrebbero avere.

Esistono milioni di motivi diversi per cui dovresti adottare questo approccio con le interfacce sui tuoi oggetti, ma ti offre un modo semplice per accedere ai tuoi oggetti senza dover sapere esattamente di cosa si tratta.

Pensa a tutte le carte di credito del mondo, ogni azienda ne crea una propria, l'interfaccia è la stessa però, quindi ogni lettore di carte può far scorrere una carta attraverso di essa che segue lo standard. Simile all'utilizzo delle interfacce.

Come è stato notato, il casting è superfluo e non necessario. Tuttavia, è una forma più esplicita di codifica che sarebbe utile ai principianti nel favorire la loro comprensione.

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

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

Il punto è che l'oggetto (dove l'hai preso?) può non implementare l'interfaccia, nel qual caso viene generata un'eccezione che può essere catturata e trattata. Ovviamente puoi usare " è " operatore da verificare, e il "come" operatore al cast invece del cast in stile C.

Per consentire il maggior disaccoppiamento tra parti di codice ...

Per ulteriori informazioni, consultare il seguente articolo: Interfaces

Autorizzato sotto: CC-BY-SA insieme a attribuzione
Non affiliato a StackOverflow
scroll top