tutorial - poo interface c#




Interfaces C#. Implementação implícita versus implementação explícita (8)

Quais são as diferenças na implementação de interfaces implícita e explicitamente em C #?

Quando você deve usar implícito e quando deve usar explícito?

Existe algum prós e / ou contras para um ou outro?

As diretrizes oficiais da Microsoft (da primeira edição do Framework Design Guidelines ) afirmam que o uso de implementações explícitas não é recomendado , uma vez que dá ao código um comportamento inesperado.

Eu acho que esta diretriz é muito válida em um tempo pré-IoC , quando você não passa as coisas como interfaces.

Alguém poderia tocar nesse aspecto também?


Razão # 1

Eu costumo usar a implementação de interface explícita quando eu quero desencorajar "programação para uma implementação" ( Princípios de Design de Design Patterns ).

Por exemplo, em um aplicativo da Web baseado em MVP :

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

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

Agora, uma outra classe (como um presenter ) é menos provável de depender da implementação do StandardNavigator e é mais provável que dependa da interface do INavigator (já que a implementação precisaria ser convertida em uma interface para fazer uso do método Redirect).

Razão # 2

Outra razão pela qual eu poderia ir com uma implementação de interface explícita seria manter o limpador de interface "padrão" de uma classe. Por exemplo, se eu estivesse desenvolvendo um controle de servidor ASP.NET , talvez eu queira duas interfaces:

  1. A interface principal da classe, que é usada pelos desenvolvedores de páginas da Web; e
  2. Uma interface "oculta" usada pelo apresentador que desenvolvo para lidar com a lógica do controle

Um exemplo simples segue. É um controle de caixa de combinação que lista os clientes. Neste exemplo, o desenvolvedor da página da Web não está interessado em preencher a lista; em vez disso, eles só querem poder selecionar um cliente pelo GUID ou obter o GUID do cliente selecionado. Um apresentador preencheria a caixa no primeiro carregamento da página e este apresentador é encapsulado pelo controle.

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; }
}

O apresentador preenche a fonte de dados e o desenvolvedor da página nunca precisa estar ciente de sua existência.

Mas não é uma bala de prata

Eu não recomendaria sempre empregar implementações explícitas de interface. Esses são apenas dois exemplos em que podem ser úteis.


A definição implícita seria apenas adicionar os métodos / propriedades, etc., exigidos pela interface diretamente à classe como métodos públicos.

A definição explícita força os membros a serem expostos somente quando você está trabalhando diretamente com a interface e não com a implementação subjacente. Isto é preferido na maioria dos casos.

  1. Trabalhando diretamente com a interface, você não está reconhecendo e acoplando seu código à implementação subjacente.
  2. No caso de você já ter, digamos, um nome de propriedade pública em seu código e desejar implementar uma interface que também tenha uma propriedade Name, fazer isso explicitamente manterá os dois separados. Mesmo se estivessem fazendo a mesma coisa, ainda delegaria a chamada explícita à propriedade Name. Você nunca sabe, você pode querer alterar como o Name funciona para a classe normal e como o Name, a propriedade da interface funciona mais tarde.
  3. Se você implementar uma interface implicitamente, sua classe agora expõe novos comportamentos que podem ser relevantes apenas para um cliente da interface e isso significa que você não está mantendo suas aulas suficientemente sucintas (minha opinião).

Além dos outros motivos já mencionados, essa é a situação na qual uma classe está implementando duas interfaces diferentes que possuem uma propriedade / método com o mesmo nome e assinatura.

/// <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 e executa OK, mas a propriedade Title é compartilhada.

Claramente, gostaríamos que o valor do Título retornado dependesse de estarmos tratando a Classe 1 como um Livro ou uma Pessoa. É quando podemos usar a interface 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 as definições explícitas da interface são inferidas como sendo públicas - e, portanto, você não pode declará-las como públicas (ou de outra forma) explicitamente.

Note também que você ainda pode ter uma versão "compartilhada" (como mostrado acima), mas enquanto isso é possível, a existência de tal propriedade é questionável. Talvez ele possa ser usado como uma implementação padrão do Title - para que o código existente não precise ser modificado para converter Class1 em IBook ou IPerson.

Se você não definir o título "compartilhado" (implícito), os consumidores de Class1 deverão converter explicitamente as instâncias de Class1 para IBook ou IPerson. Caso contrário, o código não será compilado.


