Делегаты

advertisement
Делегаты
Делегаты
Как созданные объекты могут посылать
сообщения тем объектам, которые их
породили?
При программировании под 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;
}
Download