Студопедия

КАТЕГОРИИ:


Архитектура-(3434)Астрономия-(809)Биология-(7483)Биотехнологии-(1457)Военное дело-(14632)Высокие технологии-(1363)География-(913)Геология-(1438)Государство-(451)Демография-(1065)Дом-(47672)Журналистика и СМИ-(912)Изобретательство-(14524)Иностранные языки-(4268)Информатика-(17799)Искусство-(1338)История-(13644)Компьютеры-(11121)Косметика-(55)Кулинария-(373)Культура-(8427)Лингвистика-(374)Литература-(1642)Маркетинг-(23702)Математика-(16968)Машиностроение-(1700)Медицина-(12668)Менеджмент-(24684)Механика-(15423)Науковедение-(506)Образование-(11852)Охрана труда-(3308)Педагогика-(5571)Полиграфия-(1312)Политика-(7869)Право-(5454)Приборостроение-(1369)Программирование-(2801)Производство-(97182)Промышленность-(8706)Психология-(18388)Религия-(3217)Связь-(10668)Сельское хозяйство-(299)Социология-(6455)Спорт-(42831)Строительство-(4793)Торговля-(5050)Транспорт-(2929)Туризм-(1568)Физика-(3942)Философия-(17015)Финансы-(26596)Химия-(22929)Экология-(12095)Экономика-(9961)Электроника-(8441)Электротехника-(4623)Энергетика-(12629)Юриспруденция-(1492)Ядерная техника-(1748)

Делегати




Делегат - це вид класу, призначений для зберігання посилань на методи. Делегат можна передати як параметр, а потім викликати інкапсульований в ньому метод. Делегати використовуються для підтримки подій, а також як самостійна конструкція мови. Розглянемо спочатку другий випадок.

 

10.1.1. Опис делегатів

 

Опис делегата задає сигнатуру методів, які можуть бути викликані з його допомогою:

 

[ атрибути ] [ специфікатори ] delegate тип ім'я_делегата ([ параметри ])

 

Специфікатори делегата мають той же сенс, що і для класу, причому допускаються тільки специфікатори new, public, protected, internal і private.

Тип описує повертаєме значення методів, що викликаються за допомогою делегата, а необов'язковими параметрами делегата є параметри цих методів. Делегат може зберігати посилання на декілька методів і викликати їх по черзі. При цьому сигнатури всіх методів повинні збігатися.

 

Приклад опису делегата: public delegate void D (int i);

 

Тут описаний тип делегата, який може зберігати посилання на методи, повертаючі void і що приймають один параметр цілого типу.

Делегат, як і всякий клас, є типом даних. Його базовим класом є клас System.Delegate, що забезпечує свого “нащадка” деякими корисними елементами, які ми розглянемо пізніше. Успадковувати від делегата не можна.

 

Оголошення делегата можна розміщувати безпосередньо в просторі імен або усередині класу.

 

10.1.2. Використання делегатів

 

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

§ Делегати застосовуються в основному для наступних цілей:

§ отримання можливості визначати метод, що викликається, не за допомогою компіляції, а динамічно під час виконання програми;

§ забезпечення зв'язку між об'єктами за типом “джерело - спостерігач”;

§ створення універсальних методів, в які можна передавати інші методи;

§ підтримка механізму зворотних викликів.

Розглянемо спочатку приклад реалізації першої з цих цілей. У лістингу 10.1 оголошується делегат, за допомогою якого один і той же оператор використовується для виклику двох різних методів (Сoo1 і Hack).

 

Лістинг 10.1. Просте використання делегата

using System;

using System.Collections.Generic;

using System.Linq;

using System.Text;

 

namespace examp65

{

 

delegate void Del (ref string s); // оголошення делегата

 

class Classl

{

public static void C001 (ref string s) // метод 1

{

Console.WriteLine("викликаний метод Cool");

s = s.Replace('l', '1').Replace('O', '0').Replace('o', '0');

}

 

public static void Hack (ref string s) // метод 2

 

{

char[] mas = s.ToCharArray();

s = "";

for (int i = 0; i < mas.Length; ++i)

s+= ((i & 1) == 0)? char.ToUpper(mas[i]):mas[i];

}

 

static void Main()

{

string s = "cOol hackers";

Del d; // екземпляр делегата

/*

d = new Del(C001); // ініціалізація методом 1

d += new Del(Hack);

*/

 

d = null;

for (int i = 0; i < 2; i++)

{

 

 

if (i == 0) d = new Del(C001);

if (i == 1)d = new Del(Hack); // ініціалізація методом 2

d(ref s); // використання делегата для виклику методів

Console.WriteLine(s);

}

}

}

}

