Студопедия — Необходимые теоретические сведения. Делегат — это объект, имеющий ссылку на метод
Студопедия Главная Случайная страница Обратная связь

Разделы: Автомобили Астрономия Биология География Дом и сад Другие языки Другое Информатика История Культура Литература Логика Математика Медицина Металлургия Механика Образование Охрана труда Педагогика Политика Право Психология Религия Риторика Социология Спорт Строительство Технология Туризм Физика Философия Финансы Химия Черчение Экология Экономика Электроника

Необходимые теоретические сведения. Делегат — это объект, имеющий ссылку на метод






Делегаты

Делегат — это объект, имеющий ссылку на метод. Делегат позволяет выбрать вызываемый метод во время выполнения программы. Фактически значение делегата – это адрес области памяти, где находится точка входа метода.

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

Делегат объявляется с помощью ключевого слова delegate, за которым указывается тип возвращаемого значения, имя делегата и список параметров вызываемых методов. Примеры объявления классов делегатов:

delegate int ClassDelegate(int key);delegate void XXX(int intKey, float fKey);

Xарактерной особенностью делегата является возможность его использования для вызова любого метода, который соответствует сигнатуре делегата. Это дает возможность определить во время выполнения программы, какой из методов должен быть вызван. Вызываемый метод может быть методом экземпляра, ассоциированным с объектом, либо статическим методом, ассоциированным с классом. Метод можно вызвать только тогда, когда его сигнатура(тип возвращаемого значения, набор параметров) соответствует сигнатуре делегата.

Пример объявления и применения делегата представлен ниже.

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

// целых числа и возвращающий целое число

public delegate int BinaryOp(int x, int y);

// Этот класс содержит методы, на которые будет указывать BinaryOp

public class SimpleMath

{

public int Add(int x, int y)

{ return x + y; }

public int Subtract(int x, int y)

{ return x - y; }

public static int SquareNumber(int a)

{ return a * a; }

}

class Program

{

static void Main(string[] args)

{

Console.WriteLine("***** Simple Delegate Example *****\n");

// Создадим экземпляр делегата BinaryOp, указывающий

// на метод SimpleMath.Add()

SimpleMath m = new SimpleMath();

BinaryOp b = new BinaryOp(m.Add);

// вызовем метод Add(), используя делегат

Console.WriteLine("\n10 + 10 is {0}", b(10, 10));

// Этот код является ошибочным:

// BinaryOp b = new BinaryOp(m.SquareNumber);

Console.ReadLine();

}

}

Многоадресность делегатов

Многоадресность — это способность делегата хранить несколько ссылок на различные методы, что позволяет при вызове делегата инициировать эту цепочку методов. Для создания цепочки методов необходимо создать экземпляр делегата, и пользуясь операторами + или += добавлять методы к цепочке. Для удаления метода из цепочки используется оператор - или -=.

delegate void Del(string s);

class TestClass

{

static void Hello(string s)

{

System.Console.WriteLine(" Hello, {0}!", s);

}

static void Goodbye(string s)

{

System.Console.WriteLine(" Goodbye, {0}!", s);

}

static void Main()

{

Del a, b, c, d;

// Делегат а указывает на метод Hello:

a = Hello;

// Делегат b хранит ссылку на метод Goodbye:

b = Goodbye;

// Делегат с хранит ссылки на оба метода:

c = a + b;

// Делегат d хранит ссылку только на метод Goodbye:

d = c - a;

System.Console.WriteLine("Invoking delegate a:");

a("A");

System.Console.WriteLine("Invoking delegate b:");

b("B");

System.Console.WriteLine("Invoking delegate c:");

c("C");

System.Console.WriteLine("Invoking delegate d:");

d("D");

}

}

/* Output:

Invoking delegate a: Hello, A!

Invoking delegate b: Goodbye, B!

Invoking delegate c: Hello, C! Goodbye, C!

Invoking delegate d: Goodbye, D!

*/

События

Делегаты представляют собой довольно интересные конструкции в том плане, что позволяют двум объектам в памяти участвовать в двустороннем диалоге. Так как способность одного объекта вызывать другой весьма полезна, С# предоставляет ключевое слово event, позволяющее упростить работу с делегатами. Когда компилятор обрабатывает ключевое слово event, вам автоматически предоставляются методы регистрации и отмены регистрации, а также все необходимые переменные члены для ваших типов делегатов.

Чаще всего события (events) используются в приложениях под Windows с графическим интерфейсом пользователя, в которых такие элементы управления, как Button (кнопка) или Calendar (календарь), реагируя на события, выдают информацию на той же панели, где они расположены. В качестве примера такого события можно привести, например, щелчок мышью на кнопке. Однако применение событий вовсе не ограничено приложениями с графическим интерфейсом — они могут быть исключительно полезными и в обычных консольных программах, как мы убедимся в наших примерах. Если в классе объявить член-событие, то объект — представитель этого класса сможет уведомлять объекты других классов о данном событии.

