ДЕЛЕГАТЫ Лекция 7 1
Зачем нужны делегаты 2 И данные, и код располагаются в памяти компьютера по определенным адресам. Передача адресов данных в C# происходит при помощи ссылок. Передача адресов кода в C# происходит при помощи делегатов. Делегат – это ссылка на метод, которая содержит не только его адрес, но и тип.
Делегат – это объект Делегат – это объект, который содержит в себе: адрес метода, типы параметров, тип возвращаемого значения. Делегаты могут хранить адреса как статических методов, так и на методов экземпляра. using System; delegate void D(int i); class Program { static void F(int i) { Console.WriteLine("fff"); } void G(int i) { Console.WriteLine("ggg"); } static void Main(string[] args) { D d = new D(F); // сокращенно: D d = F; d += new D((new Program()).G); d += F; d(0); } 3 Говоря точнее, один делегат может хранить адреса сразу нескольких однотипных методов. F G F
Применение делегатов 4 Благодаря делегатам мы можем обращаться с методами, как с данными, т.е. передавать их в качестве параметров, возвращать из других методов, составлять из них массивы, коллекции и т.п. (т.е. программировать функционально). Пример Объявить метод, который получает вещественно число и произвольное количество функций типа double F(double). Метод должен вернуть результат последовательного применения всех функций к первому аргументу. Решение delegate double D(double x); double SeqFun(double x, params D[] funсs) {...}
Как устроен делегат Делегат наследует библиотечный класс MulticastDelegate. К методам предка компилятор добавляет метод Invoke(), из которого вызываются все целевые методы делегата в том порядке, как они туда попали. public sealed class MyDelegate : System.MulticastDelegate { public MyDelegate(object target); public bool Invoke(int x); public IAsyncResult BeginInvoke(int x, AsyncCallback cb, object state); public bool EndInvoke(IAsyncResult result); } delegate bool MyDelegate(int x); 5 Пример делегата
Обобщенные делегаты Если тип параметра делегата – object, он может ссылаться на любые функции с одним параметром, но это не будет безопасным. Более безопасны обобщенные делегаты, например: delegate T D (T x); class Program { static int f(int i) { return i + 1; } static string g(string s) { return s + "1"; } static void Main(string[] args) { D d1 = f; D d2 = g; } 6
Функции высших порядков 7 Это функции, параметрами которых являются другие функции. Задача Объявим функцию ForEach, которая получает список и функцию-параметр и возвращает новый список, который получается из заданного путем поэлементного применения к нему функции-параметра. Например, задан список { 1, 2, 3, 4, 5} и функция "y = 10 * x". ForEach должна вернуть новый список { 10, 20, 30, 40, 50}. Если функция-параметр "y = x * x", то функция ForEach вернет список {1, 4, 9, 16, 25} и т.д.
Функция ForEach() 8 delegate T Fun (T x); static List ForEach (List list, Fun f) { var result = new List (); foreach (T x in list) result.Add(f(x)); return result; } double[] m = { 1, 4, 16 }; var r = ForEach(m, Math.Sqrt); Замечание. Если заменить тип первого параметра на IEnumerable, функцию можно будет применять не только к спискам, но и к массивам.
Функция Where 9 Объявить статический обобщенный метод, который получает список List и обобщенный делегат bool Predicate (T x) и возвращает новый список, в котором содержатся все элементы исходного списка, удовлетворяющие условию Predicate. static List Where (List list, Predicate pre) { var result = new List (); foreach (T x in list) if (pre(x)) result.Add(x); return result; } static bool Pre1(double x) { return x > 10; } static void Main() { List m = new List { 1, 4, 16 }; var r = Where(m, Pre1); }
Анонимные делегаты 10 При вызове функции Where ее первый аргумент можно сделать анонимным. static bool Pre1(double x) { return x > 10; } static void Main(string[] args) { var r = Where(new List { 1, 4, 16 }, Pre1); } Анонимным можно сделать и второй аргумент. static void Main(string[] args) { List m = new List { 1, 4, 16 }; var r = Where(m, delegate(double x) { return x > 10; }); } λ
Лямбда-выражения 11 var r = Where(m, delegate(double x) { return x > 10; }); var r = Where(m, x => x > 10 ); Лямбда-выражение в С# - это безымянный экземпляр делегата. Лямбда-выражения пришли в С# из функционального программирования, а туда – из математической логики.
Библиотечные делегаты Func и Action 12 Лямбда-выражения экономят код при вызове функций, а библиотечные делегаты – при их объявлении. До слайда: После слайда: delegate T Fun (T x); static List ForEach (List list, Fun f) delegate bool Pre (T x); static List Where (List list, Pre f) static List ForEach (List list, Func f) static List Where (List list, Func f)
Самостоятельно 13 1.Объявить делегат, который ссылается на произвольную бинарную операцию над целыми числами, т.е. int Op(int x, int y). Создать объект делегата для операции взятия остатка по модулю и вызвать его синхронно и асинхронно.