Результат роботи програми:

cOOl hackers

COOl hAcKeRs

 

Використання делегата має той же синтаксис, що і виклик методу. Якщо делегат зберігає посилання на декілька методів, вони викликаються послідовно в тому порядку, в якому були додані в делегат.

Додавання методу в список виконується або за допомогою методу Combine, успадкованого від класу System.Delegate, або, що зручніше, за допомогою перевантаженої операції складання. От як виглядає змінений метод Main з попереднього лістингу, в якому одним викликом делегата виконується перетворення початкового рядка відразу двома методами:

 

static void Main()

{

string s = "cool hackers";

Del d = new Del (C00l);

d += new Del(Hack); // додавання методу в делегат

d(ref s);

Console.WriteLine(s); // результат: C001 hAcKeRs

}

 

При виклику послідовності методів за допомогою делегата необхідно враховувати наступне:

§ сигнатура методів повинна в точності відповідати делегатові;

§ методи можуть бути як статичними, так і звичайними методами класу;

§ кожному методу в списку передається один і той же набір параметрів;

§ якщо параметр передається по посиланню, зміни параметра в одному методі відіб'ються на його значенні при виклику наступного методу;

§ якщо параметр передається з ключовим словом out або метод повертає значення, результатом виконання делегата є значення, сформоване останнім з методів списку (у зв'язку з цим рекомендується формувати списки тільки з делегатів, що мають повертаєме значення типу void);

§ якщо в процесі роботи методу виникло виключення, не оброблене в тому ж методі, подальші методи в списку не виконуються, а відбувається пошук обробників в охоплюючих делегат блоках;

§ спроба викликати делегат, в списку якого немає жодного методу, викликає генерацію виключення System.NullReferenceException.

10.1.3. Патерн “спостерігач”

 

Розглянемо застосування делегатів для забезпечення зв'язку між об'єктами за типом “джерело - спостерігач”. В результаті розбиття системи на множину спільно працюючих класів з'являється необхідність підтримувати узгоджений стан взаємозв'язаних об'єктів. При цьому бажано уникнути жорсткої зв'язаності класів, оскільки це часто негативно позначається на можливості багатократного використання коду.

Для забезпечення гнучкого, динамічного зв'язку між об'єктами під час виконання програми застосовується наступна стратегія. Об'єкт, званий джерелом, при зміні свого стану, який може представляти інтерес для інших об'єктів, посилає їм повідомлення. Ці об'єкти називаються спостерігачами. Отримавши повідомлення, спостерігач опитує джерело, щоб синхронізувати з ним свій стан.

Прикладом такої стратегії може служити зв'язок об'єкту з різними його уявленнями, наприклад, зв'язок електронної таблиці із створеними на її основі діаграмами.

Програмісти часто використовують одну і ту ж схему організації і взаємодії об'єктів в різних контекстах. За такими схемами закріпилася назва патерни, або шаблони проектування. Описана стратегія відома під назвою патерн “спостерігач”.

Спостерігач (observer) визначає між об'єктами залежність типу «один до багатьох», так що при зміні стані одного об'єкту всі залежні від нього об'єкти отримують сповіщення і автоматично оновлюються. Розглянемо приклад (лістинг 10.2), в якому демонструється схема сповіщення джерелом трьох спостерігачів. Гіпотетична зміна стану об'єкту моделюється повідомленням «OOPS!». Один з методів в демонстраційних цілях зроблений статичним.

 

Лістинг 10.2. Сповіщення спостерігачів за допомогою делегата

using System;

using System.Collections.Generic;

using System.Linq;

using System.Text;

namespace examp66

{

public delegate void Del(object o); // оголошення делегата

 

class Subj // клас-джерело

{

Del dels; // оголошення екземпляра делегата

 

public void Register (Del d) // реєстрація делегата

{

dels += d;

}

 

public void OOPS() // щось відбулося

{

Console.WriteLine("OOPS!");

if (dels!= null) dels(null); // сповіщення спостерігачів

 

}

}

 

class ObsA // клас-спостпрігач

{

public void Do(object о) // реакція на подію джерела

{

Console.WriteLine("Бачу, що OOPS!");

}

}

 

class ObsB // клас-спостерігач

{

public static void See(object о) // реакція на подію джерела {

{

Console.WriteLine("Я теж бачу, що OOPS!");

}

}

 

class Program

{

static void Main(string[] args)

{

Subj s = new Subj(); // об'єкт класу-джерела

ObsA o1 = new ObsA(); // об'єкти

ObsA o2 = new ObsA(); // класу-спостерігача

s.Register(new Del(o1.Do)); // реєстрація методів

s.Register(new Del(o2.Do)); // спостерігачів в джерелі

s.Register(new Del(ObsB.See)); // (екземпляри делегата)

s.OOPS(); // ініціалізація події

}

}

}

 

У джерелі оголошується екземпляр делегата, в цей екземпляр заносяться методи тих об'єктів, які хочуть отримувати повідомлення про зміну стану джерела. Цей процес називається реєстрацією делегатів. При реєстрації ім'я методу додається до списку. Зверніть увагу: для статичного методу називається ім'я класу, а для звичайного методу - ім'я об'єкту. При настанні “години X” всі зареєстровані методи по черзі викликаються через делегат.

Результат роботи програми:

OOPS!

Бачу, що OOPS!

Бачу, що OOPS!

Я теж бачу, що OOPS!

 

Для забезпечення зворотного зв'язку між спостерігачем і джерелом делегат оголошений з параметром типу object, через який в метод, що викликається, передається посилання на об'єкт. Отже, в методі, що викликається, можна отримувати інформацію про стан об'єкту, що викликається, і посилати йому повідомлення (тобто викликати методи цього об'єкту).

