c# desktop. Занятие 06

38
Темы лекции: Делегаты, события, анонимные методы. Практическое задание: Делегаты, события, анонимные методы. Тренер: Игорь Шкулипа, к.т.н. Платформа .Net и язык программирования C#. Занятие 6

Upload: igor-shkulipa

Post on 21-Mar-2017

90 views

Category:

Education


2 download

TRANSCRIPT

Page 1: C# Desktop. Занятие 06

Темы лекции: Делегаты, события, анонимные методы.

Практическое задание: Делегаты, события, анонимные методы.

Тренер: Игорь Шкулипа, к.т.н.

Платформа .Net и язык программирования C#.

Занятие 6

Page 2: C# Desktop. Занятие 06

http://www.slideshare.net/IgorShkulipa 2

Делегаты

Делегаты являются ссылками на методы, инкапсулирующими настоящиеуказатели и предоставляющими удобные сервисы для работы с ними.Ссылки представляют собой объекты соответствующего типа. Вседелегаты являются объектами типа System.Delegate илиSystem.MulticastDelegate, который является наследником первого.

[SerializableAttribute]

[ClassInterfaceAttribute(ClassInterfaceType.AutoDual)]

[ComVisibleAttribute(true)]

public abstract class Delegate : ICloneable, ISerializable

[SerializableAttribute]

[ComVisibleAttribute(true)]

public abstract class MulticastDelegate : Delegate

Делегаты обеспечивают, поддерживаемый языком, механизм определенияи выполнения обратных вызовов.

В С++ есть аналог делегатов – это указатели на функции.

Page 3: C# Desktop. Занятие 06

http://www.slideshare.net/IgorShkulipa 3

Создание и использование делегатов

public delegate double SomeDelegate(double x, double y);

Встречая такую конструкцию, компилятор автоматически генерируеткласс-наследник MulticastDelegate, автоматически реализуя всенеобходимые методы. В частности, метод Invoke(), который, по сути, иявляется вызовом делегата.

К делегатам можно привязывать как статичные методы класса, так иметоды конкретного экземпляра.

На практике, вызов делегата подобен вызову обычной функции.

public delegate Complex ComplAction(Complex c);

static void Main(string[] args)

{

Complex cc1 = new Complex(1, 2);

Complex cc2 = new Complex(2, 3);

ComplAction[] ca = new ComplAction[2];

ca[0] = cc1.Add;

ca[1] = cc1.Sub;

Console.WriteLine(ca[0](cc2).ToString());

Console.WriteLine(ca[1](cc2).ToString());

}

Page 4: C# Desktop. Занятие 06

http://www.slideshare.net/IgorShkulipa 4

Класс Complex и объявление делегата

public class Complex : IComparable

{

public double Re { get; set; }

public double Im { get; set; }

// ... Конструкторы

public Complex Add(Complex right)

{

return new Complex(this.Re + right.Re, this.Im + right.Im);

}

public Complex Sub(Complex right)

{

return new Complex(this.Re - right.Re, this.Im - right.Im);

}

public void Print()

{

Console.WriteLine(this.ToString());

}

// Полное описание класса см. Презентацию № 3.

}

Page 5: C# Desktop. Занятие 06

http://www.slideshare.net/IgorShkulipa 5

Цепочки делегатовЦепочка делегатов позволяет создавать связный список делегатов так, чтобы, при

вызове первого делегата из списка, вызывались все остальные последовательно.

Для управления цепочками делегатов, класс System.Delegate предоставляетследующие методы:

1. Для группирования делегатов в цепочки используется метод Combine()

public class Delegate: ICloneable, ISerializable

{

public static Delegate Combine(Delegate[] );

public static Delegate Combine(Delegate first, Delegate second);

}

2. Для удаления делегата из списка служат методы Remove и RemoveAll

public class Delegate: ICloneable, ISerializable

{

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

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

}

Так же, для цепочек делегатов перегружены операции сложения (+) и вычитания (-),обеспечивающие добавление и удаление делегата из списка.

Page 6: C# Desktop. Занятие 06

http://www.slideshare.net/IgorShkulipa 6

Использование цепочек делегатов

public delegate void ComplPrint();

