C # - Pode métodos herdados publicamente ser escondido (por exemplo, fez privada para classe derivada)

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

  •  01-07-2019
  •  | 
  •  

Pergunta

Suponha que eu tenho ClasseBase com métodos públicos A e B, e eu criar DerivedClass através de herança.

por exemplo.

public DerivedClass : BaseClass {}

Agora eu quero desenvolver um método C em DerivedClass que usa A e B. Existe uma maneira que eu posso substituir métodos A e B para ser privado em DerivedClass para que método só C está exposta a alguém que quer usar meus DerivedClass ?

Foi útil?

Solução

Não é possível, por quê?

Em C #, ele é forçado em cima de você que se você herdar métodos públicos, você deve torná-los públicos. Caso contrário, eles esperam que você não derivar da classe em primeiro lugar.

Em vez de usar a relação é-um, você teria que usar o tem-um relacionamento.

Os projetistas da linguagem não permitem isso de propósito para que você usar a herança mais corretamente.

Por exemplo, pode acidentalmente confundir um carro classe derivar de um motor de classe para obter a sua funcionalidade. Mas um motor é a funcionalidade que é usado pelo carro. Então você iria querer usar o tem-um relacionamento. O usuário do carro não quer ter acesso à interface do motor. E o próprio carro não se deve confundir os métodos do motor com o seu próprio. Nem futuras derivações do carro.

Assim, eles não permitir que ele protegê-lo de hierarquias de herança ruins.

O que você deve fazer em vez disso?

Em vez disso você deve implementar interfaces. Isto deixa-o livre para ter funcionalidade usando o tem-um relacionamento.

Outros idiomas:

Em C ++ você simplesmente especificar um modificador antes da classe base do privado, público ou protegido. Isso faz com que todos os membros da base que eram públicos a esse nível de acesso especificado. Parece bobagem para mim que você não pode fazer o mesmo em C #.

O código reestruturado:

interface I
{
    void C();
}

class BaseClass
{
    public void A() { MessageBox.Show("A"); }
    public void B() { MessageBox.Show("B"); }
}

class Derived : I
{
    public void C()
    {
        b.A();
        b.B();
    }

    private BaseClass b;
}

Eu entendo os nomes das classes acima são um pouco discutível:)

Outras sugestões:

Outros sugeriram fazer A () e B () Exceções públicas e jogar. Mas isso não faz uma classe amigável para as pessoas de usar e que realmente não faz sentido.

Outras dicas

Quando você, por exemplo, tentar herdar de uma List<object>, e você quer esconder o membro Add(object _ob) direta:

// the only way to hide
[Obsolete("This is not supported in this class.", true)]
public new void Add(object _ob)
{
    throw NotImplementedException("Don't use!!");
}

Não é realmente a solução mais preferível, mas ele faz o trabalho. Intellisense ainda aceita, mas em tempo de compilação você receber um erro:

Erro CS0619: 'TestConsole.TestClass.Add (TestConsole.TestObject)' é obsoleto: 'Isto não é suportado nesta classe'

Isso soa como uma má idéia. Liskov não seria impressionado.

Se você não quer que consumidores de DerivedClass para ser capaz de métodos de acesso DeriveClass.A () e DerivedClass.B () Gostaria de sugerir que DerivedClass deve implementar algum público de interface IWhateverMethodCIsAbout e os consumidores de DerivedClass deve realmente estar falando com IWhateverMethodCIsAbout e não sabe nada sobre a implementação de ClasseBase ou DerivedClass em tudo.

O que você precisa é a composição não herança.

class Plane
{
  public Fly() { .. }
  public string GetPilot() {...}
}

Agora, se você precisa de um tipo especial de Plane, como um que tem PairOfWings = 2 mas por outro lado faz tudo o que uma lata de avião .. Você avião herdar. Por isso, você declarar que sua derivação encontra o contrato da classe base e pode ser substituído sem piscar sempre que uma classe base é esperado. por exemplo. LogFlight (o plano) vai continuar a trabalhar com uma instância biplano.

No entanto, se você só precisa do comportamento Fly para um novo pássaro que você deseja criar e não estão dispostos a apoiar o contrato completo classe base, você compõe em seu lugar. Neste caso, refatorar o comportamento dos métodos para reutilização em um novo tipo de vôo. Agora crie e mantenha as referências a esta classe, tanto em Plano e Bird. Você não herdam porque o pássaro não suporta o contrato completo classe base ... (por exemplo, ele não pode fornecer GetPilot ()).

Pela mesma razão, você não pode reduzir a visibilidade dos métodos de classe base quando você substituir .. você pode substituir e fazer uma base pública método privado na derivação mas não vice-versa. por exemplo. Neste exemplo, se eu tirar um tipo de plano "BadPlane" e, em seguida, substituir e "Hide" GetPilot () - torná-lo privado; um LogFlight método de cliente (Plane p) irá funcionar para a maioria dos aviões, mas irá explodir para "BadPlane" se a implementação de LogFlight acontece a necessidade / chamar GetPilot (). Desde são esperados todos os derivações de uma classe base para ser 'substituível' onde quer que um param classe base é esperado, isso tem de ser anulado.

A única maneira de fazer isso que eu conheço é usar um relacionamento tem-um e apenas implementar as funções que você deseja expor.

@ Brian R. Bondy me apontou para um artigo interessante sobre Hiding através de herança e o new palavra-chave.

http://msdn.microsoft.com/ en-us / library / aa691135 (VS.71) .aspx

Assim como solução que eu sugeriria:

