Высокоуровневые методы информатики и программирования Лекция 9 Делегаты План работы • • • • • • Понятие делегатов. Описание новых delegate типов. Операции над делегатами. Использование делегатов. Стандартные делегаты. Лямбда выражения. Делегаты - delegate • В программе часто требуется хранить и передавать адреса методов. – Функции обратного вызова (callback functions, callbacks). – Используя callbacks можно указать, чтобы одна функция обращалась к другой функции. – С помощью этого подхода разработчики в Win32 могут обрабатывать нажатие клавиш, движение мыши, выбор пунктов меню. • Для реализации обратных вызовов в C# используется новая разновидность пользовательских типов - delegate (делегат). • C помощью данного типа описываются типы данных, которые могут хранить ссылки на методы с заданной сигнатурой. • C использование ключевого слова delegate можно описать новый тип. – Новый тип можно описать с помощью класса: class xxxx {...} – И новый тип можно описать с помощью делегата: delegate <сигнатура> Операции над переменными типа delegate • Тип delegate поддерживает три важных элемента информации: – Адрес метода на который он ссылается. – Параметры (если они есть) данного метода. – Возвращаемое методом значение. • После того, как тип delegate описан можно создавать экземпляры данного типа (переменные) • Этим переменным можно присваивать ссылки на методы с заданной сигнатурой. • После задания ссылки на метод этот метод можно вызывать с помощью переменной delegate с помощью операции (). • Для описания типа делегата нужно указать ключевое слово delegate после которого задать сигнатуру, которой он должен соответствовать. Описание нового типа • Сигнатура это –тип возвращаемого значения –последовательность типов передаваемых параметров • Например: int Func (int a; float b) • • Когда компилятор обработает описание типа делегата, то он создает класс производный от класса System.MulticastDelegate. Этот класс (вместе со своим базовым классом System.Delegate) предоставляет переменным данного типа необходимую инфраструктуру для хранения списка ссылок на методы с заданной сигнатурой, которые могут быть позднее вызваны. Методы – – – – • Конструктор с одним параметром – названием метода. Базовый метод Invoke() (для синхронного вызова) Методы BeginInvoke() и EndInvoke() для асинхронного вызова. Метод Delegate[] GetInvocationList() – возвращает массив переменных типа System.Delegate, каждая из которых представляет конкретный метод, который может быть вызван. Переопределены операции – – – – – = != == += (Delegate.Combine()) -= (Delegate.Remove()) Пример • Например, нужно создать тип делегата с именем BinaryOp, который может хранить ссылки на любые методы, которые возвращают целые значения и принимают в параметры целого типа: // This delegate can point to any method, // taking two integers and returning an integer. public delegate int BinaryOp(int x, int y); • Создается автоматически класс sealed class BinaryOp : System.MulticastDelegate { public BinaryOp(object target, uint functionAddress); public int Invoke(int x, int y); public IAsyncResult BeginInvoke(int x, int y, AsyncCallback cb, object state); public int EndInvoke(IAsyncResult result); } Другой пример public delegate string MyDelegate(bool a, bool b, bool c); • В этот раз будет создан следующий класс sealed class MyDelegate : System.MulticastDelegate { public MyDelegate(object target, uint functionAddress); public string Invoke(bool a, bool b, bool c); public IAsyncResult BeginInvoke(bool a, bool b, bool c, AsyncCallback cb, object state); public string EndInvoke(IAsyncResult result); } Использование делегатов • Объявление нового типа ссылок с помощью типа delegate: <режим доступа> delegate <тип> <имя типа> (формальные параметры); Например: public delegate int MyFunc(int n); • Создание экземпляра нового типа ссылок <имя типа> <имя переменной> = new <имя типа> (имя функции); Например: int myFunction(int k) { return (2*k);} ... f = new MyFunc (myFunction); • Использование экземпляра делегата (вызов методов, ссылки на которые сохранены) – задание ссылки: <имя переменной> = new <имя типа> (<имя метода>); <имя переменной> = <имя метода>; – вызов методов <имя переменной>(фактические параметры); Например: int n = f(5); Будут вызываться последовательно все методы, ссылки на которые сохранены Пример // объявляем новый тип public delegate int MyFunc(int n); // создаем метод int myFunction(int k) { return (2*k);} ... // создаем экземпляр ссылки на методы MyFunc f; // задаем значение экземпляру (присвоение ссылки) f = new MyFunc (myFunction); // или f = myFunction; // упрощенный вариант // вызов метода с использованием ссылки int n = f(5); Стандартные делегаты • • • • • • • В библиотеке FCL описаны стандартные обобщенные делегаты, которые активно используются в методах классов библиотеки: System.Action() – принимает значение (или значения) и ничего не возвращает; public delegate void Action<T>( T obj ) System.Comparison() – принимает два параметра и возвращает целое значение (< 0: x < y; 0: x == y; > 0: x > y) public delegate int Comparison<T>( T x, T y ) System.Converter() – преобразование объекта из одного типа в другой public delegate TOutput Converter<TInput, Toutput> ( TInput input ) System.EventHandler – обработчик событий public delegate void EventHandler<TEventArgs> ( Object sender, TEventArgs e ) where TEventArgs : EventArgs System.Func() – принимает значение (или значения) и возвращает результат public delegate TResult Func<T, TResult>( T arg ) System.Predicate() – принимает значение и возвращает bool public delegate bool Predicate<T>( T obj ) Анонимные методы • • Это блок операторов, который используется в качестве параметра для используемого делегата. Например: Compute func1 = delegate(float a, float b) { float с = a+b; return c; }; • • • Преимуществом анонимных методов - уменьшается объем кода, не нужно описывать метод только для того, чтобы использовать его с делегатом. Такой способ удобен при задании делегатов для событий. Используя экземпляр делегата можно вызвать метод, ссылка на который хранится в нем, используя обычную запись вызова методов. Например: float b = func(2.5F, 3.4F); Например: public MyForm() { listBox = new ListBox(...); textBox = new TextBox(...); addButton = new Button(...); addButton.Click += delegate { listBox.Items.Add(textBox.Text); }; } Лямбда выражения • • • • • Лямбда выражения это анонимные методы (функции), которые могут содержать выражения и операторы и могут использоваться для создания делегатов или деревьев выражений (expression tree types). Все lambda выражения используют лямбда операция =>, который читается как "перейти к". С левой стороны lambda операции задаются входные параметры (если они есть), а с правой стороны задается блок с выражениями и операторами. (input parameters) => expression Например, лямбда выражение x => x * x читается, как "x переходит к x умноженному на x." Данное лямбда выражение может быть присвоено типу delegate следующим образом: delegate int del(int i); static void Main(string[] args) { del myDelegate = x => x * x; int j = myDelegate(5); //j = 25 } • Способ записи (int x) => x + 1 ; // is the same as (int x) => { return x + 1; } Примеры multicasting ссылок // объявление делегата public delegate int Func(int n); // объявление переменной типа delegate Func a; a = new Func (myFunction); // a = myFunction; a += new Func (Calc); // a += Calc; ... // вызов методов по ссылке n = a(5); …. int myFunction (int n) { … } int Calc (int n) { … } ...