Зв'язок “джерело – спостерігач” встановлюється під час виконання програми для кожного об'єкту окремо. Якщо спостерігач більше не хоче отримувати повідомлення від джерела, можна видалити відповідний метод з писку делегата за допомогою методу Remove або перевантаженій операції віднімання, наприклад:

 

public void UnRegister(Del d) // видалення делегата

{

dels -= d;

}

 

10.1.4. Операції

 

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

Делегати, які розрізняються тільки іменами, вважаються за тих, які мають різні типи.

З делегатами одного типу можна виконувати операції простого і складного привласнення, наприклад:

 

Del d1 = new Del (o1.Do): // o1.Do

Del d2 = new Del (o2.Do); // o2.Do

Del d3 = d1 + d2; // ol.Do і o2.Do

d3 += d1; // ol.Do. o2.Do і ol.Do

d3 -= d2; // o1.Do і ol.Do

 

Ці операції можуть знадобитися, наприклад, в тому випадку, якщо в різних обставинах потрібно викликати різні набори і комбінації наборів методів.

Делегат, як і рядок string, є незмінним типом даних, тому при будь-якій зміні створюється новий екземпляр, а старий згодом видаляється складальником сміття.

 

10.1.5. Передача делегатів в методи

Оскільки делегат є класом, його можна передавати в методи як параметр. Цим забезпечується функціональна параметризація: у метод можна передавати не тільки різні дані, але і різні функції їх обробки. Функціональна параметризація застосовується для створення універсальних методів і забезпечення можливості зворотного виклику.

Як універсальний метод можна привести метод виведення таблиці значень функції. У метод передається діапазон значень аргументу, крок його зміни і вид обчислюваної функції. Цей приклад наводиться далі.

Зворотним викликом (callback) є виклик функції, яка передається в іншу функцію як параметр. Розглянемо рис. 10.1. Допустимо, в бібліотеці описана функція А, параметром якої є ім'я іншої функції. У коді описується функція з необхідною сигнатурою (В) і передається у функцію А. Виконання функції А приводить до виклику В, тобто управління передається з бібліотечної функції назад в код.

