2. Съдържание
• Функциите като данни
• Делегати
• Събития и обработчици
• Ламбда функции
• Примери за употреба на делегати и събития
3. Функциите като данни
• Какво е функция - преговор
– Подпрограма, която извършва определена дейност и може да
бъде извиквана от друга част на програмата
– Нула или повече параметри
• Тип на параметъра
• Вид на параметъра – предаван по стойност, по референция или изходен
– Тип на връщания резултат или липса на такъв
– Блок от операции (тяло на функцията)
4. Функциите като данни
• Разглеждане на функции като данни
– Някои езици за програмиране боравят с типове данни, чиито
дефиниционни множества обхващат семейства от функции
– На практика това означава, че:
• Функция може да бъде присвоена на променлива или член-данна от
подходящия тип
• Фунцкия може да бъде подадена като аргумент на друга функция
(композиция на функции)
• Функция може да бъде върната като резултат от изпълнението на друга
функция
– Механизъм за метапрограмиране: въвеждане в програмата на
кратки и лесни за употреба конструкции, които заместват
еднотипни фрагменти от код
5. Функциите като данни
• Функционални типове данни
– Дефиниционно множество: функциите с определен брой, тип и
вид на параметрите и тип на връщания резултат
– Предоставят механизъм за извикване на функцията, която се
разглежда като данна
6. Делегати
• Какво е „делегат“?
– Референтен тип данни
– C# еквивалент на функционален тип данни
– Наследява класа System.MulticastDelegate
– Наименование
– Нула или повече параметри
– Тип на връщания резултат
– Шаблонни делегати – аналогично на другите шаблонни типове
7. Делегати
// Делегат с един целочислен параметър
// и целочислен резултат
delegate int UnaryMethod(int x);
// Делегат с два целочислени параметъра
// и целочислен резултат
delegate int BinaryMethod(int x, int y);
// Делегат с два низови параметъра
// и без резултат
public delegate void NameChangedHandler(
string oldName, string newName);
// Шаблонен делегат с един параметър
// от тип, който имплементира интерфейса
// IComparable, и същия тип на резултата
delegate T UnaryMethod<T>(T x)
where T : IComparable;
• Деклариране на делегати
– Модификатор за достъп
(незадължителен)
– Ключова дума delegate
– Тип на връщания резултат или ключова
дума void
– Наименование – уникално в рамките
на пространството от имена
– Списък от параметри в кръгли скоби
• Деклариране на шаблонни
делегати
– Аналогично на декларирането на други
шаблонни типове
9. Делегати
• Използване на делегати
– Делегатите са пълноправни типове данни
– Автоматично предоставен конструктор с един параметър
– При създаване на делегатен екземпляр, на конструктора се подава
като аргумент обръщение към метод без кръгли скоби с аргументи
• Методът трябва да има подходящият брой, тип и вид параметри, както и тип на
връщания резултат
– За изпълнение на метода, съдържащ се в делегатен екземпляр, се
поставят кръгли скоби с аргументи след променливата/израза,
рефериращ делегатния екземпляр
10. Делегати
static int Add(int x, int y)
{
return x + y;
}
static double AddPi(double x)
{
return x + Math.PI;
}
static void Main(string[] args)
{
// Създаване на делегатен екземпляр
BinaryMethod binaryMethod =
new BinaryMethod(Add);
// Съкратен запис за горното
BinaryMethod binaryMethod2 = Add;
// Създаване на делегатен екземпляр,
// чийто тип е шаблонен екземпляр
UnaryMethod<double> unaryMethod = AddPi;
// Изпълняване на делегатен екземпляр
int a = binaryMethod(2, 5);
Console.WriteLine(a);
}
• Използване на делегати
– Деклариране на променлива от
делегатен тип
– Създаване на делегатен екземпляр
– Неявно създаване на делегатен
екземпляр – извикването на
констурктора може да бъде
пропуснато
– Извикване на метода, към който сочи
делегатен екземпляр
12. Делегати
• Приложения на делегатите
– Изпълнение на различни операции (определяни по време на
изпълнение) върху еднотипни данни
– Реализиране на взаимодействия между обекти чрез събития
– Функции от по-висок ред – функционално програмиране в C#
13. Събития
• Какво е „събитие“?
– Член на клас, структура или интерфейс
– Служи за реализиране на взаимодействие между обекти
– Тип данни – делегат
– Наименование – уникално в рамките на членовете на класа
– Към събитието се закачат обработчици – делегатни екземпляри
– При предизвикването на събитието се извикват последователно
всички закачени обработчици
14. Събития
class Person
{
// Частно поле за съхранение
// на обработчици на събитието
private NameChangedHandler _nameChanged;
// Събитие с явно декларирани блокове
// за закачане и откачане на обработчици
public event NameChangedHandler NameChanged
{
// Блок за закачане на обработчик
add { _nameChanged += value; }
// Блок за откачане на обработчик
remove { _nameChanged -= value; }
}
// Съкратен запис на горното
public event NameChangedHandler NameChanged2;
}
• Деклариране на събития
– Модификатор за достъп
(незадължителен)
– Ключова дума event
– Делегатен тип данни
– Наименование
– Блокове за закачане и откачане на
обработчици (незадължителни)
• Блок за закачане – предшества се от
стандартната дума add
• Блок за откачане – предшества се от
стандартната дума remove
– Ако блоковете се пропуснат,
компилаторът генерира частно поле от
същия тип, към което да закача
обработчиците (автоматично генерирано
събитие)
16. Събития
// Метод – обработчик на събитието
static void PersonNameChanged(
string oldName, string newName)
{
Console.WriteLine(
"The person’s name was changed to '{0}'.",
newName);
}
static void Main(string[] args)
{
Person person = new Person("Иван Петров");
// Закачане на обработчик на събитие
person.NameChanged += PersonNameChanged;
// Изпълняване на код, който би предизвикал
// събитието
person.Name = "Георги Георгиев";
// Откачане на обработчик на събитие
person.NameChanged -= PersonNameChanged;
}
• Закачане и откачане на
обработчици на събития
– Не се допуска директно присвояване
на стойност на събитие
– Обработчик на събитие се закача с
оператора +=
– Обработчик на събитие се откача с
оператора -=
18. Събития
class Person
{
// ...
private string _name;
public Person(string name)
{
_name = name;
}
public string Name
{
get { return _name; }
set
{
if (_name == value)
return;
string oldName = _name;
_name = value;
if (_nameChanged != null)
_nameChanged(oldName, _name);
}
}
}
• Предизвикване на събития
– Автоматично генерираните събития се
предизвикват по същия начин, както се
изпълняват делегатни екземпляри
– Останалите събития не се предизвикват
пряко, а трябва да бъде изпълнен
делегатният екземпляр, към който add
блокът на събитието закача
обработчиците
– Ако няма закачени обработчици,
събитието има стойност null; при опит
за предизвикването му се предизвиква
изключение от тип
System.NullReferenceException
20. Събития
• Приложения на събитията
– Реализиране на взаимодействия между обекти
– Разширяване и модифициране на поведението на обекти без пряк
достъп до реализацията им и без наследяване
– Програмиране, базирано на събития
– Приложения с графичен потребителски интерфейс
21. Ламбда функции
• Какво е „ламбда функция“?
– Специален синтаксис за създаване на делегатен екземпляр
– Може да се декларира директно в тялото на друг метод или дори
друга ламбда функция
– Параметри – типовете им може да бъдат пропуснати
– Тяло
– Тип на връщания резултат – не се посочва явно
– В тялото на ламбда функцията могат да се използват локалните
променливи на метода, в който е декларирана
22. Ламбда функции
// Деклариране на ламбда функция с израз
BinaryMethod add =
(int x, int y) => x + y;
// Ламбда функция без явно декларирани типове
// на параметрите
BinaryMethod subtract =
(x, y) => x - y;
// Ламбда функция с един параметър
UnaryMethod<double> addPi =
x => x + Math.PI;
// Деклариране на ламбда фунцкия със съждения
Person person = new Person("Иван Петров");
person.NameChanged +=
(oldName, newName) =>
{
Console.WriteLine(
"The person’s name changed to '{0}'.",
newName);
};
• Деклариране на ламбда функции
– Списък от параметри в кръгли скоби
• Посочването на типовете на
параметрите не е задължително
• Ако параметърът е един, скобите не са
задължителни
– Оператор =>
– Тяло
• Израз – резултат от изпълнението на
ламбда функцията (ламбда функция с
израз)
• Блок от операции, който може да
връща, а може и да не връща резултат
(ламбда функция със съждения)
24. Ламбда функции
// Използване на локалнa променливa
// в ламбда функция
int n = 15;
UnaryMethod addN =
x => x + n;
Console.WriteLine(addN(10));
// Промените в стойността на променливата
// след декларацията на функцията се
// отразяват на поведението ѝ
n = 20;
Console.WriteLine(addN(10));
• Използване на локални
променливи в ламбда функции
– В тялото на ламбда функция може да
се използват локалните променливи от
метода, в който е декларирана
– Стойностите на локалните променливи
на метода не се копират при
декларирането на ламбда функцията
и промените в тях след декларацията ѝ
се отразяват на нейното поведение
– Това може да доведе до трудно
откриваеми грешки
26. Ламбда функции
• Приложения на ламбда функциите
– Кратък синтаксис за създаване на делегатни екземпляри
– Подходящи, когато делегатният екземпляр ще бъде използван на
едниствено място в програмата
– Необходими, когато делегатният екземпляр трябва да използва
локални променливи на друг метод
– Доближават синтаксиса на C# до функционалното програмиране
– Правят кода по-четим, премахвайки излишната пунктуация и
декларации
28. Задачи за упражнение
• Създайте конзолно приложение – калкулатор, като
използвате подходящи делегати и делегатни екземпляри за
реализиране на различните аритметични операции
• Преработете конзолния калкулатор в приложение с
графичен потребителски интерфейс и поведение, което е
сходно на вградения в Windows калкулатор
29. Задачи за упражнение
• Преработете програмата „ролева игра“, като реализирате
събития за важни ситуации в играта и походящи
обработчици, които да извършват промени в поведението
на програмата или да извеждат на екрана подходящи
съобщения; примери за такива ситуации са:
– Раняване на чудовище
– Раняване на играч
– Смърт на чудовище
– Смърт на играч
– Опит за придвижване в неразрешена посока
– Попадане в капан и т.н.