class Program

{

static void Main(string[] args)

{

Complex cc1 = new Complex(1, 2);

Complex cc2 = new Complex(2, 3);

Complex cc3 = new Complex(3, 4);

ComplPrint[] cp = new ComplPrint[3];

cp[0] = cc1.Print;

cp[1] = cc2.Print;

cp[2] = cc3.Print;

ComplPrint chainedDelegates = cp[0] + cp[1] + cp[2];

chainedDelegates();

Console.ReadKey();

}

}

}

1+i2

2+i3

3+i4

Page 7: C# Desktop. Занятие 06

http://www.slideshare.net/IgorShkulipa 7

Итерация по цепочкам делегатов

Если требуется вызвать конкретный делегат из цепочки, или вызвать все делегаты вопределенной последовательности, то для этого, класс Delegate предоставляетметод GetInvocationList(), который возвращает массив делегатов из цепочки.

public delegate void ComplPrint();

class Program {

static void Main(string[] args)

{

Complex cc1 = new Complex(1, 2);

Complex cc2 = new Complex(2, 3);

Complex cc3 = new Complex(3, 4);

ComplPrint[] cp = new ComplPrint[3];

cp[0] = cc1.Print;

cp[1] = cc2.Print;

cp[2] = cc3.Print;

ComplPrint chainedDelegates = cp[0] + cp[1] + cp[2];

Delegate[] delegateList = chainedDelegates.GetInvocationList();

for (int i = delegateList.Length - 1; i >= 0; i--) {

((ComplPrint)delegateList[i])();

}

Console.ReadKey();

}

}

3+i4

2+i3

1+i2

Page 8: C# Desktop. Занятие 06

http://www.slideshare.net/IgorShkulipa 8

Делегаты открытого экземпляраДелегаты открытого экземпляра (несвязанные делегаты) используются, когда необходимо,

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

Класс MethodInfo выявляет атрибуты метода и обеспечивает доступ к его метаданным.

using System.Reflection;

public delegate void PrintAllComplex(Complex c);

class Program {

static void Main(string[] args) {

Complex cc1 = new Complex(1, 2); Complex cc2 = new Complex(2, 3);

Complex cc3 = new Complex(3, 4);

List<Complex> cl = new List<Complex>();

cl.Add(cc1); cl.Add(cc2); cl.Add(cc3);

// Создаем делегат открытого эезкмпляра

MethodInfo mi =

typeof(Complex).GetMethod("Print",

BindingFlags.Public | BindingFlags.Instance);

PrintAllComplex openDelegate =

(PrintAllComplex)Delegate.CreateDelegate(typeof(PrintAllComplex), mi);

foreach (var c in cl) {

openDelegate(c);

}

Console.ReadKey();

}

}

1+i2

2+i3

3+i4

Page 9: C# Desktop. Занятие 06

http://www.slideshare.net/IgorShkulipa 9

Паттерн проектирования «Мост»

Используется для того, чтобы разделять абстракцию и реализацию так,чтобы они могли изменяться независимо.

Page 10: C# Desktop. Занятие 06

http://www.slideshare.net/IgorShkulipa 10

Реализация. Классы реализаторов с общим интерфейсом

public interface Iimplementor {

void Operation();

}

class Implementor1 : Iimplementor {

void IImplementor.Operation() {

Console.WriteLine("Implementor 1");

}

}

class Implementor2 : Iimplementor {

void IImplementor.Operation() {

Console.WriteLine("Implementor 2");

}

}

Page 11: C# Desktop. Занятие 06

http://www.slideshare.net/IgorShkulipa 11

Класс-абстракция

class Abstraction

{

protected IImplementor implementor;

public Abstraction() { }

public void SetImplementor(IImplementor implementor)

{

this.implementor = implementor;

}

public void Operation()

{

implementor.Operation();

}

}

Page 12: C# Desktop. Занятие 06

http://www.slideshare.net/IgorShkulipa 12

Использование моста

class Program

{

static void Main(string[] args)

{

Abstraction abstr = new Abstraction();

abstr.SetImplementor(new Implementor1());

abstr.Operation();

abstr.SetImplementor(new Implementor2());

abstr.Operation();

Console.ReadKey();

}

}