Определение события состоит из двух шагов. Во-первых, необходимо объявить делегат, содержащий методы, вызываемые при возникновении события. Во-вторых, нужно объявить события в терминах связанного делегата.

Для определения типа, который может отправлять события, применяется следующий эталон:

public class SenderOfEvents

{

public delegate retval AssociatedDelegate(args);

// retval – тип возвращаемого значения, args - список параметров

public event AssociatedDelegate NameOfEvent;

...

}

В следующем примере определяется событие с тремя методами, которые связаны с ним. При инициировании события методы выполняются. Затем, один метод удаляется из события и событие инициируется еще раз.

// Объявление делегата, связанного с событием:

public delegate void MyEventHandler();

class TestEvent

{

// Объявление события через делегат MyEventHandler.

public event MyEventHandler TriggerIt;

// Объявление метода, который инициирует событие:

public void Trigger()

{

TriggerIt();

}

// Объявим методы, которые будут подписаны на событие.

public void MyMethod1()

{

System.Console.WriteLine("Hello!");

}

public void MyMethod2()

{

System.Console.WriteLine("Hello again!");

}

public void MyMethod3()

{

System.Console.WriteLine("Good-bye!");

}

static void Main()

{

// Создадим экземпляр класса TestEvent.

TestEvent myEvent = new TestEvent();

// Подпишем на событие три метода:

myEvent.TriggerIt += new MyEventHandler(myEvent.MyMethod1);

myEvent.TriggerIt += new MyEventHandler(myEvent.MyMethod2);

myEvent.TriggerIt += new MyEventHandler(myEvent.MyMethod3);

// Инициируем событие:

myEvent.Trigger();

// Отменим подписку на событие у второго метода:

myEvent.TriggerIt -= new MyEventHandler(myEvent.MyMethod2);

System.Console.WriteLine("\"Hello again!\" unsubscribed from the event.");

// Инициируем новое событие:

myEvent.Trigger();

}

}

// Результаты работы программы: Hello! Hello again! Good-bye!

// "Hello again!" unsubscribed from the event.

// Hello! Good-bye!

Создание событий базового класса в производных классах

В следующем примере показан стандартный способ объявления событий в базовом классе таким образом, чтобы они могли создаваться и из производного класса. Этот принцип широко используется в классах Windows Forms в библиотеке классов.NET Framework.

При создании класса, который может служить базовым для других классов, следует учитывать, что события являются делегатами особого типа, которые могут быть вызваны только из класса, который их объявил. Производные классы не могут напрямую создавать события, объявленные в базовом классе. Иногда нужно, чтобы событие могло создаваться только в базовом классе, однако чаще всего следует обеспечить производному классу возможность создания событий базового класса. Для этого следует создать в базовом защищенный метод, предоставляющий оболочку для события. Путем вызова или переопределения этого метода производные классы могут опосредованно вызывать событие.

namespace BaseClassEvents