Рис. 10.1. Механізм зворотного виклику

 

Механізм зворотного виклику широко використовується в програмуванні. Наприклад, він реалізується в багатьох стандартних функціях Windows.

Приклад передачі делегата як параметр приведений в лістингу 10.3. Програма виводить таблицю значень функції на заданому інтервалі з кроком, рівним одиниці.

 

Лістинг 10.3. Передача делегата через список параметрів

using System;

using System.Collections.Generic;

using System.Linq;

using System.Text;

 

namespace examp67

{

public delegate double Fun(double x); // оголошення делегата

 

class Classl

{

public static void Table (Fun F, double x, double b)

{

Console.WriteLine(" X Y ");

while (x <= b)

{

Console.WriteLine("| {0,8:0.000} | {1,8:0.000}", x, F(x));

x += 1;

}

Console.WriteLine(".....................");

}

public static double Simple(double x)

{

return 1;

}

 

static void Main(string[] args)

{

Console.WriteLine("Таблиця функції Sin");

Table(new Fun(Math.Sin), -2, 2);

Console.WriteLine(" Таблиця функції Simple ");

Table(new Fun(Simple), 0, 3);

}

}

}

 

Результат роботи програми:

 

Таблиця функції Sin

-------X ------- Y --------

I -2.000 I -0,909 I

I -1.000 I -0,841 I

I 0.000 I 0,000 I

I 1,000 I 0.841 I

I 2,000 I 0,909 I

-------------------------------

 

 

Таблиця функції Simple

-------X ------- Y -------

I 0.000 I 1,000 I

I 1.000 I 1,000 I

I 2.000 I 1,000 I

I 3.000 I 1,000 I

-------------------------------

 

Має місце спрощений синтаксис для делегатів. Перше спрощення полягає в тому, що в більшості випадків явним чином створювати екземпляр делегата не потрібно, оскільки він створюється автоматично по контексту. Друге спрощення полягає в можливості створення так званих анонімних методів - фрагментів коду, що описуються безпосередньо в тому місці, де використовується делегат. У лістингу 10.4 використано обидва спрощення для реалізації тих же дій, що і лістингу 10.3.

 

using System;

using System.Collections.Generic;

using System.Linq;

using System.Text;

 

namespace examp68

{

public delegate double Fun(double x); // об’ява делегата

class Classl

{

public static void Table(Fun F, double x, double b)

{

Console.WriteLine(" X Y ");

while (x <= b)

{

Console.WriteLine("| {0,8:0.000} | {1,8:0.000}", x, F(x));

x += 1;

}

Console.WriteLine(".....................");

}

 

public static double Simple(double x)

{

return 1;

}

 

static void Main(string[] args)

{

Console.WriteLine(" Таблиця функції Sin ");

Table(Math.Sin, -2, 2);

Console.WriteLine(" Таблиця функції Simple ");

Table(delegate(double x){return 1;}, 0, 3);

}

}

}

 

У першому випадку екземпляр делегата, відповідного функції Sin, створюється автоматично. Щоб це могло відбутися, список параметрів і тип повертаємого значення функції мають бути сумісні з делегатом. У другому випадку не потрібно оформляти фрагмент коду у вигляді окремої функції Simple, як це було зроблено в попередньому лістингу, - код функції оформляється як анонімний метод і вбудовується прямо в місце передачі.

Альтернативою використанню делегатів як параметрів є віртуальні методи. Метод виведення значень функції у вигляді таблиці можна реалізувати за допомогою абстрактного базового класу. Він містить два методи: метод виведення таблиці і абстрактний метод, для обчислення функції. Для виведення таблиці конкретної функції необхідно створити похідний клас, який перевизначає цей абстрактний метод. Реалізація методу виведення таблиці за допомогою спадкоємства і віртуальних методів приведена в лістингу 10.5.

Листинг 10.5. Альтернатива параметрам-делегатам

 

using System;

using System.Collections.Generic;

using System.Linq;

using System.Text;

 

namespace examp69

