Pregunta

En la Programación C # de Jesse Liberty (p.142), proporciona un ejemplo en el que proyecta un objeto a una interfaz.

 interface IStorable
 {
    ...
 }

 public class Document : IStorable
 {
    ...
 }

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

¿Cuál es el punto de esto, en particular si la clase del objeto implementa la interfaz de todos modos?

EDIT1: Para aclarar, estoy interesado en la razón para la conversión (si existe) , no la razón para implementar interfaces. Además, el libro es su primera edición de 2001 (basada en C # 1, por lo que es posible que el ejemplo no sea pertinente para versiones posteriores de C #).

EDIT2: agregué algo de contexto al código

¿Fue útil?

Solución

Hay solo una razón por la que realmente necesita una conversión: cuando doc es del tipo base de un objeto real que implementa IStorable. Déjame explicarte:

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

Otros consejos

Porque desea restringirse solo a los métodos proporcionados por la interfaz. Si utiliza la clase, corre el riesgo de llamar a un método (inadvertidamente) que no forma parte de la interfaz.

Si el objeto implementa la interfaz explícitamente ( public void IStorable.StoreThis (...) ), la conversión es la forma más fácil de llegar a los miembros de la interfaz.

No estoy seguro en qué contexto se dio el ejemplo en el libro. Pero, por lo general, puede escribir un objeto para que se interconecte y lograr una herencia múltiple. He dado el siguiente ejemplo.

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

Esto mostraría

  

Implementación IBar
  Implementación de IFoo

Es bastante difícil decirlo sin más contexto. Si se declara que la variable doc es un tipo que implementa la interfaz, entonces la conversión es redundante.

¿Qué versión del libro estás leyendo? Si es " Programación C # 3.0 " Voy a echar un vistazo esta noche cuando esté en casa.

EDITAR: Como hemos visto en las respuestas hasta ahora, hay tres posibles preguntas aquí:

  • ¿Por qué emitir en la declaración que se muestra en la pregunta? (Respuesta: no tiene que hacerlo si doc es del tipo de tiempo de compilación adecuado)
  • ¿Por qué es apropiado convertir explícitamente a una interfaz o clase base implementada? (Respuesta: implementación de la interfaz explícita como se muestra en otra respuesta, y también por el hecho de elegir una sobrecarga menos específica cuando se pasa el valor de conversión como un argumento).
  • ¿Por qué usar la interfaz? (Respuesta: trabajar con el tipo de interfaz significa que será menos susceptible a cambios en el tipo concreto más adelante).
El objeto

doc puede ser de un tipo que implemente miembros de IStorable explícitamente, no agregándolos a la interfaz principal de las clases (es decir, solo se pueden llamar a través de interfaz).

En realidad " casting " (el uso de la sintaxis (T)) no tiene ningún sentido, ya que C # maneja upcasts (conversión a tipo padre) automáticamente (a diferencia de F #, por ejemplo).

Hay muchas respuestas buenas aquí, pero realmente no creo que respondan POR QUÉ QUIERES usar la interfaz más restrictiva posible.

Las razones no involucran su codificación inicial, involucran la próxima vez que visite o refactorice el código, o cuando alguien más lo haga.

Digamos que quieres un botón y lo colocas en tu pantalla. Se está pasando el botón ya sea desde o hacia otra función, como esta:

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

Por casualidad, sabes que VisibleThingy es un botón, devuelve un botón, por lo que todo está bien aquí (no se requiere conversión).

Ahora, digamos que refactoriza VisibleThingy para devolver un botón de alternar en su lugar. Ahora tiene que refactorizar su método porque sabía demasiado sobre la implementación.

Ya que solo NECESITAS los métodos en Componente (uno de los padres del botón y Toggle, que podría haber sido una interfaz, lo mismo para nuestros propósitos), si hubieras escrito esa primera línea así:

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

No habrías tenido que refactorizar nada, simplemente hubiera funcionado.

Este es un caso muy simple, pero puede ser mucho más complejo.

Entonces, supongo que el resumen sería que una interfaz es una forma específica de " Ver " su objeto, como mirarlo a través de un filtro ... solo puede ver algunas partes. Si puede restringir su vista lo suficiente, el objeto puede " Morph " detrás de su vista particular y no afecte nada en su mundo actual, un truco muy poderoso de abstracción.

La mejor razón por la que convertirías a las interfaces sería si escribes código contra objetos y no sabes qué tipo concreto son y no quieres.

Si sabe que puede encontrarse con un objeto que implementa una interfaz específica, puede sacar los valores del objeto sin tener que conocer la clase concreta de este objeto. Además, si sabe que un objeto implementa una interfaz determinada, esa interfaz podría definir métodos que puede ejecutar para realizar ciertas acciones en el objeto.

Aquí hay un ejemplo simple:

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

ejecutar este pequeño programa te daría el siguiente resultado:

  

ID: 1

     

Texto: soy un control de texto

     

ID: 2

     

Texto: soy otro control de texto

     

ID: 3

     

Texto: Sólo porque

Tenga en cuenta que no tuve que escribir ningún código en el método DoSomething que requería que supiera nada sobre todos los objetos en los que estaba trabajando para ser tipos de objetos concretos. Lo único que sé es que estoy trabajando en objetos que son al menos una instancia de la clase Control. Luego puedo usar la interfaz para averiguar qué más podrían tener.

Hay un millón de razones diferentes por las que tomaría este enfoque con interfaces en sus objetos, pero le brinda una manera flexible de acceder a sus objetos sin tener que saber exactamente qué es.

Piense en todas las tarjetas de crédito en el mundo, cada compañía crea las suyas, aunque la interfaz es la misma, por lo que cada lector de tarjetas puede pasar una tarjeta que sigue el estándar. Similar al uso de interfaces.

Como se ha señalado, el casting es superfluo y no es necesario. Sin embargo, es una forma más explícita de codificación que sería útil para los principiantes para ayudarlos a comprenderlos.

En un libro de texto introductorio, es mejor actuar explícitamente, en lugar de dejar que el compilador haga las cosas de manera implícita, lo que sería más confuso para los principiantes.

El " doc " no es del tipo " IStorable " por lo que sería un poco confuso para los principiantes ver que se asigna a un isDoc. Al emitir explícitamente, el autor (del libro y del código) está diciendo que un documento se puede convertir en un objeto IStorable, pero NO ES LO MISMO como un objeto IStorable.

El punto es que el objeto (¿de dónde lo sacaste?) puede no implementar la interfaz, en cuyo caso se lanza una excepción que puede detectarse y solucionarse. Por supuesto que puedes usar el " es " operador para verificar, y el " como " Operador para emitir en lugar del reparto de estilo C.

Para permitir el mayor desacoplamiento entre piezas de código ...

Vea el siguiente artículo para más información: Interfaces

Licenciado bajo: CC-BY-SA con atribución
No afiliado a StackOverflow
scroll top