¿Por qué implementar la interfaz de forma explícita?

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 implementarlas 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 para ocultar el miembro no preferido. Por ejemplo, si implementa tanto IComparable como IComparable , generalmente es mejor ocultar la sobrecarga de IComparable 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 CLS, como IConvertible , por lo que si no implementa explícitamente la interfaz, los usuarios finales de idiomas que requieren cumplimiento de CLS no pueden usar su objeto. (Lo cual sería muy desastroso si los ejecutores de BCL no ocultaran los miembros IConvertibles de los primitivos :))

Otra nota interesante es que normalmente el uso de dicho constructo significa que la estructura que implementa explícitamente una interfaz solo puede invocarlos por boxeo al tipo de interfaz. Puede evitar esto mediante el uso de restricciones genéricas ::

 void SomeMethod(T obj) where T:IConvertible 

No encajonará un int cuando le pase uno.

Algunas razones adicionales para implementar una interfaz explícitamente:

Compatibilidad con versiones anteriores : en caso de que la interfaz ICloneable cambie, los miembros de la clase de método de implementación no tienen que cambiar sus firmas de métodos.

código limpiador : habrá un error del comstackdor si el método Clone se elimina de ICloneable; sin embargo, si implementa el método implícitamente, puede terminar con métodos públicos “huérfanos” no utilizados

tipificación fuerte : para ilustrar la historia de supercats con un ejemplo, este sería mi código de muestra preferido, la implementación de ICloneable permite explícitamente que Clone() se escriba fuertemente cuando lo llames directamente como miembro de instancia de MyObject :

 public class MyObject : ICloneable { public MyObject Clone() { // my cloning logic; } object ICloneable.Clone() { return this.Clone(); } } 

Otra técnica útil es hacer que la implementación pública de una función de un método devuelva un valor que es más específico que el especificado en una interfaz.

Por ejemplo, un objeto puede implementar ICloneable, pero aún así su método Clone público visible devuelve su propio tipo.

Del mismo modo, una IAutomobileFactory podría tener un método de fabricación que devuelva un automóvil, pero una FordExplorerFactory, que implementa IAutomobileFactory, podría hacer que su método de fabricación devuelva FordExplorer (que deriva de Automobile). El código que sabe que tiene una FordExplorerFactory podría usar las propiedades específicas de FordExplorer en un objeto devuelto por una FordExplorerFactory sin tener que encasillar, mientras que el código que simplemente sabía que tenía algún tipo de IAutomobileFactory simplemente se ocuparía de su devolución como Automóvil.

También es útil cuando tiene dos interfaces con el mismo nombre de miembro y firma, pero desea cambiar el comportamiento de la misma según cómo se use. (No recomiendo escribir código como este):

 interface Cat { string Name {get;} } interface Dog { string Name{get;} } public class Animal : Cat, Dog { string Cat.Name { get { return "Cat"; } } string Dog.Name { get { return "Dog"; } } } static void Main(string[] args) { Animal animal = new Animal(); Cat cat = animal; //Note the use of the same instance of Animal. All we are doing is picking which interface implementation we want to use. Dog dog = animal; Console.WriteLine(cat.Name); //Prints Cat Console.WriteLine(dog.Name); //Prints Dog } 

Si tiene una interfaz interna y no desea implementar los miembros de su clase públicamente, los implementará explícitamente. Las implementaciones implícitas son públicas.

Puede mantener la interfaz pública más limpia para implementar explícitamente una interfaz, es decir, su clase File podría implementar IDisposable explícitamente y proporcionar un método público Close() que podría tener más sentido para un consumidor que Dispose( ).

F # solo ofrece una implementación de interfaz explícita, por lo que siempre debe realizar un envío a la interfaz en particular para acceder a su funcionalidad, lo que lo convierte en un uso muy explícito (sin pretensiones de ningún tipo) de la interfaz.

Otra razón para la implementación explícita es para la mantenibilidad .

Cuando una clase se “ocupa”, sí sucede, no todos tenemos el lujo de refactorizar el código de otros miembros del equipo, tener una implementación explícita deja en claro que hay un método para satisfacer un contrato de interfaz.

Por lo tanto, mejora la “legibilidad” del código.

En el caso de las interfaces explícitamente definidas, todos los métodos son automáticamente privados, no puede darles acceso al modificador. Suponer:

 interface Iphone{ void Money(); } interface Ipen{ void Price(); } class Demo : Iphone, Ipen{ void Iphone.Money(){ //it is private you can't give public Console.WriteLine("You have no money"); } void Ipen.Price(){ //it is private you can't give public Console.WriteLine("You have to paid 3$"); } } // So you have to cast to call the method class Program { static void Main(string[] args) { Demo d = new Demo(); Iphone i1 = (Iphone)d; i1.Money(); ((Ipen)i1).Price(); Console.ReadKey(); } } // You can't call methods by direct class object 

Así es como podemos crear una interfaz explícita: si tenemos una interfaz 2 y ambas tienen el mismo método y una única clase hereda estas 2 interfaces así que cuando llamamos a un método de interfaz, el comstackdor confundió qué método llamar, para que podamos gestionar este problema utilizando la interfaz explícita. Aquí hay un ejemplo que he dado a continuación.

 using System; using System.Collections.Generic; using System.Linq; using System.Text; using System.Threading.Tasks; namespace oops3 { interface I5 { void getdata(); } interface I6 { void getdata(); } class MyClass:I5,I6 { void I5.getdata() { Console.WriteLine("I5 getdata called"); } void I6.getdata() { Console.WriteLine("I6 getdata called"); } static void Main(string[] args) { MyClass obj = new MyClass(); ((I5)obj).getdata(); Console.ReadLine(); } } } 

Un ejemplo diferente lo da 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 raspa las partes de la interfaz que no tienen ningún significado para sus nuevos tipos.

Concretamente, ImmutableList implementa IList y por lo tanto ICollection (para permitir que ImmutableList se use más fácilmente con código heredado), pero void ICollection.Add(T item) no hace sentido para una ImmutableList : ya que agregar un elemento a una lista inmutable no debe cambiar la lista existente, ImmutableList también deriva de IImmutableList cuya IImmutableList Add(T item) se puede usar para listas inmutables .

Por lo tanto, en el caso de Add , las implementaciones en ImmutableList terminan pareciendo las siguientes:

 public ImmutableList Add(T item) { // Create a new list with the added item } IImmutableList IImmutableList.Add(T value) => this.Add(value); void ICollection.Add(T item) => throw new NotSupportedException(); int IList.Add(object value) => throw new NotSupportedException();