Результат:Implementor 1

Implementor 2

Page 13: C# Desktop. Занятие 06

http://www.slideshare.net/IgorShkulipa 13

События

События позволяют классу или объекту уведомлять другие классы илиобъекты о возникновении каких-либо ситуаций. Класс, отправляющий(или вызывающий) событие, называется издателем, а классы,принимающие (или обрабатывающие) событие, называютсяподписчиками.

В C# в приложении Windows Forms или веб-приложении пользовательподписывается на события, вызываемые элементами управления,такими как кнопки и поля со списками.

С точки зрения синтаксиса объявления, события – это, по сути,сокращение, которое избавляет от необходимости ручного созданияцепочек делегатов и регистрации делегатов в цепочках.

Делегат – указатель на метод.Событие – указатель на несколько методов.

Page 14: C# Desktop. Занятие 06

http://www.slideshare.net/IgorShkulipa 14

Свойства событий

События имеют следующие свойства:

• Издатель определяет момент вызова события, подписчикиопределяют предпринятое ответное действие.

• У события может быть несколько подписчиков. Подписчикможет обрабатывать несколько событий от нескольких издателей.

• События, не имеющие подписчиков, никогда не возникают.

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

• Если событие имеет несколько подписчиков, то при еговозникновении происходит синхронный вызов обработчиковсобытий.

• В библиотеке классов .NET Framework в основе событий лежитделегат EventHandler и базовый класс EventArgs.

Page 15: C# Desktop. Занятие 06

http://www.slideshare.net/IgorShkulipa 15

Пример: класс аргументов события

public class OnPowerOnArgs : EventArgs

{

public string DisplayText

{

get;

private set;

}

public OnPowerOnArgs(string strText)

{

DisplayText = strText;

}

}

Page 16: C# Desktop. Занятие 06

http://www.slideshare.net/IgorShkulipa 16

Класс-издатель события

public class UserInterface

{

public event EventHandler<OnPowerOnArgs> OnPowerOn;

public UserInterface()

{

OnPowerOn += HandlePowerOn;

}

public void InitiatePowerOn(object sender, OnPowerOnArgs args)

{

OnPowerOn.Invoke(sender, args);

}

protected void HandlePowerOn(object sender, OnPowerOnArgs args)

{

Console.WriteLine(sender.ToString()+": "+args.DisplayText);

}

}

Page 17: C# Desktop. Занятие 06

http://www.slideshare.net/IgorShkulipa 17

Класс-подписчик

public class User

{

static void Main(string[] args)

{

UserInterface ui = new UserInterface();

ui.InitiatePowerOn

("Vasiliy Pupkin", new OnPowerOnArgs("Power On"));

Console.ReadKey();

}

}

Vasiliy Pupkin: Power On

Page 18: C# Desktop. Занятие 06

http://www.slideshare.net/IgorShkulipa 18

Паттерн Chain of Responsibility

Паттерн Chain of Responsibility позволяет избежать жесткойзависимости отправителя запроса от его получателя, при этомзапрос может быть обработан несколькими объектами.Объекты-получатели связываются в цепочку. Запроспередается по этой цепочке, пока не будет обработан.

Вводит конвейерную обработку для запроса с множествомвозможных обработчиков.

Объектно-ориентированный связанный список с рекурсивнымобходом.

Page 19: C# Desktop. Занятие 06

http://www.slideshare.net/IgorShkulipa 19

Общая схема паттерна

Клиент

Обработчик 1

Обработчик 2

Обработчик 3

Обработчик ...

Обработчик n

Page 20: C# Desktop. Занятие 06

http://www.slideshare.net/IgorShkulipa 20

Реализация цепочки. Классы событий

public abstract class IEvent{

public string EventType { get; set; }

}

class Event1 : IEvent {

public Event1() { EventType = "Event1"; }

}

class Event2 : IEvent {

public Event2() { EventType = "Event2"; }

}

class Event3 : IEvent {

public Event3() { EventType = "Event3"; }

}

class Event4 : IEvent {

public Event4() { EventType = "Event4"; }

}

class Event5 : IEvent {

public Event5() { EventType = "Event5"; }

}

