Pregunta Interfaces C #. Implementación implícita versus implementación explícita


¿Cuáles son las diferencias en la implementación de interfaces implícitamente y explícitamente Cª#?

¿Cuándo debe usar implícito y cuándo debe usar explícito?

¿Hay ventajas y desventajas para uno u otro?


Pautas oficiales de Microsoft (desde la primera edición Pautas de diseño del marco) Establece que no se recomienda el uso de implementaciones explícitas, ya que le da al código un comportamiento inesperado.

Creo que esta guía es muy válido en un tiempo pre-IoC, cuando no se pasan las cosas como interfaces.

¿Alguien podría tocar ese aspecto también?


558
2017-09-27 10:56


origen


Respuestas:


Implícito es cuando defines tu interfaz a través de un miembro en tu clase. Explícito es cuando defines los métodos dentro de tu clase en la interfaz. Sé que suena confuso, pero esto es lo que quiero decir: IList.CopyTo se implementaría implícitamente como:

public void CopyTo(Array array, int index)
{
    throw new NotImplementedException();
}

y explícitamente como:

void ICollection.CopyTo(Array array, int index)
{
    throw new NotImplementedException();
}

La diferencia es que, implícitamente, se puede acceder a través de la clase que creaste cuando se lanza como esa clase, así como cuando se utiliza como interfaz. La implementación explícita solo le permite acceder cuando se usa como interfaz.

MyClass myClass = new MyClass(); // Declared as concrete class
myclass.CopyTo //invalid with explicit
((IList)myClass).CopyTo //valid with explicit.

Utilizo explícitamente principalmente para mantener la implementación limpia, o cuando necesito dos implementaciones. Pero a pesar de que rara vez lo uso.

Estoy seguro de que hay más razones para usarlo / no usarlo que otros publicarán.

Ver el siguiente publicación en este hilo por un excelente razonamiento detrás de cada uno.


441
2017-09-27 11:07



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 estar expuestos solo cuando trabajas directamente con la interfaz, y no a la implementación subyacente. Esto es preferido en la mayoría de los casos.

  1. Al trabajar directamente con la interfaz, no está reconociendo, y el acoplamiento de su código a la implementación subyacente.
  2. En caso de que ya tenga, por ejemplo, un nombre de propiedad pública en su código y desea implementar una interfaz que también tiene un Nombre la propiedad, hacerlo explícitamente mantendrá los dos separados. Incluso si estuvieran haciendo lo mismo, yo aún delegaría lo explícito llamar a la propiedad Nombre. Nunca se sabe, es posible que desee cambiar cómo funciona el nombre para la clase normal y cómo el nombre, la interfaz la propiedad funciona más adelante.
  3. Si implementa una interfaz implícitamente, su clase ahora expone nuevos comportamientos que solo pueden ser relevantes para un cliente de la interfaz y significa que no estás manteniendo tus clases concisas suficiente (mi opinión).

179
2017-09-27 11:09



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 descubrir qué se requiere. Echa un vistazo a IEnumerable<T> como un buen ejemplo que probablemente surja con bastante frecuencia.

Aquí hay un ejemplo:

public abstract class StringList : IEnumerable<string>
{
    private string[] _list = new string[] {"foo", "bar", "baz"};

    // ...

    #region IEnumerable<string> Members
    public IEnumerator<string> GetEnumerator()
    {
        foreach (string s in _list)
        { yield return s; }
    }
    #endregion

    #region IEnumerable Members
    IEnumerator IEnumerable.GetEnumerator()
    {
        return this.GetEnumerator();
    }
    #endregion
}