{

abstract class TableFun

{

public abstract double F(double x);

 

public void Table(double x, double b)

{

Console.WriteLine(" X Y ");

while (x <= b)

{

Console.WriteLine(" {0,8:0.000} | {1,8:0.000} |", x, F(x));

x+=1;

}

Console.WriteLine(".....................");

}

}

 

class SimpleFun: TableFun

{

public override double F(double x) {

return 1;

}

 

}

 

class SinFun: TableFun

{

public override double F(double x)

{

return Math.Sin(x);

}

}

 

class Classl

{

static void Main()

{

TableFun a = new SinFun();

Console.WriteLine(" Таблица функции Sin ");

a.Table(-2, 2);

a = new SimpleFun();

Console.WriteLine(" Таблица функции Simple ");

a.Table(0,3);

}

}

}

Результат роботи цієї програми такий же, як і попередньої, але в даному випадку застосування делегатів переважно.

 

10.1.6. Обробка виключень при виклику делегатів

Раніше наголошувалося, що якщо в одному з методів списку делегата генерується виключення, наступні методи не викликаються. Це можна уникнути, якщо забезпечити явний перебір всіх методів в блоці, що перевіряється, і обробляти виникаючі виключення. Всі методи, задані в екземплярі делегата, можна отримати за допомогою успадкованого методу GetInvocationList. Цей прийом ілюструє лістинг 10.6, що є зміненим варіантом лістингу 10.1.

Лістинг 10.6. Перехоплення виключень при виклику делегата

 

using System;

using System.Collections.Generic;

using System.Linq;

using System.Text;

 

namespace examp70

{

 

delegate void Del (ref string s);

 

class Classl

{

 

public static void Cool (ref string s)

{

Console.WriteLine("викликаний метод Cool");

s = s.Replace('l', '1').Replace('O', '0').Replace('o', '0');

}

 

public static void Hack (ref string s)

{

Console.WriteLine("викликаний метод Hack");

char[] mas = s.ToCharArray();

s = "";

for (int i = 0; i < mas.Length; ++i)

s+= ((i & 1) == 0)? char.ToUpper(mas[i]):mas[i];

}

 

public static void BadHack (ref string s)

{

Console.WriteLine("викликаний метод Badhack");

throw new Exception(); // імітація помилки

}

 

static void Main()

{

 

string s = "cool hackers";

Del d = new Del(Cool); // створення екземпляра делегата

d += new Del(BadHack); // доповнення списку методів

d += new Del(Hack); // доповнення списку методів

 

foreach (Del fun in d.GetInvocationList())

{

 

try

{

fun(ref s); // виклик кожного методу із списку

}

 

catch (Exception er)

{

Console.WriteLine(er.Message);

Console.WriteLine("Exception in method " + fun.Method.Name);

}

}

 

Console.WriteLine("результат - " + s);

 

}

}

}

 

Результат роботи програми:

викликаний метод Cool

викликаний метод BadHack

Exception of type System.Exception was thrown.

Exception in method BadHack

викликаний метод Hack

результат – C001 hAcKeRs

 

У цій програмі окрім методу базового класу GetInvocationList використана властивість Method. Цю властивість повертає результат типу MethodInfo. Клас MethodInfo містить множину властивостей і методів, що дозволяють отримати повну інформацію про метод, наприклад його специфікатори доступу, ім'я і тип повертаємого значення.

 

10.2. Події

Подія - це елемент класу, що дозволяє йому посилати іншим об'єктам повідомлення про зміну свого стану. При цьому для об'єктів, що є спостерігачами події, активізуються методи-обробники цієї події. Обробники мають бути зареєстровані в об'єкті-джерелі події. Таким чином, механізм подій формалізує на мовному рівні патерн “спостерігач”, який розглядався в попередньому розділі.

Механізм подій можна також описати за допомогою моделі “публікація - підписка”: один клас, що є відправником (sender) повідомлення, публікує події, які він може ініціювати, а інші класи, повідомлення, що є одержувачами (receivers), підписуються на отримання цих подій.

Події побудовані на основі делегатів: за допомогою делегатів викликаються методи-обробники подій. Тому створення події в класі складається з наступних частин:

опис делегата, задаючого сигнатуру обробників подій;

опис події;

опис методу (методів), що ініціюють подію.

 

Синтаксис події схожий на синтаксис делегата:

 

