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:
- Tipos de valor, porque eso requerirá boxeo y menor rendimiento. Esta no es una regla estricta, y depende de la interfaz y de cómo debe usarse. IComparable? Implícito. IFormattable? Probablemente explícito.
- Interfaces triviales del sistema que tienen métodos que con frecuencia se llaman directamente (como IDisposable.Dispose).
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:
- Agregue públicos y envíe los métodos de interfaz para la implementación. Normalmente ocurre con interfaces más simples cuando se trabaja internamente.
- (Mi método preferido) Agregue un
public IMyInterface I { get { return this; } }
(que debería estar en línea) y llame foo.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).