Como declarar, instanciar e usar um delegado (Guia de Programação em C#)

Você pode declarar delegados usando qualquer um dos seguintes métodos:

  • Declarar um tipo de delegado e declarar um método com uma assinatura correspondente:
// Declare a delegate.
delegate void NotifyCallback(string str);

// Declare a method with the same signature as the delegate.
static void Notify(string name)
{
    Console.WriteLine($"Notification received for: {name}");
}
// Create an instance of the delegate.
NotifyCallback del1 = new NotifyCallback(Notify);
  • Atribuir um grupo de métodos a um tipo de delegado:
// C# 2.0 provides a simpler way to declare an instance of NotifyCallback.
NotifyCallback del2 = Notify;
  • Declarar um método anônimo:
// Instantiate NotifyCallback by using an anonymous method.
NotifyCallback del3 = delegate(string name)
    { Console.WriteLine($"Notification received for: {name}"); };
  • Usar uma expressão lambda:
// Instantiate NotifyCallback by using a lambda expression.
NotifyCallback del4 = name =>  { Console.WriteLine($"Notification received for: {name}"); };

Para obter mais informações, consulte Expressões Lambda.

O exemplo a seguir ilustra a declaração, instanciação e o uso de um delegado. A classe BookDB encapsula um banco de dados de uma livraria que mantém um banco de dados de livros. Ela expõe um método, ProcessPaperbackBooks, que localiza todos os livros de bolso no banco de dados e chama um delegado para cada um. O tipo delegate usado tem o nome ProcessBookCallback. A classe Test usa essa classe para imprimir os títulos e o preço médio dos livros de bolso.

O uso de delegados promove uma boa separação de funcionalidade entre o banco de dados da livraria e o código de cliente. O código de cliente não tem conhecimento de como os livros são armazenados ou como o código da livraria localiza os livros de bolso. O código da livraria não tem conhecimento do processamento executado nos livros de bolso após a localização.

Exemplo

// A set of classes for handling a bookstore:
namespace Bookstore
{
    using System.Collections;

    // Describes a book in the book list:
    public struct Book
    {
        public string Title;        // Title of the book.
        public string Author;       // Author of the book.
        public decimal Price;       // Price of the book.
        public bool Paperback;      // Is it paperback?

        public Book(string title, string author, decimal price, bool paperBack)
        {
            Title = title;
            Author = author;
            Price = price;
            Paperback = paperBack;
        }
    }

    // Declare a delegate type for processing a book:
    public delegate void ProcessBookCallback(Book book);

    // Maintains a book database.
    public class BookDB
    {
        // List of all books in the database:
        ArrayList list = new ArrayList();

        // Add a book to the database:
        public void AddBook(string title, string author, decimal price, bool paperBack)
        {
            list.Add(new Book(title, author, price, paperBack));
        }

        // Call a passed-in delegate on each paperback book to process it:
        public void ProcessPaperbackBooks(ProcessBookCallback processBook)
        {
            foreach (Book b in list)
            {
                if (b.Paperback)
                    // Calling the delegate:
                    processBook(b);
            }
        }
    }
}

// Using the Bookstore classes:
namespace BookTestClient
{
    using Bookstore;

    // Class to total and average prices of books:
    class PriceTotaller
    {
        int countBooks = 0;
        decimal priceBooks = 0.0m;

        internal void AddBookToTotal(Book book)
        {
            countBooks += 1;
            priceBooks += book.Price;
        }

        internal decimal AveragePrice()
        {
            return priceBooks / countBooks;
        }
    }

    // Class to test the book database:
    class Test
    {
        // Print the title of the book.
        static void PrintTitle(Book b)
        {
            Console.WriteLine($"   {b.Title}");
        }

        // Execution starts here.
        static void Main()
        {
            BookDB bookDB = new BookDB();

            // Initialize the database with some books:
            AddBooks(bookDB);

            // Print all the titles of paperbacks:
            Console.WriteLine("Paperback Book Titles:");

            // Create a new delegate object associated with the static
            // method Test.PrintTitle:
            bookDB.ProcessPaperbackBooks(PrintTitle);

            // Get the average price of a paperback by using
            // a PriceTotaller object:
            PriceTotaller totaller = new PriceTotaller();

            // Create a new delegate object associated with the nonstatic
            // method AddBookToTotal on the object totaller:
            bookDB.ProcessPaperbackBooks(totaller.AddBookToTotal);

            Console.WriteLine("Average Paperback Book Price: ${0:#.##}",
                    totaller.AveragePrice());
        }

        // Initialize the book database with some test books:
        static void AddBooks(BookDB bookDB)
        {
            bookDB.AddBook("The C Programming Language", "Brian W. Kernighan and Dennis M. Ritchie", 19.95m, true);
            bookDB.AddBook("The Unicode Standard 2.0", "The Unicode Consortium", 39.95m, true);
            bookDB.AddBook("The MS-DOS Encyclopedia", "Ray Duncan", 129.95m, false);
            bookDB.AddBook("Dogbert's Clues for the Clueless", "Scott Adams", 12.00m, true);
        }
    }
}
/* Output:
Paperback Book Titles:
   The C Programming Language
   The Unicode Standard 2.0
   Dogbert's Clues for the Clueless
Average Paperback Book Price: $23.97
*/

Programação robusta

  • Declarando um delegado.

    A instrução a seguir declara um novo tipo de delegado.

    public delegate void ProcessBookCallback(Book book);
    

    Cada tipo de delegado descreve o número e os tipos dos argumentos e o tipo do valor retornado dos métodos que pode encapsular. Sempre que um novo conjunto de tipos de argumento ou tipo de valor retornado for necessário, um novo tipo de delegado deverá ser declarado.

  • Instanciando um delegado.

    Após a declaração do tipo de delegado, um objeto delegado deve ser criado e associado a um método específico. No exemplo anterior, faça isso passando o método PrintTitle para o método ProcessPaperbackBooks, como no exemplo a seguir:

    bookDB.ProcessPaperbackBooks(PrintTitle);
    

    Isso cria um novo objeto delegado associado ao método estáticoTest.PrintTitle. Da mesma forma, o método não estático AddBookToTotal no objeto totaller é passado como no exemplo a seguir:

    bookDB.ProcessPaperbackBooks(totaller.AddBookToTotal);
    

    Em ambos os casos, um novo objeto delegado é passado para o método ProcessPaperbackBooks.

    Após a criação de um delegado, o método ao qual ele está associado nunca se altera; objetos delegados são imutáveis.

  • Chamando um delegado.

    Normalmente, o objeto delegado, após sua criação, é passado para outro código que chamará o delegado. Um objeto delegado é chamado usando seu nome seguido dos argumentos entre parênteses a serem passados para o delegado. A seguir, veja um exemplo de uma chamada de delegado:

    processBook(b);
    

    Um delegado pode ser chamado de forma síncrona, como neste exemplo ou de forma assíncrona, usando os métodos BeginInvoke e EndInvoke.

Confira também