[ атрибути ] [ специфікатори ] event тип ім'я_події

 

Для подій застосовуються специфікатори new, public, protected, internal, private, static, virtual, sealed, override, abstract і extern, які вивчалися при розгляді методів класів. Подія може бути статичною (static) або звичайною. При цьому вона відповідно пов'язана з класом або з екземпляром класу.

Тип події - це тип делегата, на якому заснована подія.

Приклад опису делегата і відповідної йому події:

 

public delegate void Del(object о); // оголошення делегата

class А

{

public event Del Oops; // оголошення події

}

Обробка подій виконується в класах-одержувачах повідомлення. Для цього в них описуються методи-обробники подій, сигнатура яких відповідає типу делегата. Кожен об'єкт (не клас!), охочий отримувати повідомлення, повинен зареєструвати в об'єкті-відправнику цей метод.

Як бачите, це в точності той же самий механізм, який розглядався в попередньому розділі. Єдина відмінність полягає в тому, що при використанні подій не потрібно описувати метод, реєструючий обробники, оскільки події підтримують операції += і -=, що додають обробник в список і що видаляють його із списку.

Подія - це зручна абстракція для програміста. Насправді вона складається із закритого статичного класу, в якому створюється екземпляр делегата, і двох методів, призначених для додавання і видалення обробника із списку цього делегата.

 

У лістингу 10.7 приведений код з лістингу 10.2, перероблений з використанням подій.

 

Лістинг 10.7. Сповіщення спостерігачів за допомогою подій

 

using System;

using System.Collections.Generic;

using System.Linq;

using System.Text;

 

namespace examp71

{

 

public delegate void Del(); // оголошення делегата

 

class Subj // клас-джерело

{

public event Del Oops; // оголошення події

public void CryOops() // метод, що ініціює подію

{

Console.WriteLine("OOPS!");

if (Oops!= null) Oops();

}

}

 

class ObsA // клас-спостерігач

{

public void Do() // реакція на подію джерела

{

Console.WriteLine("Бачу, що OOPS!");

}

}

 

class ObsB // клас-спостерігач

{

public static void See() // реакція на подію джерела

{

Console.WriteLine("Я теж бачу, що OOPS!");

}

}

 

class Classl

{

static void Main()

{

Subj s = new Subj(); // об'єкт класу-джерела

ObsA ol = new ObsA(); // об'єкти

ObsA o2 = new ObsA(); // класу-спостерігача

s.Oops += new Del(ol.Do); // додавання

s.Oops += new Del(o2.Do); // обробників

s.Oops += new Del (ObsB.See); // до події

s.CryOops(); // ініціація події

}

}

}

Зовнішній код може працювати з подіями тільки таким чином: додавати обробники в список або видаляти їх, оскільки поза класом можуть використовуватися тільки операції += і -=. Тип результату цих операцій - void, на відміну від операцій складного привласнення для арифметичних типів. Іншого способу доступу до списку обробників немає.

Усередині класу, в якому описана подія, з ним можна звертатися, як із звичайним полем, що має тип делегата: використовувати операції відношення, привласнення і так далі. Значення події за умовчанням - null. Наприклад, в методі Cryoops виконується перевірка на null для того, щоб уникнути генерації виключення System.NullReferenceException.

У бібліотеці. NET описана величезна кількість стандартних делегатів, призначених для реалізації механізму обробки подій. Більшість цих класів оформлена по одних і тих же правилах:

– ім'я делегата закінчується суфіксом EventHandler;

– делегат отримує два параметри:

– перший параметр задає джерело події і має тип object;

– другий параметр задає аргументи події і має тип EventArgs або похідний від нього.

 

Якщо обробникам події потрібна специфічна інформація про подію, то для цього створюють клас, похідний від стандартного класу EventArgs, і додають в нього необхідну інформацію. Якщо делегат не використовує таку інформацію, можна не описувати делегат і власний тип аргументів, а обійтися стандартним класом делегата System.EventHandler.

Ім'я обробника події прийнято складати з префікса On і імені події. У лістингу 10.8 приведений приклад з лістингу 10.7, оформлений відповідно до стандартних угод. NET. Знайдіть вісім відмінностей!

 