Aquí, IEnumerable<string> implementos IEnumerable, por lo tanto, necesitamos hacerlo también. Pero espera, tanto la versión genérica como la normal ambas implementan funciones con la misma firma de método (C # ignora el tipo de devolución para esto). Esto es completamente legal y bueno. ¿Cómo resuelve el compilador cuál usar? Te obliga a tener solo, a lo sumo, una definición implícita, luego puede resolver lo que sea necesario.

es decir.

StringList sl = new StringList();

// uses the implicit definition.
IEnumerator<string> enumerableString = sl.GetEnumerator();
// same as above, only a little more explicit.
IEnumerator<string> enumerableString2 = ((IEnumerable<string>)sl).GetEnumerator();
// returns the same as above, but via the explicit definition
IEnumerator enumerableStuff = ((IEnumerable)sl).GetEnumerator();

PD: la pequeña parte 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. Un pequeño y hábil hecho para implementar algunas de las capas de abstracción que algunas de las interfaces centrales de .NET parecen haber acumulado.


63
2017-09-27 11:59



Razón # 1

Tiendo a utilizar la implementación de interfaz explícita cuando quiero desalentar la "programación a una implementación" (Principios de diseño de patrones de diseño)

Por ejemplo, en un MVPaplicación web basada en la base:

public interface INavigator {
    void Redirect(string url);
}

public sealed class StandardNavigator : INavigator {
    void INavigator.Redirect(string url) {
        Response.Redirect(url);
    }
}

Ahora otra clase (como una presentador) es menos probable que dependa de la implementación de StandardNavigator y es más probable que dependa de la interfaz de INavigator (ya que la implementación debería enviarse a una interfaz para hacer uso del método Redirect).

Razón # 2

Otra razón por la que podría ir con una implementación de interfaz explícita sería mantener limpia la interfaz "predeterminada" de una clase. Por ejemplo, si estuviera desarrollando un ASP.NET control del servidor, es posible que desee dos interfaces:

  1. La interfaz principal de la clase, que es utilizada por los desarrolladores de páginas web; y
  2. Una interfaz "oculta" utilizada por el presentador que desarrollé para manejar la lógica del control

Un simple ejemplo sigue. Es un control de cuadro combinado que enumera a los clientes. En este ejemplo, el desarrollador de la página web no está interesado en rellenar la lista; en su lugar, solo desean poder seleccionar un cliente por GUID o para obtener el GUID del cliente seleccionado. Un presentador llenaría la casilla en la primera página cargada, y este presentador está encapsulado por el control.

public sealed class CustomerComboBox : ComboBox, ICustomerComboBox {
    private readonly CustomerComboBoxPresenter presenter;

    public CustomerComboBox() {
        presenter = new CustomerComboBoxPresenter(this);
    }

    protected override void OnLoad() {
        if (!Page.IsPostBack) presenter.HandleFirstLoad();
    }

    // Primary interface used by web page developers
    public Guid ClientId {
        get { return new Guid(SelectedItem.Value); }
        set { SelectedItem.Value = value.ToString(); }
    }

    // "Hidden" interface used by presenter
    IEnumerable<CustomerDto> ICustomerComboBox.DataSource { set; }
}

El presentador rellena la fuente de datos, y el desarrollador de la página web nunca necesita estar al tanto de su existencia.

Pero no es una bala de cañón plateada

No recomendaría siempre emplear implementaciones de interfaz explícitas. Esos son solo dos ejemplos donde pueden ser útiles.


28
2018-06-14 01:31



Para citar a Jeffrey Richter de CLR a través de C #
(EIMI medio mixplicit yonterface METROethod yoimplementación)

Es críticamente importante para ti   entender algunas ramificaciones que   existen cuando se usan EIMI. Y a causa de   estas ramificaciones, debes intentar   evite los EIMI tanto como sea posible.   Afortunadamente, las interfaces genéricas ayudan   evitas EIMI bastante. Pero hay   aún puede haber momentos en los que necesitará   usarlos (como implementar dos   métodos de interfaz con el mismo nombre   y firma). Aquí están los grandes   problemas con EIMIs:

  • No hay documentación que explique cómo un tipo específicamente   implementa un método EIMI, y allí   no es Microsoft Visual Studio    IntelliSense apoyo.
  • Las instancias de tipo de valor están encuadradas cuando se lanzan a una interfaz.
  • Un EIMI no puede ser llamado por un tipo derivado.

Si utiliza una referencia de interfaz, CUALQUIER cadena virtual puede ser reemplazada explícitamente por EIMI en cualquier clase derivada y cuando un objeto de ese tipo es enviado a la interfaz, su cadena virtual es ignorada y se llama a la implementación explícita. Eso es todo menos el polimorfismo.

Los EIMI también se pueden utilizar para ocultar miembros de la interfaz no fuertemente tipados de las implementaciones básicas de Framework Framework, como IEnumerable <T>, por lo que su clase no expone un método no fuertemente tipado directamente, pero es sintácticamente correcto.


28
2018-02-09 18:40



Además de las otras razones ya indicadas, esta es la situación en la que una clase implementa dos interfaces diferentes que tienen una propiedad / método con el mismo nombre y firma.

/// <summary>
/// This is a Book
/// </summary>
interface IBook
{
    string Title { get; }
    string ISBN { get; }
}

/// <summary>
/// This is a Person
/// </summary>
interface IPerson
{
    string Title { get; }
    string Forename { get; }
    string Surname { get; }
}

/// <summary>
/// This is some freaky book-person.
/// </summary>
class Class1 : IBook, IPerson
{
    /// <summary>
    /// This method is shared by both Book and Person
    /// </summary>
    public string Title
    {
        get
        {
            string personTitle = "Mr";
            string bookTitle = "The Hitchhikers Guide to the Galaxy";

            // What do we do here?
            return null;
        }
    }

    #region IPerson Members

    public string Forename
    {
        get { return "Lee"; }
    }

    public string Surname
    {
        get { return "Oades"; }
    }

    #endregion

    #region IBook Members

    public string ISBN
    {
        get { return "1-904048-46-3"; }
    }

    #endregion
}

Este código se compila y se ejecuta correctamente, pero la propiedad Título se comparte.

Claramente, desearíamos que el valor del Título devuelto dependa de si estamos tratando a Class1 como un Libro o una Persona. Aquí es cuando podemos usar la interfaz explícita.

string IBook.Title
{
    get
    {
        return "The Hitchhikers Guide to the Galaxy";
    }
}

string IPerson.Title
{
    get
    {
        return "Mr";
    }
}

public string Title
{
    get { return "Still shared"; }
}

Tenga en cuenta que las definiciones de interfaz explícita se infieren como públicas, y por lo tanto no puede declarar que sean públicas (o de otro modo) explícitamente.

Tenga en cuenta también que todavía puede tener una versión "compartida" (como se muestra arriba), pero mientras esto sea posible, la existencia de dicha propiedad es cuestionable. Tal vez se podría utilizar como una implementación predeterminada de Título, de modo que el código existente no tendría que modificarse para convertir Class1 en IBook o IPerson.

Si no define el título "compartido" (implícito), los consumidores de Class1 debe explícitamente las instancias de Class1 a IBook o IPerson primero; de lo contrario, el código no se compilará.


17
2017-10-01 13:08



Uso la implementación de interfaz explícita la mayor parte del tiempo. Aquí están los principales motivos.

Refactorizar es más seguro

Al cambiar una interfaz, es mejor si el compilador puede verificarla. Esto es más difícil con implementaciones implícitas.

Dos casos comunes vienen a la mente:

  • 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 el nuevo. Esto puede conducir a un comportamiento inesperado y me ha picado muchas veces. Es difícil "ver" cuando se realiza la depuración porque es probable que esa función no se encuentre con los otros métodos de interfaz en el archivo (el problema de la auto documentación que se menciona a continuación).

  • Eliminar una función de una interfaz. Los métodos implementados implícitamente serán repentinamente código muerto, pero los métodos implementados explícitamente quedarán atrapados por el error de compilación. Incluso si el código muerto es bueno para mantener, me obligan a revisarlo y promocionarlo.

Es desafortunado 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 obligatorio de 'anular' y 'nuevo'.

Nota: para interfaces fijas o que cambian raramente (típicamente de las API de proveedores), esto no es un problema. Para mis propias interfaces, sin embargo, no puedo predecir cuándo / cómo van a cambiar.

Es autodocumentado

Si veo 'public bool Execute ()' en una clase, va a tomar un trabajo extra para descubrir que es parte de una interfaz. Alguien probablemente tenga que comentarlo, o decirlo en un grupo de otras implementaciones de interfaz, todo bajo un comentario de región o agrupación que diga "implementación de ITask". Por supuesto, eso solo funciona si el encabezado del grupo no está fuera de la pantalla ...

Considerando que: 'bool ITask.Execute ()' es claro y no ambiguo.

Separación clara de la implementación de la interfaz

Creo que las interfaces son más "públicas" que los métodos públicos porque están diseñadas para exponer solo un poco del área de la superficie del tipo 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.

Mientras busco 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 verificación de estado / param adicional, conversión de parámetros entrantes para adecuar mejor los requisitos internos, o incluso traducción para propósitos de creación de versiones (es decir, múltiples generaciones de interfaces que bajan 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 sólidas, 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 es todo diversión y felicidad. Hay algunos casos en los que me quedo con implicits:

  • Tipos de valores, porque eso requerirá boxeo y menor rendimiento. Esta no es una regla estricta, y depende de la interfaz y de cómo se debe usar. IComparable? Implícito. IFormattable? Probablemente explícito.
  • Interfaces triviales del sistema que tienen métodos que frecuentemente se llaman directamente (como IDisposable.Dispose).

Además, puede ser difícil hacer el casting cuando de hecho tienes el tipo concreto y quieres llamar a un método de interfaz explícito. Me ocupo de esto de dos maneras:

  1. Agregue públicos y reenvíe los métodos de interfaz para la implementación. Suele pasar con interfaces más simples cuando se trabaja internamente.
  2. (Mi método preferido) Agregue un public IMyInterface I { get { return this; } } (que debe estar en línea) y llamar 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).

10
2017-08-15 08:27



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 sea del tipo de la clase implementadora no expondrá esos miembros de la interfaz.

Si su clase implementadora no es pública, excepto por el método utilizado para crear la clase (que podría ser una fábrica o IoC contenedor), y a excepción de los métodos de interfaz (por supuesto), entonces no veo ninguna ventaja para la implementación explícita de interfaces.

De lo contrario, la implementación explícita de las interfaces garantiza que no se utilicen las referencias a su clase concreta de implementación, lo que le permite cambiar esa implementación más adelante. "Se asegura", 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 se encontrará lanzando tipos a / desde la interfaz en el código de implementación que sí 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 concreta no quede expuesto.


8
2017-09-27 11:12



Una implementación de interfaz implícita es donde tienes 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.

interface I1
{
    void implicitExample();
}

interface I2
{
    void explicitExample();
}


class C : I1, I2
{
    void implicitExample()
    {
        Console.WriteLine("I1.implicitExample()");
    }


    void I2.explicitExample()
    {
        Console.WriteLine("I2.explicitExample()");
    }
}

MSDN: implementaciones de interfaces implícitas y explícitas


6
2018-05-26 14:12