Кратко съдържание



страница47/73
Дата21.07.2018
Размер8.57 Mb.
1   ...   43   44   45   46   47   48   49   50   ...   73

Инструментът .NET Reflector


Инструментът .NET Reflector е декомпилатор за .NET асемблита. Изпол­зва се за генериране на програмен код от изпълним код. Той има удобен потребителски интерфейс и за разлика от вградения в .NET Framework SDK инструмент ILDASM .NET Reflector може да декомпилира до код на C# и VB.NET, а не само до MSIL код. Инструментът е безплатен и може да бъде изтеглен от адрес http://www.aisto.com/roeder/dotnet/.

Използване на .NET Reflector


Настоящият пример илюстрира използването на инструмента .NET Reflector за разглеждане на кода, който компилаторът на C# генерира при деклариране на делегат.

За да декомпилираме асемблито от предишния пример и да разгледаме кода му, трябва да изпълним следните стъпки:



  1. Стартираме .NET Reflector.

  2. От менюто File | Open отваряме асем­блито Demo-1-Multicast-Delegates.exe.

  3. В дървото в лявата част на .NET Reflector намираме дефиницията на делегата StringDelegate. Вижда се, че той е обикновен клас, който наследява класа System.MulticastDelegate (това се вижда като се разгърне клона Base Types).

  4. Намираме в дървото класа TestDelegateClass. Разгръщаме мето­дите на класа и щракваме два пъти върху главния метод на класа. Разглеждаме генерирания при декомпилацията C# код. Можем да проверим, че при извикването на multicast делегата всъщност се извиква неговият Invoke() метод.

Събития (Events)


Събитията могат да се разглеждат като съобщения за настъпване на някакво действие. В компонентно-ориентираното програмиране компонен­тите изпращат събития (events) към своя притежател за да го уведомят за настъпването на интересна за него ситуация. Този модел е много характе­рен например за графичните потребителски интерфейси, където контро­лите уведомяват чрез събития други класове от програмата за действия от страна на потребителя. Например, когато потребителят натисне бутон, бутонът предизвиква събитие, с което известява, че е бил натиснат. Раз­бира се, събития могат да се предизвикват не само при реализиране на потребителски интерфейси. Нека вземем за пример програма, в която като част от функционалността влиза трансфер на файлове. Приключването на трансфера на файл може да се съобщава чрез събитие.

Шаблонът "Наблюдател"


Механизмът на събитията реализира шаблона "Наблюдател" (Observer) или, както още се нарича, Публикуващ/Абонати (Publisher/Subscriber), при който един клас публикува събитие, а произволен брой други класове могат да се абонират за това събитие. По този начин се реализира връзка на зависимост от тип един към много, при която когато един обект проме­ни състоянието си, зависещите от него обекти биват информирани за промяната и автоматично се обновяват.

Изпращачи и получатели


Обектът, който предизвиква дадено събитие се нарича изпращач на събитието (event sender). Обектът, който получава дадено събитие се нарича получател на събитието (event receiver). За да могат да получават дадено събитие, получателите му трябва преди това да се абонират за него (subscribe for event).

За едно събитие могат да се абонират произволен брой получатели. Изпращачът на събитието не знае кои ще са получателите на събитието, което той предизвиква. Затова чрез механизма на събитията се постига по-ниска степен на свързаност (coupling) между отделните компоненти на програмата.


Събитията в .NET Framework


В компонентния модел на .NET Framework абонирането, изпращането и получаването на събития се поддържа чрез делегати и събития. Реализа­цията на механизма на събитията е едно от главните приложения на делегатите. Класът, който публикува събитието, дефинира делегат, който абонатите на събитието трябва да имплементират. Когато събитието бъде предизвикано, методите на абонатите се извикват посредством делегата. Тези методи обикновено се наричат обработчици на събитието. Делега­тът е multicast делегат, за да могат чрез него да се извикват много обра­ботващи методи (на всички абонати).

Деклариране на събития


В C# събитията представляват специални инстанции на делегати. Те се декларират с ключовата дума event, която може да се предшества от модификатори, като например модификатори за достъп. Обикновено съби­тията са с модификатор public. След ключовата дума event се записва името на делегата, с който се свързва съответното събитие. За тази цел делегатът трябва да бъде дефиниран предварително. Той може да бъде дефиниран от потребителя, но може да се използва и вграден делегат. Тези делегати трябва да бъдат от тип void.

Операции върху събития


Единствените операции, които са позволени върху събития са операциите за абониране и премахване на абонамент. За целта при деклариране на събитие компилаторът автоматично дефинира операторите += за абони­ране за събитие и -= за премахване на абонамент. Тези оператори могат да бъдат извиквани от класове, външни за класа, в който е дефинирано събитието, така че външен код може да добавя и премахва обработчици на събитието, но не може по никакъв друг начин да манипулира списъка с обработчици. Възможно е подразбиращото се поведение на операторите += и -= да бъде предефинирано.

Разлика между събитие и делегат


Събитията и делегатите са много тясно свързани. Въпреки това член-променлива от тип делегат не е еквивалентна на събитие, декларирано с ключовата дума event, т.е. public MyDelegate m не е същото като public event MyDelegate m. Първото е декларация на променлива m, която е от тип MyDelegate, докато второто декларира събитие, което ще се обра­ботва от делегат от тип MyDelegate.

Между делегатите и събитията има и други разлики, освен в деклари­рането. Например, делегатите не могат да бъдат членове на интерфейси, докато събитията могат. В такъв случай класът, който имплементира интерфейса, трябва да предостави подходящо събитие.


Извикване на събитие


Извикването на събитие може да стане само в класа, в който то е дефинирано. Това означава, че само класът, в който се дефинира събитие, може да предизвика това събитие. Това е наложително, за да се спази шаблонът на Публикуващ/Абонати – абонираните класове се информират при промяна на състоянието на публикуващия и именно публикуващият е отговорен за разпращане на съобщенията за промяната, настъпила у него.

Друга важна подробност за събитията е, че достъпът до тях е синхро­низиран. Това има значение при създаването на многонишкови приложе­ния, с които ще се запознаем подробно в темата "Многонишково програ­миране и синхронизация".


Конвенция за събитията


В .NET Framework се използва утвърдена конвенция за събитията. Тя определя именуването на събитията и свързаните с тях методи и делегати, връщаните типове и приеманите аргументи от делегатите.

Делегати за събития


Делегатите, използвани за събития по конвенция имат имена, които се състоят от глагол и EventHandler (SomeVerbEventHandler). Така те се различават лесно от декларациите на други делегати в приложението.



Делегатите, използвани за събития, не трябва да връщат стойност. Връщаният тип от делегата трябва да бъде void.

Аргументи на делегата


Конвенцията налага делегатите, които ще се използват от събития, да приемат два аргумента. Единият представлява обектът-изпращач на съби­тието, т. е. това е източникът на събитието, или публикуващият от шабло­на Публикуващ/Абонати. Той трябва да е от тип System.Object. Другият аргумент представя информация за изпращаното събитие. Той е от тип, наследник на System.EventArgs.

Ето пример за деклариране на делегат, който ще бъде използван от събитие:



public delegate void ItemChangedEventHandler(

object aSender, ItemChangedEventArgs aEventArgs);


Конвенция за деклариране на събитията


Събитията обикновено се обявяват като public, въпреки че са възможни и останалите модификатори за достъп. Имената им започват с главна буква, а последната дума от името е глагол. Следва пример за декларация на събитие:

public event ItemChangedEventHandler ItemChanged;

Предизвикване на събитие


За предизвикване на събитие се създава protected void метод. Прието е името му да започва с On, следвано от името на събитието (например OnEventName). Този метод предизвиква събитието като извиква делегата.

Методът трябва да е protected, защото това позволява при наследяване на класа, в който е декларирано събитието, наследниците да могат да предизвикват събитието. Ако методът не е protected, наследниците няма да могат да предизвикат събитието, защото не могат да се обърнат директно към него, тъй като то е достъпно единствено в класа, в който е декларирано. За още по-голяма гъвкавост е възможно освен protected, методът да бъде обявен virtual, което би позволило на наследниците да го предефинират. Така те биха могли да прихващат извикването на събитията от базовия клас и евентуално да извършват собствена обработка. Следващият пример показва как се декларира метод за предизвикване на събитие:



protected void OnItemChanged() { … }

Конвенция за обработчиците


Обикновено името на метода-получател (обработчикът) на събитието има вида Обект_Събитие, както се илюстрира в следния пример:

private void OrderList_ItemChanged () { … }

Събития – пример


В настоящия пример се разглежда дефинирането и използването на събития като се спазва утвърдената конвенция в .NET Framework. Демон­стрира се изпращане и получаване на събития.

// A delegate type for hooking up change notifications

public delegate void TimeChangedEventHandler(

object aSender, TimeChangedEventArgs aEventArgs);
// A class that inherits System.EventArgs and adds

// information for the current time

public class TimeChangedEventArgs : EventArgs

{

private int mTicksLeft;


public TimeChangedEventArgs(int aTicksLeft)

{

mTicksLeft = aTicksLeft;



}

public int TicksLeft

{

get


{

return mTicksLeft;

}

}

}


public class Timer

{

private int mTickCount;



private int mInterval;
// The event that will be raised when the time changes

public event TimeChangedEventHandler TimeChanged;


public Timer(int aTickCount, int aInterval)

{

mTickCount = aTickCount;



mInterval = aInterval;

}
public int TickCount

{

get


{

return mTickCount;

}

}
public int Interval



{

get


{

return mInterval;

}

}
// The method that invokes the event



protected void OnTimeChanged(int aTick)

{

if (TimeChanged != null)



{

TimeChangedEventArgs args =

new TimeChangedEventArgs(aTick);

TimeChanged(this, args);

}

}
public void Run()



{

int tick = mTickCount;

while (tick > 0)

{

System.Threading.Thread.Sleep(mInterval);



tick--;

OnTimeChanged(tick);

}

}

}


public class TimerDemo

{

// The event handler method



private static void Timer_TimeChanged(object aSender,

TimeChangedEventArgs aEventArgs)

{

Console.WriteLine("Timer! Ticks left = {0}",



aEventArgs.TicksLeft);

}
public static void Main()

{

Timer timer = new Timer(10, 1000);



timer.TimeChanged +=

new TimeChangedEventHandler(Timer_TimeChanged);

Console.WriteLine(

"Timer started for 10 ticks at interval 1000 ms.");

timer.Run();

}

}



При изпълнение на примера се получава следният резултат:


Описание на примера


Класът Timer от примера служи за предизвикване на дадено събитие през определен интервал от време.

Най-напред се декларира делегатът TimeChangedEventHandler, който ще бъде типа на предизвикваното събитие. Според обяснените конвенции той приема два аргумента – един от тип Object, и един от тип, наследник на EventArgs. Този наследник е класът TimeChangedEventArgs. Той добавя член, който съдържа информация за оставащия брой извиквания.

В класа Timer се декларира събитието TimeChanged от тип TimeChangedEventHandler. Методът OnTimeChanged на класа Timer прове­рява дали има абонати за събитието (за целта проверява дали събитието няма стойност null) и в случай, че има абонати предизвиква събитието. В метода Run() на класа Timer периодично се предизвиква събитието TimeChanged чрез обръщение към метода OnTimeChanged.

В класа TimerDemo се декларира обработчик на събитието TimeChanged и в главната функция TimerDemo се абонира за събитието.


Проследяване на изпълнението на примера


За проследяване на примера стъпка по стъпка можем да използваме про­екта Demo-3-Events от демонстрациите. За целта изпълняваме след­ните стъпки:

  1. Отваряме проекта Demo-3-Events.sln. Той съдържа горния пример.

  2. Слагаме точки на прекъсване в методите timer_TimeChanged() и OnTimeChanged().

  3. Стартираме приложението с [F5].

  4. Натискаме след това последователно [F11], за да видим как през една секунда се изпраща и съответно получава събитието TimeChanged.

На картинката е показан изглед от VS.NET в момент на постъпково проследяване на изпълнението на примера:


Делегатът System.EventHandler


Не винаги има нужда събитието да генерира някакви данни, които да изпрати на абонатите. В такъв случай може да се използва вградения делегат System.EventHandler. Той дефинира референция към callback метод, който обработва именно такива събития. Съответно методите-обра­ботчици на такива събития трябва да съответстват на декларираните от делегата System.EventHandler връщан тип и сигнатура. Следва деклара­цията на делегата:

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

Този делегат се използва на много места вътрешно от .NET Framework. Например, типът на събитието, което възниква при натискане на бутон, е точно EventHandler, тъй като при това събитие не се генерира информа­ция за предаване.

Класът EventArgs


Както се вижда от горната дефиниция, вграденият делегат EventHandler има като втори аргумент обект от тип EventArgs. За този тип вече стана дума – всеки делегат, който се използва за тип на събитие трябва да приема аргумент от тип, който е наследник на EventArgs.

Класът EventArgs наследява всичките си членове от System.Object, като добавя конструктор и публично статично поле Empty, което представя събитие без данни. По този начин се улеснява използването на събития, които не носят данни.

Ако събитието трябва да съдържа информация се използва клас, който наследява System.EventArgs и добавя необходимите членове за нейното представяне. Ако все пак няма нужда от подобна информация, може директно да се използва инстанция на System.EventArgs при предизвик­ване на събитието. Случаят с EventHandler е точно такъв, тъй като той се използва за обработка на събития, които не носят информация. Затова той приема като аргумент директно EventArgs.

Пример за използване на System.EventHandler


В настоящия пример ще се илюстрира употребата на вградения делегат System.EventHandler:

public class Button

{

public event EventHandler Click;



public event EventHandler GotFocus;

public event EventHandler TextChanged;

...

}
public class ButtonTest



{

private static void Button_Click(object aSender,

EventArgs aEventArgs)

{

Console.WriteLine("Button_Click() event called.");



}
public static void Main()

{

Button button = new Button();



button.Click += new EventHandler(Button_Click);

button.DoClick();

}

}

Описание на примера


В горния пример се дефинира клас Button с набор събития. Класът ButtonTest дефинира функция Button_Click(…), съответстваща на деле­гата за обработване на събитието Click на класа Button и в главния си метод се абонира за това събитие и го предизвиква с обръщение към метода DoClick(), който за краткост е изпуснат в примера.

1   ...   43   44   45   46   47   48   49   50   ...   73


База данных защищена авторским правом ©obuch.info 2016
отнасят до администрацията

    Начална страница