130 likes | 434 Views
Лекция 7. Делегаты. Зачем нужны делегаты. И данные , и код располагаются в памяти компьютера по определенным адресам. Передача адресов данных в C# происходит при помощи ссылок. Передача адресов кода в C# происходит при помощи делегатов.
E N D
Лекция 7 Делегаты
Зачем нужны делегаты И данные, и код располагаются в памяти компьютера по определенным адресам. Передача адресов данных в C# происходит при помощи ссылок. Передача адресов кода в C# происходит при помощи делегатов. Делегат – это ссылка на метод, которая содержит не только его адрес, но и тип.
Делегат – это объект • Делегат – это объект, который содержит в себе: • адрес метода, • типы параметров, • тип возвращаемого значения. • Делегаты могут хранить адреса как статическихметодов, так и на методов экземпляра. using System; delegatevoidD(inti); classProgram { staticvoid F(inti) { Console.WriteLine("fff"); } void G(inti) { Console.WriteLine("ggg"); } staticvoid Main(string[] args) { Dd = newD(F); // сокращенно: D d = F; d += newD((newProgram()).G); d += F; d(0); } } Говоря точнее, один делегат может хранить адреса сразу нескольких однотипных методов. F G F
Применение делегатов Благодаря делегатам мы можем обращаться с методами, как с данными, т.е. передавать их в качестве параметров, возвращать из других методов, составлять из них массивы, коллекции и т.п. (т.е. программировать функционально). Пример Объявить метод, который получает вещественно число и произвольное количество функций типа double F(double). Метод должен вернуть результат последовательного применения всех функций к первому аргументу. Решение delegate double D(double x); double SeqFun(double x, paramsD[] funсs) {...}
Как устроен делегат Пример делегата delegateboolMyDelegate(int x); Делегат наследует библиотечный класс MulticastDelegate. К методам предка компилятор добавляет метод Invoke(), из которого вызываются все целевые методы делегатав том порядке, как они туда попали. publicsealedclassMyDelegate : System.MulticastDelegate { publicMyDelegate(object target); publicbool Invoke(int x); publicIAsyncResultBeginInvoke(int x, AsyncCallbackcb, object state); publicboolEndInvoke(IAsyncResult result); }
Обобщенные делегаты Если тип параметра делегата – object, он может ссылаться на любые функции с одним параметром, но это не будет безопасным. Более безопасны обобщенные делегаты, например: delegateTD<T>(Tx); classProgram { staticint f(inti) { returni + 1; } staticstring g(string s) { return s + "1"; } staticvoid Main(string[] args) { D<int> d1 = f; D<string> d2 = g; } }
Функции высших порядков Это функции, параметрами которых являются другие функции. Задача Объявим функцию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() delegate T Fun<T>(T x); staticList<T> ForEach<T>(List<T> list, Fun<T> f) { varresult = newList<T>(); foreach (T x inlist) result.Add(f(x)); returnresult; } Замечание. Если заменить тип первого параметра на IEnumerable<T>, функцию можно будет применять не только к спискам, но и к массивам. double[] m = { 1, 4, 16 }; var r = ForEach(m, Math.Sqrt);
Функция Where Объявить статический обобщенный метод, который получает список List<T> и обобщенный делегат bool Predicate<T>(T x) и возвращает новый список, в котором содержатся все элементы исходного списка, удовлетворяющие условию Predicate. staticList<T> Where<T>(List<T> list, Predicate<T> pre) { varresult= newList<T>(); foreach (T x inlist) if(pre(x)) result.Add(x); returnresult; } staticbool Pre1(double x) { return x > 10; } staticvoid Main() { List<double> m = newList<double> { 1, 4, 16 }; var r = Where(m, Pre1); }
Анонимные делегаты При вызове функции Where ее первый аргумент можно сделать анонимным. staticbool Pre1(double x) { return x > 10; } staticvoid Main(string[] args) { var r = Where(newList<double> { 1, 4, 16 }, Pre1); } Анонимнымможно сделать и второй аргумент. staticvoid Main(string[] args) { List<double> m = newList<double> { 1, 4, 16 }; var r = Where(m, delegate(double x) { return x > 10; }); } λ
Лямбда-выражения Лямбда-выражение в С# - это безымянный экземпляр делегата. var r = Where(m, delegate(double x) { return x > 10; }); var r = Where(m, x => x > 10 ); Лямбда-выражения пришли в С# из функционального программирования, а туда – из математической логики.
Библиотечные делегаты Func и Action Лямбда-выражения экономят код при вызове функций, а библиотечные делегаты – при их объявлении. До слайда: После слайда: delegate T Fun<T>(T x); staticList<T> ForEach<T>(List<T> list, Fun<T> f) delegateboolPre<T>(T x); staticList<T> Where<T>(List<T> list, Pre<T> f) staticList<T> ForEach<T>(List<T> list, Func<T, T> f) staticList<T> Where<T>(List<T> list, Func<T, bool> f)
Самостоятельно Объявить делегат, который ссылается на произвольную бинарную операцию над целыми числами, т.е. int Op(intx, int y). Создать объект делегата для операции взятия остатка по модулю и вызвать его синхронно и асинхронно.