{

//Специальный EventArgs клас для хранения информации о фигуре.

public class ShapeEventArgs: EventArgs

{

private double newArea;

public ShapeEventArgs(double d)

{

newArea = d;

}

public double NewArea

{

get { return newArea; }

}

}

// Базовый класс, который содержит событие

public abstract class Shape

{

protected double area;

public double Area

{

get { return area; }

set { area = value; }

}

// Событие. Заметим, что мы используем обобщенный тип события
// EventHandler<T> и поэтому можем не объявлять отдельный делегат

public event EventHandler<ShapeEventArgs> ShapeChanged;

public abstract void Draw();

// Метод, иницирующий событие, который производный класс может
// переопределить.

protected virtual void OnShapeChanged(ShapeEventArgs e)

{

// Создадим временную копию события, чтобы избежать ошибки,

// если последний подписчик отпишется от события немедленно после //проверки на null и перед возбуждением события.

EventHandler<ShapeEventArgs> handler = ShapeChanged;

if (handler!= null)

{

handler(this, e);

}

}

}

// производный класс окружности

public class Circle: Shape

{

private double radius;

public Circle(double d)

{

radius = d;

area = 3.14 * radius;

}

public void Update(double d)

{

radius = d;

area = 3.14 * radius;

OnShapeChanged(new ShapeEventArgs(area));

}

protected override void OnShapeChanged(ShapeEventArgs e)

{

// Выполним действия, специфичные для сircle.

...

// Вызов метода базового класса, инициирующего событие

base.OnShapeChanged(e);

}

public override void Draw()

{

Console.WriteLine("Drawing a circle");

}

}

// производный класс прямоугольник

public class Rectangle: Shape

{

private double length;

private double width;

public Rectangle(double length, double width)

{

this.length = length;

this.width = width;

area = length * width;

}

public void Update(double length, double width)

{

this.length = length;

this.width = width;

area = length * width;

OnShapeChanged(new ShapeEventArgs(area));

}

protected override void OnShapeChanged(ShapeEventArgs e)

{

// Выполнение действий, специфичных для прямоугольника

...

// Вызов метода базового класса, инициирующего событие

base.OnShapeChanged(e);

}

public override void Draw()

{

Console.WriteLine("Drawing a rectangle");

}

}

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

public class ShapeContainer

{

List<Shape> _list;

public ShapeContainer()

{

_list = new List<Shape>();

}

public void AddShape(Shape s)

{

_list.Add(s);

// Подписка на событие базового класса

s.ShapeChanged += HandleShapeChanged;

}

//...Другие методы для рисования, изменения размера и т.д.

private void HandleShapeChanged(object sender, ShapeEventArgs e)

{

Shape s = (Shape)sender;

// Сообщение для демонстрации

Console.WriteLine("Received event. Shape area is now {0}", e.NewArea);

// Перерисовка фигуры

s.Draw();

}

}

class Test

{

static void Main(string[] args)

{

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

Circle c1 = new Circle(54);

Rectangle r1 = new Rectangle(12, 9);

ShapeContainer sc = new ShapeContainer();

// Добавим фигуры в контейнер

sc.AddShape(c1);

sc.AddShape(r1);

// Инициируем события

c1.Update(57);

r1.Update(7, 7);

// Задержка окна консоли для просмотра результатов

System.Console.WriteLine("Press any key to exit.");

System.Console.ReadKey();

}

}

}

/* Output: Received event. Shape area is now 178.98

Drawing a circle

Received event. Shape area is now 49

Drawing a rectangle

*/

Контрольные вопросы

1) Дайте определение делегата. Приведите фрагмент кода иллюстрирующий создание и использование делегата.

2) Что понимается под многоадресностью делегата?

3) В чем состоят преимущества использования делегатов?

4) В какой момент осуществляется выбор вызываемого метода в

случае использования делегатов?

5) Что является значением делегата?

6) В чем состоит практическое значение многоадресности?

7) Каким образом осуществляется создание цепочки методов для

многоадресных делегатов?

8) Для чего служит ключевое слово event? Являются ли события членами классов?

9) Приведите синтаксис описания события в общем виде.

Проиллюстрируйте его фрагментом программы на языке C#.

10) Как подписаться на событие и отписаться от него?

11) Как создать событие базового класса в производном?







Дата добавления: 2015-09-06; просмотров: 854. Нарушение авторских прав; Мы поможем в написании вашей работы!



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

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

Обзор компонентов Multisim Компоненты – это основа любой схемы, это все элементы, из которых она состоит. Multisim оперирует с двумя категориями...

Композиция из абстрактных геометрических фигур Данная композиция состоит из линий, штриховки, абстрактных геометрических форм...

Ганглиоблокаторы. Классификация. Механизм действия. Фармакодинамика. Применение.Побочные эфффекты Никотинчувствительные холинорецепторы (н-холинорецепторы) в основном локализованы на постсинаптических мембранах в синапсах скелетной мускулатуры...

Шов первичный, первично отсроченный, вторичный (показания) В зависимости от времени и условий наложения выделяют швы: 1) первичные...

Предпосылки, условия и движущие силы психического развития Предпосылки –это факторы. Факторы психического развития –это ведущие детерминанты развития чел. К ним относят: среду...

Неисправности автосцепки, с которыми запрещается постановка вагонов в поезд. Причины саморасцепов ЗАПРЕЩАЕТСЯ: постановка в поезда и следование в них вагонов, у которых автосцепное устройство имеет хотя бы одну из следующих неисправностей: - трещину в корпусе автосцепки, излом деталей механизма...

Понятие метода в психологии. Классификация методов психологии и их характеристика Метод – это путь, способ познания, посредством которого познается предмет науки (С...

ЛЕКАРСТВЕННЫЕ ФОРМЫ ДЛЯ ИНЪЕКЦИЙ К лекарственным формам для инъекций относятся водные, спиртовые и масляные растворы, суспензии, эмульсии, ново­галеновые препараты, жидкие органопрепараты и жидкие экс­тракты, а также порошки и таблетки для имплантации...

Studopedia.info - Студопедия - 2014-2024 год . (0.014 сек.) русская версия | украинская версия