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



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

В тази тема ...


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

Какво представляват делегатите?


Делегатите са референтни типове, които описват сигнатурата на даден метод (броя, типа и последователността на параметрите му) и връщания от него тип. Могат да се разглеждат като "обвивки" на методи - те представляват структури от данни, които приемат като стойност методи, отговарящи на описаната от делегата сигнатура и връщан тип.

Инстанциране на делегат


Делегатът се инстанцира като клас и методът се подава като параметър на конструктора. Възможно е делегатът да сочи към повече от един метод, но на това ще се спрем подробно малко по-нататък.

Делегатите и указателите към функции


Съществува известна прилика между делегатите и указателите към функ­ции в други езици, например Pascal, C, C++, тъй като последните пред­ставляват типизиран указател към функция. Делегатите също съдър­жат силно типизиран указател към функция, но те са и нещо повече – те са напълно обектно-ориентирани. На практика делегатите представляват класове. Инстанцията на един делегат може да съдържа в себе си както инстанция на обект, така и метод.

Callback методи и делегати


Едно от основните приложения на делегатите е реализацията на "обратни извиквания", т.нар. callbacks. Идеята е да се предаде референция към метод, който да бъде извикан по-късно. Така може да се осъществи например асинхронна обработка – от даден код извикваме метод, като му подаваме callback метод и продължаваме работа, а извиканият метод извиква callback метода когато е необходимо. Със средствата на делега­тите е възможно даден клас да позволи на потребителите си да предос­тавят метод, извършващ специфична обработка, като по този начин обработката не се фиксира предварително.

Статични или екземплярни методи


Делегатите могат да сочат както към методи на инстанция на класа, в който са декларирани, така и към статични методи. Това представлява удобство, защото можем да използваме делегат, без да сме създали инстанция на съдържащия го клас. Така се спестява създаването на допълнителна инстанция на клас. Друга възможност е да се отложи създаването на инстанция на делегат докато тя стане необходима. За целта можем да дефинираме свойство на класа, който ползва делегата, и в get метода на свойството да създадем делегата.

Пример за делегат


Следващият пример демонстрира деклариране на делегат, инстанциране на делегат и извикване на метод, сочен от него.

// Declaration of a delegate

public delegate void SimpleDelegate(string aParam);


class TestDelegate

{

public static void TestFunction(string aParam)



{

Console.WriteLine("I was called by a delegate.");

Console.WriteLine("I got parameter {0}.", aParam);

}
public static void Main()

{

// Instantiation of а delegate



SimpleDelegate simpleDelegate =

new SimpleDelegate(TestFunction);

// Invocation of the method, pointed by a delegate

simpleDelegate("test");

}

}


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


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


В първия ред от кода се декларира делегат. За целта се използва ключо­вата дума delegate. След това в класа се дефинира функция, която има сигнатура и връщан тип като тези, декларирани от делегата. В главния метод на класа се инстанцира делегата, като дефинираният метод се подава като параметър и след това той се извиква чрез делегата.

Видове делегати


Делегатите в .NET Framework са специални класове, които наследяват System.Delegate или System.MulticastDelegate. От тези класове обаче явно могат да наследяват само CLR и компилаторът. Всъщност, те не са от тип делегат – тези класове се използват, за да се наследяват от тях типове делегат.

Всеки делегат има "списък на извикване" (invocation list), който представлява наредено множество делегати, като всеки елемент от него съдържа конкретен метод, рефериран от делегата. Делегатите могат да бъдат единични и множествени.


Единични (singlecast) делегати


Единичните делегати наследяват класа System.Delegate. Тези делегати извикват точно един метод. В списъка си на извикване имат единствен елемент, съдържащ референция към метод.

Множествени (multicast) делегати


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

Езикът C# съдържа запазената дума delegate, чрез която се декларира делегат. При тази декларация компилаторът автоматично наследява типа MulticastDelegate., т.е. създава множествен делегат. Затова ще обърнем по-голямо внимание именно на този вид делегати.





На практика singlecast делегатите почти не се използват и под делегат обикновено се има предвид multicast делегат.

Извикване на multicast делегати


При извикване на multicast делегат се изпълняват всички методи от него­вия списък на извикване. Методите се викат в реда, в който се намират в списъка, като дублиращите се методи (ако има такива) се викат толкова пъти, колкото се срещат в списъка.

Делегати и връщани стойности


Ако сигнатурата на методите, викани от делегата включва връщана стойност, връща се стойността, получена при изпълнението на последния елемент от списъка на делегата. Когато сигнатурата включва out или ref параметър, то всички извикани методи променят неговата стойност после­дователно в реда си на извикване и крайният резултат е резултата от последния извикан метод.

Делегати и изключения


Възможно е при извикване на multicast делегат някой от методите от списъка му на извикване да хвърли изключение. В този случай методът спира изпълнение и управлението се връща в кода, извикал делегата. Останалите методи от списъка не се извикват. Дори извикващият метода да хване изключението, останалите методи от списъка не се изпълняват.

System.MulticastDelegate


