Лекция 5.pptx
- Количество слайдов: 41
Лекция 5 Делегаты и лямбда выражения
Делегаты (Представители) 2
Что такое делегаты? Делегат - это класс который может хранить ссылку на метод определённой сигнатуры. Наследуется от Object. Метод может быть как статическим так и методом экземпляра класса. В случае метода экземпляра класса в делегате должна также храниться и ссылка на экземпляр. Делегат имеет возможность вызвать метод ссылку на который он хранит. Двумя важными преимуществами делегатов являются поддержка асинхронных вызовов и поддержка позднего связывания. Фактически делегат является объектноориентированной ссылкой на метод. 3
Использование делегатов Объектно-ориентированные технологии 4
Статический метод и метод экземпляра class Some. Class { public static void Some. Static. Method(string arg) { Console. Write. Line("Статический метод говорит: " + arg); } public void Some. Instance. Method(string arg) { Console. Write. Line("Метод экземпляра говорит: " + arg); } } 5
Использование делегатов class Program { public delegate void Some. Delegate(string arg); static void Main() { Some. Delegate static. Delegate = new Some. Delegate(Some. Class. Some. Static. Method); static. Delegate("Привет Мир!"); Some. Class some. Obj = new Some. Class(); Some. Delegate instance. Delegate = new Some. Delegate(some. Obj. Some. Instance. Method); instance. Delegate("Мир Привет!"); }} 6
Что происходит когда объявляем делегат? На самом деле создаётся класс наследующий класс Multicast. Delegate. Модификатор доступа у этого класса такой же как у delegate. В этом классе определяется конструктор с двумя параметрами. Помимо конструктора в этом классе присутствует метод для вызова метода делегата он имеет те же самые параметры и возвращаемое значение что и делегат. Кроме того присутствуют два метода для вызова метода делегата асинхронно. 7
Класс для делегата public class Some. Delegate: System. Multicast. Delegate { public Some. Delegate(object target, Int 32 method. Ptr); public void virtual Invoke(string arg); public virtual IAsync. Result Begin. Invoke(string arg, Async. Callback callback, Object object); public virtual void End. Invoke(IAsync. Result result); } 8
Что происходит когда создаём делегат? Независимо от того для какого метода создаётся делегат вызывается конструктор в качестве второго параметра ему передаётся указатель на метод. Когда создаётся делегат для статического метода в качестве первого параметра конструктору передаётся null. Когда создаётся делегат для метода экземпляра класса в качестве первого параметра передаётся ссылка на объект. 9
Создание делегата Some. Delegate static. Delegate = new Some. Delegate(Some. Class. Some. Static. Method); // на самом деле // Some. Delegate(null, . . . ) // второй параметр int который определяет метод Some. Class some. Obj = new Some. Class(); Some. Delegate instance. Delegate = new Some. Delegate(some. Obj. Some. Instance. Method); // на самом деле // Some. Delegate(some. Obj, . . . ) // второй параметр int который определяет метод 10
Что происходит когда вызываем делегат? Вызов метода делегата компилятор преобразовывает в вызов метода Invoke. Этому методу передаются параметры переданные при вызове делегата. Возвращаемое значение этого метода возвращается вызовом делегата. Несмотря на то что этот метод публичный компилятор не позволяет его вызывать вручную. 11
Вызов делегата instance. Delegate("Мир Привет!"); // на самом деле // instance. Delegate. Invoke("Мир Привет!"); static. Delegate("Привет Мир!"); // на самом деле //static. Delegate. Invoke("Привет Мир!"); 12
Зачем два класса для делегатов? Первоначально планировалось что будет две разновидности делегатов. Одиночный делегат должен был поддерживать вызов одного метода. Групповой делегат должен был поддерживать целый список вызовов. В конце концов решили что одиночный делегат это не такая уж и хорошая идея. Сейчас все делегаты наследуют Multicast. Delegate и поддерживают список вызовов. 13
Класс Delegate public abstract class Delegate : ICloneable, Iserializable { public static Delegate Combine( Delegate a, Delegate b ); protected virtual Delegate Combine. Impl( Delegate d ) { throw new Multicast. Not. Supported. Exception(. . . ); } ); public Object Dynamic. Invoke( Object[] args 14
Класс Delegate protected virtual Object Dynamic. Invoke. Impl( Object[] args ); public virtual Delegate[] Get. Invocation. List(); public static Delegate Remove( Delegate source, Delegate value ); protected virtual Delegate Remove. Impl( Delegate d ); public static Delegate Create. Delegate(Type del. Type, Method. Info mi); } public Method. Info Method { get; } public Object Target { get; } 15
Методы класса Delegate §Метод Combine предназначен для добавления делегатов в цепочку. §Метод Remove предназначен для удаления делегатов из цепочки. §Метод Dynamic. Invoke предназначен для вызова методов делегата с использованием позднего связывания. §Метод Create. Delegate предназначен для динамического создания делегата. Этот метод используется для позднего связывания. §Свойство Method можно использовать чтобы получить информацию о методе который будет вызван делегатом. §Свойство Target можно использовать чтобы получить информацию об объекте метод которого будет вызван делегатом. 16
Класс Multicast. Delegate public abstract class Multicast. Delegate : Delegate { protected override sealed Delegate Combine. Impl( Delegate follow ); public override sealed Delegate[] Get. Invocation. List(); protected override sealed Delegate Remove. Impl( Delegate value ); protected override sealed Object Dynamic. Invoke. Impl( Object[] args ) } public static bool operator ==( Multicast. Delegate d 1, Multicast. Delegate d 2 ); public static bool operator !=( Multicast. Delegate d 1, Multicast. Delegate d 2 ); 17
Методы класса Multicast. Delegate §Метод Combine. Impl предназначен для добавления делегатов в цепочку §Метод Remove. Impl предназначен для удаления делегатов из цепочки §Метод Get. Invocation. List предназначен для получения всех делегатов в цепочке. §Метод Dynamic. Invoke. Impl предназначен для вызова методов делегата с использованием позднего связывания. 18
Цепочки делегатов Объектно-ориентированные технологии 19
Цепочки делегатов Делегаты можно собирать вместе. Тогда при вызове одного делегата вызываются все делегаты в цепочке. Возвращаемые значения кроме последнего игнорируются. Если один из вызванных методов выбрасывает исключение вызов оставшихся методов не происходит. Если такая ситуация не устраивает можно разбить цепочку на отдельные делегаты и вызвать каждого из них индивидуально. 20
Вызов цепочки делегатов class Hello. Class { public static void Hello. Console() { Console. Write. Line("Привет Мир!"); } public static void Hello. Msg. Box() { Message. Box. Show("Привет Мир!"); } } 21
Вызов цепочки делегатов class Program { public delegate void Hello. Delegate(); static void Main() { Hello. Delegate hello = null; hello += new Hello. Delegate(Hello. Class. Hello. Msg. Box); hello += new Hello. Delegate(Hello. Class. Hello. Console); } } hello(); 22
Вызов делегатов по одиночке Delegate[] array. Of. Delegates = Some. Delegate. Get. Invocation. List(); foreach (Some. Delegate some. Delegate in array. Of. Delegates) { try { some. Delegate("Привет Мир!"); } catch(Some. Exception) { // обработка исключения } } 23
Позднее связывание
Позднее связывание До сих пор мы рассматривали раннее связывание делегатов. Во время компиляции было известно делегат какого типа создаётся и для вызова какого метода он будет использован. Можно использовать и позднее связывание когда во время компиляции неизвестен ни тип делегата ни метод. Делегаты можно создавать и вызывать динамически. С помощью такого подхода можно строить расширяемые приложения. 25
Позднее связывание class Binary. Class { public static int Add(int n 1, int n 2) { return n 1+n 2; } public static int Subtract(int n 1, int n 2) { return n 1 -n 2; } } 26
Позднее связывание class Program { public delegate int Binary. Delegate(int n 1, int n 2); static void Main(string args[]) { Delegate d = Delegate. Create. Delegate(typeof(Binary. Delegate), typeof(Binary. Class), args[0]); Console. Write. Line(d. Dynamic. Invoke(10, 20)); } } 27
Ковариантность и контрвариантность 28
Ковариантность и контрвариантность Ковариантность позволяет методу иметь тип возвращаемого значения который является потомком типа возвращаемого значения делегата. Действительно если мы ожидаем от делегата объект класса A то мы можем использовать и объект класса B так как он является его потомком. Контрвариантность позволяет методу иметь типы параметров которые являются предками типов параметров делегата Действительно делегат гарантирует что передаваемый ему параметр типа B. Тип В является потомком A и поэтому может использоваться в методе которому нужен A. 29
Ковариантность class A {. . . }; class B : A {. . . }; class Get. Aor. B { public static A get. A(){. . . } public static B get. B(){. . . } } class Program() { static void Main() { delegate A Adelegate(); Adelegate adelegate = new Adelegate(Get. Aor. B. get. A); Adelegate bdelegate = new Adelegate(Get. Aor. B. get. B); } } 30
Контрвариантность class A {. . . } class B : A {. . . } class Use. Aor. B { public static void Use. A (A arg){. . . } public static void Use. B (B arg ){. . . } } class Program { static void Main() { delegate void Use. Bdelegate(B arg); Use. Bdelegate use. Bdelegate = new Use. Bdelegate(Use. Aor. B. Use. B); Use. Bdelegate use. Adelegate = new Use. Bdelegate(Use. Aor. B. Use. A); }} 31
Анонимные методы 32
Анонимные методы Иногда с помощью делегата необходимо вызвать метод который больше нигде не используется. Часто такая ситуация возникает когда делегаты используются для обработки событий. Для того чтобы не создавать специальный метод который больше нигде не используется можно связывать экземпляр делегата при создании с блоком кода. Такой блок фактически является безымянным или анонимным методом. 33
Без анонимного метода delegate void Some. Delegate(String arg); class Some. Class { static void To. Console(String arg) { Console. Write. Line(arg); } } class Program { static void Main() { Some. Delegate some. Delegate = delegate(Some. Class. To. Console); } } 34
Анонимный метод delegate void Some. Delegate(String arg); class Program { static void Main() { Some. Delegate some. Delegate = delegate(String arg) { Console. Write. Line(arg); }; } } Видим, что блок безымянный. Просто конкретному делегату мы указали данный блок. 35
Лямбда-выражения 36
Лямбда выражения, это лучшая замена анонимным методам, которая основываются на новом синтаксисе. Во всех лямбда выражениях должен быть лямбда оператор “=>”. Этот оператор разделяет выражение на две части. В левой части – параметры (может быть несколько); В правой части – тело метода;
Есть еще такие понятия, как одиночные и блочные лямбда выражения. Все просто, если после цикла for или оператора if включается одна строчка кода, то незачем включать эту строку в фигурные скобки. Это относится к одиночным лямбда выражениям, но если в выражение несколько строчек, нужно захватить этот блок в фигурные скобки. Использование лямбда выражения : 1. Определения делегата, совместимого с лямбда-выражением. 2. Создание экземпляра делегата, которому присваивается лямбда-выражение. 3. Этот этап, уже само использование выражение, которое происходит при обращение к делегату.
1. delegate int Pow(int change); 2. Pow pow = change => change * change; 3. Console. Write. Line(pow(10)); // результат будет 100 / /сигнатура делегата // совместимого с лямбда-выражением. delegate int Lambda. Delegate(int step); Lambda. Delegate lambda. Del = a => ++a; int step = 1; int finish = 10; while (step <= finish) { Console. Write("Бегун сейчас на {0} километре. " + " До финиша осталось {1}n", step, finish - step); step = lambda. Del(step); }
Результат будет тот же, что при использование анонимных методов. Типы параметров не указываются. Компилятор сам определяет, какие типы имеют параметры, исходя из делегата и его типа, который возвращается. Хотя, можно и явно указать тип, к примеру так: Lambda. Delegate lambda. Del = (int a) => ++a; Обратите внимание на выражение “++a”. Об этом нюансе говорилось в курсе по C# первой части. Если переписать, к примеру, вот так: “a++” – будет бесконечный цикл!
Блочные лямбда-выражения: Чтоб создать блочное лямбда-выражение, нужно просто его взять в фигурные скобки. Преимущества в использование блочных выражений проявляется в том, что есть возможность управлять такими операторами как if, for и т. д. Все также как при написание простых методов. 1. delegate int Pow(int change); 2. Pow lambda. Del = change => { if (change != 0) return change * change; return 0; }; 3. Console. Write. Line(pow(10));
Лекция 5.pptx