Come dichiarare, creare un'istanza e usare un delegato (Guida per programmatori C#)

È possibile dichiarare delegati usando uno dei metodi seguenti:

  • Dichiarare un tipo delegato e dichiarare un metodo con una firma corrispondente:
// 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);
  • Assegnare un gruppo di metodi a un tipo delegato:
// C# 2.0 provides a simpler way to declare an instance of NotifyCallback.
NotifyCallback del2 = Notify;
  • Dichiarare un metodo anonimo:
// Instantiate NotifyCallback by using an anonymous method.
NotifyCallback del3 = delegate(string name)
    { Console.WriteLine($"Notification received for: {name}"); };
  • Usare un'espressione lambda:
// Instantiate NotifyCallback by using a lambda expression.
NotifyCallback del4 = name =>  { Console.WriteLine($"Notification received for: {name}"); };

Per altre informazioni, vedere Espressioni lambda.

Nell'esempio che segue viene illustrato come dichiarare un delegato, crearne un'istanza e usarlo. La classe BookDB incapsula il database di una libreria che gestisce un database di volumi. Espone un metodo, ProcessPaperbackBooks, che ricerca tutti i tascabili all'interno del database e chiama un delegato per ognuno di essi. Il tipo delegate usato viene denominato ProcessBookCallback. La classe Test usa questa classe per stampare i titoli e il prezzo medio dei tascabili.

L'uso dei delegati consente la separazione ottimale delle funzionalità tra il database della libreria e il codice client. Il codice client non contiene alcuna informazione sulle modalità di archiviazione dei libri o sul meccanismo che consente al codice di individuare i tascabili. Il codice della libreria non contiene alcuna informazione sull'elaborazione effettuata sui tascabili individuati.

Esempio

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

Programmazione efficiente

  • Dichiarazione di un delegato.

    L'istruzione seguente dichiara un nuovo tipo di delegato.

    public delegate void ProcessBookCallback(Book book);
    

    Ogni tipo delegato descrive il numero e il tipo degli argomenti e il tipo di valore restituito dai metodi in esso incapsulati. Ogni volta che è necessario un nuovo set di tipi di argomento o un nuovo tipo di valore restituito, è necessario dichiarare un nuovo tipo di delegato.

  • Creazione di un'istanza di un delegato.

    Dopo aver dichiarato un tipo delegato, è necessario creare un oggetto delegato e associarlo a un determinato metodo. Nell'esempio precedente questa operazione viene eseguita passando il metodo PrintTitle al metodo ProcessPaperbackBooks, come illustrato nell'esempio seguente:

    bookDB.ProcessPaperbackBooks(PrintTitle);
    

    In questo modo viene creato un nuovo oggetto delegato associato al metodo staticoTest.PrintTitle. Analogamente, il metodo non statico AddBookToTotal dell'oggetto totaller viene passato come illustrato nell'esempio seguente:

    bookDB.ProcessPaperbackBooks(totaller.AddBookToTotal);
    

    In entrambi i casi, al metodo ProcessPaperbackBooks viene passato un nuovo oggetto delegato.

    Dopo la creazione di un delegato, il metodo ad esso associato non viene mai modificato, poiché gli oggetti delegati non sono modificabili.

  • Chiamata a un delegato.

    Una volta creato, un oggetto delegato viene in genere passato a un altro codice che chiamerà il delegato. Per la chiamata di un oggetto delegato viene usato il nome dell'oggetto stesso, seguito dagli argomenti, racchiusi tra parentesi, che devono essere passati al delegato. Di seguito viene riportato un esempio di chiamata a un delegato:

    processBook(b);
    

    Un delegato può essere chiamato in modo sincrono, come in questo esempio, oppure in modo asincrono usando i metodi BeginInvoke e EndInvoke.

Vedi anche