Класът System.MulticastDelegat е наследник на System.Delegate. Той е базов клас за всички делегати в C#, но самият той не е тип делегат – при срещане на ключовата дума delegate компилаторът създава клас, наследник на System.MulticastDelegat. Всички делегати наследяват от него няколко важни метода, които сега ще разгледаме.

Комбиниране на делегати


Multicast делегатите могат да участват в комбиниращи операции. Това се реализира с метода Combine() на класа. Той слива списъците от методи на няколко делегата от еднакъв тип. Този метод е предефиниран и може да приема като параметри както два multicast делегата от еднакъв тип, така и масив от multicast делегати от еднакъв тип. В резултат връща нов multicast делегат, чийто списък от методи съдържа списъците на подадените като параметри делегати.

Неизменяемост на делегатите




Делегатът е неизменяем обект – веднъж създаден, повече не може да се променя.

Операцията "сливане" не променя съществуващите делегати, а създава нов делегат. Ако делегатът, получен в резултат на комбиниране не рефе­рира нито един метод, Combine() връща стойност null, а не делегат с празен списък от методи. В C# е предефиниран операторът += за комби­ниране на делегати.

Метод Remove()


Освен, че списъците от методи на няколко делегата могат да бъдат обединявани в един, възможно е също от списъка на един делегат да се извади списъкът на друг. Това се извършва чрез метода Remove(). Той приема като параметри два делегата и в резултат връща нов делегат, чийто списък е получен като от списъка на първия аргумент е премахнато последното срещане на списъка на втория аргумент. Ако двата списъка са еднакви, или ако списъкът на втория аргумент не се среща в списъка на първия, резултатът е null. В езика C# е предефиниран операторът -= за изваждане на списъци на делегати.

Получаване на списъка от методи


С метода GetInvocationList() може да се получат методите, викани от делегата. По-точно, методът връща масив от делегати от типа на делегата, за който се вика методът. Всеки делегат от масива съдържа в списъка си от методи единствен елемент – някой от методите, викани от делегата. В масива има по един делегат за всеки метод и делегатите са подредени така, както се извикват от multicast делегата. Извикването на делегатите последователно в реда, в който се срещат в масива, ще има същия ефект като от извикване на самия делегат.

Други методи


При декларирането на делегат компилаторът създава няколко служебни метода, които не могат да се извикват явно. Методът Invoke() извиква метода (методите), сочен (сочени) от делегата. Следователно при обръ­щение към делегата всъщност се извиква методът Invoke(), а той вика методите от списъка на делегата. Други методи са BeginInvoke() и EndInvoke(), чрез които се реализира асинхронно извикване. Освен това компилаторът декларира и конструктор на делегата.

Освен описаните методи, класът System.MulticastDelegate има и едно важно свойство – Method. То връща първия метод от списъка на делегата.


Multicast делегати – пример


В настоящия пример се демонстрира работата с multicast делегати и се илюстрира хода на изпълнение на програмния код:

using System;
public delegate void StringDelegate(string aValue);
public class TestDelegateClass

{

void PrintString(string aValue)



{

Console.WriteLine(aValue);

}
void PrintStringLength(string aValue)

{

Console.WriteLine("Length = {0}", aValue.Length);



}
static void PrintStringWithDate(string aValue)

{

Console.WriteLine("{0}: {1}", DateTime.Now, aValue);



}
static void PrintInvocationList(Delegate aDelegate)

{

Console.Write("(");



Delegate[] list = aDelegate.GetInvocationList();

foreach (Delegate d in list)

{

Console.Write(" {0}", d.Method.Name);



}

Console.WriteLine(" )");

}
public static void Main()

{

TestDelegateClass tdc = new TestDelegateClass();



StringDelegate printDelegate =

new StringDelegate(tdc.PrintString);

StringDelegate printLengthDelegate =

new StringDelegate(tdc.PrintStringLength);

StringDelegate printWithDateDelegate =

new StringDelegate(PrintStringWithDate);


PrintInvocationList(printDelegate);

// Prints: ( PrintString )


StringDelegate combinedDelegate = (StringDelegate)

Delegate.Combine(printDelegate, printLengthDelegate);


PrintInvocationList(combinedDelegate);

// Prints: ( PrintString PrintStringLength )


combinedDelegate = (StringDelegate)

Delegate.Combine(combinedDelegate,

printWithDateDelegate);
PrintInvocationList(combinedDelegate);

// Prints: ( PrintString PrintStringLength

// PrintStringWithDate )
// Invoke the delegate

combinedDelegate("test");

}

}


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


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


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

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


За проследяване изпълнението на примера стъпка по стъпка ще изпол­зваме проекта Demo-1-Multicast-Delegates от демонстрациите, който съ­държа кода от горния пример. Изпълняваме следните стъпки:

  1. Отваряме проекта Demo-1-Multicast-Delegates.sln.

  2. Слагаме точка на прекъсване на последния ред на Main() метода на основния клас, където става извикването на комбинирания делегат и стартираме приложението с [F5].



  1. Натискаме последователно [F11], за да видим как трите метода, подадени при създаването на инстан­циите на трите делегата, се извикват един след друг.

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




Сподели с приятели:
1   ...   42   43   44   45   46   47   48   49   ...   73




©obuch.info 2024
отнасят до администрацията

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