ti-enxame.com

Por que minha classe pública não pode estender uma classe interna?

Eu realmente não entendo.

Se a classe base é abstrata e se destina apenas a ser usada para fornecer funcionalidade comum às subclasses públicas definidas no Assembly, por que não deveria ser declarada interna?

Eu não quero que a classe abstrata seja visível para codificar fora da Assembly. Não quero que o código externo saiba sobre isso.

47
David

Ao herdar de uma classe, você expõe a funcionalidade da classe base através do seu filho.

Como a classe filho tem maior visibilidade que seu pai, você exporia membros que, de outra forma, seriam protegidos.

Você não pode violar o nível de proteção da classe pai implementando um filho com maior visibilidade.

Se a classe base é realmente destinada a ser usada por classes filho públicas, você também precisa tornar o pai público.

A outra opção é manter o "pai" interno, torná-lo não abstrato e usá-lo para compor as classes filho e usar uma interface para forçar as classes a implementar a funcionalidade:

public interface ISomething
{
    void HelloWorld();
}

internal class OldParent : ISomething
{
    public void HelloWorld(){ Console.WriteLine("Hello World!"); }
}

public class OldChild : ISomething
{
    OldParent _oldParent = new OldParent();

    public void HelloWorld() { _oldParent.HelloWorld(); }
}
38
Justin Niessner

ATUALIZAÇÃO: Esta pergunta foi o assunto do meu blog em 13 de novembro de 2012 . Veja-o para mais algumas reflexões sobre esse assunto. Obrigado pela ótima pergunta!


Você está certo; não precisa ser assim. Outras linguagens OO permitem "herança privada", segundo a qual o fato de D herdar de B só pode ser aproveitado pelo código que tem a capacidade de ver B.

Essa foi uma decisão de design dos designers de C # originais. Infelizmente, estou longe da minha mesa agora - estou tirando alguns dias de folga no fim de semana prolongado - para não ter as notas de design de idiomas de 1999 na minha frente. Se eu pensar nisso quando voltar, vou procurá-los e ver se há uma justificativa para esta decisão.

Minha opinião pessoal é que a herança deve ser usada para representar "é uma espécie de" relacionamentos; isto é, a herança deve representar a semântica do domínio que está sendo modelado no idioma . Eu tento evitar situações em que a herança é usada como um mecanismo de compartilhamento de código . Como outros já mencionaram, provavelmente é melhor preferir composição a herança se o que você deseja representar é "esta classe compartilha mecanismos de implementação com outras classes".

40
Eric Lippert

Eu acho que a coisa mais próxima que você pode fazer é impedir que outros assemblies criem a classe abstrata, tornando seu construtor interno, para citar MSDN :

Um construtor interno impede que a classe abstrata seja usada como a classe base dos tipos que não estão no mesmo assembly que a classe abstrata.

Você pode tentar adicionar um EditorBrowsableAttribute à classe para tentar ocultá-lo do Intellisense (embora eu tenha tido resultados mistos usando-o para ser honesto) ou colocar a classe base em um espaço para nome aninhado, como Como MyLibrary.Internals para separá-lo do resto de suas aulas.

16
Samuel

Eu acho que você está misturando preocupações aqui, e C # é o culpado, na verdade (e Java antes dele).

A herança deve servir como um mecanismo de categorização, enquanto é frequentemente usada para reutilização de código.

Para reutilização de código, sempre se sabe que a composição supera a herança. O problema com o C # é que ele nos fornece uma maneira fácil de herdar:

class MyClass : MyReusedClass { }

Mas, para compor, precisamos fazer isso sozinhos:

class MyClass {
  MyReusedClass _reused;
  // need to expose all the methods from MyReusedClass and delegate to _reused
}

O que está faltando é uma construção como característica (pdf) , que trará composição ao mesmo nível de usabilidade que a herança.

Há pesquisas sobre características em C # (pdf) , e seria algo como isto:

class MyClass {
  uses { MyTrait; }
}

Embora eu gostaria de ver outro modelo (o dos papéis do Perl 6).

ATUALIZAÇÃO:

Como observação lateral, a linguagem Oxygene possui m recurso que permite delegar todos os membros de uma interface a uma propriedade de membro que implementa essa interface:

type
  MyClass = class(IReusable)
  private
    property Reused : IReusable := new MyReusedClass(); readonly;
      implements public IReusable;
  end;

Aqui, todos os membros da interface de IReusable serão expostos por meio de MyClass e todos delegarão na propriedade Reused. Existem alguns problemas nessa abordagem, no entanto.

OUTRA ATUALIZAÇÃO:

Comecei a implementar esse conceito de composição automática em C #: dê uma olhada em NRoles .

4
Jordão

Eu acho que isso violaria o Princípio da Substituição de Liskov .

Em casos como esse, usei classes internas e prefiro composição a herança. Existe algo no seu design que proíba conter toda essa funcionalidade em sua classe interna e faça com que suas classes públicas contenham uma instância dessa classe interna?

3
Dave