- •Мови програмування. Представлення даних.
- •1. Вступ. Про обчислювальну техніку.
- •2. Історія мов програмування.
- •3. Поняття про платформу .Net
- •4. Створення мови програмування с#.
- •5. Представлення даних. Необхідність типізації. Двійкова арифметика.
- •6. Основні поняття програмування.
- •Поняття про інтегроване середовище розробки VisualStudio (на прикладі Visual Studio 2005). Структура c#-програми.
- •1. Основні можливості інтегрованого середовища розробки VisualStudio .Net.
- •2. Структура програми мовою с#.
- •Int I; // Визначили цілу змінну
- •Основні елементи мови с#.
- •1. Основні вбудовані типи мови с#
- •2. Визначення та ініціалізація змінних, область їх видимості.
- •3. Приведення типів.
- •Інакше обидва операнди перетворюються до типу int;
- •4. Літерали (константи) мови с#.
- •5. Операції мови с#.
- •5.1. Арифметичні операції.
- •5.2. Операції інкременту та декременту.
- •5.3. Операції відношення (порівняння).
- •5.4. Логічні операції.
- •5.5. Порозрядні (бітові) операції.
- •5.6. Умовна (тернарна) операція.
- •5.7. Операції присвоєння.
- •5.8. Пріоритет операцій.
- •Основні інструкції керування мови с# – розгалуження та цикли.
- •1. Розгалуження у мові с#
- •2. Цикли у мові с#
- •2.1. Цикл for.
- •2.2. Цикл while.
- •2.3. Цикл do-while.
- •3. Керування виходом із циклів с#
- •Масиви в мові с#.
- •1. Визначення та ініціалізація масиву.
- •2. Цикл foreach
- •3. Багатовимірні масиви.
- •4. Використання деяких методів класу System.Array.
- •5. Масиви масивів. Непрямокутні масиви.
- •Структуровані типи даних (колекції) в мові c#
- •1. Основні структури даних та їх призначення
- •2. Використання списку ArrayList та узагальненого списку List
- •3. Використання асоційованого списку Hashtable та узагальненого словника Dictionary
- •Класи в мові с#.
- •1. Визначення класу.
- •2. Методи класу.
- •3. Методи з параметрами.
- •4. Конструктор класу.
- •Методи в мові с#.
- •1. Передача об’єктів методам.
- •2. Використання модифікаторів для параметрів методів.
- •3. Методи, що повертають об’єкти.
- •Перевантаження методів в мові с#.
- •1. Перевантаження методів.
- •2. Перевантаження конструкторів.
- •3. Використання ключового слова this.
- •4. Деструктор класу.
- •5. Метод Main ().
- •Статичні члени класу.
- •1. Статичні дані-члени класу.
- •2. Статичні методи-члени класу.
- •3. Статичний конструктор класу.
- •4. Статичні класи, локалізація та глобалізація
- •Властивості та індексатори.
- •1. Властивості.
- •2. Індексатори.
- •Спадкування в мові с#.
- •1. Поняття про спадкування та ієрархію класів.
- •2. Спадкування та правила доступу до членів класів.
- •3. Конструктори базового та похідних класів.
- •4. Посилання на екземпляри базового та похідних класів.
- •5. Поняття про поліморфізм.
- •6. Віртуальні функції – більш детальний погляд.
- •7. Абстрактні методи та класи.
- •Перевантаження операцій в мові с#.
- •1. Загальні відомості.
- •2. Перевантаження бінарних арифметичних операцій.
- •3. Перевантаження унарних операцій.
- •4. Перевантаження операцій відношення.
- •5. Перевантаження логічних операцій.
- •6. Підсумкові зауваження.
- •Структури та переліки в мові с#.
- •1. Структури.
- •2. Переліки.
- •Делегати, події та обробники подій
- •1. Делегати (delegate).
- •2. Події та їх обробники.
- •Атрибути та їх використання
- •Рекомендована література
Делегати, події та обробники подій
В цьому розділі будуть розглянуті деякі питання, пов’язані з реалізацією реакції програми на події, що можуть виникати під час її роботи, – це так звана модель подій в мові С#. Події (events) найчастіше виникають при розробці програм, орієнтованих на роботу під Windows. Ними можуть бути натискання певних клавіш або кнопок, «кліки» миші, тощо. З технічної точки зору при цьому необхідні засоби, які дозволяють викликати деякий конкретний метод, як реакцію на певну подію, що сталась в системі під час роботи. При цьому, який саме метод має зреагувати на подію, звісно, не відомо в момент компіляції програми. В багатьох мовах програмування, зокрема в С++, цій меті слугують вказівники на функцію, які виступають в ролі параметрів функції-обробника події. Проте використання прямої адресації, пов’язаної із застосуванням вказівників, не завжди бездоганне з точки зору безпеки програми. Мова С# має безпечний засіб реалізації подієорієнтованої моделі програми – це делегати.
1. Делегати (delegate).
Отже, делегати призначені для збереження відомостей про метод, який може бути переданий у метод-обробник події в ролі його параметру. Фактично вони «делегують» деякий метод для виконання іншим методом. З синтаксичної точки зору делегат – це посилальний тип даних (reference type), що визначає повну сигнатуру методу разом із його типом результату. При створенні екземплярів класу ми спочатку визначаємо клас, лише потім створюємо і використовуємо відповідні об’єкти. Так само маємо поступити і з делегатами – спочатку декларуємо тип делегата, потім створюємо і використовуємо його екземпляри, передаючи їх у метод для виконання ним.
Для декларації делегату використовується службове слово delegate, після якого просто вказується результат та сигнатура методу, що відповідає даному типу делегату. Наведемо приклади декларацій делегатів:
// делегат DoSth представляє функції, що приймають 1 рядковий
// параметр та не повертають результат
delegate void DoSth(string x);
// делегат MyFun представляє функції, що приймають 1 дійсний
// параметр і повертає дійсне число
delegate double MyFun(double x);
Делегат насправді являє собою клас, успадкований від класу System.Delegate. Тому декларувати делегат можна як у просторі імен, так і всередині іншого класу.
Для ілюстрації корисності делегатів, розглянемо типовий приклад. Нехай необхідно програмно побудувати графік скалярної функції виду . Для скінченого набору функцій можна написати метод, який повертає результат виклику функції . Програма з методом для обрахунку лише трьох функцій (синусу, косинусу та модуля) наведена нижче:
using System;
namespace Demo
{
class Program
{
static void Main(string[] args)
{
// косинус в точці 1
Console.WriteLine(Method(1, 1));
// синус в точці 1
Console.WriteLine(Method(2, 1));
// модуль в точці 1
Console.WriteLine(Method(3, 1));
}
// Метод обраховує значення функції,
// визначеної номером iFunctionNumber, в точці x
static double Method(int iFunctionNumber, double x)
{
if (iFunctionNumber == 1)
return Math.Cos(x);
if (iFunctionNumber == 2)
return Math.Sin(x);
if (iFunctionNumber == 3)
return Math.Abs(x);
else return 0;
}
}
}
Як можна бачити, даний код є важко піддається подальшому розширенню кількості функцій, тому що всі можливі функції, значення яких можуть в ньому обраховуватись, повинні бути жорстко закодовані (hard coded) в метод Method класу Program. Подібні програми являють собою зразок поганого стилю програмування. Спробуємо покращити код програми, використавши в ролі параметру методу Method() не номер функції, а делегат для цього методу.
using System;
namespace Demo
{
// делегат MyFun представляє функції, що приймають 1 дійсний
// параметр та повертають дійсний результат
delegate double MyFun(double x);
class Program
{
static void Main(string[] args)
{
// косинус в точці 1
Console.WriteLine(Method(Math.Cos, 1));
// синус в точці 1
Console.WriteLine(Method(Math.Sin, 1));
// модуль в точці 1
Console.WriteLine(Method(Math.Abs, 1));
}
// Метод обраховує значення функції,
// що визначається делегатом MyFun, в точці x
static double Method(MyFun f, double x)
{ return f(x); }
}
}
Зауваження
-
Як можна помітити, тепер код програми значно скоротився.
-
Даний метод Method() має першим параметром функцію-делегат, значення якої буде повертатись як результат цього методу.
-
Аргументом для функції з параметром-делегатом може бути довільний метод з іншого класу, причому як статичний, так і метод екземпляру.
-
В ролі аргументу для параметра-делегата також може виступати метод, безпосередньо написаний в даному коді – так званий вбудований метод.
Вдосконалимо попередню програму, щоб проілюструвати зміст двох останніх зауважень та продемонструємо використання оператора += для делегатів. Оператор += для делегатів дозволяє побудувати послідовність методів-делегатів, які будуть виконані один за одним («ланцюжком») – таким чином буде організована та виконана ціла черга методів. (Так само можна використати і оператор -= для виключення членів черги із ланцюжка обробки). В реальному житті ця ситуація дійсно нагадує чергу, коли всі, хто в ній знаходяться, послідовно виконують одну й ту саму дію:
using System;
namespace Demo
{
// делегат MyFun представляє функції, що приймають 1 дійсний
// параметр та повертають дійсний результат
delegate double MyFun(double x);
class Program
{
static void Main(string[] args)
{
// Визначаємо делегат f, який поки що є нульовим посиланням
MyFun f = null;
// включимо до ланцюжка делегатів статичний метод:
f += Math.Sin;
// включимо до ланцюжка деякий тестовий метод TestFun з
// відповідною сигнатурою:
f += Program.TestFun;
// включимо до ланцюжка вбудований метод – його код
// визначений безпосередньо тут:
f += delegate(double x)
{
return x * x - 1;
};
// запускаємо чергу-ланцюжок в метод Method():
Console.WriteLine(Method(f, 1));
}
// Визначення тестового методу TestFun, сигнатура якого
// відповідає делегату
static double TestFun(double x)
{
return x * x * x;
}
// Метод обраховує значення функції,
// визначеної делегатом f, в точці x
static double Method(MyFun f, double x)
{ return f(x); }
}
}
В результаті виконання цього прикладу на екрані побачимо 0. Це результат виконання інструкції Console.WriteLine (Method(f, 1));. В ній викликається метод Method (f, 1), в якому змінна f одержує по черзі посилання на всі методи із ланцюжка делегатів, а на екрані ми бачимо лише результат звертання до останнього з них – вбудованого методу, який повертає значення .
Зауваження
-
Оскільки делегат є представником reference-типу, то його значенням за замовченням є null. Таким чином, якщо в метод замість функції буде переданий нульовий вказівник null (у випадку відсутньої ініціалізації делегату), відбудеться виключення NullReferenceException. Тому, коректним підходом до використання екземпляру-делегату є перевірка його значення на нерівність нульовій адресі null.
-
У випадку побудови ланцюжка з функцій-делегатів, що повертають значення, та виклику методу з аргументом-делегатом, послідовно виконаються всі функції, що входять до ланцюжка, а результатом всього ланцюжка буде результат виконання останньої функції в ланцюжку (last wins).