Cada membro da classe que implementa uma interface exporta uma declaração que é semanticamente similar à forma como as declarações da interface VB.NET são escritas, por exemplo

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

Embora o nome do membro da classe geralmente corresponda ao nome do membro da interface, e o membro da classe geralmente será público, nenhuma dessas coisas é necessária. Pode-se também declarar:

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

Nesse caso, a classe e seus derivados teriam permissão para acessar um membro da classe usando o nome IFoo_Foo , mas o mundo externo só poderia acessar esse membro em particular IFoo para o IFoo . Essa abordagem geralmente é boa em casos em que um método de interface terá comportamento especificado em todas as implementações, mas o comportamento útil em apenas alguns [por exemplo, o comportamento especificado para um método IList<T>.Add da coleção somente leitura é lançar NotSupportedException ]. Infelizmente, a única maneira correta de implementar a interface em C # é:

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

Não é tão bom.


Para citar Jeffrey Richter do CLR via C #
( EIMI significa Implementação de Método de Interface explícita)

É extremamente importante que você entenda algumas ramificações que existem ao usar EIMIs. E devido a essas ramificações, você deve tentar evitar as EIMIs o máximo possível. Felizmente, as interfaces genéricas ajudam a evitar um pouco as EIMIs. Mas ainda pode haver momentos em que você precisará usá-los (como implementar dois métodos de interface com o mesmo nome e assinatura). Aqui estão os grandes problemas com os EIMIs:

  • Não há documentação explicando como um tipo implementa especificamente um método EIMI e não há suporte para o Microsoft Visual Studio IntelliSense .
  • Instâncias de tipo de valor são encaixadas quando convertidas em uma interface.
  • Um EIMI não pode ser chamado por um tipo derivado.

Se você usar uma referência de interface QUALQUER cadeia virtual pode ser explicitamente substituída pelo EIMI em qualquer classe derivada e quando um objeto desse tipo for convertido para a interface, sua cadeia virtual será ignorada e a implementação explícita será chamada. Isso é tudo menos polimorfismo.

Os EIMIs também podem ser usados ​​para ocultar membros da interface não fortemente tipados de implementações básicas de Interfaces de Estrutura, como IEnumerable <T>, para que sua classe não exponha um método não fortemente tipado diretamente, mas é sintático correto.


Se você implementar explicitamente, só poderá referenciar os membros da interface por meio de uma referência do tipo da interface. Uma referência que é o tipo da classe de implementação não exporá esses membros da interface.

Se sua classe de implementação não for pública, exceto pelo método usado para criar a classe (que pode ser um contêiner de fábrica ou IoC ) e, exceto pelos métodos de interface (claro), não vejo nenhuma vantagem em implementar explicitamente interfaces.

Caso contrário, implementar explicitamente as interfaces garante que as referências à sua classe de implementação concreta não sejam usadas, permitindo que você altere essa implementação posteriormente. "Tem certeza", suponho, é a "vantagem". Uma implementação bem-fatorada pode realizar isso sem implementação explícita.

A desvantagem, na minha opinião, é que você vai encontrar tipos de transmissão para / da interface no código de implementação que tem acesso a membros não públicos.

Como muitas outras coisas, a vantagem é a desvantagem (e vice-versa). A implementação explícita das interfaces garantirá que o código de implementação da sua classe concreta não seja exposto.


Uma implementação de interface implícita é onde você tem um método com a mesma assinatura da interface.

Uma implementação de interface explícita é onde você declara explicitamente a qual interface o método pertence.

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: implementações implícitas e explícitas de interface


Implícito é quando você define sua interface através de um membro em sua classe. É explícito quando você define métodos em sua classe na interface. Eu sei que parece confuso, mas aqui está o que quero dizer: IList.CopyTo seria implicitamente implementado como:

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

e explicitamente como:

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

A diferença é que, implicitamente, é acessível através de sua classe criada quando é lançada como essa classe, bem como quando é lançada como a interface. A implementação explícita permite que seja acessível apenas quando convertida como a própria interface.

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

Eu uso explícito principalmente para manter a implementação limpa, ou quando preciso de duas implementações. Mas, independentemente disso, raramente uso.

Estou certo de que há mais razões para usá-lo / não usá-lo que outros publicarão.

Veja o próximo post neste tópico para um excelente raciocínio por trás de cada um.





interface