CSBasicCourse2ndedPodbelsky / CSBasicCourse2ndedPodbelsky
.pdfлогические функции, и построить для них таблицы истинности.
// 17_07.cs - свойство с типом делегата и анонимные методы using System;
public delegate bool BoolDel(bool x, bool y); // Делегат-тип. public class Create // Класс таблиц истинности.
{
BoolDel specificFun; // Поле, определяющее логическую функцию public BoolDel SpecificFun
{
set { specificFun = value; }
}
public bool[,] define() // Формирование логического массива
{
bool[,] res = new bool[4, 3]; bool bx, by;
int k = 0;
for (int i = 0; i <= 1; i++) for (int j = 0; j <= 1; j++)
{
bx = (i == 0 ? false : true); by = (j == 0 ? false : true); res[k, 0] = bx;
res[k, 1] = by;
res[k++, 2] = specificFun(bx, by);
}
return res;
}
}
public class Methods // Класс с методами
{
static public void printTabl(bool[,] tabl)
{
Console.WriteLine("A B F");
for (int i = 0; i < tabl.GetLength(0); i++) Console.WriteLine("{0} {1} {2}",
tabl[i, 0] ? 1 : 0, tabl[i, 1] ? 1 : 0, tabl[i, 2] ? 1 : 0);
}
}
class Program // Основной класс программы
{
static void Main()
{
Create create = new Create(); create.SpecificFun = delegate(bool a, bool b) { return a || b; };
Console.WriteLine("Таблица для (A || B):");
Methods.printTabl(create.define());
create.SpecificFun = delegate(bool d, bool e) { return d && !e; };
Console.WriteLine("\nТаблица для (A && !B):"); Methods.printTabl(create.define());
}
}
Результаты выполнения программы:
Таблица для (A || B): A B F
0 0 0
0 1 1
1 0 1
1 1 1
Таблица для (A && !B): A B F
0 0 0
0 1 0
1 0 1
1 1 0
17.6. События
Событие "событие" – средство, позволяющее объекту (или классу) послать во "внешний для объекта или класса мир" сообщение о переходе в некоторое новое состояние или о получении сообщения из внешнего источника. Так как на уровне программы все действия объектов и классов реализуются с помощью методов, то и посылка сообщения оформляется как оператор в теле некоторого метода.
Синтаксически оператор посылки сообщения "посылки сообщения" выглядит так:
имя_события (аргументы_для_делегата);
Разберем, о каком событии идет речь и какую роль играет делегат, которому нужно предоставить аргументы.
Отметим, что объявление события может размещаться в классе и в интерфейсе. Начнём с классов. Событие это член класса (или его объекта),
вводимый объявлением "событие:объявление события" \y "события" :
модификаторыopt event имя_делегата имя_события;
модификатором может быть abstract, new, override, static, virtual, public, protected, private, internal.
event – служебное слово "служебное слово: event" \y "служебное слово"
декларации события.
имя_события – идентификатор, выбираемый программистом в качестве названия
конкретного члена, называемого переменной события. В практике программирования на C# принято начинать имена событий с префикса on.
имя_делегата – имя того делегата-типа (его называют делегатом события или
событийным делегатом), который должен представлять событию те методы,
которые будут вызываться в ответ на посылку сообщения о событии.
Таким образом, событие это член класса, имеющий тип делегата. Этот
делегат-тип должен быть доступен в точке объявления события. Например, его определение должно быть в том же файле. Событийный делегат-тип определяет для события сигнатуру тех методов, которые могут быть вызваны в ответ на посылку сообщения о нём. Напомним, что в сигнатуру "сигнатура" , вводимую делегатом,
входит спецификация параметров метода и тип возвращаемого методом значения. В
соответствии с этой сигнатурой определяются типы аргументов в операторе посылки сообщения о событии. В качестве типа возвращаемого значения в событийном делегате обычно используется void.
В качестве примера определим статический метод, посылающий через каждую
секунду сообщение о событии. Чтобы такая возможность появилась, необходимо,
чтобы в классе, которому принадлежит метод, было объявлено событие, и был доступен соответствующий этому событию делегат-тип. Соответствующие
объявления могут быть такими: |
|
delegate void ТimeHandler(); |
// объявление делегата-типа |
static event ТimeHandler onTime; // объявление события
Рекомендуется в название событийного делегата включать в качестве
суффикса слово Handler (обработчик). Делегат ТimeHandler в соответствии с его объявлением предназначен "представлять" методы без параметров, с возвращаемым значением типа void (ничего не возвращающие в точку вызова). Событие с именем onTime "настроено" на работу с экземплярами делегата ТimeHandler.
В том же классе, где размещено объявление события и доступен делегат-тип,
можно определить метод, через каждую секунду "генерирующий" посылку
сообщений:
static void run() { // процесс с событиями Console.WriteLine("Для выхода нажмите Ctrl+C!");
while (true) |
{ // бесконечный цикл |
onTime(); |
// посылка сообщения о событии |
System.Threading.Thread.Sleep(1000); // задержка на 1 секунду |
|
} |
|
} |
|
В методе |
run() бесконечный цикл, в каждой итерации которого оператор |
onTime() посылает сообщение о событии, связанном с делегатом TimeHandler. Затем
вызывается статический метод Sleep() "метод:метод Sleep()" |
класса Thread "класс: |
|
Thread" \y "класс" |
из пространства имен System.Threading |
"пространство имен |
System.Threading" |
. Назначение этого метода состоит в "задержке" процесса |
выполнения программы на количество миллисекунд, соответствующее значению аргумента. В данном случае задержка равна 1000 миллисекундам, то есть одной секунде.
Метод run(), посылая сообщения о событиях, "ничего не знает" о том, кто
будет получать эти сообщения, и как они будут обрабатываться. В технологии
Windows-программирования принято говорить, что объект (в нашем примере не
объект, а статический метод класса) публикует события "события:публикует
события" , посылая сообщения о них. Другие объекты (в нашем примере это будут статические методы) могут подписаться на события.
Подписка на получение сообщений о событии в языке C# предусматривает следующие действия:
создание экземпляра того делегата, на который настроено событие;
подключение экземпляра делегата к событию.
Обычно эти два действия объединяют в одном операторе следующего формата:
имя_события += new имя_делегата (имя_метода);
Условие применимости подписки на событие "события:подписка на событие"
– наличие и доступность метода, который будет вызван для обработки события. Имя этого метода используется в качестве аргумента конструктора делегата. Само собой, и делегат события и имя события должны быть доступны в месте подписки.
На одно событие могут быть подписаны несколько методов, для каждого из которых нужно использовать свой оператор приведенного вида.
Предположим, что "принимать сообщения" о событиях в методе run() должен метод Main() того же класса, в котором определен метод run(). Пусть
обработчиками сообщения о событии должны быть два метода с заголовками: static void one( )
static void two( )
Тогда метод Main может быть таким (программа 17_08.cs): static void Main() {
onTime += new TimeHandler(one); // подписка на событие...
onTime += new TimeHandler(two); // ...для метода two run(); // запуск процесса
}
Остальное, то есть функциональность программы в целом, зависит от возможностей и особенностей методов one() и two(). В следующей ниже программе метод one() выводит в консольное окно дату и посекундно изменяющееся значение времени. Метод two() в начале той же строки выводит порядковый номер события.
Номер изменяется каждую секунду (при каждом обращении к методу).
// 17_08.cs - статические события и статические методы using System;
delegate void TimeHandler( ); // объявление делегата-типа class test_cs {
static event TimeHandler onTime;// объявление события static void run( ) // процесс с генерацией событий
{
Console.WriteLine("Для выхода нажмите Ctrl+C!"); while (true) { // бесконечный цикл
onTime( ); // посылка сообщения о событии
System.Threading.Thread.Sleep(1000); // задержка на 1 сек.
}
}
static void Main( ) {
onTime += new TimeHandler(one); // подписка на событие...
onTime += new TimeHandler(two); // для метода two run(); // запуск процесса
}
static void one( ) { // приемник сообщения string newTime = DateTime.Now.ToString(); Console.Write("\r\t\t{0}", newTime);
}
static int count = 0;
static void two( ) { // приемник сообщения Console.Write("\r{0}", count++);
}
}
Результат выполнения программы на 7-й секунде:
Для выхода нажмите Ctrl+C!
716.05.2009 10:27:17
Втексте программы обратим внимание на вывод предупреждения пользователю:
Console.WriteLine("Для выхода нажмите Ctrl+C!");
Сочетание клавиш Ctrl и C приводит к незамедлительному прекращению выполнения программы. Чтобы не "затемнять" основную схему программы,
иллюстрирующей только механизм работы с событиями, в нее не введены никакие средства диалога с пользователем.
В строке-аргументе метода консольного вывода Write() управляющая эскейп-
последовательность \r обеспечивает при каждом обращении переход в начало строки дисплея. Тем самым вывод все время выполняется в одну и ту же строку,
изображение на которой обновляется ежесекундно.
В методе one() используется свойство Now "свойство: Now" класса Data.Time
"класс: Data.Time" \y "класс" . Его назначение – вернуть текущее значение даты и времени. Применение метода ToString() позволяет представить эти значения в виде одной строки, которая затем выводится на дисплей.
Для подсчета событий (секунд) определена статическая переменная int count.
Ее значение выводит и затем увеличивает на 1 метод two().
В рассмотренном примере делегат объявлен вне классов и все методы статические – генерацию событий выполняет статический метод run(), подписаны на события два других статических метода. Таким образом, с помощью механизма событий взаимодействуют не объекты, а методы одного класса test_cs. Кроме того,
в объявлении делегата отсутствуют параметры. Поэтому при посылке сообщения о событии методы обработки не получают никакой информации из точки возникновения события.
Более общий случай – событие создаётся объектом, а в других объектах (в
объектах других классов) имеется возможность реагировать на эти события. Как мы уже показали, к одному событию может быть "приписано" несколько обработчиков и все они будут вызваны при наступлении события.
Механизм работы с событиями предусматривает несколько этапов.
Объявление делегата-типа, задающего сигнатуру тех (ещё неизвестных на данном этапе) методов, которые будут вызываться при обработке события;
Определение переменной события, имеющей тип делегата события;
Определения генератора события (посылки сообщения), с указанием аргументов, информирующих получателей о состоянии объекта,
пославшего сообщение;
Определение методов обработки события. Сигнатура каждого метода должна соответствовать типу делегата события;
Создание экземпляра того делегата, на который "настроено" событие.
Аргумент конструктора – имя метода обработки;
Подключение экземпляра делегата к переменной события.
Перечисленные этапы обычно относятся к разным классам программы. И этих
разных классов по меньшей мере два. Класс, обрабатывающий события, должен содержать методы обработки или по крайней мере иметь доступ к этим методам. В
нём реализуются этапы 4, 5, 6. Второй класс – это класс, генерирующий события,
реализует этапы 1, 2, 3.
Зачастую в программе присутствует третий класс – управляющий процессом
на более высоком уровне. В разных задачах его называют супервизором,
монитором, диспетчером и т.п. При наличии такого класса и двух подчинённых –
класса генерации и класса обработки событий |
"класс: обработки событий" |
\y |
"класс" - схема работы супервизора сводятся к следующим шагам: |
|
|
Создать объект класса генерации событий |
"класс: генерации событий" |
\y |
"класс" ; |
|
|
Создать объект класса обработки событий (этого может не потребоваться, если метод обработки является статическим);
Создать экземпляр делегата, "настроив" его на метод класса обработки событий;
Подключить экземпляр делегата к переменной события из объекта класса генерации;
Передать управление объекту класса генерации событий (какому-либо из его методов, генерирующих события).
Далее всё выполняется в соответствии с общими принципами событийного управления.
В качестве примера рассмотрим программу с делегатом и четырьмя классами.
Класс Sorting содержит метод, который сортирует в порядке возрастания одномерный целочисленный массив. В процессе сортировки подсчитывается количество перестановок значений элементов. При каждом завершении внутреннего цикла формируется событие, передающее "во внешний мир" количество выполненных перестановок, размер массива и счётчик итераций внешнего цикла.
Для работы с событиями в классе объявлено событие onSort, имеющее тип
внешнего делегата SortHandler.
Класс View содержит метод обработки события. Метод выводит на консоль значение счётчика перестановок.
Класс display визуализирует динамику процесса сортировки – выводит на консоль имитацию элемента управления ProgressBar.
Метод Main() управляющего класса Controller в соответствии с общей схемой создаёт объект класса, генерирующего события, создаёт объект класса – обработчика (View). Затем подключает к переменной события два наблюдателя – два безымянных экземпляра делегата SortHandler. И, наконец, управление передаётся методу сортировки объекта – генератора событий.
//17_09.cs - события и сортировка using System;
using System.Text;
//Объявление делегата-типа:
public delegate void SortHandler(long cn, int si, int kl); class Sorting // класс сортировки массивов
{
int size; // размер массива int[ ] ar; // ссылка на массив
public long count; // счетчик обменов при сортировке public event SortHandler onSort; // объявление события public Sorting(int[ ] ls) // конструктор
{
size = ls.Length; count = 0;
ar = ls;
}
public void sort( ) // сортировка с посылкой извещений
{
int temp;
for (int i = 0; i < size - 1; i++)
{
for (int j = i + 1; j < size; j++) if (ar[i] > ar[j])
{
temp = ar[i]; ar[i] = ar[j]; ar[j] = temp; count++;
}
if (onSort != null)
onSort(count, size, i); // генерация события
}
}
}
class View
{ // Обработчик событий в объектах: public void nShow(long n, int si, int kl)
{
Console.Write("\r" + n);
}
}
class Display // Обработчик событий в этом классе
{
static int len=30; static string st = null;
public static void barShow(long n, int si, int kl)
{
int pos = Math.Abs((int)((double)kl / si * len)); string s1 = new string('\u258c', pos);
string s2 = new string('-', len - pos-1) + '\u25c4'; // unicode для треугольника;
st = s1 + '\u258c' + s2; //'\u258c' - код прямоугольника Console.Write("\r\t\t" + st);
}
}
class Controller
{
static void Main()
{
Random ran = new Random(55); int[ ] ar = new int[19999];
for (int i = 0; i < ar.Length; i++) ar[i] = ran.Next( );
Sorting run = new Sorting(ar);
View watch = new View( ); // Создан объект run.onSort += new SortHandler(Display.barShow); run.onSort += new SortHandler(watch.nShow); run.sort( );
Console.Write("\n");
}
}
1 |
Результат выполнения программы: |
7 |
2 |
6 |
1 |
0 |
||
0 |
0 |
3 |