class Event6 : IEvent {

public Event6() { EventType = "Event6"; }

}

Page 21: C# Desktop. Занятие 06

http://www.slideshare.net/IgorShkulipa 21

Базовый класс-обработчикpublic abstract class BaseHandler {

public BaseHandler() { Next = null; }

public virtual void Handle(IEvent ev) {

if (PrivateEvent.EventType == ev.EventType)

{

Console.WriteLine("{0} successfully handled", PrivateEvent.EventType);

} else {

Console.WriteLine("Sending event to next Handler...");

if (Next != null)

Next.Handle(ev);

else

Console.WriteLine("Unknown event. Can't handle.");

}

}

protected void SetNextHandler(BaseHandler newHandler) {

Next = newHandler;

}

protected BaseHandler Next { get; set; }

protected IEvent PrivateEvent { get; set; }

}

Page 22: C# Desktop. Занятие 06

http://www.slideshare.net/IgorShkulipa 22

Классы-обработчики

class Handler5 : BaseHandler {

public Handler5() {

PrivateEvent = new Event5(); Next = null;

} }

class Handler4 : BaseHandler {

public Handler4() {

PrivateEvent = new Event4(); Next = new Handler5();

} }

class Handler3 : BaseHandler {

public Handler3() {

PrivateEvent = new Event3(); Next = new Handler4();

} }

class Handler2 : BaseHandler {

public Handler2() {

PrivateEvent = new Event2(); Next = new Handler3();

} }

class Handler1 : BaseHandler {

public Handler1() {

PrivateEvent = new Event1(); Next = new Handler2();

} }

Page 23: C# Desktop. Занятие 06

http://www.slideshare.net/IgorShkulipa 23

Класс тестового приложенияpublic class ChainApplication {

public ChainApplication() {

eventHandler = new Handler1(); Rand = new Random();

}

public void Run(int EventCount) {

for (int i = 0; i < EventCount; i++) {

HandleEvent(GenerateRandomEvent());

} }

private void HandleEvent(IEvent ev) {

eventHandler.Handle(ev);

}

private IEvent GenerateRandomEvent() {

IEvent result;

switch (Rand.Next(1,6)) {

case 0: result = new Event1(); break; case 1: result = new Event2(); break;

case 2: result = new Event3(); break; case 3: result = new Event4(); break;

case 4: result = new Event5(); break; default: result = new Event6(); break; }

Console.WriteLine("Generated event: {0}", result.EventType);

return result; }

private BaseHandler eventHandler;

private Random Rand;

}

Page 24: C# Desktop. Занятие 06

http://www.slideshare.net/IgorShkulipa 24

Результат цепочки ответственностей

class Program

{

static void Main(string[] args)

{

ChainApplication app = new ChainApplication();

app.Run(3);

Console.ReadKey();

}

}

Результат:Generated event: Event4

Sending event to next Handler...

Sending event to next Handler...

Sending event to next Handler...

Event4 successfully handled

Generated event: Event6

Sending event to next Handler...

Sending event to next Handler...

Sending event to next Handler...

Sending event to next Handler...

Sending event to next Handler...

Unknown event. Can't handle.

Generated event: Event5

Sending event to next Handler...

Sending event to next Handler...

Sending event to next Handler...

Sending event to next Handler...

Event5 successfully handled

Page 25: C# Desktop. Занятие 06

http://www.slideshare.net/IgorShkulipa 25

Паттерн «Стратегия»

Паттерн Стратегия (Strategy) предназначен для определениясемейства алгоритмов и инкапсуляции каждого из них иобеспечения их взаимозаменяемости.

Переносит в отдельную иерархию классов все детали, связанныес реализацией алгоритмов.

Page 26: C# Desktop. Занятие 06

http://www.slideshare.net/IgorShkulipa 26

Пример

public abstract class IStrategy

