¿Cuáles son las diferencias en la implementación de interfaces implícita y explícitamente en C #?
¿Cuándo debería usar implícito y cuándo debería usar explícito?
¿Hay ventajas o desventajas para uno u otro?
Las pautas oficiales de Microsoft (de la primera edición de las Pautas de diseño de marcos ) establecen que no se recomienda el uso de implementaciones explícitas , ya que le da al código un comportamiento inesperado.
Creo que esta directriz es muy válida en un tiempo anterior a IoC , cuando no pasas las cosas como interfaces.
¿Alguien podría tocar ese aspecto también?
Respuestas:
Implícito es cuando define su interfaz a través de un miembro de su clase. Explícito es cuando define métodos dentro de su clase en la interfaz. Sé que suena confuso, pero esto es lo que quiero decir:
IList.CopyTo
se implementaría implícitamente como:y explícitamente como:
La diferencia es que la implementación implícita le permite acceder a la interfaz a través de la clase que creó al convertir la interfaz como esa clase y como la interfaz misma. La implementación explícita le permite acceder a la interfaz solo convirtiéndola como la interfaz misma.
Utilizo explícitamente principalmente para mantener limpia la implementación, o cuando necesito dos implementaciones. De todos modos, rara vez lo uso.
Estoy seguro de que hay más razones para usar / no usar explícitamente que otros publicarán.
Vea la próxima publicación en este hilo para un excelente razonamiento detrás de cada uno.
fuente
public
palabra clave ... de lo contrario, obtendrá un errorUISwitch IScoreRegPlayerViewCell.markerSwitch { get { return markerSwitch; } }
.IEnumerator<T>.Current
,IEnumerable<T>.GetEnumerator()
yISet<T>.Add(T)
. Esto se menciona en otra respuesta .La definición implícita sería simplemente agregar los métodos / propiedades, etc., exigidos por la interfaz directamente a la clase como métodos públicos.
La definición explícita obliga a los miembros a exponerse solo cuando está trabajando con la interfaz directamente, y no con la implementación subyacente. Esto se prefiere en la mayoría de los casos.
fuente
Cat
estáIEatable
sin objeciones.Además de las excelentes respuestas ya proporcionadas, hay algunos casos en los que se REQUIERE una implementación explícita para que el compilador pueda determinar qué se requiere. Eche un vistazo
IEnumerable<T>
como un excelente ejemplo que probablemente surja con bastante frecuencia.Aquí hay un ejemplo:
Aquí,
IEnumerable<string>
implementosIEnumerable
, por lo tanto, también tenemos que hacerlo. Pero espere, tanto la versión genérica como la normal implementan funciones con la misma firma de método (C # ignora el tipo de retorno para esto). Esto es completamente legal y está bien. ¿Cómo resuelve el compilador cuál usar? Te obliga a tener, como máximo, una definición implícita, luego puede resolver lo que necesite.es decir.
PD: El pequeño fragmento de indirección en la definición explícita de IEnumerable funciona porque dentro de la función el compilador sabe que el tipo real de la variable es una StringList, y así es como resuelve la llamada a la función. Pequeño hecho ingenioso para implementar algunas de las capas de abstracción que algunas de las interfaces principales de .NET parecen haberse acumulado.
fuente
abstract
.Para citar a Jeffrey Richter de CLR a través de C #
( EIMI significa E xplicit I nterface M ethod I mplementation)
Si usa una referencia de interfaz, CUALQUIER cadena virtual se puede reemplazar explícitamente con EIMI en cualquier clase derivada y cuando un objeto de ese tipo se lanza a la interfaz, su cadena virtual se ignora y se llama a la implementación explícita. Eso es todo menos polimorfismo.
Los EIMI también se pueden usar para ocultar miembros de interfaz no fuertemente tipados de implementaciones básicas de Framework Framework como IEnumerable <T> para que su clase no exponga un método no fuertemente tipado directamente, pero es sintácticamente correcto.
fuente
Razón # 1
Tiendo a usar una implementación de interfaz explícita cuando quiero desalentar la "programación para una implementación" ( Principios de diseño de patrones de diseño ).
Por ejemplo, en una aplicación web basada en MVP :
Ahora es menos probable que otra clase (como un presentador ) dependa de la implementación de StandardNavigator y más probable que dependa de la interfaz INavigator (ya que la implementación necesitaría ser convertida en una interfaz para hacer uso del método Redirect).
Razón # 2
Otra razón por la que podría optar por una implementación de interfaz explícita sería mantener limpia la interfaz "predeterminada" de una clase. Por ejemplo, si estuviera desarrollando un control de servidor ASP.NET , podría querer dos interfaces:
Un ejemplo simple sigue. Es un control de cuadro combinado que enumera los clientes. En este ejemplo, el desarrollador de la página web no está interesado en completar la lista; en cambio, solo quieren poder seleccionar un cliente por GUID u obtener el GUID del cliente seleccionado. Un presentador llenaría el cuadro en la carga de la primera página, y este presentador está encapsulado por el control.
El presentador llena la fuente de datos, y el desarrollador de la página web nunca necesita ser consciente de su existencia.
Pero no es una bala de cañón de plata
No recomendaría siempre emplear implementaciones explícitas de interfaz. Esos son solo dos ejemplos en los que podrían ser útiles.
fuente
Además de las otras razones ya mencionadas, esta es la situación en la que una clase está implementando dos interfaces diferentes que tienen una propiedad / método con el mismo nombre y firma.
Este código se compila y se ejecuta correctamente, pero la propiedad Título se comparte.
Claramente, nos gustaría que el valor del Título se devuelva dependiendo de si tratamos la Clase 1 como un Libro o una Persona. Aquí es cuando podemos usar la interfaz explícita.
Tenga en cuenta que las definiciones de interfaz explícitas se infieren como públicas y, por lo tanto, no puede declararlas como públicas (o de otro modo) explícitamente.
Tenga en cuenta también que aún puede tener una versión "compartida" (como se muestra arriba), pero aunque esto es posible, la existencia de dicha propiedad es cuestionable. Tal vez podría usarse como una implementación predeterminada de Título, de modo que el código existente no tenga que modificarse para emitir Class1 a IBook o IPerson.
Si no define el título "compartido" (implícito), los consumidores de Class1 deben emitir explícitamente instancias de Class1 a IBook o IPerson primero; de lo contrario, el código no se compilará.
fuente
Uso la implementación explícita de la interfaz la mayor parte del tiempo. Aquí están las razones principales.
Refactorizar es más seguro
Al cambiar una interfaz, es mejor si el compilador puede verificarlo. Esto es más difícil con implementaciones implícitas.
Me vienen a la mente dos casos comunes:
Agregar una función a una interfaz, donde una clase existente que implementa esta interfaz ya tiene un método con la misma firma que la nueva . Esto puede conducir a un comportamiento inesperado y me ha mordido varias veces. Es difícil "ver" al depurar porque es probable que esa función no se encuentre con los otros métodos de interfaz en el archivo (el problema de autodocumentación mencionado a continuación).
Eliminar una función de una interfaz . Los métodos implementados implícitamente serán de repente código muerto, pero los métodos implementados explícitamente quedarán atrapados por un error de compilación. Incluso si es bueno mantener el código muerto, quiero verme obligado a revisarlo y promocionarlo.
Es lamentable que C # no tenga una palabra clave que nos obligue a marcar un método como una implementación implícita, por lo que el compilador podría hacer las comprobaciones adicionales. Los métodos virtuales no tienen ninguno de los problemas anteriores debido al uso requerido de 'anular' y 'nuevo'.
Nota: para interfaces fijas o que cambian raramente (generalmente de API de proveedores), esto no es un problema. Sin embargo, para mis propias interfaces, no puedo predecir cuándo / cómo cambiarán.
Es autodocumentado
Si veo 'public bool Execute ()' en una clase, me tomará un trabajo extra descubrir que es parte de una interfaz. Alguien probablemente tendrá que comentarlo diciendo eso, o ponerlo en un grupo de otras implementaciones de interfaz, todo bajo una región o un comentario grupal que diga "implementación de ITask". Por supuesto, eso solo funciona si el encabezado del grupo no está fuera de la pantalla.
Mientras que: 'bool ITask.Execute ()' es claro e inequívoco.
Separación clara de la implementación de la interfaz
Pienso que las interfaces son más 'públicas' que métodos públicos porque están diseñadas para exponer solo un poco de la superficie del tipo de concreto. Reducen el tipo a una capacidad, un comportamiento, un conjunto de rasgos, etc. Y en la implementación, creo que es útil mantener esta separación.
Cuando miro el código de una clase, cuando encuentro implementaciones de interfaz explícitas, mi cerebro cambia al modo de "contrato de código". A menudo, estas implementaciones simplemente reenvían a otros métodos, pero a veces harán una comprobación adicional de estado / parámetro, conversión de parámetros entrantes para que coincidan mejor con los requisitos internos, o incluso traducción para propósitos de versionado (es decir, múltiples generaciones de interfaces, todo apuntando a implementaciones comunes).
(Me doy cuenta de que los públicos también son contratos de código, pero las interfaces son mucho más fuertes, especialmente en una base de código impulsada por interfaz donde el uso directo de tipos concretos suele ser un signo de código solo interno).
Relacionado: Razón 2 anterior por Jon .
Y así
Además de las ventajas ya mencionadas en otras respuestas aquí:
Problemas
No todo es diversión y felicidad. Hay algunos casos en los que me quedo con implicidades:
Además, puede ser difícil hacer el casting cuando, de hecho, tiene el tipo concreto y desea llamar a un método de interfaz explícito. Trato esto de una de dos maneras:
public IMyInterface I { get { return this; } }
(que debería estar en línea) y llamefoo.I.InterfaceMethod()
. Si hay varias interfaces que necesitan esta capacidad, expanda el nombre más allá de I (en mi experiencia, es raro que tenga esta necesidad).fuente
Si implementa explícitamente, solo podrá hacer referencia a los miembros de la interfaz a través de una referencia que sea del tipo de la interfaz. Una referencia que es el tipo de la clase implementadora no expondrá esos miembros de la interfaz.
Si su clase de implementación no es pública, excepto por el método utilizado para crear la clase (que podría ser un contenedor de fábrica o IoC ), y excepto por los métodos de interfaz (por supuesto), entonces no veo ninguna ventaja para implementar explícitamente interfaces
De lo contrario, la implementación explícita de interfaces asegura que no se utilicen referencias a su clase de implementación concreta, lo que le permite cambiar esa implementación más adelante. "Asegurarse", supongo, es la "ventaja". Una implementación bien factorizada puede lograr esto sin una implementación explícita.
La desventaja, en mi opinión, es que te encontrarás enviando tipos a / desde la interfaz en el código de implementación que tiene acceso a miembros no públicos.
Como muchas cosas, la ventaja es la desventaja (y viceversa). La implementación explícita de interfaces asegurará que su código de implementación de clase concreto no esté expuesto.
fuente
Una implementación de interfaz implícita es donde tiene un método con la misma firma de la interfaz.
Una implementación de interfaz explícita es donde declaras explícitamente a qué interfaz pertenece el método.
MSDN: implementaciones de interfaz implícitas y explícitas
fuente
Cada miembro de la clase que implementa una interfaz exporta una declaración que es semánticamente similar a la forma en que se escriben las declaraciones de la interfaz VB.NET, por ejemplo
Aunque el nombre del miembro de la clase a menudo coincidirá con el del miembro de la interfaz, y el miembro de la clase a menudo será público, ninguna de esas cosas es necesaria. También se puede declarar:
En cuyo caso, se le permitiría a la clase y sus derivados acceder a un miembro de la clase utilizando el nombre
IFoo_Foo
, pero el mundo exterior solo podría acceder a ese miembro en particular mediante el envío aIFoo
. Este enfoque suele ser bueno en los casos en que un método de interfaz tendrá un comportamiento específico en todas las implementaciones, pero un comportamiento útil solo en algunos [por ejemplo, el comportamiento especificado para elIList<T>.Add
método de una colección de solo lectura es lanzarNotSupportedException
]. Desafortunadamente, la única forma correcta de implementar la interfaz en C # es:No tan bueno
fuente
Un uso importante de la implementación de interfaz explícita es cuando se necesita implementar interfaces con visibilidad mixta .
El problema y la solución se explican bien en el artículo C # Interfaz interna .
Por ejemplo, si desea proteger la fuga de objetos entre las capas de la aplicación, esta técnica le permite especificar una visibilidad diferente de los miembros que podrían causar la fuga.
fuente
Las respuestas anteriores explican por qué implementar una interfaz explícitamente en C # puede ser preferible (por razones principalmente formales). Sin embargo, hay una situación en la que la implementación explícita es obligatoria : para evitar fugas de la encapsulación cuando la interfaz no es
public
, pero la clase de implementación sí lo espublic
.La fuga anterior es inevitable porque, de acuerdo con la especificación C # , "Todos los miembros de la interfaz tienen acceso público implícitamente". Como consecuencia, las implementaciones implícitas también deben dar
public
acceso, incluso si la interfaz en sí es, por ejemplointernal
.La implementación de interfaz implícita en C # es una gran conveniencia. En la práctica, muchos programadores lo usan todo el tiempo / en todas partes sin mayor consideración. Esto conduce a superficies de tipo desordenado en el mejor de los casos y filtración encapsulada en el peor Otros idiomas, como F #, ni siquiera lo permiten .
fuente