Делегаты События Лекция 7
Делегаты Одиночные делегаты Цепочка делегатов Обобщенные делегаты Анонимные методы Лямбда выражения Замыкания Ковариантность и контравариантность Делегатов Интерфейсов События
Делегаты Одиночные делегаты Цепочка делегатов Обобщенные делегаты Анонимные методы Лямбда выражения Замыкания Ковариантность и контравариантность Делегатов Интерфейсов События
Обратный вызов При работе с Win API С, С ++ Вызов глобальных функций Вызов статических функций Вызов функций объекта. Необходима ссылка на объект. Host Slave ( ничего не знает о host) Вызовы функций Обратный вызов Функция обратного вызова (Callback)
объект Делегат – объект, безопасный в отношении типов, указывающий на метод Содержит : ссылку на объект ссылку на метод жестко определяет типы и количество параметров метода жестко определяет тип возвращаемого значения Может указывать на статический метод или метод экземпляра Если ссылка на объект равна null, это означает, что вызываемый метод статический Обеспечивает обратный вызов Вызов делегата синтаксически такой же как вызов обычной функции
1. Объявить ( описать ) делегат Описать тип делегата, используя специальный синтаксис ( описать класс ) 2. Создать экземпляр делегата Объявление обычной переменной типа Вызов конструктора Передача конструктору ссылки на метод экземпляра или статический метод 3. Вызвать делегат
Синтаксис delegate [attributes] [modifiers] delegate return- type type-name(args-list); похож на определения абстрактного метода, но это определение типа жестко задает сигнатуру вызываемого метода Примеры public delegate double Function2d(double x, double y); public delegate Complex ComplexFunction(Complex z); public delegate void EventHandler(object o, EventArgs e);
Пример : public delegate double ProcessResults(double x, double y); За кулисами ( создается класс наследник от MulticastDelegate) public sealed class ProcessResults : System.MulticastDelegate { public ProcessResults (object target, uint funcAdress); // для понимания public object Target { get; } public MethodInfo Method { get; } public double Invoke (double x, double y); // Сигнатура совпадает ….. } Вызов делегата синтаксически такой же как вызов обычной функции, но реально будет вызываться метод Invoke Создаются еще методы BeginInvoke() и EndInvoke() для асинхронного вызова метода Самостоятельно нельзя создать класс наследник от MulticastDelegate или от Delegate. Только через синтаксис delegate
При создании требуется связать с вызываемым методом ( передать в конструктор ) Для метода экземпляра ProcessResults del = new ProcessResults(objectName.Function); Для статического метода ProcessResults del = new ProcessResults(typeName.Function); Сокращенная запись ProcessResults del = objectName.Function; ProcessResults del = typeName.Function; Сигнатура метода и делегата должна совпадать
Как и вызов обычной функции, где в качестве вызываемой функции указывается экземпляр делегата double d = del(x, y); double d = del(4+12, 37); Вызывать метод Invoke не рекомендуется, но не возбраняется. В некоторых случаях это необходимо. double d = del.Invoke(4+12, 37);
Одиночный делегат
1. Объявить ( описать ) делегат Описать тип делегата, используя специальный синтаксис ( описать класс ) 2. Создать экземпляр делегата Объявление обычной переменной типа Вызов конструктора Передача конструктору ссылки на метод экземпляра или статический метод 3. Вызвать делегат
Делегаты можно использовать для передачи функций как параметров public delegate double RealFunc (double x); public double Integrate (double a, double b, int n, RealFunc f) { double dx = (b – a) / n, res = 0.0; for(int j = 0; j < n; j++) res += f (a + j * dx) * dx; return res; } // end of Integrate() double s = Integrate(0, 1, 1000, Math.Sin);
Делегаты Одиночные делегаты Цепочка делегатов Обобщенные делегаты Анонимные методы Лямбда выражения Замыкания Ковариантность и контравариантность Делегатов Интерфейсов События
Позволяет, вызвав один делегат, последовательно вызвать несколько методов ( с одинаковой сигнатурой ) public abstract class MulticastDelegate : Delegate { public sealed override Delegate[] GetInvocationList(); // возвращает список делегатов private IntPtr _invocationCount; private object _invocationList; … } MulticastDelegate может хранить ссылку не на одну функцию, а на несколько При вызове делегата функции могут выполнятся в произвольном порядке Если функции возвращают значение, то только последнее значение можно будет использовать В случае возникновения необработанной исключительной ситуации, прерывается вся цепочка
public abstract class Delegate : ICloneable { public static Delegate Combine (params Delegate[] delegates); public static Delegate Combine (Delegate delegate1, Delegate delegate2); public static Delegate Remove (Delegate source, Delegate value); public static Delegate RemoveAll (Delegate source, Delegate value); public virtual Delegate[] GetInvocationList(); … } Классы Delegate и MulticastDelegate неизменяемые, поэтому все методы комбинации делегатов статические и возвращают новый экземпляр делегата Combine() – объединяет делегаты или цепочки делегатов в новую цепочку делегатов Remove() – удаляет указанный делегат из цепочки ( первый встретившийся с конца ) RemoveAll() – удаляет все копии указанного делегата из цепочки GetInvocationList() – возвращает цепочку делегатов в виде массива одиночных делегатов
Использование операция сложения и вычитания +, -. Использование += и -= Примеры : ProcessResult delegate1 = new …., delegate2 = new …. ProcessResult chain = delegate1 + delegate2; chain += delegate3; ProcessResult chain = (ProcessResult)Delegate.Combine(delegate1, delegate2);
Вызов цепочки делегатов такой же ( последовательно вызываются все методы в этой цепочке ) double d = chain(5, 10); Итерация по цепочке делегатов Delegate[] delegates = chain.GetIvocationList(); ProcessResult pr = (ProcessResult) delegates[0]; double result = pr(5, 6); foreach (ProcessResult del in chain.GetInvocationList()) { Console.WriteLine(del(x,y)); }
Цепочка делегатов
Делегаты Одиночные делегаты Цепочка делегатов Обобщенные делегаты Анонимные методы Лямбда выражения Замыкания Ковариантность и контравариантность Делегатов Интерфейсов События
Аналогично обобщенным методам Значение типа параметра указывается при создании экземпляра делегата ( и только там ) Вызов делегата при этом ничем не отличается от вызова необобщенного делегата Примеры : Описание : delegate T UnarOperation (T t); delegate T SumValueDelegate (T t1, T t2) where T : struct; delegate List Delegatishe (X x, Z[] z, IEnumerable ns, K k); Создание : UnarOperation uo = emp.SetEmployee; SumValueDelegate sd = new SumValueDelegate (vector.Sum); Delegatishe d = new Delegatishe (variable.Method); Вызов : Employee e = uo(new Employee()); int i = sd(3, 4); List result = d(23,myString, EmployeeList, 5);
Уже описанные типы делегатов Делегаты принимающие параметры и ничего не возвращающие Action Action public delegate void Action (T1 arg1, T2 arg2); … Action Делегаты, возвращающие данные ( тип - параметр TResult), и, принимающие параметры : Func Func public delegate TResult Func (T1 arg1, T2 arg2); … Func Предикаты ( возвращает bool. Выполняется ли условие ): Predicate public delegate bool Predicate (T obj); Обработчики событий : EventHandlerpublic delegate void EventHandler(object sender, EventArgs e); EventHandler RoutedEventHandler
Обобщенные и стандартные делегаты
Делегаты Одиночные делегаты Цепочка делегатов Обобщенные делегаты Анонимные методы Лямбда выражения Замыкания Ковариантность и контравариантность Делегатов Интерфейсов События
Обеспечивают более простой и компактный способ определения простых делегатов Позволяет создать тело метода делегата в месте создания экземпляра делегата Анонимный метод – выражение типа « кусок кода », которое может быть присвоено переменной - делегату Синтаксис : delegate() {}; delegate_var = delegate(arg_list) { method body }; Func del = delegate(int x, int y) { return x + y; }; Action act = delegate(string s) { Console.WriteLine(s); }; act += delegate(string a) { Console.WriteLine(a); };
Анонимные методы
Делегаты Одиночные делегаты Цепочка делегатов Обобщенные делегаты Анонимные методы Лямбда выражения Замыкания Ковариантность и контравариантность Делегатов Интерфейсов События
Краткая запись анонимных делегатов Элементы функционального программирования Два вида записи : Лямбда оператор Лямбда выражение Преобразуются в анонимный метод
Лямбда оператор Лямбда оператор ( если тела метода состоит из более одного оператора ): (in_arg_list) => {method body} Пример : Func del = (int x) => { x++; return –x; } Action del = (string s) => { Console.WriteLine(s); } Типы входных параметров обычно не указываются Func del = (x) => { x++; return –x; } Func del = () => {DateTime dt = DateTime.Now; return dt; } Количество и типы входных и возвращаемых параметров определяются по делегату. Только если нужен другой ( совместимый тип ), указывается тип входного параметра. Если входной параметр один, то скобки можно опустить : Func del = x => { x++; return x*4; } Лямбда выражение : Лямбда выражение : (in_arg_list) => return_value Нет оператора return Пример : Func del = x => x*x; Func del = (x, y) => x+y; List complexList = ….; Complex finded = complexList.Find(compl => compl.Re > 5);
Лямбда выражения
Делегаты Одиночные делегаты Цепочка делегатов Обобщенные делегаты Анонимные методы Лямбда выражения Замыкания Ковариантность и контравариантность Делегатов Интерфейсов События
Лямбда выражения и анонимные методы могут использовать внутри себя переменные окружения. int i = 5; Func del = x => x+i; Происходит захват внешней переменной Осторожно, переменная может изменяться внутри анонимного метода и снаружи Анонимная рекурсия Func fact; fact = x => x>1 ? x*fact(x-1) : 1; Console.WriteLine(fact(5));
Замыкания
Делегаты Одиночные делегаты Цепочка делегатов Обобщенные делегаты Анонимные методы Лямбда выражения Замыкания Ковариантность и контравариантность Делегатов Интерфейсов События
class People { Person Person GetPersons(); } class CoursePeople : People { Student Student GetPersons(); } Соблюдение контракта базового класса class ClassPeople { Student Student GetStudents(); } class TodayPeople : ClassPeople { Person Person GetStudents(); } Нарушение контракта базового класса Нарушение принципа ООП class Person{…} class Student : Person {…} Не.NET. О бщее п онимание.Ковариантность Возможна только в выходных параметрах class People { void SetPerson void SetPersons(Person p); } class CoursePeople : People { Student void SetPersons(Student p); } Нарушение контракта базового класса Нарушение принципа ООП class ClassPeople { Student void SetStudents(Student p); } class TodayPeople : ClassPeople { void Person void SetStudents (Person p); } Соблюдение контракта базового класса Контрвариантность Возможна только во входных параметрах
Ковариантность – приведение частного к общему В терминах ООП : Там где требуется базовый тип можно присвоить экземпляр типа наследника Делегаты ковариантны по возвращаемому типу Делегаты ковариантны по возвращаемому типу Ковариантность позволяет присвоить делегату метод, возвращаемым типом которого служит класс, производный от класса, указываемого в возвращаемом типе делегата. class Employee { } class Programmer : Employee { } delegate Employee GetEmployeeDelegate(); class Person { public static Employee GetEmployee() {…} public static Programmer GetProgrammer() {…} } GetEmployeeDelegate del = Person.GetEmployee; Employee emp = del(); del = Person.GetProgrammer; Programmer prgmr = (Programmer)del();
Контрвариантность – приведение общего к частному В терминах ООП : Там где требуется тип можно присвоить экземпляр базового типа Делегаты контрвариантны по типам входных параметров Делегаты контрвариантны по типам входных параметров Контравариантность позволяет присвоить делегату метод, типом параметра которого служит класс, являющийся базовым для класса, указываемого в объявлении делегата. class Employee { } class Programmer : Employee { } delegate void SetProgrammerDelegate(Programmer emp); class Person { public static void SetEmployee(Employee emp) { } public static void SetProgrammer(Programmer prog) { } } SetProgrammerDelegate del = Person.SetEmployee; del(new Programmer()); del = Person.SetProgrammer; del(new Programmer());
out out - обозначение ковариантного типа - параметра. Тип, обозначенный как out, может присутствовать только как возвращаемый параметр out interface IEnumerable { IEnumerator GetEnumerator(); } in- in- обозначения контрвариантного типа - параметра. Тип, обозначенный как in, может присутствовать только как входной параметр in interface IComparable { int CompareTo( T other ); } Параметры in и out могут быть в одном интерфейсе interface IMyInterface { int SetT( T t ); K GetK(); K Convert(T t); } Преобразования class Person{…}; class Student : Person {…} IEnumerable students = new List (); IEnumerable persons = students; IComparable students = … IComparable persons = students;
Ковариантность и контравариантность
Делегаты Одиночные делегаты Цепочка делегатов Обобщенные делегаты Анонимные методы Лямбда выражения Замыкания Ковариантность и контравариантность Делегатов Интерфейсов События
class Car { public delegate void PetrolIsOver(string message); public public PetrolIsOver PetrolIsOverCallBack; const float lPer100 = 10; private float petrol = 50; public void Drive(int km) { for (int i =km; i > 0; i--) { petrol -= 1 * lPer100 / 100; if (petrol <= 0) { PetrolIsOverCallBack(" Приехали "); break; } if (petrol < 5) PetrolIsOverCallBack(" Бензин заканчивается "); } Car opel = new Car(); opel.PetrolIsOverCallBack = message => Console.WriteLine(message); opel.Drive(600); += opel.PetrolIsOverCallBack += message => Console.WriteLine(" Можно добавить подписку : " + message); opel.Drive(10); = opel.PetrolIsOverCallBack = message => Console.WriteLine(" А Можно и затереть подписку "); opel.Drive(10); opel.PetrolIsOverCallBack += message => Console.WriteLine(message); opel.PetrolIsOverCallBack opel.PetrolIsOverCallBack(" Более того можно и самим вызвать callback, т. е. симулировать событие "); Возможность снаружи управлять подписками Возможность снаружи вызвать делегат Нарушение инкапсуляции Возможность снаружи управлять подписками Возможность снаружи вызвать делегат Нарушение инкапсуляции
Недостаток public переменной - экземпляра делегата
Событие – некоторая программная конструкция, которая упрощает создание делегатов и методов работы с ним, служащая для оповещения заинтересованных подписчиков о возникновении некоторой интересной ситуации ( события ) На событие можно подписаться и от него можно получать оповещения Оповещения приходят в виде вызовов зарегистрированных методов
[attributes] [modifiers] event event delegate-type event-name add [ { add { accessor-body } remove remove { accessor-body } } ]; Примеры создания : public event EventHandler Selected; public event PaintEventHandler Paint; public event MouseEventHandler MouseUp; public event MyDelegate MyEvent { add { MyEvent += value; } remove { MyEvent -= value; } }
Изнутри Событие – свойство - делегат, с которым можно обращаться точно так же Вызов делегата – инициация события public delegate void MyDelegate(string message); public event MyDelegate MyEvent; … MyDelegate e = MyEvent; // для потокобезопасности if (e != null) e(" Параметры делегата "); // обязательна проверка на null Снаружи С событием можно общаться только при помощи двух аксессоров += подписаться на событие -= отписаться от события myVar.MyEvent += new MyDelegate(MyHandler); myVar.MyEvent += message => Console.WriteLine(message); myVar.MyEvent -= MyHandler; Когда происходит событие, вызывается ваш метод
События Частное событие
Тип делегата - события : delegate void EventHandler(object sender, EventArgs e); delegate void EventHandler (object sender, TEventArgs e) where TEventArgs : EventArgs; sender – объект, породивший событие Один обработчик на несколько событий EventArgs – дополнительная передаваемая информация о событии Для передачи своей информации о событии необходима создать класс наследник от EventArgs и расширить его для передачи дополнительной информации о событии
События Стандартные делегаты
Делегаты Одиночные делегаты Цепочка делегатов Обобщенные делегаты Анонимные методы Лямбда выражения Замыкания Ковариантность и контравариантность Делегатов Интерфейсов События