class BaseClass
{
    public void A()
    {
        Console.WriteLine("BaseClass.A");
    }

    public void B()
    {
        Console.WriteLine("BaseClass.B");
    }
}

class DerivedClass : BaseClass
{
    new public void A()
    {
        throw new NotSupportedException();
    }

    new public void B()
    {
        throw new NotSupportedException();
    }

    public void C()
    {
        base.A();
        base.B();
    }
}

Este código forma como este irá lançar um NotSupportedException :

    DerivedClass d = new DerivedClass();
    d.A();

Hiding é uma ladeira muito escorregadia. As principais questões, IMO, são:

  • É dependente do tempo de design Tipo de declaração da instância, ou seja, se você faz algo como ClasseBase obj = nova subclasse (), em seguida chamada obj.A (), esconderijo é derrotado. BaseClass.A () será executada.

  • Hiding pode muito facilmente obscurecer de comportamento (ou alterações de comportamento) em o tipo de base. Esta é, obviamente, menos de uma preocupação quando você possui dois os lados da equação, ou se chamando 'base.xxx' é parte de sua sub-membro.

  • Se você realmente não possuir ambos os lados da equação de base / sub-classe, então você deve ser capaz de conceber uma solução mais manejável do que esconder institucionalizada / sombreamento.

Eu diria que se você tiver uma base de código que você está querendo fazer isso com, não é a melhor base de código projetado. É tipicamente um sinal de uma classe em um nível da hierarquia necessitando um certo assinatura pública, enquanto outra classe derivada dessa classe não precisa dele.

Um próximo codificação paradigma é chamado de "composição sobre herança." Isto joga diretamente fora dos princípios de desenvolvimento orientado a objetos (especialmente a responsabilidade única Princípio e aberto / fechado Princípio).

Infelizmente, o lote maneira que um de nós desenvolvedores foram ensinados orientação a objetos, temos formado um hábito de imediatamente pensar sobre herança em vez de composição. Nós tendem a ter classes maiores que têm muitas responsabilidades diferentes, simplesmente porque eles podem estar contidas com o mesmo objeto "mundo real". Isso pode levar a hierarquias de classe que são 5+ níveis de profundidade.

Um infeliz efeito colateral que os desenvolvedores normalmente não pensam sobre quando se trata de herança é que as formas de herança uma das formas mais fortes de dependências que você pode sempre introduzir em seu código. Sua classe derivada está agora fortemente dependente da classe que foi herdado de. Isso pode tornar o código mais frágil no longo prazo e levar a confundir problemas onde mudando um determinado comportamento em uma quebra de classe base classes derivadas de maneiras obscuras.

Uma maneira de quebrar o seu código-se é através de interfaces como mencionado em outra resposta. Isso é uma coisa inteligente a fazer de qualquer maneira que você quiser dependências externas de uma classe para vincular a abstrações, não de concreto / tipos derivados. Isso permite que você altere a implementação sem alterar a interface, tudo sem efetuar uma linha de código em sua classe dependente.

Eu prefiro muito mais do que manter um sistema com centenas / milhares / ainda mais classes que são todos pequenos e fracamente acoplado, de acordo com um sistema que faz uso pesado de polimorfismo / herança e tem menos classes que são mais fortemente acoplados .

Talvez o melhor recursos lá fora, sobre o desenvolvimento orientado a objeto é o livro de Robert C. Martin, Agile Software Development, Princípios, padrões e práticas .

Se eles estão definidos pública na classe original, você não pode substituir-los a ser privado em sua classe derivada. No entanto, você pode fazer o método público lançar uma exceção e implementar sua própria função particular.

Edit:. Jorge Ferreira está correto

Enquanto a resposta à pergunta é "não", há uma dica que eu gostaria de salientar para os outros que chegam aqui (dado que o OP era uma espécie de alusão ao acesso assembléia por 3 partes). Quando os outros referenciar uma montagem, Visual Studio deve ser honrando o seguinte atributo para que ele não vai aparecer no intellisense (escondido, mas ainda pode ser chamado, então cuidado):

[System.ComponentModel.EditorBrowsable(System.ComponentModel.EditorBrowsableState.Never)]

Se você não tinha outra escolha, você deve ser capaz de usar new em um método que esconde um método de tipo base, => throw new NotSupportedException(); retorno, e combiná-lo com o atributo acima.

Outro truque depende não herdar de uma classe base, se possível, onde a base tem uma interface correspondente (como IList<T> para List<T>). Implementação de interfaces "explicitamente" também irá esconder os métodos de intellisense sobre o tipo de classe. Por exemplo:

public class GoodForNothing: IDisposable
{
    void IDisposable.Dispose() { ... }
}

No caso de var obj = new GoodForNothing(), o método Dispose() não estará disponível on obj. No entanto, ele estará disponível para quem digita-casts explicitamente obj para IDisposable.

Além disso, você também pode embrulhar um tipo de base em vez de herdar a partir dele, então esconder alguns métodos:

public class MyList<T> : IList<T>
{
    List<T> _Items = new List<T>();
    public T this[int index] => _Items[index];
    public int Count => _Items.Count;
    public void Add(T item) => _Items.Add(item);
    [System.ComponentModel.EditorBrowsable(System.ComponentModel.EditorBrowsableState.Never)]
    void ICollection<T>.Clear() => throw new InvalidOperationException("No you may not!"); // (hidden)
    /*...etc...*/
}
Licenciado em: CC-BY-SA com atribuição
Não afiliado a StackOverflow
scroll top