Cómo: Declarar un delegado, crear instancias del mismo y utilizarlo (Guía de programación de C#)

Actualización: noviembre 2007

En C# 1.0 y versiones posteriores, los delegados se pueden declarar como se muestra aquí:

public delegate void Del<T>(T item);
public void Notify(int i) { }
Del<int> d1 = new Del<int>(Notify);

En C# 2.0 y versiones posteriores, también es posible utilizar un método anónimo para declarar e inicializar un delegado mediante esta sintaxis simplificada:

Del<int> d2 = Notify;

En C# 3.0 y versiones posteriores, también es posible declarar delegados y crear instancias de ellos mediante una expresión lambda. Para obtener más información, vea Expresiones lambda (Guía de programación de C#).

El siguiente ejemplo ilustra la declaración, creación de instancias y uso de un delegado. La clase BookDB encapsula una base de datos de los libros de una librería. Expone un método ProcessPaperbackBooks, el cual busca todos los libros en edición rústica de la base de datos y llama a un delegado para cada uno. El tipo delegate que se utiliza se denomina ProcessBookDelegate. La clase Test utiliza esta clase para imprimir los títulos y el precio medio de los libros en edición rústica.

El uso de delegados promueve una buena separación de la funcionalidad entre la base de datos de la librería y el código del programa cliente. El código del cliente no tiene conocimiento de cómo están almacenados los libros ni de cómo busca el código de la librería de los libros en rústica. El código de la librería no conoce qué procesamiento se realiza sobre los libros en rústica después de encontrarlos.

Ejemplo

// 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 ProcessBookDelegate(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(ProcessBookDelegate 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 TestBookDB
    {
        // Print the title of the book.
        static void PrintTitle(Book b)
        {
            System.Console.WriteLine("   {0}", 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:
            System.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);

            System.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
*/

Programación eficaz

  • Declarar un delegado.

    La instrucción siguiente declara un nuevo tipo delegado.

    public delegate void ProcessBookDelegate(Book book);
    

    Cada tipo delegado describe el número y tipo de los argumentos, así como el tipo del valor devuelto de los métodos que puede encapsular. Cuando se necesita un nuevo conjunto de tipos de argumentos o de valor devuelto, se debe declarar un nuevo tipo delegado.

  • Crear instancias de un delegado.

    Una vez declarado un tipo delegado, debe crearse un objeto delegado y asociarlo con un determinado método. En el ejemplo anterior, esto se hace pasando el método PrintTitle al método ProcessPaperbackBooks como en el ejemplo siguiente:

    bookDB.ProcessPaperbackBooks(PrintTitle);
    

    Esto crea un nuevo objeto delegado asociado con el método estáticoTest.PrintTitle. De igual forma, el método no estático AddBookToTotal del objeto totaller se pasa como en el ejemplo siguiente:

    bookDB.ProcessPaperbackBooks(totaller.AddBookToTotal);
    

    En ambos casos, este nuevo objeto delegado se pasa al método ProcessPaperbackBooks.

    Una vez que se crea el delegado, el método con el que está asociado no cambia nunca; los objetos delegados son inmutables.

  • Llamar a un delegado.

    Una vez creado un objeto delegado, éste se pasa normalmente a otro código que llamará al delegado. La llamada a un objeto delegado se realiza mediante el nombre del objeto delegado, seguido por los argumentos entre paréntesis que se pasarán al delegado. A continuación, se muestra un ejemplo de una llamada a delegado:

    processBook(b);
    

    Se puede llamar a un delegado de forma sincrónica, como en este ejemplo, o de forma asincrónica, utilizando los métodos BeginInvoke y EndInvoke.

Vea también

Conceptos

Guía de programación de C#

Referencia

Eventos (Guía de programación de C#)

Delegados (Guía de programación de C#)