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:
class MyClass {
// Note: EventHandler is just a multicast delegate,
// that returns void and accepts (object sender, EventArgs e)!
public event EventHandler MyEvent;
public void DoSomethingThatTriggersMyEvent() {
// ... some code
var handler = MyEvent;
if (handler != null)
handler(this, EventArgs.Empty);
// ... some other code
}
}
O compilador realmente transforma isso no seguinte código:
class MyClass {
private EventHandler MyEvent = null;
public void add_MyEvent(EventHandler value) {
MyEvent += value;
}
public void remove_MyEvent(EventHandler value) {
MyEvent -= value;
}
public void DoSomethingThatTriggersMyEvent() {
// ... some code
var handler = MyEvent;
if (handler != null)
handler(this, EventArgs.Empty);
// ... some other code
}
}
Você então se inscreve em um evento fazendo
MyClass instance = new MyClass();
instance.MyEvent += SomeMethodInMyClass;
Que compila até
MyClass instance = new MyClass();
instance.add_MyEvent(new EventHandler(SomeMethodInMyClass));
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:
interface RequiredMethods {
void DoX();
int DoY();
};
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:
sealed class RequiredMethods {
public Action DoX;
public Func<int> DoY();
}
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.