Entonces, ¿qué es exactamente un buen caso de uso para implementar una interfaz explícitamente?
¿Es solo para que las personas que usan la clase no tengan que mirar todos esos métodos / propiedades en intellisense?
Si implementa dos interfaces, ambas con el mismo método y diferentes implementaciones, entonces debe implementar explícitamente.
public interface IDoItFast
{
void Go();
}
public interface IDoItSlow
{
void Go();
}
public class JustDoIt : IDoItFast, IDoItSlow
{
void IDoItFast.Go()
{
}
void IDoItSlow.Go()
{
}
}
Es útil ocultar el miembro no preferido. Por ejemplo, si implementa ambos
IComparable<T>
yIComparable
generalmente es mejor ocultar laIComparable
sobrecarga para no dar a la gente la impresión de que puede comparar objetos de diferentes tipos. Del mismo modo, algunas interfaces no son compatibles con CLSIConvertible
, por lo que si no implementa explícitamente la interfaz, los usuarios finales de idiomas que requieren el cumplimiento de CLS no pueden usar su objeto. (Lo cual sería muy desastroso si los implementadores de BCL no ocultaran los miembros IConvertible de las primitivas :))Otra nota interesante es que normalmente el uso de una construcción de este tipo significa que la estructura que implementa explícitamente una interfaz solo puede invocarlos mediante un cuadro al tipo de interfaz. Puede evitar esto utilizando restricciones genéricas ::
No boxeará un int cuando le pase uno.
fuente
string
existía antes de los genéricos y esta práctica estaba de moda. Cuando apareció .net 2 no querían romper la interfaz pública de la,string
así que lo dejaron como estaba con la protección en su lugar.Algunas razones adicionales para implementar una interfaz explícitamente:
compatibilidad con versiones anteriores : en caso de que la
ICloneable
interfaz cambie, los miembros de la clase de método de implementación no tienen que cambiar sus firmas de método.código más limpio : habrá un error de compilación si el
Clone
método se elimina de ICloneable, sin embargo, si implementa el método implícitamente, puede terminar con métodos públicos 'huérfanos' no utilizadosescritura fuerte : para ilustrar la historia de supercat con un ejemplo, este sería mi código de muestra preferido, la implementación
ICloneable
explícitamente permiteClone()
escribir fuertemente cuando lo llamas directamente comoMyObject
miembro de la instancia:fuente
interface ICloneable<out T> { T Clone(); T self {get;} }
. Tenga en cuenta que no existe unaICloneable<T>
restricción deliberada sobre T. Si bien un objeto generalmente solo puede clonarse de manera segura si su base puede serlo, es posible que desee derivar de una clase base que podría clonarse de manera segura un objeto de una clase que no puede. Para permitir eso, recomendaría que las clases heredables no expongan un método de clonación pública. En cambio, tenga clases heredables con unprotected
método de clonación y clases selladas que se deriven de ellas y expongan la clonación pública.Otra técnica útil es hacer que la implementación pública de un método de una función devuelva un valor más específico que el especificado en una interfaz.
Por ejemplo, un objeto puede implementar
ICloneable
, pero aún así suClone
método públicamente visible devuelve su propio tipo.Del mismo modo, un
IAutomobileFactory
podría tener unManufacture
método que devuelve unAutomobile
, pero aFordExplorerFactory
, que implementaIAutomobileFactory
, puede hacer que suManufacture
método devuelva unFordExplorer
(que se deriva deAutomobile
). Código que sabe que tiene unFordExplorerFactory
podría usarFordExplorer
propiedades específicas en un objeto devuelto por aFordExplorerFactory
sin tener que escribirlo, mientras que el código que simplemente sabía que tenía algún tipoIAutomobileFactory
simplemente trataría su retorno como unAutomobile
.fuente
También es útil cuando tiene dos interfaces con el mismo nombre de miembro y firma, pero desea cambiar su comportamiento dependiendo de cómo se use. (No recomiendo escribir código como este):
fuente
public class Animal : Cat, Dog
Puede mantener limpia la interfaz pública para implementar explícitamente una interfaz, es decir, su
File
clase podría implementarIDisposable
explícitamente y proporcionar un método públicoClose()
que podría tener más sentido para un consumidor queDispose(
).F # solo ofrece una implementación explícita de la interfaz, por lo que siempre debe enviar a la interfaz particular para acceder a su funcionalidad, lo que hace que el uso de la interfaz sea muy explícito (sin juego de palabras).
fuente
Dispose
son aquellas que nunca requerirán limpieza); un mejor ejemplo sería algo así como la implementación de una colección inmutableIList<T>.Add
.Si tiene una interfaz interna y no desea implementar públicamente a los miembros de su clase, los implementará explícitamente. Las implementaciones implícitas deben ser públicas.
fuente
Otra razón para la implementación explícita es la mantenibilidad .
Cuando una clase se "ocupa" - sí, sucede, no todos tenemos el lujo de refactorizar el código de otros miembros del equipo - luego tener una implementación explícita deja en claro que existe un método para satisfacer un contrato de interfaz.
Por lo tanto, mejora la "legibilidad" del código.
fuente
#region
es, con una cadena de título apropiada. Y un comentario sobre el método.Un ejemplo diferente es dado por
System.Collections.Immutable
, en el que los autores optaron por utilizar la técnica para preservar una API familiar para los tipos de colección, mientras que eliminan las partes de la interfaz que no tienen ningún significado para sus nuevos tipos.Concretamente,
ImmutableList<T>
los implementosIList<T>
y por lo tantoICollection<T>
( con el fin de permitirImmutableList<T>
a utilizar más fácilmente con código anterior), sin embargo,void ICollection<T>.Add(T item)
no tiene sentido para unImmutableList<T>
: desde la adición de un elemento a una lista inmutable no debe cambiar la lista existente,ImmutableList<T>
también se deriva deIImmutableList<T>
queIImmutableList<T> Add(T item)
se puede utilizar para listas inmutablesPor lo tanto, en el caso de
Add
, las implementacionesImmutableList<T>
terminan teniendo el siguiente aspecto:fuente
En el caso de interfaces explícitamente definidas, todos los métodos son privados de forma automática, no puede darles acceso al modificador de acceso. Suponer:
fuente
Así es como podemos crear una interfaz explícita: si tenemos 2 interfaces y ambas tienen el mismo método y una sola clase hereda estas 2 interfaces, entonces, cuando llamamos a un método de interfaz, el compilador se confundió a qué método llamar, para que podamos maneje este problema usando la interfaz explícita. Aquí hay un ejemplo que he dado a continuación.
fuente