C # Interfaces. implementação implícita contra a implementação explícita

StackOverflow https://stackoverflow.com/questions/143405

  •  02-07-2019
  •  | 
  •  

Pergunta

Quais são as diferenças entre as interfaces de aplicação implicitamente e explicitamente em C #?

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

Existem prós e / ou contras para um ou o outro?


orientações oficiais da Microsoft (da primeira edição Diretrizes de Design Framework ) afirma que < strong> usando implementações explícitas não são recomendados , uma vez que dá o comportamento inesperado código.

Eu acho que essa diretriz é muito válido em um pré-IoC-time , quando você não passar as coisas como interfaces.

Poderia alguém toque sobre esse aspecto também?

Foi útil?

Solução

implícita é quando você definir sua interface através de um membro da sua classe. explícita é quando você definir métodos dentro de sua classe na interface. Eu sei que parece confuso, mas aqui é o que eu quero dizer: IList.CopyTo seria implementado implicitamente 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 que você criou quando é escalado como essa classe, bem como quando seu elenco como a interface. implementação explícita permite que ele seja acessível somente quando elenco 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 limpo, ou quando eu preciso de duas implementações. Mas, independentemente eu raramente usá-lo.

Estou certo de que há mais motivos para usá-lo / não usá-lo que os outros vão postar.

Veja a próximo post neste segmento para excelente raciocínio por trás de cada um.

Outras dicas

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

forças explícitas definição dos membros de ser exposto apenas quando você está trabalhando com a interface diretamente, e não a implementação subjacente. Este é o preferido na maioria dos casos.

  1. Ao trabalhar diretamente com a interface, você não está reconhecendo, e acoplamento seu código para a implementação subjacente.
  2. No caso em que você já tem, digamos, uma propriedade pública Nome em seu código e você deseja implementar uma interface que também tem um Nome da propriedade, fazê-lo explicitamente irá manter os dois separados. Até se eles estavam fazendo a mesma coisa eu ainda delegar a explícita chamada para a propriedade Name. Você nunca sabe, você pode querer mudar como Nome trabalha para a classe normal e como Nome, a interface propriedade funciona mais tarde.
  3. Se você implementar uma interface implicitamente, em seguida, sua classe agora expõe novos comportamentos que só poderia ser relevantes para um cliente do interface e isso significa que você não está mantendo suas classes sucinto suficiente (minha opinião).

Além de excelentes respostas já fornecidas, existem alguns casos onde a implementação explícita é necessária para o compilador para ser capaz de descobrir o que é necessário. Dê uma olhada em IEnumerable<T> como um excelente exemplo que provavelmente vai vir para cima com bastante frequência.

Aqui está um exemplo:

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
}

