Делегаты
Как созданные объекты могут посылать сообщения тем объектам, которые их породили? При программировании под Windows на С и C++ основное средство для решения этой проблемы это функция обратного вызова (callback function, или просто callback), которая основана на использовании указателей на функции в oпeративной памяти.
Делегаты Делегат выполняет те же действия, что и указатель на функцию, но способами гораздо более безопасными и лучше соответствующими принципам объектно- ориентированного программирования. Как и все в мире С#, делегат это специальный класс. Любой делегат производится от единого базового класса System.MulticastDelegate
Делегаты public delegate void PlayAcidHouse (object PaulQakenfold, int volume); На самом деле: public class PlayAcidHouse : System.MulticastDelegate { PlayAcidHouse(object target, int ptr); // Синхронный метод Invoke() public void virtual Invoke(object PaulOakenfold, int volume); // Асинхронная версия того же самого обратного вызова public virtual lAsyncResult Beginlnvoke (object PaulOakenfold, int volume, AsyncCallback cb, object o); public virtual void Endlnvoke(IAsyncResult result); }
Пример делегата // Класс Саг вновь изменился public class Car { // Новые переменные! private bool isDirty; // Испачкан ли наш автомобиль? private bool shouldRotate; // Нужна ли замена шин? // Конструктор с новыми параметрами public Car(string name, int max, int curr, bool dirty, bool rotate) {… isDirty = dirty; shouldRotate = rotate; } // Свойство для isDirty public bool Dirty { get { return isDirty; } set { isDirty = value;} } // Свойство для shouldRotate public bool Rotate {get { return shouldRotate; } set { shouldRotate = value; } }}
Пример делегата // Делегат - это класс, инкапсулирующий указатель на функцию. В нашем случае // этой функцией должен стать какой-то метод, принимающий в качестве параметра // объект класса Саг и ничего не возвращающий; public delegate void CarDelegate(Car с); Делегат может быть определен внутри другого класса // Помещаем определение делегата внутрь определения класса public class Car : Object ( // Теперь наш делегат получит служебное имя Car$CarDelegate, то есть станет // вложенным типом... public delegate void CarDelegate(Саг с);... }
Члены System.MulticastDelegate Таблица 5.2. Некоторые унаследованные члены делегатов Method - Это свойство возвращает имя метода, на который указывает делегат Target - Если делегат указывает на метод член класса, то этот член возвращает имя этого класса. Если Target возвращает значение типа null, то делегат указывает на статический метод Combine() - Этот статический метод используется для создания делегата, указывающего на несколько разных функций GetlnvocationList() - Возвращает массив типов Delegate, каждый из которых представляет собой запись во внутреннем списке указателей на функции делегата Remove() - Этот статический метод удаляет делегат из списка указателей на функции
Применение CarDelegate // В классе Garage предусмотрен метод, принимающий CarDelegate в качестве параметра public class Garage { ArrayList theCars = new ArrayList(); // Набор машин в гараже public Garage() // Создаем объекты машин в гараже { theCars.Add(new car(Viper, 100, 0, true, false)); theCars.Add(new car("Fred, 100, 0, false, false)); theCars.Add(new car("BillyBob, 100, 0, false, true)); theCars.Add(new car(Bart, 100, 0, true, true)); theCars.Add(new car("Stan, 100, 0, false, true)); }
Применение CarDelegate // Можно считать, что ргос - это эквивалент указателя на функцию public void ProcessCars(Car.CarDelegate proc) { // Интересно, а куда мы передаем наш вызов? Console.WriteLine(***** Calling: {0} *****, proc.Method.ToString()); // Еще одна проверка: вызываемый метод является статическим или обычным? if (proc. Target != null) Console. WriteLine("->Target: {0}, proc.Target.ToString()): else Console. WriteLine("->Target Is a static method"); // Для чего это все затевалось: при помощи делегата вызываем метод // и передаем ему все объекты Саг foreach (car с in theCars) ргос(с); }
Применение CarDelegate // Гараж передает право выполнить всю работу этим статическим функциям - наверное, у него нет хороших механиков... public class CarApp { public static void WashCar(Car с) // Первый метод, на который будет указывать делегат {If (с.Dirty) { Console.WriteLine("Cleaning a car"); с.Dirty=false; } else Console.WriteLine("This car is already clean..."); ) public static void RotateTires(Car c) // Второй метод для делегата { if(c.Rotate) { Console.WriteLine(Tires have been rotated"); c.Rotate=false; } else Console.WriteLine("Don't need to be rotated..."); } public static int Main(string[] args) { // Создаем объект Garage Garage g = new Garage(); // Моем все грязные машины g.ProcessCars(new Car.CarDelegate(WashCar)); II Меняем шины g.ProcessCars(new Car.CarDelegate (RotateTires)) ; return 0; }
Многоадресность Многоадресный делегат это объект, который может содержать в себе сразу несколько указателей на функции. // Добавляем во внутренний список указателей делегата сразу два указателя на функции: public static int Main(string[] args) { // Создаем объект Garage Garage g = new Garage(): // Создаем два новых делегата Car.CarDelegate wash = new Car.CarDelegate(WashCar): Car.CarDelegate rotate = new Car.CarDelegate(RotateTires); // Чтобы объединить два указателя на функции в многоадресном делегате, // используется перегруженный оператор сложения (+). В результате создается новый // делегат, который содержит указатели на обе функции g.ProcessCars(wash + rotate); return 0: }
Многоадресность Изменяем функцию ProcessCars public void ProcessCars (CarDelegate proc) { // Куда мы передаем вызов? foreach (Delegate d in proc.GetInvocationList()) { Console.WriteLinet"***** Calling: " + d.method.ToString() + " *****");... }
Делегаты, указывающие на обычные функции // Статические функции перестали быть статическими и переместились // во вспомогательный класс public class ServiceDept { // Уже не статическая! public void WashCar(Car с) { if(c.Dirty) Console.WriteLine('Cleaning a car"); else Console.WnteLine(This car is already clean...); } // To же самое public void RotateTires(Car c) { If(c.Rotate) Console.WriteLine(Tlres have been rotated"); else Console.WriteLine(Don't need to be rotated..."); }
Делегаты, указывающие на обычные функции // Делегаты будут указывать на обычные методы класса ServiceDept public static int Main(string[] args) { // Создаем гараж Garage g = new Garage(); II Создаем отдел обслуживания ServlceDept sd = new ServiceDept(); // Гараж делегирует работу отделу обслуживания Car.CarDelegate wash = new Car.CarDelegate(sd.WashCar); Car.CarDelegate rotate = new Car.CarDelegate(sd.RotateTires); MulticastDelegate d = wash + rotate; // Обращаемся в гараж с просьбой сделать эту работу g.ProcessCars(Car.CarDelegate)d); return 0; }