Тип delegate

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

Upload: tahlia

Post on 19-Jan-2016

79 views

Category:

Documents


0 download

DESCRIPTION

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

TRANSCRIPT

Page 1: Тип  delegate

Тип delegateТип delegate Объявление делегата определяет ссылочный тип, который является оболочкой для метода с заданной сигнатурой.

public delegate void AlarmHandler(int code);

Возвращаемое значение и параметры хранимого в делегате метода

Имя нового типа

Объект-делегат может инкапсулировать статический или экземплярный метод.

Для экземплярного метода делегат содержит пару <объект, метод>.

Делегаты в С# - это объектно-ориентированная реализация указателей на функцию.

Делегаты поддерживают тип event.

Page 2: Тип  delegate

Делегаты. ПримерДелегаты. Пример

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;}

}

delegate string MyDelegate ( int j);

class Program {

static void Main(string[] args){ MyDelegate myd1 = new MyDelegate (Abc.FS); FInMain (myd1, 1);

Abc abc1 = new Abc(55); MyDelegate myd2 = abc1.FI; // 2.0 и выше FInMain (myd2, 2);

Abc abc2 = 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)); }

}

Вывод: FS 1 FI 2 55 FI 89 55 FI 89 77

Page 3: Тип  delegate

Класс делегатаКласс делегата При компиляции кода для делегата создается класс с именем типа-делегата и методами для синхронного и асинхронного вызова делегата.

Для делегата MyDelegate из примера создается класс

рrivate sealed class MyDelegate : MulticastDelegate {

public string Invoke ( int j){ … }public System.IAsyncResult BeginInvoke (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

Page 4: Тип  delegate

Классы System.Delegate и System.MulticastDelegateКлассы System.Delegate и System.MulticastDelegate

Класс, который создается при компиляции кода для делегата, является производным от System.MulticastDelegate.

System.Object   System.Delegate      System.MulticastDelegate

Некоторые методы класса System.Delegate:

public abstract class Delegate : ICloneable, ISerializable

{...

public MethodInfo Method {get;}

public object Target {get;} // объект, для которого делегат (объект-

// делегат) вызывает экземплярный метод, null для статического метода

}

Page 5: Тип  delegate

Класс System.Delegate. ПримерКласс System.Delegate. Пример

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;}}

delegate string MyDelegate ( int j);

class Program {

static void Main(string[] args){ Abc abc1 = 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)); }

}

Вывод: Abc object n=55 FI 2 55

Page 6: Тип  delegate

Цепочки делегатовЦепочки делегатов

В классе System.MulticastDelegate поддерживается список вызовов (invocation list), который дает возможность создавать цепочки делегатов. Цепочки делегатов обычно используются при обработке событий. В классе System.Delegate есть методы для добавления и удаления делегатов из списка:

public static Delegate Combine( Delegate a, Delegate b );

public static Delegate Remove( Delegate source, Delegate value );

В C# определены операции для += и -= для добавления и удаления делегатов из цепочки (вызывают Delegate.Combine и Delegate.Remove).

Page 7: Тип  delegate

Цепочки делегатов. ПримерЦепочки делегатов. Пример

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

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;}

}

Page 8: Тип  delegate

Covariance and Contravariance в делегатахCovariance and Contravariance в делегатах В .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; ..... }}

Page 9: Тип  delegate

Анонимные методы (Anonymous Methods )Анонимные методы (Anonymous Methods ) .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

Page 10: Тип  delegate

Анонимные методы. ОграниченияАнонимные методы. Ограничения

Анонимные методы упрощают определение (instantiating ) делегатов для событий.

Ограничения:

• Нельзя передавать управление из объемлющего метода в блок анонимного метода (или наоборот) с помощью операторов goto, continue или break.

• В блоке анонимного метода можно использовать переменные объемлющего метода. Нельзя использовать значения параметров объемлющего метода с модификаторами ref и out.

• В анонимном методе нельзя использовать небезопасный (unsafe) код.

Page 11: Тип  delegate

Обобщенные делегатыОбобщенные делегаты

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; } }

Обобщенные делегаты можно определить вне класса. Делегаты, определенные в обобщенном классе, могут использовать обобщенные параметры класса и/или иметь свои собственные обобщенные параметры.

Page 12: Тип  delegate

Обобщенные делегаты. ПримерОбобщенные делегаты. Пример

