- •Розділ 1. Основи платформи .Net
- •1.1. Основи платформи .Net
- •1.2. Загальні відомості об'єктно-орієнтованого програмування
- •1.3. Середовище Visual Studio .Net
- •1.4 Консольні додатки
- •Розділ 2. Основні поняття мови
- •2.1. Склад мови
- •2.1.1 Алфавіт і лексеми
- •2.1.2. Ідентифікатори і ключові слова
- •2.1.3. Знаки операцій і роздільники
- •2.1.4. Літерали
- •Константи в с#
- •Суфікси цілих і дійсних констант
- •Управляючі послідовності у с#
- •2.1.5. Коментарі
- •2.2. Типи даних
- •2.2.1. Класифікація типів
- •2.2.2. Типи літералів
- •2.2.3. Типи-значення і посилальні типи
- •2.2.4. Упаковка і розпаковування
- •2.3. Рекомендації по програмуванню
- •Розділ 3. Змінні, іменовані константи, операції і вирази
- •3.1. Змінні і іменовані константи
- •3.2. Операції і вирази
- •3.2.1. Перетворення вбудованих арифметичних типів-значень
- •3.2.2. Введення у виключення
- •3.2.3. Основні операції с#
- •Операнд_1 ? операнд_2 : операнд_3.
- •3.3. Лінійні програми (програмування лінійних обчислювальних процесів)
- •3.3.1. Просте введення-виведення даних
- •3.3.2. Математичні функції - клас Math
- •Розділ 4. Оператори
- •4.1. Вирази, блоки
- •4.2. Оператори розгалуження
- •4.2.1. Умовний оператор if
- •4.2.1. Умовний оператор switch
- •4.3. Оператори циклу
- •4.3.1. Цикл з передумовою while
- •4.3.2. Цикл з постумовою do
- •4.3.3. Цикл з параметром for
- •For ( ініціалізація; вираз; модифікації ) оператор;
- •4.3.4. Цикл перебору foreach
- •4.3.5. Рекомендації по вибору оператора циклу
- •4.4. Обробка виняткових ситуацій
- •4.4.1. Оператор try
- •Try блок [ блоки catch ] [ блок finally ]
- •4.4.2. Оператор throw
- •Throw [ вираз ];
- •4.4.3. Клас Exception
- •4.4.4. Оператори checked и unchecked
- •4.5. Рекомендації по програмуванню
- •Розділ 5. Класи: основні поняття
- •5.1. Привласнення і порівняння об'єктів
- •5.2. Дані: поля і константи
- •5.3. Методи
- •5.3.1. Параметри методів
- •5.3.2. Параметри-значення
- •5.3.3. Параметри-посилання
- •5.3.4. Вихідні параметри
- •5.4. Ключове слово this
- •5.5. Конструктори
- •5.6. Властивості
- •[ Атрибути ] [ специфікатори ] тип ім’я_властивості
- •[ Get код_доступа ] [ set код_доступа ]
- •5.7. Рекомендації по програмуванню
- •Розділ 6. Масиви і рядки
- •6.1. Одновимірні масиви
- •6.2. Прямокутні масиви
- •6.3. Ступінчасті масиви
- •6.4. Клас System.Array
- •6.5. Клас Random
- •6.6. Оператор foreach
- •6.7. Масиви об’єктів
- •6.8. Символи і рядки
- •6.8.1. Символи
- •6.8.2. Масиви символів
- •6.8.3. Рядки типу string
- •6.8.4. Форматування рядків
- •6.8.5. Рядки типу StringBuilder
- •6.9. Рекомендації з програмування
- •Розділ 7. Класи: подробиці
- •7.1. Перевантаження методів
- •7.2. Рекурсивні методи
- •7.3. Методи із змінною кількістю аргументів
- •7.4. Метод Main
- •7.5. Індексатори
- •7.6. Операції класу
- •7.6.1. Унарні операції
- •7.6.2. Бінарні операції
- •7.6.3. Операції перетворення типу
- •7.7. Деструктор
- •7.8. Вкладені типи
- •7.9. Рекомендації по програмуванню
- •Розділ 8. Ієрархії класів
- •8.1. Спадкоємство
- •8.2. Віртуальні методи
- •8.3. Абстрактні класи
- •8.4. Безплідні класи
- •8.5. Клас object
- •8.6. Рекомендації по програмуванню
- •Розділ 9. Інтерфейси і структурні типи
- •9.1. Синтаксис інтерфейсу
- •9.2. Реалізація інтерфейсу
- •9.3. Робота з об'єктами через інтерфейси. Операції is і as
- •9.4. Інтерфейси і спадкоємство
- •9.5. Стандартні інтерфейси .Net
- •9.5.1. Порівняння об'єктів (інтерфейс iComparable)
- •9.5.2 Сортування по різних критеріях (інтерфейс iComparer)
- •9.5.3 Перевантаження операцій відношення
- •9.5.4. Клонування об'єктів (інтерфейс iСloneable)
- •9.5.5. Перебір об'єктів (інтерфейс iEnumerable) і ітератори
- •9.6. Структури
- •9.7. Перелічення
- •9.7.1 Операції з переліченнями
- •9.7.2. Базовий клас System.Enum
- •9.8. Рекомендації по програмуванню
- •Розділ 10. Делегати, події і потоки виконання
- •10.1. Делегати
- •10.1.1. Опис делегатів
- •10.1.2. Використання делегатів
- •10.1.3. Патерн “спостерігач”
- •10.1.4. Операції
- •10.1.5. Передача делегатів в методи
- •10.1.6. Обробка виключень при виклику делегатів
- •10.3. Багатопотокові додатки
- •10.3.1. Клас Thread
- •Lock ( вираз ) блок_операторів
- •10.3.2. Асинхронні делегати
- •10.4. Рекомендації по програмуванню
- •Розділ 11. Робота з файлами
- •11.1. Потоки байтів
- •11.2. Асинхронне уведення-виведення
- •11.3. Потоки символів
- •11.4. Двійкові потоки
- •11.5. Консольне уведення-виведення
- •11.6. Робота з каталогами і файлами
- •11.7. Збереження об'єктів (серіалізація)
- •11.8. Рекомендації по програмуванню
- •Розділ 12. Збірки, бібліотеки, атрибути, директиви
- •12.2. Створення бібліотеки
- •12.3. Рефлексія
- •12.4. Атрибути
- •12.5. Простір імен
- •12.6. Директиви препроцесора
- •# Константний_вираз
- •[ #Elif константний_вираз
- •[ #Elif константний_вираз
- •Розділ 13. Структури даних, колекції і класи-прототипи
- •13.1. Абстрактні структури даних
- •13.2. Простір імен System.Collections
- •13.3. Клас ArrayList
- •13.4. Класи-прототипи
- •13.5. Створення класу-прототипу
- •13.6. Узагальнені методи
- •13.7. Часткові типи
- •13.8. Типи, що обнуляються
- •13.9. Рекомендації по програмуванню
- •Розділ 14. Додаткові засоби с#
- •14.1. Небезпечний код
- •Unsafe блок
- •14.1.1. Синтаксис вказівок
- •14.1.2. Перетворення та ініціалізація вказівок
- •14.1.3. Операції з вказівками
- •14.2. Регулярні вирази
- •14.2.1. Метасимволи
- •14.2.2. Класи бібліотеки .Net для роботи з регулярними виразами
- •14.3. Документування у форматі xml
- •Лабораторні роботи
- •Лабораторна робота 5. Одновимірні масиви
- •Лабораторна робота 6. Двовимірні масиви
- •Лабораторна робота 7. Рядки
- •Лабораторна робота 8. Класи і операції
- •Лабораторна робота 9. Спадкоємство
- •Лабораторна робота 10. Структури
- •Лабораторна робота 11. Інтерфейси і параметризовані колекції
- •Список літератури
- •Додатки Додаток 1. Специфікатори формату для рядків с#
9.3. Робота з об'єктами через інтерфейси. Операції is і as
При роботі з об'єктом через об'єкт типу інтерфейсу буває необхідно переконатися, що об'єкт підтримує даний інтерфейс. Перевірка виконується за допомогою бінарної операції is. Ця операція визначає, чи сумісний поточний тип об'єкту, що знаходиться зліва від ключового слова is, з типом, заданим праворуч.
Результат операції рівний true, якщо об'єкт можна перетворити до заданого типу, і false – в іншому випадку. Операція зазвичай використовується в наступному контексті:
if ( об’єкт is тип )
{
// виконати перетворення "об'єкту" до "типу"
// виконати дії з перетвореним об'єктом
}
Допустимо, ми оформили якісь дії з об'єктами у вигляді методу з параметром типу object. Перш ніж використовувати цей параметр усередині методу для звернення до методів, описаних в похідних класах, потрібно виконати перетворення до похідного класу. Для безпечного перетворення слід перевірити, чи можливо воно, наприклад так:
static vod Act(object A)
{
if ( A is IAction )
{
IAction Actor = (IAction) A;
Actor.Draw();
}
}
У метод Act можна передавати будь-які об'єкти, але на екран будуть виведені тільки ті, які підтримують інтерфейс IAction.
Недоліком використання операції is є те, що перетворення фактично виконується двічі: при перевірці і при власне перетворенні. Ефективнішою є інша операція - as. Вона виконує перетворення до заданого типу, а якщо це неможливо, формує результат null, наприклад:
static void Act(object A)
{
IAction Actor = A as IAction;
if (Actor != null )Actor.Draw();
}
Обидві розглянуті операції застосовуються як до інтерфейсів, так і до класів.
9.4. Інтерфейси і спадкоємство
Інтерфейс може не мати або мати скільки завгодно інтерфейсів-предків, в останньому випадку він успадковує всі елементи всіх своїх базових інтерфейсів, починаючи з самого верхнього рівня. Базові інтерфейси мають бути доступні як і їх нащадки. Як і в звичайній ієрархії класів, базові інтерфейси визначають загальну поведінку, а їх нащадки конкретизують і доповнюють його. У інтерфейсі-нащадку можна також вказати елементи, які змінюють успадковані елементи з такою ж сигнатурою. В цьому випадку перед елементом указується ключове слово new, як і в аналогічній ситуації в класах. За допомогою цього слова відповідний елемент базового інтерфейсу приховується. Приклад:
interface IBase
{
void F(int i );
}
interface Ileft : IBase
{
new void F( int i ); // перевизначення методу F
{
interface Iright : IBase
{
void G();
}
interface IDerived : ILeft, IRight {}
class A
{
void Test( IDerived d ) {
d.F(1); // Викликається ILeft.F
((IBase)d). F(1): // Викликається IBase.F
((ILeft )d). F(1); //' Викликається ILeft. F
((IRight)d).F(1); // Викликається IBase.F
}
}
Метод F з інтерфейсу IBase прихований інтерфейсом ILeft, не дивлячись на те що в ланцюжку IDerived - IRight - IBase він не перевизначався.
Клас, що реалізовує інтерфейс, повинен визначати всі його елементи, зокрема успадковані. Якщо при цьому явно указується ім'я інтерфейсу, воно повинне посилатися на той інтерфейс, в якому був описаний відповідний елемент, наприклад:
class А : IRight
{
IRight.G() { ... }
IBase.F( int i ) { ... } // IRight.F( int i ) - не можна
}
Інтерфейс, на власні або успадковані елементи якого є явне посилання, має бути вказаний в списку предків класу, наприклад:
class В : А
{
// IRight.G() { ... } не можна!
}
class С : A, IRight
{
IRight.G() { ... } // можна
IBase.F( int i ) { ... } // можна
}
Клас успадковує всі методи свого предка, зокрема ті, які реалізовували інтерфейси. Він може перевизначити ці методи за допомогою специфікатора new, але звертатися до них можна буде тільки через об'єкт класу. Якщо використовувати для звернення посилання на інтерфейс, викликається не перевизначена версія:
interface IBase
{
void А();
}
class Base : IBase
{
public void A() { ... }
}
class Derived: Base
{
new public void A() { ... }
}
...
Derived d = new Derived ();
d.A(); // викликається Derived.A();
IBase id = d;
id.A(); // викликається Base.A();
Проте якщо інтерфейс реалізується за допомогою віртуального методу класу, після його перевизначення в нащадку будь-який варіант звернення (через клас або через інтерфейс) приведе до одного і того ж результату:
interface IBase
{
void А();
}
class Base : IBase
{
public virtual void A() { ... }
}
class Derived: Base
{
Public override void A() { ... }
}
...
Derived d = new Derived ();
d.A(); // викликається Derived.A();
IBase id = d;
id.A(); // викликається Base.A();
Метод інтерфейсу, реалізований явною вказівкою імені, оголошувати віртуальним забороняється. При необхідності перевизначити в нащадках його поведінку користуються наступним прийомом: з цього методу викликається інший, захищений метод, який оголошується віртуальним. У приведеному далі прикладі метод А інтерфейсу IВase реалізується за допомогою захищеного віртуального методу А, який можна перевизначати в нащадках класу Base:
interface IBase
{
void A();
}
class Base : IBase
{
void IBase.A() { A_(); }
protected virtual void A_() { ... }
}
class Derived: Base
{
protected override void A() { ... }
}
Існує можливість повторно реалізувати інтерфейс, вказавши його ім'я в списку предків класу разом з класом-предком, що вже реалізував цей інтерфейс. При цьому реалізація перевизначених методів базового класу до уваги не береться:
interface IBase
{
void А();
}
class Base : IBase
{
void IBase.A() { ... } // не використовується в Derived
}
class Derived : Base, IBase
{
public void A() { ... }
}
Якщо клас успадковує від класу і інтерфейсу, які містять методи з однаковими сигнатурами, успадкований метод класу сприймається як реалізація інтерфейсу, наприклад:
interface Interfacel
{
void F( ):
}
class Class1
{
public void F() { ... }
public void G{} { ... }
}
class Class2 : Class1, Interface1
{
new public void G( ) { ... }
}
Тут клас Class2 успадковує від класу Class1 метод F. Інтерфейс Interface1 також містить метод F. Компілятор не видає помилку, тому що клас Class2 містить метод, відповідний для реалізації інтерфейсу.
Взагалі при реалізації інтерфейсу враховується наявність “відповідних” методів в класі незалежно від їх походження. Це можуть бути методи (описані в поточному або базовому класі) які реалізують інтерфейс явним або неявним чином.