{

public abstract void Use();

protected void WakeUp() { Console.WriteLine("Wake up."); }

protected void Shower() { Console.WriteLine("Take shower."); }

protected void Dress() { Console.WriteLine("Dress."); }

protected void GoToBusStop() { Console.WriteLine("Go to bus stop."); }

protected void Wait() { Console.WriteLine("Wait."); }

protected void Arrive() { Console.WriteLine("Arrive."); }

protected void DoWork() { Console.WriteLine("Do work."); }

protected void DoExercises() { Console.WriteLine("Do exercises."); }

protected void Walk() { Console.WriteLine("Walk."); }

protected void GoOut() { Console.WriteLine("Go out."); }

protected void GoToPark() { Console.WriteLine("Go to park."); }

}

Page 27: C# Desktop. Занятие 06

http://www.slideshare.net/IgorShkulipa 27

Классы конкретных стратегий

class GoToWorkStrategy : Istrategy {

public override void Use()

{

WakeUp(); Shower(); Dress(); GoOut();

GoToBusStop(); Wait(); Arrive(); DoWork();

}

}

class GoWalkStrategy : Istrategy {

public override void Use()

{

GoOut(); GoToPark(); Walk();

}

}

class GoToGymStrategy : Istrategy {

public override void Use()

{

GoOut(); GoToBusStop(); Arrive(); DoExercises();

}

}

Page 28: C# Desktop. Занятие 06

http://www.slideshare.net/IgorShkulipa 28

Клиент стратегий

public abstract class IStrategyClient

{

public abstract void UseStrategy();

public void SetStrategy(IStrategy st) { strategy = st; }

protected IStrategy strategy;

}

class StrategyClient1 : IStrategyClient

{

public StrategyClient1() { }

public override void UseStrategy()

{

strategy.Use();

}

}

Page 29: C# Desktop. Занятие 06

http://www.slideshare.net/IgorShkulipa 29

Использование стратегий

class Program

{

static void Main(string[] args)

{

IStrategyClient stClient = new StrategyClient1();

stClient.SetStrategy(new GoToWorkStrategy());

stClient.UseStrategy();

Console.WriteLine();

stClient.SetStrategy(new GoToGymStrategy());

stClient.UseStrategy();

Console.WriteLine();

stClient.SetStrategy(new GoWalkStrategy());

stClient.UseStrategy();

Console.ReadKey();

}

}

Wake up.

Take shower.

Dress.

Go out.

Go to bus stop.

Wait.

Arrive.

Do work.

Go out.

Go to bus stop.

Arrive.

Do exercises.

Go out.

Go to park.

Walk.

Page 30: C# Desktop. Занятие 06

http://www.slideshare.net/IgorShkulipa 30

Анонимные методы

Новый класс стратегий:

public delegate void StrategyDelegate();

public abstract class IStrategyClient

{

public abstract void UseStrategy();

public StrategyDelegate Strategy { get; set; }

}

class StrategyClient1 : IStrategyClient

{

public StrategyClient1() { }

public override void UseStrategy()

{

Strategy();

}

}

Page 31: C# Desktop. Занятие 06

http://www.slideshare.net/IgorShkulipa 31

Использование анонимных методовclass Program

{

static void Main(string[] args)

{

IStrategyClient stClient = new StrategyClient1();

IStrategy goWork = new GoToWorkStrategy();

IStrategy goGym = new GoToGymStrategy();

IStrategy goWalk = new GoWalkStrategy();

stClient.Strategy = delegate {

Console.WriteLine("Anonymous Method:");

goWork.Use();

Console.WriteLine(); };

stClient.UseStrategy();

stClient.Strategy = delegate {

Console.WriteLine("Anonymous Method:");

goGym.Use(); Console.WriteLine(); };

stClient.UseStrategy();

stClient.Strategy = delegate {

Console.WriteLine("Anonymous Method:");

goWalk.Use(); Console.WriteLine(); };

stClient.UseStrategy();

Console.ReadKey();

}

}

Anonymous Method:

Wake up.

Take shower.

Dress.

Go out.

Go to bus stop.

Wait.

Arrive.

Do work.

Anonymous Method:

Go out.

Go to bus stop.

Arrive.

Do exercises.

Anonymous Method:

Go out.

Go to park.

Walk.

Page 32: C# Desktop. Занятие 06

http://www.slideshare.net/IgorShkulipa 32

Расширяющие методы

