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 C, os delegados são orientados a objetos, tipos seguros e protegidos. O tipo de delegado é definido pelo nome do delegado. 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 irá encapsular, ou com uma expressão lambda. Uma vez que um delegado é instanciado dessa maneira, ele pode ser invocado. Invocar um delegado chama o método anexado à instância delegada. Os parâmetros passados para o delegado pelo chamador são passados para o método, e o valor de retorno, se houver, do método é retornado ao chamador pelo delegado. 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 selados — 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. Isso permite que um método aceite um delegado como um parâmetro e chame o delegado em algum momento posterior. Isso é conhecido como retorno de chamada assíncrono e é um método comum de notificar um chamador quando um processo longo é concluído. Quando um delegado é usado dessa maneira, o código usando 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 de retornos de chamada é definir um método de comparação personalizado e passar esse delegado para um método de classificaçã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 Del 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 acima para esse método:

MethodWithCallback(1, 2, handler);

e receba a seguinte saída para o console:

The number is: 3

Usar o delegado como uma abstração, MethodWithCallback não precisa chamar o console diretamente — ele não precisa ser projetado com um console em mente. O que MethodWithCallback faz é simplesmente preparar uma string e passá-la para outro método. Isso é especialmente poderoso, uma vez que 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 encapsula, portanto, 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 podem ser encapsulados por uma Del instância.

Um delegado pode chamar mais de um método quando invocado. Isso é conhecido como multicasting. Para adicionar um método extra à lista de métodos do delegado, a lista de invocação, basta adicionar dois delegados usando os operadores de atribuição de adição ou 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;

Neste ponto allMethodsDelegate contém três métodos em sua lista de invocação—Method1, Method2, e 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 e 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 atribuição de subtração ou 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 acima 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 origem chama o delegado 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. O tipo de delegado para um determinado evento é definido pela origem do evento. Para mais informações, consulte Eventos.

Comparar delegados de dois tipos diferentes atribuídos em tempo de compilação resultará em um 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);
}

Consulte também