Лістинг 10.8. Використання стандартного делегата EventHandler

using System;

using System.Collections.Generic;

using System.Linq;

using System.Text;

 

namespace examp72

{

class Subj

{

public event EventHandler Oops;

public void CryOops()

{

Console.WriteLine("OOPS!");

if (Oops!= null) Oops (this, null);

}

}

 

class ObsA

{

public void OnOops(object sender, EventArgs e)

{

Console.WriteLine("Бачу, що OOPS!");

}

 

}

 

class ObsB

{

 

public static void OnOops(object sender, EventArgs e)

{

Console.WriteLine("Я теж бачу, що OOPS!");

}

 

}

 

class Classl

{

static void Main()

{

Subj s = new Subj();

 

ObsA ol = new ObsA();

ObsA o2 = new ObsA();

 

s.Oops += new EventHandler(ol.OnOops);

s.Oops += new EventHandler(o2.OnOops);

s.Oops += new EventHandler(ObsB.OnOops);

s.CryOops ();

}

}

}

Можна спростити цю програму, використовуючи можливість неявного створення делегатів при реєстрації обробників подій. Відповідний варіант приведений в лістингу 10.9.

 

Лістинг 10.9. Використання делегатів і анонімних методів

 

using System;

using System.Collections.Generic;

using System.Linq;

using System.Text;

 

namespace examp73

{

class Subj

{

public event EventHandler Oops;

public void CryOops()

{

Console.WriteLine("OOPS!");

if (Oops!= null) Oops(this, null);

}

}

class ObsA

{

public void OnOops(object sender, EventArgs e)

{

Console.WriteLine("Бачу, що OOPS!");

}

}

 

class ObsB

{

public static void OnOops(object sender, EventArgs e)

{

Console.WriteLine("Я теж бачу, що OOPS!");

}

}

 

class Classl

{

static void Main()

{

Subj s = new Subj();

ObsA o1 = new ObsA();

ObsA o2 = new ObsA();

 

s.Oops += o1.OnOops;

s.Oops += o2.OnOops;

s.Oops += ObsB.OnOops;

s.Oops += delegate(object sender, EventArgs e)

{

Console.WriteLine("Я з вами!");

};

s.CryOops ();

 

}

}

}

Події включені в багато стандартних класів.NET, наприклад, в класи простору імен Windows.Forms, використовувані для розробки Windows-додатків.

 

10.3. Багатопотокові додатки

Додаток .NET складається з одного або декількох процесів. Процесу належать виділена для нього область оперативної пам'яті і ресурси. Кожен процес може складатися з декількох доменів (частин) додатків, ресурси яких ізольовані один від одного. В рамках домена може бути запущене декілька потоків виконання. Потоком (thread) є частина виконуваного коду програми. У кожному процесі є первинний потік, виконуючий роль точки входу в додаток. Для консольних застосувань це метод Main.

Багатопотокові застосування створюють як для багатопроцесорних, так і для однопроцесорних систем. Основною метою при цьому є підвищення загальної продуктивності і скорочення часу реакції додатку. Управління потоками здійснює операційна система. Кожен потік отримує деяку кількість квантів часу, після закінчення якого управління передається іншому потоку. Це створює у користувача однопроцесорної машини враження одночасної роботи декількох потоків і дозволяє, наприклад, виконувати введення тексту одночасно з тривалою операцією по передачі даних.

 

Недоліки багатопоточності:

§ велика кількість потоків веде до збільшення накладних витрат, пов'язаних з їх перемиканням, яке знижує загальну продуктивність системи;

§ у багатопотокових застосуваннях виникають проблеми синхронізації даних, що пов'язані з доступом до одних і тих же даних з боку декількох потоків.

 




Поделиться с друзьями:


Дата добавления: 2014-12-27; Просмотров: 1358; Нарушение авторских прав?; Мы поможем в написании вашей работы!


Нам важно ваше мнение! Был ли полезен опубликованный материал? Да | Нет



studopedia.su - Студопедия (2013 - 2024) год. Все материалы представленные на сайте исключительно с целью ознакомления читателями и не преследуют коммерческих целей или нарушение авторских прав! Последнее добавление




Генерация страницы за: 0.009 сек.