Расширяющие методы (методы расширения) позволяют "добавлять" методы всуществующие типы без создания нового производного типа, перекомпиляции илииного изменения исходного типа.

Расширяющие методы являются особым видом статического метода, но онивызываются, как если бы они были методами экземпляра в расширенном типе. Дляклиентского кода, написанного на языках C#, нет видимого различия междувызовом метода расширения и вызовом методов, фактически определенных в типе.

public static class MyExtensions

{

public static int WordCount(this String str)

{

return str.Split(new char[] { ' ', '.', '?' },

StringSplitOptions.RemoveEmptyEntries).Length;

}

}

Методы расширения определяются как статические методы, но вызываются спомощью синтаксиса обращения к методу экземпляра. Их первый параметропределяет, с каким типом оперирует метод, и перед параметром идетмодификатор this. Методы расширения находятся в области действия, только еслипространство имен было явно импортировано в исходный код с помощьюдирективы using.

Page 33: C# Desktop. Занятие 06

http://www.slideshare.net/IgorShkulipa 33

Пример расширяющих методов

public static class ExtensionMethods

{

public static double Angle(this Complex compl)

{

if ((compl.Re == 0) && (compl.Im >= 0)) {

return Math.PI / 2;

}

if ((compl.Re == 0) && (compl.Im < 0)) {

return 3 * Math.PI / 2;

}

return Math.Atan(compl.Im / compl.Re);

}

}

class Program

{

static void Main(string[] args)

{

Complex c1 = new Complex(1,2);

Console.WriteLine(c1);

double angle = c1.Angle();

Console.WriteLine(angle);

Console.ReadKey();

}

}

Page 34: C# Desktop. Занятие 06

http://www.slideshare.net/IgorShkulipa 34

Шаблон проектирования «Visitor»

Паттерн Visitor определяет операцию, выполняемую на каждомэлементе из некоторой структуры без изменения классов этихобъектов.

• Паттерн Visitor определяет операцию, выполняемую на каждомэлементе из некоторой структуры. Позволяет, не изменяя классыэтих объектов, добавлять в них новые операции.

Применение расширяющих методов значительно упрощает реализациюэтого паттерна.

Page 35: C# Desktop. Занятие 06

http://www.slideshare.net/IgorShkulipa 35

Классы «компонентов»

public class SomeClass1

{

public SomeClass1(int c) { SomeProperty1 = c; }

public int SomeProperty1 { get; set; }

}

public class SomeClass2

{

public SomeClass2(int c) { SomeProperty2 = c; }

public int SomeProperty2 { get; set; }

}

public class SomeClass3

{

public SomeClass3(int c) { SomeProperty3 = c; }

public int SomeProperty3 { get; set; }

}

Page 36: C# Desktop. Занятие 06

http://www.slideshare.net/IgorShkulipa 36

Класс-посетитель

public static class Visitor

{

public static void Visit(this SomeClass1 sc1)

{

Console.WriteLine(sc1.SomeProperty1);

}

public static void Visit(this SomeClass2 sc2)

{

Console.WriteLine(sc2.SomeProperty2);

}

public static void Visit(this SomeClass3 sc3)

{

Console.WriteLine(sc3.SomeProperty3);

}

}

Page 37: C# Desktop. Занятие 06

http://www.slideshare.net/IgorShkulipa 37

Использование

class Program

{

static void Main(string[] args)

{

SomeClass1 c1 = new SomeClass1(1);

SomeClass2 c2 = new SomeClass2(2);

SomeClass3 c3 = new SomeClass3(3);

c1.Visit(); c2.Visit(); c3.Visit();

Console.ReadKey();

}

}

1

2

3

Page 38: C# Desktop. Занятие 06

http://www.slideshare.net/IgorShkulipa 38

Лабораторная работа № 6. Делегаты, события

Используя класс «Квадратная матрица» из лабораторной работы №3,реализовать тестовое приложение «Матричный калькулятор».

Добавить к классу расширяющие методы транспонирования матрицы инахождения следа матрицы (сумма диагональных элементов).

Создать делегат на основе анонимного метода приведения матрицы кдиагональному виду.

Реализовать меню для управления вычислений на основе делегатов сиспользованием паттерна проектирования «Цепочка ответственности».