Практическое руководство. Объявление, создание экземпляра и использование делегата (Руководство по программированию в C#)
В C# версии 1.0 и более поздней версии делегаты можно объявлять, как показано в следующем примере.
// Declare a delegate.
delegate void Del(string str);
// Declare a method with the same signature as the delegate.
static void Notify(string name)
{
Console.WriteLine("Notification received for: {0}", name);
}
// Create an instance of the delegate.
Del del1 = new Del(Notify);
C# версии 2.0 предоставляет упрощенный способ записи предыдущего объявления, как показано в следующем примере.
// C# 2.0 provides a simpler way to declare an instance of Del.
Del del2 = Notify;
В C# версии 2.0 и более поздней версии также можно использовать анонимный метод для объявления и инициализации делегата, как показано в следующем примере.
// Instantiate Del by using an anonymous method.
Del del3 = delegate(string name)
{ Console.WriteLine("Notification received for: {0}", name); };
В C# версии 3.0 и более поздней версии делегаты можно также объявлять и создавать их экземпляры при помощи лямбда-выражения, как показано в следующем примере.
// Instantiate Del by using a lambda expression.
Del del4 = name => { Console.WriteLine("Notification received for: {0}", name); };
Дополнительные сведения см. в разделе Лямбда-выражения (Руководство по программированию в C#).
Следующий пример демонстрирует объявление, создание экземпляра и использование делегата. Класс BookDB инкапсулирует базу данных книжного магазина, в котором ведется база данных книг. Он предоставляет метод ProcessPaperbackBooks, который находит все книги в мягкой обложке по базе данных и вызывает делегат для каждой книги. Используемый тип delegate имеет имя ProcessBookDelegate. Класс Test использует этот класс для печати заголовков и средней цены книг в мягкой обложке.
Использование делегата способствует правильному разделению функций между базой данных книжного магазина и клиентским кодом. Клиентскому коду неизвестен порядок хранения книг и то, как код книжного магазина выполняет поиск книг в мягкой обложке. Коду книжного магазина неизвестно о выполняемой обработке книг с мягкой обложкой после их нахождения.
Пример
// 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
*/
Отказоустойчивость
Объявление делегата.
Следующий оператор объявляет новый типа делегата.
public delegate void ProcessBookDelegate(Book book);
Каждый тип делегата описывает число и типы аргументов, а также тип возвращаемого значения методов, которые могут быть им инкапсулированы. Каждый раз, когда требуется новый набор типов аргументов или тип возвращаемого значения, необходимо объявить новый тип делегата.
Создание экземпляра делегата
После объявления типа делегата необходимо создать объект делегата и связать его с определенным методом. В предыдущем примере это выполняется путем передачи метода PrintTitle в метод ProcessPaperbackBooks, как показано в следующем примере:
bookDB.ProcessPaperbackBooks(PrintTitle);
Это создает новый объект делегата, связанный со статическим методом Test.PrintTitle. Подобным образом передается не статический метод AddBookToTotal объекта totaller, как показано в следующем примере:
bookDB.ProcessPaperbackBooks(totaller.AddBookToTotal);
В обоих случаях новый объект делегата передается в метод ProcessPaperbackBooks.
После создания делегата метод, с которым он связан, никогда не изменится; объекты делегатов являются неизменяемыми.
Вызов делегата.
После создания объекта делегата, он как правило передается в другой код, который вызовет делегат. Объект делегата вызывается при помощи имени объекта делегата, после которого следуют параметризованные аргументы, передаваемые в делегат. Ниже приведен пример вызова делегата.
processBook(b);
Делегат может быть вызван синхронно (как в этом примере), или асинхронно при помощи методов BeginInvoke и EndInvoke.
См. также
Ссылки
События (Руководство по программированию в C#)
Делегаты (Руководство по программированию на C#)