O que eles querem dizer com um padrão de design de eventos?
Eles provavelmente se referem a uma implementação do padrão observador, que é uma construção de linguagem principal em C #, exposta como ' eventos '. É possível ouvir eventos conectando um delegado a eles. Como Yam Marcovic apontou, EventHandler
é o tipo de delegado base convencional para eventos, mas qualquer tipo de delegado pode ser usado.
Como a composição se torna fácil se um delegado é usado?
Provavelmente isso se refere apenas à flexibilidade que os delegados oferecem. Você pode facilmente 'compor' determinado comportamento. Com a ajuda de lambdas , a sintaxe para fazer isso também é muito concisa. Considere o seguinte exemplo.
class Bunny
{
Func<bool> _canHop;
public Bunny( Func<bool> canHop )
{
_canHop = canHop;
}
public void Hop()
{
if ( _canHop() ) Console.WriteLine( "Hop!" );
}
}
Bunny captiveBunny = new Bunny( () => IsBunnyReleased );
Bunny lazyBunny = new Bunny( () => !IsLazyDay );
Bunny captiveLazyBunny = new Bunny( () => IsBunnyReleased && !IsLazyDay );
Fazer algo semelhante com interfaces requer que você use o padrão de estratégia ou use uma Bunny
classe base (abstrata) a partir da qual você estende coelhos mais específicos.
se houver um grupo de métodos relacionados que possa ser chamado, use a interface - que benefício ele tem?
Mais uma vez, usarei coelhos para demonstrar como seria mais fácil.
interface IAnimal
{
void Jump();
void Eat();
void Poo();
}
class Bunny : IAnimal { ... }
class Chick : IAnimal { ... }
// Using the interface.
IAnimal bunny = new Bunny();
bunny.Jump(); bunny.Eat(); bunny.Poo();
IAnimal chick = new Chick();
chick.Jump(); chick.Eat(); chick.Poo();
// Without the interface.
Action bunnyJump = () => bunny.Jump();
Action bunnyEat = () => bunny.Eat();
Action bunnyPoo = () => bunny.Poo();
bunnyJump(); bunnyEat(); bunnyPoo();
Action chickJump = () => chick.Jump();
Action chickEat = () => chick.Eat();
...
se uma classe precisa apenas de uma implementação do método, use a interface - como isso se justifica em termos de benefícios?
Para isso, considere o primeiro exemplo com o coelho novamente. Se apenas uma implementação for necessária - nenhuma composição personalizada será necessária -, você poderá expor esse comportamento como uma interface. Você nunca precisará construir as lambdas, basta usar a interface.
Conclusão
Os delegados oferecem muito mais flexibilidade, enquanto as interfaces ajudam a estabelecer contratos firmes. Portanto, acho o último ponto mencionado: "Uma classe pode precisar de mais de uma implementação do método". , de longe o mais relevante.
Um motivo adicional para usar delegados é quando você deseja expor apenas parte de uma classe da qual não é possível ajustar o arquivo de origem.
Como exemplo de um cenário desse tipo (flexibilidade máxima, sem necessidade de modificar fontes), considere esta implementação do algoritmo de pesquisa binária para qualquer coleção possível, passando apenas dois delegados.
1) Padrões de eventos, bem, o clássico é o padrão Observer, aqui está um bom link da Microsoft Microsoft talk Observer
O artigo ao qual você vinculou não está muito bem escrito e torna as coisas mais complicadas do que o necessário. O negócio estático é senso comum, você não pode definir uma interface com membros estáticos; portanto, se desejar um comportamento polimórfico em um método estático, use um representante.
O link acima fala sobre os delegados e por que existem boas composições para ajudar na sua consulta.
fonte
Primeiro de tudo, boa pergunta. Admiro seu foco na utilidade, em vez de aceitar cegamente as "melhores práticas". +1 para isso.
Eu já li esse guia antes. Você precisa se lembrar de algo sobre isso - é apenas um guia, principalmente para iniciantes em C # que sabem como programar, mas não estão tão familiarizados com a maneira como fazem as coisas em C #. Não é apenas uma página de regras, mas uma página que descreve como as coisas já são feitas. E como eles já foram feitos dessa maneira em todos os lugares, pode ser uma boa ideia permanecer consistente.
Vou direto ao ponto, respondendo às suas perguntas.
Primeiro de tudo, suponho que você já saiba o que é uma interface. Quanto a um delegado, basta dizer que é uma estrutura que contém um ponteiro digitado para um método, juntamente com um ponteiro opcional para o objeto que representa o
this
argumento desse método. No caso de métodos estáticos, o último ponteiro é nulo.Também existem delegados de difusão seletiva, que são iguais aos delegados, mas podem ter várias dessas estruturas atribuídas a eles (ou seja, uma única chamada para Invocar em um delegado de difusão seletiva invoca todos os métodos em sua lista de chamadas atribuídas).
O que eles querem dizer com um padrão de design de eventos?
Eles significam o uso de eventos em C # (que possui palavras-chave especiais para implementar sofisticadamente esse padrão extremamente útil). Eventos em C # são alimentados por delegados multicast.
Quando você define um evento, como neste exemplo:
O compilador realmente transforma isso no seguinte código:
Você então se inscreve em um evento fazendo
Que compila até
Então, isso está ocorrendo em C # (ou .NET em geral).
Como a composição se torna fácil se um delegado é usado?
Isso pode ser facilmente demonstrado:
Suponha que você tenha uma classe que depende de um conjunto de ações a serem passadas para ela. Você pode encapsular essas ações em uma interface:
E qualquer pessoa que desejasse passar ações para sua classe teria que implementar essa interface. Ou você pode facilitar a vida deles dependendo da classe a seguir:
Dessa forma, os chamadores precisam apenas criar uma instância de RequiredMethods e ligar os métodos aos delegados em tempo de execução. Isso geralmente é mais fácil.
Essa maneira de fazer as coisas é extremamente benéfica nas circunstâncias certas. Pense nisso - por que depender de uma interface quando tudo o que realmente importa é ter uma implementação passada para você?
Benefícios do uso de interfaces quando há um grupo de métodos relacionados
É benéfico usar interfaces, porque normalmente requerem implementações explícitas em tempo de compilação. Isso significa que você cria uma nova classe.
E se você tiver um grupo de métodos relacionados em um único pacote, é benéfico que esse pacote seja reutilizável por outras partes do código. Portanto, se eles podem simplesmente instanciar uma classe em vez de criar um conjunto de delegados, é mais fácil.
Benefícios do uso de interfaces se uma classe precisar apenas de uma implementação
Como observado anteriormente, as interfaces são implementadas em tempo de compilação - o que significa que são mais eficientes do que chamar um delegado (que é um nível de indireção per se).
"Uma implementação" pode significar uma implementação que existe em um único local bem definido.
Caso contrário, uma implementação pode vir de qualquer lugar do programa, o que acontece apenas com a assinatura do método. Isso permite mais flexibilidade, porque os métodos precisam apenas estar em conformidade com a assinatura esperada, em vez de pertencer a uma classe que implementa explicitamente uma interface específica. Mas essa flexibilidade pode ter um custo e, na verdade, quebra o princípio da Substituição de Liskov , porque na maioria das vezes você deseja explicitação, porque minimiza a chance de acidentes. Assim como a digitação estática.
O termo também pode se referir a delegados multicast aqui. Os métodos declarados por interfaces podem ser implementados apenas uma vez em uma classe de implementação. Mas os delegados podem acumular vários métodos, que serão chamados seqüencialmente.
Portanto, no geral, parece que o guia não é informativo o suficiente e simplesmente funciona como é - um guia, não um livro de regras. Alguns conselhos podem realmente parecer um pouco contraditórios. Cabe a você decidir quando é certo aplicar o quê. O guia parece nos dar apenas um caminho geral.
Espero que suas perguntas tenham sido respondidas para sua satisfação. E, novamente, parabéns pela pergunta.
fonte
Se minha memória do .NET ainda se mantiver, um delegado é basicamente uma função ptr ou functor. Ele adiciona uma camada de indireção a uma chamada de função para que as funções possam ser substituídas sem que o código de chamada precise ser alterado. É a mesma coisa que uma interface faz, exceto que uma interface reúne várias funções e um implementador precisa implementá-las.
Em geral, um padrão de evento é aquele em que algo responde a eventos de outros países (como mensagens do Windows). O conjunto de eventos geralmente é aberto, eles podem ocorrer em qualquer ordem e não estão necessariamente relacionados um ao outro. Os delegados trabalham bem para isso, pois cada evento pode chamar uma única função sem precisar de uma referência a uma série de objetos de implementação que também podem conter inúmeras funções irrelevantes. Além disso (é aqui que minha memória do .NET é vaga), acho que vários delegados podem ser anexados a um evento.
A composição, embora eu não esteja muito familiarizado com o termo, é basicamente projetar um objeto para ter várias sub-partes ou filhos agregados aos quais o trabalho é passado. Os delegados permitem que os filhos sejam misturados e combinados de uma maneira mais ad-hoc, onde uma interface pode ser um exagero ou causar muito acoplamento e a rigidez e fragilidade que o acompanham.
O benefício para uma interface para métodos relacionados é que os métodos podem compartilhar o estado do objeto de implementação. As funções de delegado não podem compartilhar de maneira tão limpa, nem mesmo conter, estado.
Se uma classe precisa de uma única implementação, uma interface é mais adequada, pois em qualquer classe que implemente toda a coleção, apenas uma implementação pode ser feita e você obtém os benefícios de uma classe de implementação (estado, encapsulamento etc.). Se a implementação pode mudar devido ao estado de tempo de execução, os delegados funcionam melhor porque podem ser trocados por outras implementações sem afetar os outros métodos. Por exemplo, se houver três delegados, cada um com duas implementações possíveis, você precisará de oito classes diferentes para implementar a interface de três métodos para contabilizar todas as combinações possíveis de estados.
fonte
O padrão de design "evento" (mais conhecido como Padrão do Observador) permite anexar vários métodos da mesma assinatura ao delegado. Você realmente não pode fazer isso com uma interface.
Não estou convencido de que a composição seja mais fácil para um delegado do que uma interface. Essa é uma afirmação muito estranha. Gostaria de saber se ele quis dizer porque você pode anexar métodos anônimos a um delegado.
fonte
O maior esclarecimento que posso oferecer:
Tão:
fonte
Sua pergunta sobre eventos já foi bem abordada. E também é verdade que uma interface pode definir vários métodos (mas na verdade não precisa), enquanto um tipo de função apenas impõe restrições a uma função individual.
A diferença real, porém, é:
Claro, mas o que isso significa?
Vamos pegar este exemplo (o código está no haXe, pois meu c # não é muito bom):
Agora, o método de filtragem facilita a transmissão conveniente de apenas uma pequena parte da lógica, que não tem conhecimento da organização interna da coleção, enquanto a coleção não depende da lógica que lhe é dada. Ótimo. Exceto por um problema:
A coleção não dependem da lógica. A coleção supõe inerentemente que a função transmitida foi projetada para testar um valor em relação a uma condição e retornar o sucesso do teste. Observe que nem todas as funções, que recebem um valor como argumento e retornam um booleano, na verdade são meros predicados. Por exemplo, o método remove da nossa coleção é uma função.
Suponha que ligamos
c.filter(c.remove)
. O resultado seria uma coleção com todos os elementos dec
whilec
se torna vazio. Isso é lamentável, porque naturalmente esperaríamosc
ser invariáveis.O exemplo é muito construído. Mas o principal problema é que o código que chama
c.filter
com algum valor de função como argumento não tem como saber se esse argumento é adequado (ou seja, acabará por conservar a invariante). O código que criou o valor da função pode ou não saber, que será interpretado como um predicado.Agora vamos mudar as coisas:
O que mudou? O que mudou é que, seja qual for o valor agora atribuído
filter
, assinou explicitamente o contrato de ser um predicado. É claro que programadores mal-intencionados ou extremamente estúpidos criam implementações da interface que não são livres de efeitos colaterais e, portanto, não são predicados.Mas o que não pode mais acontecer é que alguém vincula uma unidade lógica de dados / código, que por engano é interpretada como predicado por causa de sua estrutura externa.
Então, para reformular o que foi dito acima em apenas algumas palavras:
A vantagem de relacionamentos explícitos é que você pode ter certeza deles. A desvantagem é que eles exigem a sobrecarga da explicitação. Por outro lado, a desvantagem dos relacionamentos implícitos (no nosso caso, a assinatura de uma função que corresponde à assinatura desejada) é que você não pode realmente ter 100% de certeza de que pode usar as coisas dessa maneira. A vantagem é que você pode estabelecer relacionamentos sem toda a sobrecarga. Você pode juntar as coisas rapidamente, porque a estrutura delas permite. É isso que significa composição fácil.
É um pouco como LEGO: você pode simplesmente conectar uma figura LEGO de Star Wars em um navio pirata LEGO, simplesmente porque a estrutura externa permite. Agora você pode achar que isso é terrivelmente errado ou pode ser exatamente o que você deseja. Ninguém vai te parar.
fonte
int IList.Count { get { ... } }
) ou implicitamente (public int Count { get { ... } }
). Essa distinção não é relevante para esta discussão, mas merece menção para evitar confundir os leitores.fonte