В пространстве имен System определены обобщенные делегаты

public static bool Exists<T> ( T[] array, Predicate<T> match );

public static bool TrueForAll<T> ( T[] array, Predicate<T> match );

public delegate TOutput Converter<TInput,TOutput> ( TInput input )

public delegate bool Predicate<T> (T obj);

public delegate TOutput Converter<TInput,TOutput> ( TInput input );

В классе System.Array определены методы, проверяющие выполнение условий, определенных в заданном предикате, для элементов массива.

Обобщенный метод ConvertAll в классе System.Array преобразует массива одного типа в массив другого типа.

public static TOutput[] ConvertAll <TInput,TOutput> ( TInput[] array, Converter<TInput,TOutput> converter ) ; 

Page 13: Тип  delegate

Обобщенные делегаты. ПримерОбобщенные делегаты. Пример

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"); }

}

В приведенном ниже примере проверяется наличие символа ‘a’ в элементах массива и выполняется преобразование массива типа string[] в массив типа StringBuilder[].

Page 14: Тип  delegate

СобытияСобытия

public class Teacher : Person

{ public event EventHandler GetListEvent;

public static event AddHandler AddStudentEvent;

...

List<Student> stdlist = new List<Student>();

}

В классе могут быть объявлены события - поля данных типа delegate

Делегат EventHandler объявлен в BCL в пространстве имен System и используется для событий, которые не передают дополнительной информации в обработчик

public delegate void EventHandler( object sender, EventArgs e );

public class EventArgs { public EventArgs(); public static readonly EventArgs Empty; ...}

Page 15: Тип  delegate

СобытияСобытия

Делегат 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; } }}

Page 16: Тип  delegate

Вызов событияВызов события Вызвать(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; } }…}

Page 17: Тип  delegate

Подписка на событияПодписка на события Любые объекты могут подписаться на событие. На статическое событие 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);

}

}

Page 18: Тип  delegate

Подписка на событияПодписка на события

На событие 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());

}

Page 19: Тип  delegate

Inside eventInside event При компиляции класса, содержащего поле event, в классе создаются :

• закрытое поле с соответствующим типом делегата - ссылка на начало списка делегатов, которые будут вызваны как реакция на событие;

• метод add_... , вызывающий метод Combine() из класса Delegate, для добавления нового делегата в список;

• метод remove_..., вызывающий метод Remove() из класса Delegate, для удаления делегата из списка.

В класс 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) {…}

public class Teacher : Person{ public event EventHandler GetListEvent; public static event AddHandler AddStudentEvent; ...}

Page 20: Тип  delegate

События в стиле .NETСобытия в стиле .NET

public delegate void MouseEventHandler ( object sender, MouseEventArgs e );

public class Control : Component, ISynchronizeInvoke, IWin32Window { public event MouseEventHandler MouseDown; public event MouseEventHandler MouseUp; public event MouseEventHandler MouseMove; ...}

public class MouseEventArgs : EventArgs { … }

В базовой библиотеке классов .NET Framework все обработчики событий принимают два параметра:

• через первый параметр передается объект – источник события;• второй параметр является производным от класса EventArgs и содержит информацию, связанную с событием.

Например,

Page 21: Тип  delegate

Делегаты EventHandler и EventHandler<TEventArgs> Делегаты EventHandler и EventHandler<TEventArgs>

public delegate void EventHandler( object sender, EventArgs e );

public class EventArgs

{ public EventArgs();

public static readonly EventArgs Empty;

...

}

Для событий, которые не передают дополнительной информации, в BCL определен делегат EventHandler.

public delegatevoid EventHandler<TEventArgs> ( Object sender, TEventArgs e )

where TEventArgs : EventArgs

В .NET Framework 2.0 и выше для обработчиков событий определен обобщенный делегат

Page 22: Тип  delegate

Свойства события (event properties)Свойства события (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); }}

Page 23: Тип  delegate

Свойства события. ПримерСвойства события. Пример

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 addMyControlEvent addcnt_EventAcnt_EventBMyControlEvent removecnt_EventA

Page 24: Тип  delegate

События и интерфейсыСобытия и интерфейсы

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");}

}

События могут быть членами интерейсов. Класс, реализующий интерфейс, должен содержать это событие. Если класс реализует два интерфейса с событиями, имеющими одно и то же имя, необходимо явно реализовать свойства события.

Пример из документации Microsoft: