русс | укр

Мови програмуванняВідео уроки php mysqlПаскальСіАсемблерJavaMatlabPhpHtmlJavaScriptCSSC#DelphiТурбо Пролог

Компьютерные сетиСистемное программное обеспечениеИнформационные технологииПрограммирование


Linux Unix Алгоритмічні мови Архітектура мікроконтролерів Введення в розробку розподілених інформаційних систем Дискретна математика Інформаційне обслуговування користувачів Інформація та моделювання в управлінні виробництвом Комп'ютерна графіка Лекції


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


Дата додавання: 2015-01-08; переглядів: 965.


Раніше наголошувалося, що якщо в одному з методів списку делегата генерується виключення, наступні методи не викликаються. Це можна уникнути, якщо забезпечити явний перебір всіх методів в блоці, що перевіряється, і обробляти виникаючі виключення. Всі методи, задані в екземплярі делегата, можна отримати за допомогою успадкованого методу 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 містить множину властивостей і методів, що дозволяють отримати повну інформацію про метод, наприклад його специфікатори доступу, ім'я і тип повертаємого значення.

 

Події

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

Механізм подій можна також описати за допомогою моделі “публікація - підписка”: один клас, що є відправником (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-додатків.

 


<== попередня лекція | наступна лекція ==>
Передача делегатів в методи | Багатопотокові додатки


Онлайн система числення Калькулятор онлайн звичайний Науковий калькулятор онлайн