Merge delegation
This example demonstrates how to create a multicast delegate. A useful property of a delegate object is that multiple objects can be assigned to a delegate instance using the + operator. A multicast delegation contains a list of assigned delegations. When a multicast delegate is called, it calls the delegates in the list in order. Only delegates of the same type can be merged.
- Operators can be used to remove component delegates from multicast delegates.
using System; // Define a custom delegate that has a string parameter and returns void. delegate void CustomDel(string s); class TestClass { // Define two methods that have the same signature as CustomDel. static void Hello(string s) { (" Hello, {0}!", s); } static void Goodbye(string s) { (" Goodbye, {0}!", s); } static void Main() { // Declare instances of the custom delegate. CustomDel hiDel, byeDel, multiDel, multiMinusHiDel; // In this example, you can omit the custom delegate if you // want to and use Action<string> instead. //Action<string> hiDel, byeDel, multiDel, multiMinusHiDel; // Create the delegate object hiDel that references the // method Hello. hiDel = Hello; // Create the delegate object byeDel that references the // method Goodbye. byeDel = Goodbye; // The two delegates, hiDel and byeDel, are combined to // form multiDel. multiDel = hiDel + byeDel; // Remove hiDel from the multicast delegate, leaving byeDel, // which calls only the method Goodbye. multiMinusHiDel = multiDel - hiDel; ("Invoking delegate hiDel:"); hiDel("A"); ("Invoking delegate byeDel:"); byeDel("B"); ("Invoking delegate multiDel:"); multiDel("C"); ("Invoking delegate multiMinusHiDel:"); multiMinusHiDel("D"); } }
Output:
Invoking delegate hiDel: Hello, A! Invoking delegate byeDel: Goodbye, B! Invoking delegate multiDel: Hello, C! Goodbye, C! Invoking delegate multiMinusHiDel: Goodbye, D!
Declare, instantiate, and use delegates
In C# 1.0 and later, you can declare the delegation as shown in the following example.
// Declare a delegate. delegate void Del(string str); // Declare a method with the same signature as the delegate. static void Notify(string name) { ("Notification received for: {0}", name); } // Create an instance of the delegate. Del del1 = new Del(Notify);
C# 2.0 provides an easier way to write the above declaration, as shown in the following example.
// C# 2.0 provides a simpler way to declare an instance of Del. Del del2 = Notify;
In C# 2.0 and later, anonymous methods can also be used to declare and initialize delegates, as shown in the following example.
// Instantiate Del by using an anonymous method. Del del3 = delegate(string name) { ("Notification received for: {0}", name); };
In C# 3.0 and later, you can also use Lambda expressions to declare and instantiate delegates, as shown in the following example.
// Instantiate Del by using a lambda expression. Del del4 = name => { ("Notification received for: {0}", name); };
The following examples illustrate declarations, instantiation, and use of delegates. The BookDB class encapsulates a bookstore database, which maintains a book database. It exposes the ProcessPaperbackBooks method, which looks for all paperbacks in the database and calls a delegate for each paperback. The delegate type used is named ProcessBookDelegate. The Test class uses the class to print the title and average price of paperback books.
The use of commissions promotes a good separation of functions between the bookstore database and customer code. Customer code does not know how books are stored and how bookstore codes look for paperbacks. The bookstore code doesn't know what will be done to the paperback after finding the paperback.
// A set of classes for handling a bookstore: namespace Bookstore { using ; // 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) { (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 () // 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 += ; } 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) { (" {0}", ); } // Execution starts here. static void Main() { BookDB bookDB = new BookDB(); // Initialize the database with some books: AddBooks(bookDB); // Print all the titles of paperbacks: ("Paperback Book Titles:"); // Create a new delegate object associated with the static // method : (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: (); ("Average Paperback Book Price: ${0:#.##}", ()); } // Initialize the book database with some test books: static void AddBooks(BookDB bookDB) { ("The C Programming Language", "Brian W. Kernighan and Dennis M. Ritchie", 19.95m, true); ("The Unicode Standard 2.0", "The Unicode Consortium", 39.95m, true); ("The MS-DOS Encyclopedia", "Ray Duncan", 129.95m, false); ("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
Reliable programming
Declare the delegation.
The following statement declares a new delegate type.
public delegate void ProcessBookDelegate(Book book);
Each delegate type describes the number and type of parameters, and the return value type of the method it can encapsulate. Whenever a new set of parameter types or new return value types are required, a new delegate type must be declared.
Instantiate the delegation.
After declaring a delegate type, you must create a delegate object and associate it with a specific method. In the previous example, you did this by passing the PrintTitle method to the ProcessPaperbackBooks method as in the following example:
(PrintTitle);
This creates a new delegate object associated with the static method. Similarly, the non-static method of the object totaller AddBookToTotal is passed in the following example:
();
In both examples, a new delegate object is passed to the ProcessPaperbackBooks method.
After the delegate is created, its association method cannot be changed; the delegate object is immutable.
Call delegate.
After the delegate object is created, the delegate object is usually passed to other code that will call the delegate. Call the delegate object by the name of the delegate object (sequent to the parameters to be passed to the delegate, enclosed in brackets). Here is an example of a delegate call:
processBook(b);
As in this example, delegates can be called synchronously or asynchronously by using the BeginInvoke and EndInvoke methods.