Como a herança múltipla é ruim (torna a fonte mais complicada), o C # não fornece esse padrão diretamente. Mas, às vezes, seria útil ter essa capacidade.
Por exemplo, eu sou capaz de implementar o padrão de herança múltipla ausente usando interfaces e três classes como essa:
public interface IFirst { void FirstMethod(); }
public interface ISecond { void SecondMethod(); }
public class First:IFirst
{
public void FirstMethod() { Console.WriteLine("First"); }
}
public class Second:ISecond
{
public void SecondMethod() { Console.WriteLine("Second"); }
}
public class FirstAndSecond: IFirst, ISecond
{
First first = new First();
Second second = new Second();
public void FirstMethod() { first.FirstMethod(); }
public void SecondMethod() { second.SecondMethod(); }
}
Toda vez que adiciono um método a uma das interfaces, também preciso alterar a classe FirstAndSecond .
Existe uma maneira de injetar várias classes existentes em uma nova classe, como é possível em C ++?
Talvez haja uma solução usando algum tipo de geração de código?
Ou pode ficar assim (sintaxe c # imaginária):
public class FirstAndSecond: IFirst from First, ISecond from Second
{ }
Para que não seja necessário atualizar a classe FirstAndSecond quando eu modificar uma das interfaces.
EDITAR
Talvez seja melhor considerar um exemplo prático:
Você tem uma classe existente (por exemplo, um cliente TCP baseado em texto, com base no ITextTcpClient), que você já usa em diferentes locais dentro do seu projeto. Agora você sente a necessidade de criar um componente da sua classe para ser facilmente acessível aos desenvolvedores de formulários do Windows.
Até onde eu sei, atualmente você tem duas maneiras de fazer isso:
Escreva uma nova classe que seja herdada dos componentes e implemente a interface da classe TextTcpClient usando uma instância da própria classe, como mostrado em FirstAndSecond.
Escreva uma nova classe que herda de TextTcpClient e de alguma forma implemente IComponent (na verdade, ainda não tentei isso).
Nos dois casos, você precisa trabalhar por método e não por classe. Como você sabe que precisaremos de todos os métodos de TextTcpClient e Component, seria a solução mais fácil apenas combinar os dois em uma classe.
Para evitar conflitos, isso pode ser feito por geração de código, onde o resultado pode ser alterado posteriormente, mas digitar manualmente é uma pura dor de cabeça.
fonte
Respostas:
O C # e o .net CLR não implementaram o MI porque ainda não concluíram como ele interoperaria entre o C #, o VB.net e os outros idiomas, não porque "tornaria a fonte mais complexa"
O MI é um conceito útil, e as perguntas não respondidas são: - "O que você faz quando tem várias classes de base comuns nas diferentes superclasses?
Perl é a única linguagem com a qual trabalhei onde o MI funciona e funciona bem. A .net pode muito bem apresentá-lo um dia, mas ainda não, o CLR já suporta MI, mas, como eu disse, ainda não há construções de linguagem para ele.
Até então, você fica preso com objetos Proxy e várias interfaces :(
fonte
Considere apenas usar a composição em vez de tentar simular herança múltipla. Você pode usar interfaces para definir quais classes compõem a composição, por exemplo:
ISteerable
implica uma propriedade do tipoSteeringWheel
,IBrakable
implica uma propriedade do tipoBrakePedal
, etc.Depois de fazer isso, você poderá usar o recurso Extension Methods adicionado ao C # 3.0 para simplificar ainda mais os métodos de chamada nessas propriedades implícitas, por exemplo:
fonte
myCar
direção terminou antes de ligarStop
. Pode rolar seStop
aplicado enquantomyCar
estiver em velocidade excessiva. : DEu criei um pós-compilador em C # que permite esse tipo de coisa:
Você pode executar o pós-compilador como um evento de pós-compilação do Visual Studio:
Na mesma montagem, você a usa assim:
Em outra montagem, você a usa assim:
fonte
Você poderia ter uma classe base abstrata que implementa IFirst e ISecond e, em seguida, herdar apenas dessa base.
fonte
MI NÃO é ruim, todo mundo que (seriamente) o usou AMA e NÃO complica o código! Pelo menos não mais do que outras construções podem complicar o código. Código incorreto é código incorreto, independentemente de o MI estar ou não na imagem.
De qualquer forma, eu tenho uma ótima solução para a Herança Múltipla que eu gostaria de compartilhar. http://ra-ajax.org/lsp-liskov-substitution-principle-to-be-or-not-to-be.blog ou você pode seguir o link no meu sig ... :)
fonte
myFoo
é do tipoFoo
, que herda deMoo
eGoo
, os quais herdam deBoo
, então(Boo)(Moo)myFoo
e(Boo)(Goo)myFoo
não seriam equivalentes). Você conhece alguma abordagem de preservação de identidade?Em minha própria implementação, descobri que o uso de classes / interfaces para MI, embora "em boa forma", tendia a ser uma complicação excessiva, pois você precisa configurar toda essa herança múltipla para apenas algumas chamadas de função necessárias e, no meu caso, precisava ser feito literalmente dezenas de vezes de forma redundante.
Em vez disso, era mais fácil simplesmente criar "funções que chamam funções que chamam funções" estáticas em diferentes variedades modulares como uma espécie de substituição de POO. A solução que eu estava trabalhando em foi o "sistema de feitiço" para um RPG onde os efeitos precisa fortemente mix-and-match função de chamada para dar uma extrema variedade de magias, sem código de escrita re, muito parecido com o exemplo parece indicar.
A maioria das funções agora pode ser estática, porque eu não preciso necessariamente de uma instância para lógica ortográfica, enquanto a herança de classe nem sequer pode usar palavras-chave virtuais ou abstratas enquanto estática. As interfaces não podem usá-las.
A codificação parece muito mais rápida e limpa dessa maneira, na IMO. Se você está apenas executando funções e não precisa de propriedades herdadas , use funções.
fonte
Com o C # 8 agora você praticamente tem herança múltipla via implementação padrão dos membros da interface:
fonte
new ConsoleLogger().Log(someEception)
-lo - simplesmente não funcionaria; seria necessário converter explicitamente seu objeto em umILogger
para usar o método de interface padrão. Portanto, sua utilidade é um pouco limitada.Se você pode conviver com a restrição de que os métodos do IFirst e do ISecond devem interagir apenas com o contrato do IFirst e do ISecond (como no seu exemplo) ... você pode fazer o que solicita com os métodos de extensão. Na prática, isso raramente é o caso.
///
Portanto, a idéia básica é que você defina a implementação necessária nas interfaces ... esse material necessário deve suportar a implementação flexível nos métodos de extensão. Sempre que você precisar "adicionar métodos à interface", adicione um método de extensão.
fonte
Sim, usar Interface é um aborrecimento, porque sempre que adicionamos um método na classe, precisamos adicionar a assinatura na interface. Além disso, e se já tivermos uma classe com vários métodos, mas sem interface? precisamos criar manualmente a interface para todas as classes das quais queremos herdar. E o pior é que temos que implementar todos os métodos nas interfaces na classe filho para que a classe filho seja herdada da interface múltipla.
Seguindo o padrão de design do Facade, podemos simular a herança de várias classes usando acessadores . Declare as classes como propriedades com {get; set;} dentro da classe que precisa herdar e todas as propriedades e métodos públicos são dessa classe e, no construtor da classe filho, instanciam as classes pai.
Por exemplo:
com essa estrutura, a classe Child terá acesso a todos os métodos e propriedades da classe Pai e Mãe, simulando herança múltipla, herdando uma instância das classes pai. Não é exatamente o mesmo, mas é prático.
fonte
Quick Actions and Refactorings...
este é um deve saber, ela vai lhe poupar muito tempoTodos nós parecemos estar seguindo o caminho da interface com isso, mas a outra possibilidade óbvia, aqui, é fazer o que o OOP deve fazer e construir sua árvore de herança ... (não é isso que design de classe é tudo sobre?)
Essa estrutura fornece blocos de código reutilizáveis e, certamente, é como o código OOP deve ser escrito?
Se essa abordagem em particular não se encaixar perfeitamente, simplesmente criamos novas classes com base nos objetos necessários ...
fonte
A herança múltipla é uma daquelas coisas que geralmente causa mais problemas do que resolve. No C ++, ele se encaixa no padrão de fornecer corda suficiente para você se enforcar, mas Java e C # optaram por seguir a rota mais segura de não oferecer a opção. O maior problema é o que fazer se você herdar várias classes que possuem um método com a mesma assinatura que o herdado não implementa. Qual método de classe deve escolher? Ou isso não deve compilar? Geralmente, existe outra maneira de implementar a maioria das coisas que não depende de herança múltipla.
fonte
Se X herda de Y, isso tem dois efeitos um tanto ortogonais:
Embora a herança preveja os dois recursos, não é difícil imaginar circunstâncias em que uma delas possa ser útil sem a outra. Nenhuma linguagem .net que eu conheça tem uma maneira direta de implementar a primeira sem a segunda, embora se possa obter essa funcionalidade definindo uma classe base que nunca é usada diretamente e possuindo uma ou mais classes que herdam diretamente dela sem adicionar nada novo (essas classes poderiam compartilhar todo o código, mas não seriam substituíveis uma pela outra). Qualquer linguagem compatível com CLR, no entanto, permitirá o uso de interfaces que fornecem o segundo recurso de interfaces (substituibilidade) sem o primeiro (reutilização de membro).
fonte
Eu sei que sei que, embora não seja permitido e assim por diante, às vezes você realmente precisa disso para aqueles:
como no meu caso, eu queria fazer essa classe b: Form (sim, windows.forms) classe c: b {}
porque metade da função era idêntica e com a interface você deve reescrevê-las todas
fonte
class a : b, c
(implementando quaisquer buracos de contrato necessários). Talvez seus exemplos sejam simplificados demais?Como a questão da herança múltipla (IM) surge de tempos em tempos, eu gostaria de adicionar uma abordagem que resolva alguns problemas com o padrão de composição.
Eu construir sobre o
IFirst
,ISecond
,First
,Second
,FirstAndSecond
abordagem, como foi apresentado na pergunta. Reduzo o código de exemplo paraIFirst
, pois o padrão permanece o mesmo, independentemente do número de interfaces / classes básicas do MI.Vamos supor que, com MI
First
eSecond
que ambos derivam da mesma classe baseBaseClass
, utilizando elementos de interface única públicas deBaseClass
Isso pode ser expresso, adicionando uma referência de contêiner
BaseClass
noFirst
e naSecond
implementação:As coisas se tornam mais complicadas, quando elementos de interface protegidos
BaseClass
são referenciados ou quandoFirst
eSecond
seriam classes abstratas no MI, exigindo que suas subclasses implementem algumas partes abstratas.O C # permite que classes aninhadas acessem elementos protegidos / privados de suas classes que os contêm, portanto, isso pode ser usado para vincular os bits abstratos da
First
implementação.Há bastante clichê envolvido, mas se a implementação real do FirstMethod e SecondMethod for suficientemente complexa e a quantidade de métodos privados / protegidos acessados for moderada, esse padrão poderá ajudar a superar a falta de herança múltipla.
fonte
Isso segue as linhas da resposta de Lawrence Wenham, mas, dependendo do seu caso de uso, pode ou não ser uma melhoria - você não precisa dos levantadores.
Agora, qualquer objeto que saiba como obter uma pessoa pode implementar IGetPerson e terá automaticamente os métodos GetAgeViaPerson () e GetNameViaPerson (). A partir deste ponto, basicamente todo o código Person entra no IGetPerson, não no IPerson, exceto os novos ivars, que precisam entrar nos dois. E ao usar esse código, você não precisa se preocupar se o objeto IGetPerson é ou não um IPerson.
fonte
Agora isso é possível através de
partial
classes, cada uma delas pode herdar uma classe por conta própria, fazendo com que o objeto final herde todas as classes base. Você pode aprender mais sobre isso aqui .fonte