1 / 24

Тип delegate

Тип delegate. Объявление делегата определяет ссылочный тип, который является оболочкой для метода с заданной сигнатурой. public delegate void AlarmHandler(int code);. Возвращаемое значение и параметры хранимого в делегате метода. Имя нового типа.

tahlia
Download Presentation

Тип delegate

An Image/Link below is provided (as is) to download presentation Download Policy: Content on the Website is provided to you AS IS for your information and personal use and may not be sold / licensed / shared on other websites without getting consent from its author. Content is provided to you AS IS for your information and personal use only. Download presentation by click this link. While downloading, if for some reason you are not able to download a presentation, the publisher may have deleted the file from their server. During download, if you can't get a presentation, the file might be deleted by the publisher.

E N D

Presentation Transcript


  1. Тип delegate • Объявление делегата определяет ссылочный тип, который является оболочкой для метода с заданной сигнатурой. public delegate void AlarmHandler(int code); Возвращаемое значение и параметры хранимого в делегате метода Имя нового типа • Объект-делегат может инкапсулировать статический или экземплярный метод. • Для экземплярного метода делегат содержит пару <объект, метод>. • Делегаты в С# - это объектно-ориентированная реализация указателей на функцию. • Делегаты поддерживают тип event.

  2. Делегаты. Пример delegate string MyDelegate ( int j); class Program { static void Main(string[] args) { MyDelegate myd1 = new MyDelegate (Abc.FS); FInMain (myd1, 1); Abcabc1 = new Abc(55); MyDelegate myd2 = abc1.FI; // 2.0 и выше FInMain (myd2, 2); Abcabc2 = new Abc(77); MyDelegate myd3 = abc2.FI; MyDelegate[] arr = new MyDelegate[2] {myd2, myd3}; foreach ( MyDelegate f in arr) FInMain (f, 89); } static void FInMain (MyDelegate dlgt, int j) { Console.WriteLine ( dlgt(j));} } class Abc{ int dt; public Abc( int n) { dt = n; } public string FI (int j) { return "FI" + j + "" + dt; } public static string FS (int j) { return "FS" + j; } } Вывод: FS 1 FI 2 55 FI 89 55 FI 89 77

  3. Класс делегата • При компиляции кода для делегата создается класс с именем типа-делегата и методами для синхронного и асинхронного вызова делегата. • Для делегата MyDelegate из примера создается класс рrivate sealed class MyDelegate : MulticastDelegate { public string Invoke (int j) { … } public System.IAsyncResultBeginInvoke (int j, System.AsyncCallback, object) { … } public string EndInvoke( System.IAsyncResult obj) { … } } • Метод-делегат может быть вызван как синхронно ( возврат не произойдет до того, как метод-делегат завершит свою работу) , так и асинхронно ( метод сразу возвращает управление). • Program Files\\ Microsoft Visual Studio 8 \\ SDK \\ v2.0\\ Bin \\ ildasm.exe • Дж. Рихтер - Программирование на платформе Microsoft .NET Framework, гл. 17

  4. КлассыSystem.Delegateи System.MulticastDelegate • Класс, который создается при компиляции кода для делегата, является производным от System.MulticastDelegate. System.ObjectSystem.DelegateSystem.MulticastDelegate • Некоторые методы класса System.Delegate: public abstract class Delegate : ICloneable, ISerializable {... public MethodInfo Method {get;} public object Target {get;} // объект, для которого делегат (объект- // делегат) вызывает экземплярный метод, null для статического метода }

  5. КлассSystem.Delegate. Пример delegate string MyDelegate ( int j); class Program { static void Main(string[] args) { Abcabc1 = new Abc(55); MyDelegate myd2 = abc1.FI; FInMain (myd2, 2); } static void FInMain (MyDelegate dlgt, int j) { Console.WriteLine ( dlgt.Target); Console.WriteLine ( dlgt(j)); } } class Abc{ int dt; public Abc( int n) { dt = n; } public override string ToString() { return "Abc object n=" + dt; } public string FI (int j) { return "FI" + j+ "" + dt; } public static string FS (int j) { return "FS" + j; }} Вывод: Abc object n=55 FI 2 55

  6. Цепочки делегатов • В классе System.MulticastDelegate поддерживается список вызовов (invocation list), который дает возможность создавать цепочки делегатов. • Цепочки делегатов обычно используются при обработке событий. • В классе System.Delegate есть методы для добавления и удаления делегатов из списка: publicstaticDelegateCombine( Delegatea, Delegateb ); publicstaticDelegateRemove( Delegatesource, Delegatevalue ); • В C# определены операции для += и -= для добавления и удаления делегатов из цепочки (вызывают Delegate.Combine и Delegate.Remove).

  7. Цепочки делегатов. Пример class Abc{ int dt; public Abc( int n) { dt = n; } public string FI (int j) { Console.WriteLine(“FI”); return "FI" + j + "" + dt; } public static string FS (int j) { Console.WriteLine(“FS”); return "FS" + j; } } delegate string MyDelegate ( int j); class Class1{ static void Main(string[] args) { MyDelegate myd1 = new MyDelegate (Abc.FS); Abc abc1 = new Abc(11); MyDelegate myd2 = new MyDelegate (abc1.FI); Abc abc2 = new Abc(22); MyDelegate myd3 = new MyDelegate (abc2.FI); myd1 += myd2; myd1 += myd3; FInMain (myd1, 555); } static void FInMain (MyDelegate dlgt, int j) { Console.WriteLine ( dlgt(j));} } Вывод: FS FI FI FI 555 22

  8. CovarianceandContravariance в делегатах • В .NET Framework 2.0 и выше метод, который используется при создании делегата, может иметь сигнатуру, не полностью совпадающую с сигнатурой делегата: • тип возвращаемого значения метода может быть производным от типа, указанного при объявлении делегата ( covariance); • тип параметра(ов) может быть базовым для типа параметра, указанного при объявлении делегата (сontravariance). class Bs {} class Dr : Bs {} delegate Bs Delegate_1(); delegate void Delegate_2( Dr dr ); class Test { public static Bs F1() { return new Bs(); } public static Dr F2() { return new Dr(); } public static void F3(Bs bs) { Console.WriteLine(“F3"); } } class Program { static void Main() { // Covariance Delegate_1 dlg1 = Test.F2; // Contravariance Delegate_2 dlg2 = Test.F3; ..... } }

  9. Анонимные методы (AnonymousMethods ) • .NET Framewok 2.0 и выше поддерживают анонимные методы. • Анонимный метод позволяет передать блок кода объемлющего метода как параметр делегата. delegate string MyDelegate ( int j); class Program { static void Main(string[] args) {int jmain = 111; Console.WriteLine("1"); MyDelegate anm1 = delegate(int j) { Console.WriteLine("MyDelegate anm1"); jmain += 222; return "anm1 j= " + j + " jmain=" + jmain; }; Console.WriteLine("2"); Console.WriteLine(anm1(123)); Console.WriteLine(anm1(456)); MyDelegate anm2 = delegate(int j) { Console.WriteLine("MyDelegate anm2"); return "anm2 j= " + j + " jmain=" + jmain; }; Console.WriteLine(anm2(789)); Console.WriteLine("jmain={0}", jmain); } Вывод: 1 2 MyDelegate anm1 anm1 j= 123 jmain=333 MyDelegate anm1 anm1 j= 456 jmain=555 MyDelegate anm2 anm2 j= 789 jmain=555 jmain=555

  10. Анонимные методы. Ограничения • Анонимные методы упрощают определение (instantiating ) делегатов для событий. • Ограничения: • Нельзя передавать управление из объемлющего метода в блок анонимного метода (или наоборот) с помощью операторов goto, continue или break. • В блоке анонимного метода можно использовать переменные объемлющего метода. Нельзя использовать значения параметров объемлющего метода с модификаторами ref и out. • В анонимном методе нельзя использовать небезопасный (unsafe) код.

  11. Обобщенные делегаты • Обобщенные делегаты можно определить вне класса. • Делегаты, определенные вобобщенном классе, могут использовать обобщенные параметры класса и/или иметь свои собственные обобщенные параметры. public delegate void GenericDelegate<T>(T t); public class MyClass1<T> { public delegate void GenericDelegate_1(T t); public delegate void GenericDelegate_2 <X> (T t, X x) where X : IComparable<X>; } public class MyClass2 { public static void FS<T>(T t) { Console.WriteLine("FS<T>(T t)"); } public static void FS1(string t) { Console.WriteLine("FS1(string t)"); } public static void FS2(string t, int j) { Console.WriteLine("FS2(string t, int j)"); } } class Program { static void Main(string[] args) { GenericDelegate<string> dlg1 = MyClass2.FS; GenericDelegate<string> dlg2 = MyClass2.FS1; MyClass1<string>.GenericDelegate_1 dlg3 = MyClass2.FS1; MyClass1<string>.GenericDelegate_2<int> dlg4 = MyClass2.FS2; } }

  12. public delegate TOutput Converter<TInput,TOutput> ( TInput input ) Обобщенные делегаты. Пример • В пространстве имен System определены обобщенные делегаты public delegate boolPredicate<T> (T obj); public delegate TOutput Converter<TInput,TOutput> ( TInput input ); • В классе System.Array определены методы, проверяющие выполнение условий, определенных в заданном предикате, для элементов массива. public static bool Exists<T> ( T[] array, Predicate<T> match ); public static bool TrueForAll<T> (T[] array, Predicate<T> match ); • Обобщенный метод ConvertAll в классе System.Array преобразует массива одного типа в массив другого типа. public static TOutput[] ConvertAll<TInput,TOutput> ( TInput[] array, Converter<TInput,TOutput> converter ) ;

  13. Обобщенные делегаты. Пример • В приведенном ниже примере проверяется наличие символа ‘a’ в элементах массива и выполняется преобразование массива типа string[] в массив типа StringBuilder[]. class Program { static void Main(string[] args) { string[] str = new string[] { "abc", "efg", "asd" }; Predicate<string> pred = Program.ContainsA; Console.WriteLine(System.Array.Exists<string>(str, pred));// true Console.WriteLine(System.Array.TrueForAll<string>(str, pred)); // false // Convert array type StringBuilder[] sb = Array.ConvertAll<string, StringBuilder> (str, Program.MySbConverter); } public static StringBuilder MySbConverter(string str) { return new StringBuilder(str); } public static bool ContainsA(string str) { return str.Contains("a"); } }

  14. События • В классе могут быть объявлены события - поля данных типа delegate public class Teacher : Person { public event EventHandler GetListEvent; public static event AddHandler AddStudentEvent; ... List<Student> stdlist = new List<Student>(); } • Делегат EventHandler объявлен в BCL в пространстве имен System и используется для событий, которые не передают дополнительной информации в обработчик publicdelegatevoidEventHandler( objectsender, EventArgse ); publicclassEventArgs { publicEventArgs(); publicstaticreadonlyEventArgsEmpty; ... }

  15. События • Делегат AddHandler – пользовательский тип public delegate void AddHandler( object obj, AddHandlerArgs h); public class AddHandlerArgs : EventArgs { Student st; Teacher tch; public AddHandlerArgs( Student st, Teacher tch) { this.st = st; this.tch = tch; } public Teacher Teacher { get { return tch; } set { tch = value; } } public Student Student { get { return st; } set { st = value; } } }

  16. Вызов события • Вызвать(raise) событие можно только из класса, в котором находится событие. • Событие AddStudentEvent происходит при добавлении объекта типа Student в список public class Teacher : Person { … public void Add( Student st) { stdlist.Add(st); if (AddStudentEvent != null) AddStudentEvent(this, new AddHandlerArgs(st, this)); } … } • Событие GetListEvent происходит при вызове get в свойстве StudentList public class Teacher : Person { … public List<Student> StudentList { get { if (GetListEvent != null) GetListEvent(this, EventArgs.Empty); return stdlist; } } … }

  17. Подписка на события • Любые объекты могут подписаться на событие. • На статическое событие AddStudentEvent (пользовательский тип AddHandler ) из класса Teacher подписывается объект типа StudentsList StudentsList list1 = new StudentsList(); Teacher.AddStudentEvent += list1.AddStudentHandler; • В классе StudentsList есть метод AddStudentHandler с сигнатурой делегата AddHandler public class StudentsList { List<Student> stlist = new List<Student>(); public void AddStudentHandler( object obj, AddHandlerArgs args) { if( !stlist.Contains(args.Student)) stlist.Add(args.Student); } … }

  18. Подписка на события • На событие GetListEvent ( тип EventHandler) из класса Teacher подписывается класс Program tc1.GetListEvent += Program.GetListHandler; • В классе Program есть статический метод GetListHandler с сигнатурой делегата EventHandler public static void GetListHandler(object obj, EventArgs args) { Teacher tch = obj as Teacher; Console.WriteLine("\n GetListHandler " + tch.ToShortString()); }

  19. Inside event • При компиляции класса, содержащего поле event, в классе создаются : • закрытое поле с соответствующим типом делегата - ссылка на начало списка делегатов, которые будут вызваны как реакция на событие; • метод add_... , вызывающий метод Combine() из класса Delegate, для добавления нового делегата в список; • метод remove_..., вызывающий метод Remove() из класса Delegate, для удаления делегата из списка. public class Teacher : Person { public event EventHandler GetListEvent; public static event AddHandler AddStudentEvent; ... } • В класс Teacher при компиляции будут добавлены поля private EventHandler GetListEvent = null; private static AddHandler AddStudentEvent = null; и методы add_GetListEvent( EventHandler handler) {…} remove_GetListEvent(EventHandler handler) {…} add_AddStudentEvent( AddHandler handler) {…} remove_AddStudentEvent(AddHandler handler) {…}

  20. Событияв стиле .NET • В базовой библиотеке классов .NET Framework все обработчики событий принимают два параметра: • через первый параметр передается объект – источник события; • второй параметр является производным от класса EventArgs и содержит информацию, связанную с событием. • Например, publicdelegatevoidMouseEventHandler( objectsender,MouseEventArgse ); publicclassControl : Component, ISynchronizeInvoke, IWin32Window { publiceventMouseEventHandlerMouseDown; publiceventMouseEventHandlerMouseUp; publiceventMouseEventHandlerMouseMove; ... } publicclassMouseEventArgs : EventArgs{ … }

  21. Делегаты EventHandler иEventHandler<TEventArgs> • Для событий, которые не передают дополнительной информации, в BCL определен делегат EventHandler. publicdelegatevoidEventHandler( objectsender, EventArgse ); publicclassEventArgs { publicEventArgs(); publicstaticreadonlyEventArgsEmpty; ... } • В .NET Framework 2.0 и выше для обработчиков событий определен обобщенный делегат publicdelegate voidEventHandler<TEventArgs> ( Objectsender, TEventArgse ) whereTEventArgs : EventArgs

  22. Свойства события (event properties) • C# позволяет реализовать событие “вручную”. Для этого необходимо • явно определить поле-делегат для поддержки события; • реализовать методы-аксессоры add и remove. • Метод add выполняетсякаждый раз, когда добавляется делегат в цепочку обработчиков для события (подписка на событие), remove – при удалении делегата из цепочки (отказ от подписки). Должны быть определены оба метода add и remove. public delegate void MyEventHandler(object src, EventArgs e); class MyControl {private MyEventHandler meh; public event MyEventHandler myControlEvent { add { // Console.WriteLine("myControlEvent add"); meh += value; } remove { // Console.WriteLine("myControlEvent remove"); meh -= value; } } public void RaiseEvent() { if (meh != null) meh (this, EventArgs.Empty); } }

  23. Свойства события. Пример static void Main(string[] args) {MyControl cnt = new MyControl(); cnt.myControlEvent += new MyEventHandler(cnt_EventA); cnt.myControlEvent += new MyEventHandler(cnt_EventB); cnt.RaiseEvent(); cnt.myControlEvent -= new MyEventHandler(cnt_EventB); cnt.RaiseEvent(); } private static void cnt_EventA (object src, EventArgs e) { Console.WriteLine("cnt_EventA");} private static void cnt_EventB (object src, EventArgs e) { Console.WriteLine("cnt_EventB");} Вывод: MyControlEvent add MyControlEvent add cnt_EventA cnt_EventB MyControlEvent remove cnt_EventA

  24. События могут быть членами интерейсов. • Класс, реализующий интерфейс, должен содержать это событие. • Если класс реализует два интерфейса с событиями, имеющими одно и то же имя, необходимо явно реализовать свойства события. Пример из документации Microsoft: События и интерфейсы public delegate void MyDelegate_1(); public delegate int MyDelegate_2(string s); public interface I1 { event MyDelegate_1 MyEvent;} public interface I2 {event MyDelegate_2 MyEvent;} public class ExplicitEventsSample: I1, I2 {public event MyDelegate_1 MyEvent; // normal implementation of I1.MyEvent. private MyDelegate_2 MyEvent2Storage; // underlying storage for I2.MyEvent. event MyDelegate_2 I2.MyEvent // explicit implementation of I2.MyEvent {add { MyEvent2Storage += value;} remove { MyEvent2Storage -= value;} } private void FireEvents() {if (MyEvent != null) MyEvent(); if (MyEvent2Storage != null) MyEvent2Storage("hello"); } }

More Related