Похожие презентации:
7_Классы_События и делегаты (1)
1. Курс «С#. Программирование на языке высокого уровня»
Павловская Т.А.©Павловская Т.А. (СПбГУ ИТМО)
2.
Делегаты©Павловская Т.А. (СПбГУ ИТМО)
3. Определение делегата
Делегат — это вид класса, предназначенный для храненияссылок на методы. Делегат, как и любой другой класс,
можно передать в качестве параметра, а затем вызвать
инкапсулированный в нем метод.
Делегаты используются для поддержки событий, а также
как самостоятельная конструкция языка.
Описание делегата задает сигнатуру методов, которые
могут быть вызваны с его помощью:
[ атрибуты ] [ спецификаторы ] delegate тип имя([ параметры ])
Пример описания делегата:
public delegate void D ( int i );
Базовым классом делегата является класс System.Delegate
Для того чтобы использовать делегат, необходимо создать
его экземпляр и задать имена методов, на которые он
будет ссылаться.
©Павловская Т.А. (СПбГУ ИТМО)
4.
class Program{
delegate void GetMessage(); // 1. Объявляем делегат
static void Main(string[] args)
{
GetMessage del; // 2. Создаем переменную делегата
if (DateTime.Now.Hour < 12)
{
del = GoodMorning; // 3. Присваиваем этой переменной адрес метода
}
else
{
del = GoodEvening;
}
del.Invoke(); // 4. Вызываем метод
del();
Console.ReadLine();
}
}
private static void GoodMorning()
{
Console.WriteLine("Good Morning");
}
private static void GoodEvening()
{
Console.WriteLine("Good Evening");
}
©Павловская Т.А. (СПбГУ ИТМО)
5.
class Program{
delegate int Operation(int x, int y);
static void Main(string[] args)
{
// присваивание адреса метода через конструктор
Operation del = new Operation(Add); // делегат указывает на метод Add
int result = del.Invoke(4,5);
Console.WriteLine(result);
del = Multiply; // теперь делегат указывает на метод Multiply
result = del (4, 5);
Console.WriteLine(result);
Console.Read();
}
}
private static int Add(int x, int y)
{
return x+y;
}
private static int Multiply (int x, int y)
{
return x * y;
}
©Павловская Т.А. (СПбГУ ИТМО)
6. Использование делегатов
Делегаты применяются в основном для следующих целей:получения возможности определять вызываемый метод
не при компиляции, а динамически во время
выполнения программы;
обеспечения связи между объектами по типу
«источник — наблюдатель»;
создания универсальных методов, в которые можно
передавать другие методы (поддержки механизма
обратных вызовов).
©Павловская Т.А. (СПбГУ ИТМО)
7. Паттерн «источник-наблюдатель»
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(this); // оповещение наблюдателей
}
}
class Program
{
static void Main(string[] args)
{
Subj s = new Subj();
ObsA a1 = new ObsA();
ObsA a2 = new ObsA();
s.Register(a1.Do); //
регистрация набл-й в источнике
s.Register(a2.Do);
s.Register(ObsB.Do);
s.OOPS();
class ObsA // класс-наблюдатель
Console.ReadKey();
{
public void Do(object o)
}
{
}
Console.WriteLine("Вижу, что OOPS!!"); // реакция на событие источника
}
}
class ObsB // класс-наблюдатель
{
public static void Do(object o)
{
Console.WriteLine("Я тоже вижу, что OOPS!!");
}
}
©Павловская Т.А. (СПбГУ ИТМО)
8. При вызове последовательности методов с помощью делегата необходимо учитывать следующее
Привызове
последовательности
помощью
делегата
необходимо
следующее
методов
с
учитывать
Сигнатура методов должна в точности соответствовать делегату.
Методы могут быть как статические, так и обычными методами класса.
Каждому методу в списке передается один и тот же набор параметров.
Если параметр передается по ссылке, изменения параметра в одном
методе отразятся на его значении при вызове следующего метода.
Если параметр передается с ключевым слова out или метод
возвращает значение, результатом выполнения делегата является
значение, сформированное последним из методов списка.
Если в процессе работы метода возникло исключение, не
обработанное в том же методу, последующие методы в списке не
выполняются, а происходит поиск обработчиков в объемлющих
делегат блоках.
Попытка вызвать делегат, в списке которого нет ни одного метода,
вызывает генерацию исключения System.NullReferenceException.
При вызове делегата всегда лучше проверять,
не равен ли он null. Либо можно использовать
метод Invoke и оператор условного null.
Message mes = null;
mes?.Invoke();
9.
©Павловская Т.А. (СПбГУ ИТМО)10. Передача делегата через список параметров
namespace ConsoleApplication1 {public delegate double Fun( double x );
// объявление делегата
class Class1 {
public static void Table( Fun F, double x, double b )
{
Console.WriteLine( " ----- X ----- Y -----" );
while (x <= b)
{ Console.WriteLine( "| {0,8} | {1,8} |", x, F(x));
x += 1; }
}
public static double Simple( double x ) { return 1; }
static void Main()
{ Table( Simple, 0, 3 );
Table( Math.Sin, -2, 2 );
// new Fun(Math.Sin)
Table( delegate (double x ){ return 2; }, 0, 3 );
}}}
©Павловская Т.А. (СПбГУ ИТМО)
11.
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} | {1} |", x, F(x));
x += 1;
} } }
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 Program
{
static void Main()
{
TableFun a = new SinFun();
a.Table(-2, 2);
a = new SimpleFun();
a.Table(0, 3);
Console.ReadKey();
} }}
©Павловская Т.А. (СПбГУ ИТМО)
12. Анонимные методы
delegate(параметры){
// инструкции
}
class Program
{
delegate void MessageHandler(string
message);
static void Main(string[] args)
{
MessageHandler handler = delegate(string
mes)
{
Console.WriteLine(mes);
};
handler("hello world!");
}
}
Console.Read();
©Павловская Т.А. (СПбГУ ИТМО)
delegate int Operation(int x, int y);
static void Main(string[] args)
{
int z = 8;
Operation operation = delegate (int x, int y)
{
return x + y + z;
};
int d = operation(4, 5);
Console.WriteLine(d);
// 17
Console.Read();
}
13. Примеры анонимных методов
Анонимный методбез параметров
Передача анонимного метода
в качестве параметра
class Program
class Program
{
{
delegate void MessageHandler(string
delegate void MessageHandler(string
message);
message);
static void Main(string[] args)
static void Main(string[] args)
{
{
ShowMessage("hello!", delegate(string
MessageHandler handler = delegate
mes){Console.WriteLine(mes);});
{
Console.WriteLine("анонимный метод");
Console.Read();
};
}
handler("hello world!"); // анонимный
static void ShowMessage(string mes,
метод
MessageHandler handler)
{
Console.Read();
handler(mes);
}
}
}
}
Metanit.com
14. Лямбда-выражения
class Program{ delegate int Operation(int x, int y);
static void Main(string[] args)
{
Operation operation = (x, y) => x + y;
Console.WriteLine(operation(10, 20));
// 30
(список_параметров) => выражение
Console.WriteLine(operation(40, 20));
// 60
Console.Read();
}}
Лямбда-выражения
class Program
{ delegate void Hello(); // делегат без параметров
static void Main(string[] args)
{
Hello hello1 = () => Console.WriteLine("Hello");
Hello hello2 = () => Console.WriteLine("Welcome");
hello1();
// Hello
hello2();
// Welcome
Console.Read(); }}
class Program
{
delegate void Hello(); // делегат без параметров
static void Main(string[] args)
{
Hello message = () => Show_Message();
message();
}
private static void Show_Message()
{
Console.WriteLine("Привет мир!");
}}
©Павловская Т.А. (СПбГУ ИТМО)
15. Примеры использования лямбда-выражений
Лямбда-выражение с одним параметромclass Program
{ delegate int Square(int x);
static void Main(string[] args)
{
Square square = i => i * i;
int z = square(6);
Console.Read();}}
class Program
{
delegate void ChangeHandler(ref int x);
static void Main(string[] args)
{
int x = 9;
ChangeHandler ch = (ref int n) => n = n * 2;
ch(ref x);
Console.WriteLine(x);
// 18
Console.Read();
}}
©Павловская Т.А. (СПбГУ ИТМО)
16. Лямбда-выражения как аргументы методов
class Program{ delegate bool IsEqual(int x);
static void Main(string[] args)
{
int[] integers = { 1, 2, 3, 4, 5, 6, 7, 8, 9 };
// найдем сумму чисел больше 5
int result1 = Sum(integers, x => x > 5);
Console.WriteLine(result1); // 30
// найдем сумму четных чисел
int result2 = Sum(integers, x => x % 2 == 0);
Console.WriteLine(result2); //20
}
private static int Sum (int[] numbers, IsEqual func)
{
int result = 0;
foreach(int i in numbers)
{
if (func(i)) result += i;
}
return result;
}} Т.А. (СПбГУ ИТМО)
©Павловская
17. Операции
Делегаты можно сравнивать на равенство и неравенство. Два делегатаравны, если они оба не содержат ссылок на методы или если они
содержат ссылки на одни и те же методы в одном и том же порядке.
С делегатами одного типа можно выполнять операции простого и
сложного присваивания.
Делегат, как и строка string, является неизменяемым типом данных,
поэтому при любом изменении создается новый экземпляр, а старый
впоследствии удаляется сборщиком мусора.
Использование делегата имеет тот же синтаксис, что и вызов метода.
Если делегат хранит ссылки на несколько методов, они вызываются
последовательно в том порядке, в котором были добавлены в делегат.
©Павловская Т.А. (СПбГУ ИТМО)
18.
События©Павловская Т.А. (СПбГУ ИТМО)
19. Определение события
Событие — элемент класса, позволяющий ему посылатьдругим объектам (наблюдателям) уведомления об
изменении своего состояния.
Чтобы стать наблюдателем, объект должен иметь
обработчик события и зарегистрировать его в объектеисточнике
Источник:
- Описано событие OOPS
- Иниц-я события OOPS
Наблюдатель 1:
-Обработчик события OOPS
(реакция на это событие)
Наблюдатель 2:
- Обработчик события OOPS
(реакция на это событие)
Наблюдатель 3:
- Обработчик события OOPS
(реакция на это событие)
©Павловская Т.А. (СПбГУ ИТМО)
20. Механизм событий
Механизм события можно описать с помощью модели«публикация-подписка»: класс, являющийся отправителем
сообщения, публикует события, которые он может
инициировать, а классы получатели, подписываются на
получение этих событий.
События построены на основе делегатов: с помощью
делегатов вызываются методы-обработчики событий.
Поэтому создание события в классе состоит из следующих
частей:
описание делегата, задающего сигнатуру обработчиков
событий;
описание события;
описание метода (методов), инициирующих событие.
Синтаксис события:
[ атрибуты ] [ спецификаторы ] event тип имя
©Павловская Т.А. (СПбГУ ИТМО)
21. Пример
public delegate void Del( object o );// объявление делегата
class A
{
public event Del Oops;
...
}
©Павловская Т.А. (СПбГУ ИТМО)
// объявление события
22.
Обработка событий выполняется в классах-получателяхсообщения. Для этого в них описываются методыобработчики событий, сигнатура которых соответствует
типу делегата. Каждый объект (не класс!), желающий
получать сообщение, должен зарегистрировать в объектеотправителе этот метод.
Событие — это удобная абстракция для программиста. На
самом деле оно состоит из закрытого статического класса,
в котором создается экземпляр делегата, и двух методов,
предназначенных для добавления и удаления обработчика
из списка этого делегата.
Внешний код может работать с событиями единственным
образом: добавлять обработчики в список или удалять их,
поскольку вне класса могут использоваться только
операции += и -=. Тип результата этих операций — void, в
отличие от операций сложного присваивания для
арифметических типов. Иного способа доступа к списку
обработчиков нет.
©Павловская Т.А. (СПбГУ ИТМО)
23.
// Объявляем делегатpublic delegate void AccountStateHandler(string message);
// Событие, возникающее при выводе денег
public event AccountStateHandler Withdrawn;
class Account
{
// Объявляем делегат
public delegate void AccountStateHandler(string
message);
// Событие, возникающее при выводе денег
public event AccountStateHandler Withdrawn;
// Событие, возникающее при добавление на счет
public event AccountStateHandler Added;
int _sum; // Переменная для хранения суммы
public Account(int sum)
{
_sum = sum;
}
public int CurrentSum
{
get { return _sum; }
}
©Павловская Т.А. (СПбГУ ИТМО)
public void Put(int sum)
{
_sum += sum;
if (Added != null)
Added($"На счет поступило {sum}");
}
public void Withdraw(int sum)
{
if (sum <= _sum)
{
_sum -= sum;
if (Withdrawn != null)
Withdrawn($"Сумма {sum} снята со счета");
}
else
{
if (Withdrawn != null)
Withdrawn("Недостаточно денег на счете");
} }}
24.
class Program{
static void Main(string[] args)
{
Account account = new Account(200);
// Добавляем обработчики события
account.Added += Show_Message;
account.Withdrawn += Show_Message;
account.Withdraw(100);
// Удаляем обработчик события
account.Withdrawn -= Show_Message;
account.Withdraw(50);
account.Put(150);
Console.ReadLine();
}
private static void Show_Message(string message)
{
Console.WriteLine(message);
Сумма 100 снята со счета
}}
На счет поступило 150
©Павловская Т.А. (СПбГУ ИТМО)
25.
Класс данных события AccountEventArgsprivate void button1_Click(object sender, System.EventArgs e){}
class AccountEventArgs
{
// Сообщение
public string Message{get;}
// Сумма, на которую изменился счет
public int Sum {get;}
public AccountEventArgs(string mes, int sum) {
Message = mes;
Sum = sum;
}}
class Account
{
// Объявляем делегат
public delegate void AccountStateHandler(object sender,
AccountEventArgs e);
// Событие, возникающее при выводе денег
public event AccountStateHandler Withdrawn;
// Событие, возникающее при добавлении на счет
public event AccountStateHandler Added;
int _sum; // Переменная для хранения суммы
public Account(int sum)
{
_sum = sum;
}
©Павловская Т.А. (СПбГУ ИТМО)
public int CurrentSum
{
get { return _sum; }
}
public void Put(int sum)
{
_sum += sum;
if (Added != null)
Added(this, new AccountEventArgs($"На счет поступило
{sum}", sum));
}
public void Withdraw(int sum)
{
if (_sum >= sum)
{
_sum -= sum;
if (Withdrawn != null)
Withdrawn(this, new AccountEventArgs($"Сумма
{sum} снята со счета", sum));
}
else
{
if (Withdrawn != null)
Withdrawn(this, new
AccountEventArgs("Недостаточно денег на счете", sum));
}
}
}
26.
class Program{
static void Main(string[] args)
{
Account account = new Account(200);
// Добавляем обработчики события
account.Added += Show_Message;
account.Withdrawn += Show_Message;
account.Withdraw(100);
// Удаляем обработчик события
account.Withdrawn -= Show_Message;
account.Withdraw(50);
account.Put(150);
Console.ReadLine();
}
}
private static void Show_Message(object sender, AccountEventArgs e)
{
Console.WriteLine($"Сумма транзакции: e.Sum");
Console.WriteLine(e.Message);
}
©Павловская Т.А. (СПбГУ ИТМО)
27. Пример
class Subj {// -------------- Класс-источник события ---------------------
public event EventHandler Oops;
// Описание события станд. типа
public void CryOops() {
// Метод, инициирующий событие
Console.WriteLine( "OOPS!" ); if ( Oops != null ) Oops( this, null ); }
}
class Obs
{
// --------------- Класс-наблюдатель --------------------------
public void OnOops( object sender, EventArgs e ) {
// Обработчик соб-я
Console.WriteLine( «Оййй!" );
}
}
class Class1 {
static void Main()
{
OOPS!
Оййй!
Оййй!
Subj s = new Subj();
Obs o1 = new Obs(); Obs o2 = new Obs();
s.Oops += o1.OnOops;
// регистрация обработчика
s.Oops += o2.OnOops;
// регистрация обработчика
s.CryOops();
}
}
©Павловская Т.А. (СПбГУ ИТМО)
28. Встроенные делегаты Action, Predicate и Func
public delegate void Action();public delegate void Action<in T>(T obj);
public delegate void Action<in T1,in T2>(T1 arg1, T2 arg2);
public delegate void Action<in T1,in T2,in T3>(T1 arg1, T2 arg2, T3 arg3);
Инкапсулирует метод, который принимает от нуля до 16 параметров и не
возвращает значения.
static void Main(string[] args)
{ Action<int, int> op;
op = Add;
Operation(10, 6, op);
op = Substract;
Operation(10, 6, op); }
static void Operation(int x1, int x2,
Action<int, int> op)
{ if (x1 > x2) op(x1, x2);}
static void Add(int x1, int x2)
{
Console.WriteLine("Сумма чисел: " + (x1
+ x2));
}
static void Substract(int x1, int x2)
{
Console.WriteLine("Разность чисел:
" + (x1 - x2));
}
29. Встроенные делегаты Action, Predicate и Func
public delegate bool Predicate<in T>(T obj);Используется для сравнения, сопоставления некоторого объекта T
определенному условию. В качестве выходного результата возвращается
значение true, если условие соблюдено, и false, если не соблюдено
Predicate<int> isPositive = delegate (int x) { return x > 0; };
Console.WriteLine(isPositive(20));
Console.WriteLine(isPositive(-20));
©Павловская Т.А. (СПбГУ ИТМО)
30. Встроенные делегаты Action, Predicate и Func
public delegate TResult Func<out TResult>();public delegate TResult Func<in T,out TResult>(T arg);
public delegate TResult Func<in T1,in T2,in T3,out TResult>(T1 arg1, T2 arg2, T3 arg3);
Представляет метод, определяющий набор критериев и соответствие указанного
объекта этим критериям.
static void Main(string[] args)
{
Func<int, int> retFunc = Factorial;
int n1 = GetInt(6, retFunc);
Console.WriteLine(n1); // 720
int n2 = GetInt(6, x=> x *x);
Console.WriteLine(n2); // 36
Console.Read();
}
©Павловская Т.А. (СПбГУ ИТМО)
static int GetInt(int x1, Func<int, int>
retF)
{
int result = 0;
if (x1 > 0)
result = retF(x1);
return result;
}
static int Factorial(int x)
{
int result = 1;
for (int i = 1; i <= x; i++)
{
result *= i;
}
return result;
}
31. Пример использования делегата Func
class Class1{
public static void Table(Func<double, double> F, double x,
double b)
{
Console.WriteLine(" ----- X ----- Y -----");
while (x <= b)
{
Console.WriteLine("| {0,8} | {1,8} |", x, F(x));
x += 1;
}
}
public static double Simple(double x) { return 1; }
static void Main()
{
Table(Simple, 0, 3);
Table(Math.Sin, -2, 2);
Table(delegate (double x) { return 2; }, 0, 3);
}
}