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 en C#?

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

¿Hay pros y/o contras en uno u otro?


Las directrices oficiales de Microsoft (de la primera edición Framework Design Guidelines) establecen que no se recomienda el uso de implementaciones explícitas, ya que da al código un comportamiento inesperado.

I piensa que esta guía es muy válida en un tiempo pre-IoC, cuando no pasas cosas como interfaces.

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

Author: Guillermo Gutiérrez, 2008-09-27

11 answers

Implicit es cuando defines tu interfaz a través de un miembro de tu clase. Explicit 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:

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 es accesible a través de su clase que creó cuando se cast como esa clase, así como cuando se cast como la interfaz. Expresa la implementación permite que solo sea accesible cuando se emite como la interfaz en sí.

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

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

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

Ver el siguiente post en este hilo para un excelente razonamiento detrás de cada uno.

 447
Author: mattlant,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/ajaxhispano.com/template/agent.layouts/content.php on line 61
2017-05-23 12:26:32

La definición implícita sería simplemente agregar los métodos / propiedades, etc. exigido por la interfaz directamente a la clase como métodos públicos.

La definición explícita obliga a exponer los miembros solo cuando se trabaja directamente con la interfaz, y no con la implementación subyacente. Esto se prefiere 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 el evento que ya tiene, por ejemplo, un nombre de propiedad pública en su código y desea implementar una interfaz que también tiene un Nombre propiedad, hacerlo explícitamente mantendrá los dos separados. Incluso si estuvieran haciendo lo mismo todavía delegaría la explícita llamar a la propiedad Name. Nunca se sabe, es posible que desee cambiar cómo funciona Name para la clase normal y cómo Name, la interfaz la propiedad funciona más adelante.
  3. Si implementa una interfaz implícitamente, su clase ahora expone nuevo comportamientos que solo podrían ser relevantes para un cliente del interfaz y significa que no estás manteniendo tus clases sucintas suficiente (mi opinión).
 180
Author: Phil Bennett,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/ajaxhispano.com/template/agent.layouts/content.php on line 61
2017-12-04 18:32:55

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 averiguar lo que se requiere. Echa un vistazo a IEnumerable<T> como un ejemplo excelente que probablemente aparecerá 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> implementa IEnumerable, por lo tanto necesitamos demasiado. Pero espera, tanto la versión genérica como la normal implementan funciones con la misma firma de método (C # ignora tipo de retorno para esto). Esto es completamente legal y está bien. ¿Cómo resuelve el compilador cuál usar? Te obliga a tener solo, a lo sumo, una definición implícita, entonces puede resolver lo que sea necesario.

Ie.

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 deerableumerable funciona porque dentro de la función el compilador sabe que el tipo real de la variable es una lista de cadenas, y así es como resuelve la llamada a la función. Pequeño dato ingenioso para implementación de algunas de las capas de abstracción algunas de las interfaces. NET core parecen haberse acumulado.

 63
Author: Matthew Scharley,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/ajaxhispano.com/template/agent.layouts/content.php on line 61
2014-07-07 11:09:44

Razón #1

Tiendo a usar la implementación de interfaz explícita cuando quiero desalentar la "programación a una implementación" (Principios de Diseño a Partir de Patrones de Diseño).

Por ejemplo, en una aplicación web basada en MVP:

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

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

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

Razón # 2

Otra razón por la que podría ir con una implementación de interfaz explícita sería para mantener la interfaz "predeterminada" de una clase más limpia. Por ejemplo, si estuviera desarrollando un ASP.NET control de servidor, podría querer 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 desarrollo para manejar la la lógica del control

Un ejemplo simple 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 quiere 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 es 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 ser consciente de su existencia.

Pero No es una Bala de Cañón de Plata

No recomendaría siempre emplear implementaciones de interfaz explícitas. Estos son solo dos ejemplos en los que podrían ser útiles.

 29
Author: Jon Nadal,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/ajaxhispano.com/template/agent.layouts/content.php on line 61
2016-05-23 17:17:00

Para citar a Jeffrey Richter de CLR vía C#
(EIMI medio Explicit Interface Mmétodo Implantación)

Es críticamente importante que usted entender algunas ramificaciones que existe cuando se usa EIMIs. Y debido a estas ramificaciones, usted debe tratar de evite EIMIs tanto como sea posible. Afortunadamente, las interfaces genéricas ayudan evitas a EIMIs bastante. Pero hay todavía puede haber momentos en los que necesitará utilizar (como la implementación de 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 Soporte IntelliSense.
  • Las instancias de tipo de valor se encajonan cuando se envían a una interfaz.
  • Un EIMI no puede ser llamado por un tipo derivado.

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

EIMIs también se puede usar para ocultar miembros de interfaces no fuertemente tipados de implementaciones de Interfaces de framework básicas comoEnumerable para que su clase no exponga un método no fuertemente tipado directamente, sino que sea sintáctico correcto.

 29
Author: Valentin Kuzub,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/ajaxhispano.com/template/agent.layouts/content.php on line 61
2016-05-23 17:22:41

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.

/// <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 compila y se ejecuta bien, pero la propiedad Title es compartida.

Claramente, querríamos que el valor de Title dependiera de si estábamos tratando a Class1 como un Libro o como 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"; }
}

Observe que el explícito se infiere que las definiciones de la interfaz son Públicas y, por lo tanto, no puede declararlas 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 aunque esto es posible, la existencia de tal propiedad es cuestionable. Tal vez podría usarse como una implementación predeterminada de Title, para que el código existente no tenga que modificarse para convertir Class1 a iBook o IPerson.

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

 17
Author: Lee Oades,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/ajaxhispano.com/template/agent.layouts/content.php on line 61
2016-05-23 17:12:57

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

La refactorización es más segura

Al cambiar una interfaz, es mejor si el compilador puede comprobarla. 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 la nueva one . Esto puede llevar a un comportamiento inesperado, y me ha mordido duro 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 auto-documentación mencionado 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 serán atrapados por un error de compilación. Incluso si el código muerto es bueno para quédate, quiero que me obliguen 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 'override' y 'new'.

Nota: para interfaces fijas o que rara vez cambian (normalmente de API de proveedores), esto no es un problema. Para mis propias interfaces, sin embargo, no puedo predecir cuándo y cómo van a cambiar.

Es auto-documentación

Si veo 'public bool Execute()' en una clase, va a tomar trabajo extra descubrir que es parte de una interfaz. Probablemente alguien tendrá que comentarlo diciéndolo, o ponerlo en un grupo de otras implementaciones de interfaz, todas bajo un comentario de región o agrupación diciendo "implementación de ITask". Por supuesto, eso solo funciona si el encabezado del grupo no está fuera de la pantalla..

Considerando: 'bool ITask.Execute()' es claro e inequívoco.

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 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 estoy mirando a través del código de una clase, cuando me encuentro con una interfaz explícita implementaciones, mi cerebro cambia al modo de "contrato de código". A menudo, estas implementaciones simplemente reenvían a otros métodos, pero a veces hacen comprobaciones adicionales de estado/parámetros, conversión de parámetros entrantes para que coincidan mejor con los requisitos internos, o incluso traducción para fines de control de versiones (es decir, múltiples generaciones de interfaces que se dirigen 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 un base de código basada en interfaces donde el uso directo de tipos concretos suele ser un signo de código solo interno.)

Relacionado: Razón 2 arriba por Jon .

Y así sucesivamente

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 implicitos:

  • Tipos de valor, porque eso requerirá boxeo y menor perf. Esta no es una regla estricta, y depende de la interfaz y cómo se pretende que se use. ¿Comparable? Implícita. IFormattable? Probablemente explícito.
  • Interfaces de sistema triviales que tienen métodos que se llaman con frecuencia directamente (como Imposible.Disponer).

Además, puede ser una molestia hacer el casting cuando de hecho tiene el tipo concreto y desea llamar a un método de interfaz explícito. Trato con esto de una de dos maneras:

  1. Agregue publics y haga que los métodos de interfaz les envíen para la implementación. Normalmente sucede con interfaces más simples cuando se trabaja internamente.
  2. (Mi método preferido) Agrega un public IMyInterface I { get { return this; } } (que debería estar en línea) y llama a foo.I.InterfaceMethod(). Si múltiples interfaces que necesitan esta capacidad, ampliar el nombre más allá de mí (en mi experiencia es raro que tenga esta necesidad).
 11
Author: scobi,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/ajaxhispano.com/template/agent.layouts/content.php on line 61
2017-05-23 12:34:41

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 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 una fábrica o contenedor IoC), y excepto por los métodos de interfaz (por supuesto), entonces no veo ninguna ventaja para explícitamente implementación de interfaces.