Aqui, implementos IEnumerable<string> IEnumerable, portanto, precisamos também. Mas espere, tanto o genérico e a versão normal tanto implementar funções com a mesma assinatura do método (C # ignora tipo de retorno para isso). Isto é completamente legal e multa. Como a determinação compilador qual usar? Obriga-nos a ter apenas, no máximo, uma definição implícita, então ele pode resolver tudo o que ele precisa.

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();

PS: O pequeno pedaço de engano na definição explícita para IEnumerable funciona porque dentro da função o compilador sabe que o tipo real da variável é um StringList, e é assim que resolve a chamada de função. fato pouco Nifty para a implementação de algumas das camadas de abstração algum do .NET interfaces de núcleo parecem ter acumulado.

Razão # 1

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

Por exemplo, em um MVP aplicação web baseada:

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

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

Agora, outra classe (como um apresentador ) é menos provável que dependem da implementação StandardNavigator e mais propensos a depender da interface INavigator (desde a implementação precisaria ser convertido para uma interface para fazer uso do método Redirect).

Razão # 2

Outra razão que eu poderia ir com uma implementação de interface explícita seria manter "default" mais limpo interface de uma classe. Por exemplo, se eu estivesse desenvolvendo um controle de servidor ASP.NET , eu poderia querer duas interfaces :

  1. interface primária da classe, que é usado por desenvolvedores de páginas web; e
  2. A "escondido" interface usada pelo apresentador que eu desenvolver para lidar com a lógica do controle

Um exemplo simples a seguir. É um controle de caixa de combinação que os clientes listas. Neste exemplo, o desenvolvedor de página web não está interessado em preencher a lista; em vez disso, eles só querem ser capaz de selecionar um cliente por GUID ou para obter GUID do cliente selecionado. Um apresentador iria preencher 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 a página web developer nunca precisa estar ciente de sua existência.

Mas está não é uma prata Cannonball

Eu não recomendaria sempre empregando implementações de interfaces explícitas. Esses são apenas dois exemplos onde eles podem ser úteis.

Para citar Jeffrey Richter de CLR via C #
( Eimi meios E xplicit I nterface M étodo I MPLEMENTAÇÃO)

É extremamente importante para você entender algumas ramificações que existe quando usando EIMIs. E por causa de essas ramificações, você deve tentar evitar EIMIs, tanto quanto possível. Felizmente, interfaces genéricas ajudar -lo a evitar EIMIs um pouco. Mas lá ainda pode haver momentos em que você precisa usá-los (tais como a implementação de dois métodos de interface com o mesmo nome e assinatura). Aqui são o grande problemas com EIMIs:

  • Não há nenhuma documentação explicando como um tipo especificamente implementa um método de EIMI, e há há Microsoft Visual Studio IntelliSense apoio.
  • instâncias do tipo de valor são encaixotados quando fundido a uma interface.
  • Uma EIMI não pode ser chamado por um tipo derivado.

Se você usar uma referência de interface de qualquer cadeia virtual pode ser explicitamente substituído por EIMI em qualquer classe derivada e quando um objeto desse tipo é convertido para o interface, a sua cadeia virtual é ignorada ea implementação explícita é chamado. Isso é qualquer coisa, mas polimorfismo.

EIMIs pode também ser usado para membros de interface esconder não-rigidez de implementações Interfaces estrutura básica, como IEnumerable para sua classe não expõe um método não fortemente digitado diretamente, mas é sintática correta.

Além das outras razões já expostas, esta é a situação em que uma classe implementa duas interfaces diferentes que têm 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 compila código e funciona OK, mas a propriedade Title é compartilhado.

Claramente, nós queremos o valor do título voltou a depender se estávamos tratando Class1 como um livro ou uma pessoa. Isto é, 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 de interface explícitas são inferida a ser pública -. E, portanto, você não pode declarar-los a ser público (ou não) de forma explícita

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 um é questionável. Talvez pudesse ser usado como uma implementação padrão do título -. Para que o código existente não teria que ser modificado para elenco Class1 para iBook ou IPerson

Se você não definir o título "compartilhada" (implícito), os consumidores de Class1 deve explicitamente lançar instâncias de Class1 para iBook ou IPerson primeiro -. Caso contrário, o código não irá compilar

Eu uso implementação de interface explícita na maioria das vezes. Aqui estão as principais razões.

Refactoring é mais seguro

Ao alterar uma interface, é melhor se o compilador pode verificá-lo. Isso é mais difícil com implementações implícitas.

Dois casos comuns vêm à mente:

  • Como adicionar uma função para uma interface, onde uma classe existente que implementa essa interface já acontece de ter um método com a mesma assinatura como o novo . Isso pode levar a um comportamento inesperado, e me mordeu duro várias vezes. É difícil "ver" quando a depuração porque essa função provavelmente não fica com os outros métodos de interface no arquivo (a questão de auto-documentando mencionado abaixo).

  • Remover uma função a partir de uma interface . Implicitamente métodos implementados será código de repente morto, mas os métodos explicitamente implementados será pego por erro de compilação. Mesmo que o código morto é bom para manter ao redor, eu quero ser forçado a analisá-lo e promovê-lo.

É lamentável que C # não tem uma palavra-chave que nos obriga a marcar um método como uma implementação implícita, para que o compilador poderia fazer as verificações extras. métodos virtuais não têm qualquer um dos problemas acima devido ao uso necessário de 'override' e 'novo'.

Nota: para fixo ou raramente mudança interfaces (tipicamente de fornecedor API), este não é um problema. Para minhas próprias interfaces, porém, eu não posso prever quando / como eles vão mudar.

é auto-documentado

Se eu ver 'bool público Execute ()' em uma classe, ele vai ter trabalho extra para descobrir que ele é parte de uma interface. Alguém provavelmente terá de comentá-lo dizer isso, ou colocá-lo em um grupo de outras implementações de interface, tudo sob uma região ou agrupamento comentário dizendo "implementação de ITask". Claro, isso só funciona se o cabeçalho do grupo não é fora da tela ..

Considerando o seguinte:. 'Bool ITask.Execute ()' é clara e inequívoca

Limpar separação de implementação da interface

Eu acho que de interfaces como sendo mais 'público' do que os métodos públicos porque eles são criados para expor um pouco da área de superfície do tipo concreto. Eles reduzem o tipo para uma capacidade, um comportamento, um conjunto de características, etc, e na implementação, eu acho que é útil para manter essa separação.

Como eu estou olhando através de código de uma classe, quando me deparo com implementações de interfaces explícitas, meus turnos cérebro em modo de "contrato de código". Muitas vezes, essas implementações simplesmente encaminhar a outros métodos, mas às vezes eles vão fazer estado de cama / verificação param, a conversão de parâmetros de entrada para melhor atender aos requisitos internos, ou tradução, mesmo para fins de controle de versão (ou seja, várias gerações de interfaces de todos remam para baixo para implementações comuns).

(eu percebo que os públicos são também os contratos de código, mas as interfaces são muito mais fortes, especialmente em uma base de código-driven interface onde o uso direto de tipos de concreto é geralmente um sinal de interno-somente código.)

Relacionados:. Razão 2 acima por Jon

E assim por diante

Além disso, o vantagens já mencionado em outras respostas aqui:

Problemas

Não é todo o divertimento e felicidade. Há alguns casos onde eu ficar com implícitos:

  • Tipos de valor, porque isso vai exigir boxe e menor perf. Esta não é uma regra rígida, e depende da interface e como ele é destinado a ser utilizado. IComparable? Implícito. IFormattable? provavelmente explícita.
  • interfaces do sistema triviais que possuem métodos que são frequentemente chamados diretamente (como IDisposable.Dispose).

Além disso, ele pode ser uma dor de fazer o casting quando você de fato tem o tipo concreto e deseja chamar um método de interface explícita. Eu lido com isso em uma de duas maneiras:

  1. Adicionar públicos e têm os métodos de interface para a frente a eles para a implementação. Normalmente acontece com interfaces mais simples quando se trabalha internamente.
  2. (Meu método preferido) Adicionar um public IMyInterface I { get { return this; } } (que deve ficar embutido) e foo.I.InterfaceMethod() chamada. Se múltiplas interfaces que precisam dessa habilidade, expanda o nome além I (na minha experiência é raro que eu tenho essa necessidade).

Se você implementar explicitamente, você só vai ser capaz de referenciar os membros de interface através de uma referência que é do tipo da interface. Uma referência que é o tipo da classe de implementação não irá expor os membros de interface.

Se a sua classe de implementação não é público, exceto para o método utilizado para criar a classe (que poderia ser uma fábrica ou IoC container), e exceto para os métodos de interface (é claro), então eu não vejo qualquer vantagem para as interfaces de aplicação de forma explícita.

Caso contrário, implementando explicitamente as interfaces garante que as referências a seu concreto implementação de classe não são utilizados, permitindo que você mude de que a implementação em um momento posterior. "Garante", suponho, é a "vantagem". A implementação bem consignado pode fazer isso sem implementação explícita.

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

Como muitas coisas, a vantagem é a desvantagem (e vice-versa). Explicitamente interfaces de execução irá assegurar que seu código de implementação classe concreta não é 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ê declarar explicitamente que a interface do 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 de interface implícito e explícito

Cada membro da classe que implementa uma interface de exportação uma declaração que é semanticamente semelhante à maneira como VB.NET declarações de interface são escritos, por exemplo.

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

Embora o nome do membro da classe, muitas vezes vai coincidir com a do membro de interface, e o membro da classe, muitas vezes, ser público, nenhuma dessas coisas é necessária. Pode-se também declarar:

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

Em que caso a classe e seus derivados seriam autorizados a aceder a um membro da classe usando o nome IFoo_Foo, mas o mundo exterior só seria capaz de acesso que determinado membro, lançando a IFoo. Tal abordagem é sempre bom nos casos em que um método de interface terão especificado comportamento em todas as implementações, mas útil comportamento em apenas algumas [por exemplo, o comportamento especificado para o método IList<T>.Add uma coleção somente leitura é jogar 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.

Um uso importante de implementação de interface explícita é quando na necessidade de implementar interfaces com visibilidade misto .

O problema ea solução são bem explicado no artigo C # interface Interno .

Por exemplo, se você quiser proteger o vazamento de objetos entre camadas de aplicação, esta técnica permite que você especifique diferente visibilidade de membros que pode causar o vazamento.

Licenciado em: CC-BY-SA com atribuição
Não afiliado a StackOverflow
scroll top