Delegates e Eventos
Temos estado a implementar o interface do jogo Intersecções mas, na última mensagem, deparamo-nos com algo novo, os eventos. Esta mensagem serve para estudar os eventos e os delegates, mecanismo subjacente aos eventos.
A utilização de apontadores para funções é algo muito útil noutras linguagens/plataformas. Pode ser utilizado, por exemplo, para o mecanismo de callback. Este mecanismo consiste em indicar o endereço de uma função que será chamada posteriormente. Por exemplo, com a API Win32, quando se pretende criar uma janela deve-se preencher uma estrutura WNDCLASS com diversos dados sobre essa janela. Um dos dados é precisamente o endereço da função responsável pelo processamento das mensagens que chegam à janela.
No entanto o mecanismo que é utilizado tem os seus problemas. Como estamos apenas a falar de um endereço podemos fazer todos os tipos de conversões e estragar a aplicação das formas mais imagináveis. Podemos utilizar o endereço de uma função que não tenha o mesmo número ou tipo dos parâmetros ou podemos até converter o endereço de algo que não seja sequer uma função para um apontador para uma função.
A plataforma .NET oferece uma versão mais segura dos apontadores para funções na forma do mecanismo de delegate. Um delegate não é mais que um tipo que encapsula uma referência para uma função. Podemos definir um delegate da seguinte forma:
delegate void Func(Int32 i);
Estamos, com esta linha de código, a criar um tipo que encapsula endereços de funções que recebem um inteiro e não devolvem qualquer valor. A utilização é igual à utilização de qualquer outro tipo.
class App {
static void Main() {
Func f;
f = new Func(MyFuncInt);
f(3);
}
private static void MyFuncInt(Int32 i) {
Console.WriteLine(i);
}
}
Na função Main estamos a criar uma instância do delegate, em que o construtor recebe o nome da função. Após a criação da instância, estamos a chamar a função que está encapsulada. Para esta acção o sintaxe é igual à chamada de uma função. Se executarmos este código, vemos o valor 3 no ecrã, confirmando que a função é executada.
De notar que, como os delegates são tipos, estes podem ser colocados ao nível da classe. Assim sendo, Func pode ser colocado antes da definição de App.
Então e tendo um delegate com a assinatura igual ao de cima, como é que é definido o tipo? O tipo será definido assim:
class Func : MulticastDelegate {
public Func(Object obj, Int32 methodPtr) { /* ... */ }
public void Invoke(Int32 i) { /* ... */ }
public IAsyncResult BeginInvoke(Int32 i,
IAsyncCallback callback, Object obj) { /* ... */ }
public void EndInvoke(IAsyncResult result) { /* ... */ }
}
É importante referir o construtor e o método Invoke. Quando foi utilizado o delegate ainda há pouco, este não foi construído com os parâmetros que são indicados no construtor. O sintaxe utilizado na linguagem C# é um sintaxe simplificado que permite abstrair dos pormenores de implementação dos delegates. Quando um delegate é construído, é indicado no construtor a instância da qual pertence o método (ou null se for um método de tipo) e um valor inteiro que representa o método.
Da mesma forma, quando o foi chamado o método representado pelo delegate, não foi chamada explicitamente o método Invoke. Mas foi isso que aconteceu. De notar que o método Invoke tem exactamente a mesma assinatura do delegate.
Para confirmar estes pormenores pode ser utilizada a ferramenta ILDasm, com a qual podemos ver o tipo criado e o código em IL deste.
Então e os eventos? Um tipo que expõe um evento oferece a funcionalidade de notificar outros tipos da ocorrência de algo. Vamos considerar este exemplo: pretende-se um tipo capaz de gerar mensagens e que deve notificar outros tipos quando existir uma nova mensagem. Podemos escrever o seguinte código:
class MessageProducer {
public delegate void MessageEventHandler(String message);
public event MessageEventHandler MessageEvent;
public void SendMessage(String message) {
if(null == MessageEvent) {
return;
}
MessageEvent(message);
}
}
De notar que o tipo do evento é um delegate. Isto significa que todos os tipos que pretendam ser notificados da ocorrência do evento devem indicar um método com a assinatura definida no delegate para ser chamada aquando da ocorrência. Podemos, então, definir o tipo que irá receber as mensagens da seguinte forma:
class MessageConsumer {
public MessageConsumer(MessageProducer mp) {
mp.MessageEvent +=
new MessageProducer.MessageEventHandler(ShowMessage);
}
private void ShowMessage(String message) {
Console.WriteLine("Message: " + message);
}
private static void Main() {
MessageProducer mp = new MessageProducer();
MessageConsumer mc = new MessageConsumer(mp);
mp.SendMessage("Some Message");
}
}
Não é díficil, como se pode confirmar. Claro que existem mais coisas para referir, mas delego essa tarefa para uma mensagem posterior, para que se possa seguir com o jogo Intersecções. O objectivo desta mensagem era apenas fazer uma referência superficial ao tema.


0 Comments:
Enviar um comentário
<< Home