Partilhar via


Usando delegados (Guia de Programação em C#)

Um delegado é um tipo que encapsula com segurança um método, semelhante a um ponteiro de função em C e C++. Ao contrário dos ponteiros de função em C, os delegados são orientados a objetos, seguros quanto ao tipo e protegidos. O exemplo a seguir declara um delegado chamado Callback que pode encapsular um método que usa uma cadeia de caracteres como um argumento e retorna void:

public delegate void Callback(string message);

Um objeto delegado é normalmente construído fornecendo o nome do método que o delegado encapsula ou com uma expressão lambda. Um delegado pode ser invocado uma vez instanciado dessa maneira. Invocar um delegado chama o método anexado à instância delegada. Os parâmetros passados ao delegado pelo chamador são passados para o método. O delegado retorna o valor de retorno, se houver, do método. Por exemplo:

// Create a method for a delegate.
public static void DelegateMethod(string message)
{
    Console.WriteLine(message);
}
// Instantiate the delegate.
Callback handler = DelegateMethod;

// Call the delegate.
handler("Hello World");

Os tipos delegados são derivados da Delegate classe no .NET. Os tipos delegados são lacrados, não podem ser derivados e não é possível derivar classes personalizadas do Delegate. Como o delegado instanciado é um objeto, ele pode ser passado como um argumento ou atribuído a uma propriedade. Um método pode aceitar um delegado como um parâmetro e chamar o delegado em algum momento posterior. Isto é conhecido como um callback assíncrono e é um método comum de notificar o invocador quando um processo longo é concluído. Quando um delegado é usado dessa maneira, o código que usa o delegado não precisa de nenhum conhecimento da implementação do método que está sendo usado. A funcionalidade é semelhante às interfaces de encapsulamento fornecidas.

Outro uso comum dos callbacks é definir um método de comparação personalizado e passar essa delegação para um método de ordenação. Ele permite que o código do chamador se torne parte do algoritmo de classificação. O método de exemplo a seguir usa o Callback tipo como um parâmetro:

public static void MethodWithCallback(int param1, int param2, Callback callback)
{
    callback("The number is: " + (param1 + param2).ToString());
}

Em seguida, você pode passar o delegado criado no exemplo anterior para esse método:

MethodWithCallback(1, 2, handler);

E receba a seguinte saída para o console:

The number is: 3

MethodWithCallback não precisa ligar diretamente para o console — ele não precisa ser projetado com um console em mente. O que MethodWithCallback faz é preparar uma string e passá-la para outro método. Um método delegado pode usar qualquer número de parâmetros.

Quando um delegado é construído para encapsular um método de instância, o delegado faz referência à instância e ao método. Um delegado não tem conhecimento do tipo de instância além do método que ele envolve. Um delegado pode se referir a qualquer tipo de objeto, desde que haja um método nesse objeto que corresponda à assinatura do delegado. Quando um delegado é construído para encapsular um método estático, ele apenas faz referência ao método. Considere as seguintes declarações:

public class MethodClass
{
    public void Method1(string message) { }
    public void Method2(string message) { }
}

Junto com a estática DelegateMethod mostrada anteriormente, agora temos três métodos que você pode encapsular em uma Callback instância.

Um delegado, quando invocado, pode chamar mais de um método, um processo conhecido como multicasting. Para adicionar um método extra à lista de métodos do delegado—também conhecida como lista de invocação—simplesmente requer adicionar um delegado usando os operadores de adição ou atribuição de adição ('+' ou '+='). Por exemplo:

var obj = new MethodClass();
Callback d1 = obj.Method1;
Callback d2 = obj.Method2;
Callback d3 = DelegateMethod;

//Both types of assignment are valid.
Callback allMethodsDelegate = d1 + d2;
allMethodsDelegate += d3;

O allMethodsDelegate contém três métodos em sua lista de invocação—Method1, Method2e DelegateMethod. Os três delegados originais, d1, d2, e d3, permanecem inalterados. Quando allMethodsDelegate é invocado, todos os três métodos são chamados em ordem. Se o delegado usa parâmetros de referência, a referência é passada sequencialmente para cada um dos três métodos por sua vez, e quaisquer alterações por um método são visíveis para o próximo método. Quando qualquer um dos métodos lança uma exceção que não é capturada dentro do método, essa exceção é passada para o chamador do delegado. Nenhum método subsequente na lista de invocação é chamado. Se o delegado tiver um valor de retorno e/ou parâmetros de saída, ele retornará o valor de retorno e os parâmetros do último método invocado. Para remover um método da lista de invocação, use os operadores de subtração ou atribuição de subtração (- ou -=). Por exemplo:

//remove Method1
allMethodsDelegate -= d1;

// copy AllMethodsDelegate while removing d2
Callback oneMethodDelegate = (allMethodsDelegate - d2)!;

Como os tipos de delegado são derivados de System.Delegate, os métodos e propriedades definidos por essa classe podem ser chamados no delegado. Por exemplo, para localizar o número de métodos na lista de invocação de um delegado, você pode escrever:

int invocationCount = d1.GetInvocationList().GetLength(0);

Os delegados com mais de um método em sua lista de invocação derivam de MulticastDelegate, que é uma subclasse de System.Delegate. O código anterior funciona em ambos os casos porque ambas as classes suportam GetInvocationList.

Os delegados multicast são amplamente utilizados no tratamento de eventos. Os objetos de origem do evento enviam notificações de eventos para objetos de destinatário que se registraram para receber esse evento. Para se registrar em um evento, o destinatário cria um método projetado para manipular o evento, cria um delegado para esse método e passa o delegado para a fonte do evento. A fonte chama o representante quando o evento ocorre. Em seguida, o delegado chama o método de manipulação de eventos no destinatário, entregando os dados do evento. A origem do evento define o tipo de delegado para um determinado evento. Para mais informações, consulte Eventos.

A comparação de delegados de dois tipos diferentes atribuídos no momento da compilação resulta num erro de compilação. Se as instâncias delegadas forem estaticamente do tipo System.Delegate, a comparação será permitida, mas retornará false em tempo de execução. Por exemplo:

delegate void Callback1();
delegate void Callback2();

static void method(Callback1 d, Callback2 e, System.Delegate f)
{
    // Compile-time error.
    Console.WriteLine(d == e);

    // OK at compile-time. False if the run-time type of f
    // is not the same as that of d.
    Console.WriteLine(d == f);
}

Ver também