De lo contrario, la implementación explícita de interfaces se asegura de que las referencias a su clase de implementación concreta no se utilicen, lo que le permite cambiar esa implementación en un momento posterior. "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 te encontrarás lanzando tipos hacia / desde la interfaz en el código de implementación que lo hace tener 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 clases concretas no esté expuesto.

 8
Author: Bill,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/ajaxhispano.com/template/agent.layouts/content.php on line 61
2016-07-17 18:28:25

Una implementación de interfaz implícita es donde se tiene un método con la misma firma de la interfaz.

Una implementación de interfaz explícita es donde se declara 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
Author: Yochai Timmer,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/ajaxhispano.com/template/agent.layouts/content.php on line 61
2011-05-26 14:12:52

Cada miembro de la clase que implementa una interfaz exporta una declaración que es semánticamente similar a la forma VB.NET las declaraciones de interfaz se escriben, por ejemplo,

Public Overridable Function Foo() As Integer Implements IFoo.Foo

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 requerida. También se puede declarar:

Protected Overridable Function IFoo_Foo() As Integer Implements IFoo.Foo

En cuyo caso se permitiría a la clase y sus derivadas 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 echando a IFoo. Este enfoque es a menudo bueno en los casos en que un método de interfaz tendrá especificado comportamiento en todas las implementaciones, pero útil comportamiento en solo algunos [por ejemplo, el comportamiento especificado para el método IList<T>.Add de una colección de solo lectura es lanzar NotSupportedException]. Desafortunadamente, la única manera adecuada de implementar la interfaz en C# es:

int IFoo.Foo() { return IFoo_Foo(); }
protected virtual int IFoo_Foo() { ... real code goes here ... }

No tan agradable.

 5
Author: supercat,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/ajaxhispano.com/template/agent.layouts/content.php on line 61
2016-05-23 17:29:12

Un uso importante de la implementación de interfaces explícitas es cuando se necesita implementar interfaces con visibilidad mixta.

El problema y la solución están bien explicados en el artículo Interfaz Interna de C# .

Por ejemplo, si desea proteger la fuga de objetos entre capas de aplicación, esta técnica le permite especificar una visibilidad diferente de los miembros que podrían causar la fuga.

 1
Author: nrodic,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/ajaxhispano.com/template/agent.layouts/content.php on line 61
2016-05-23 17:27:47