790 likes | 1.01k Views
Тема № 4. Об ’ єктні властивості C#. Класи і об ’ єкти. Поняття класу та об ’ єкту в C# в цілому співпадає з загальноприйнятим. Опис класу: class KlName { члени класу }. Reference Type : загальна характеристика.
E N D
Тема №4 Об’єктні властивості C#
Класи і об’єкти • Поняття класу та об’єкту в C# в цілому співпадає з загальноприйнятим. • Опис класу: class KlName { членикласу }
Reference Type: загальна характеристика • Загальна риса: доступ до змінних цих типів здійснюється через вказівники на них. • Але: змінні вказівникових типів ведуть себе подібно до вказівників. • Такими типами є, наприклад: екземпляри класів, в т.ч. рядки; масиви. • null - спеціальне значення для пустого вказівника.
Характерний приклад Нехай є клас Kl і оголошення Kl ekz; Чи означає це оголошення створення екземпляру класу і виділення пам’яті під нього? Ні, створюється лише вказівник відповідного типу. Сам екземпляр створюється шляхом виклику конструктора класу: Kl ekz = new Kl(…); Аналогічно - операції.
Екземпляри класів vN1a vN2a a N1 N2 vN1b vN2b b N1 N2 b=a; b.N1=vNEW; вив. a.N1; //vNEW
Рядки: особливість (рядки є незмінюваними) “AAAA” a “BBBB” b “CCCC” b=a; b=“CCCC”; вив. а; //AAAA
Модифікатори доступу • public – загальнодоступні; • protected – доступні для підкласів; • internal – доступні для класів в межах тієї ж збірки; • internal protected – доступні для підкласів, а також для будь-яких класів у межах збірки; • private - тільки для класу, в якому вони визначені (для членів класу – за замовченням).
Поняття властивості • Концепція властивостей є однією з найбільш характерних рис C#. • Властивість – це конструкція, яка передбачає методи get та/або set для отримання або встановлення відповідного значення.
Приклад властивості (“класичний” синтаксис) У класі: int QP; public int Q { get { return QP; } set { QP = value; } } У клієнті, який використовує клас: sp.Q = 2;
Властивості: коментар до прикладу • Ззовні звернення до властивості виглядає як звернення до поля. • У найбільш типовому випадку властивість пов’язана з окремим полем, але це не обов’язково. У цьому випадку може бути так, що деяка властивість L встановлюється в одне значення, а при зчитуванні отримуємо інше значення. • Якщо потрібно тільки для читання або для запису – тільки get або, відповідно, set.
Приклад public int L { get { return QP + LP; } set { LP = value; } }
З 2.0 – можливість розділення прав доступу • В С# 2.0 з’явилася можливість розділити права доступу для селекторів (get) і модифікаторів (set). • Приклад: public int L { get { return QP + LP; } private set { LP = value; } }
Новий спрощений синтаксис для властивостей – з C# 3.0 public int R { get;set; }
Індексатори • Відповідають параметризованим властивостям специфікації CLR. • В C# - щось подібне до встановлення операції звернення за індексом. • Приклад – клас, який реалізує “безпечний” масив, тобто при зверненні до елементу масиву в межах допустимого діапазону повертається відповідне значення, в іншому випадку – 0. При цьому бажано, щоб звернення відбувалося так, як звичайне звернення до масиву, а нумерація починалася з 1.
Код класу з індексатором class Kl { public int[] M = new int[10]; // базовий масив public int this[int index] { get { if ((index <= 10) && (index > 0)) return M[index - 1]; else return 0; } set { if ((index<=10)&&(index>0)) M[index-1] = value; } } }
Індексатори: інші типові використання • “Неявний” масив, тобто звернення як до масиву, але значення обчислюється динамічно. • Якщо потрібно в класі реалізувати щось на зразок асоціативного масиву.
Створення об’єктів. Конструктори • За допомогою оператора new: KlName ekz=new KlName(параметри); • І за синтаксисом, і за семантикою це виклик конструктора - спеціального методу, який описує, які дії повинні виконуватися при ініціалізації об’єкта. • Синтаксично - метод, ім’я якого співпадає з іменем класу. Нічого не повертає (навіть void). • Конструктори можуть бути перевантажені. • Якщо конструкторів немає - надається конструктор за замовченням. • Але, якщо є хоча б один конструктор - конструктор за замовченням не надається.
Новий синтаксис ініціалізації об’єктів – з C# 3.0 Kl s1 = new Kl { R = 30 }; Семантично цееквівалентно виклику конструктора класу без параметрів і встановленню значень полів або властивостей.
Cпадкування class K1 : K2 { . . . } К1-підклас; К2-суперклас. Так само, як і Java, C# не підтримує множинного спадкування. На вершині ієрархії – клас System.Object.
Закриті класи і методи • Модифікатор sealed. • Для класів цей модифікатор означає, що від цього класу не можна утворювати похідні класи. Таким є, наприклад, клас string. • Для методів цей модифікатор означає, що цей метод не можна перевизначати в підкласах.
Вказівники this та base • this - вказівник на даний екземпляр. • Передається методам екземпляра (нестатичним методам) як неявний параметр. • Одне з типових використань - в конструкторах. • base – посилання на суперклас (нестатичне).
Виклик іншого конструктора Того ж класу: public Kl(…):this(…) { … } Суперкласу: public Kl(…):base(…) { … }
Статичні поля і методи • Супроводжуються модифікатором static. • Поля і методи класу, а не екземпляру. • Для статичних полів - одна копія для всіх екземплярів; для нестатичних - кожний екземпляр має свою копію.Одне з типових використань - підрахунок кількості екземплярів класу. • Виклик - через ім’я класу. • З статичними полями і методами можна працювати, коли ще немає жодного екземпляра.
Статичні поля і методи: продовження • Статичні методи не можуть викликати нестатичні методи, а також звертатися до нестатичних полів. • Типовий механізм ініціалізації статичних полів – статичний конструктор, тобто конструктор з модифікатором static. • Статичним може бути і весь клас – якщо в ньому є лише статичні члени. Неможливо створити екземпляр такого класу; звернення до методів можуть бути тільки статичними.
Приклад статичного класу (ілюструється також readonly) static class Stalker { static readonly public int Pole; static Stalker() //статичний конструктор { Pole = 10; } static public void Metod() { Console.WriteLine(Pole); } }
Ланцюжки конструкторів • Ситуація, подібна до Java. • Перед виконанням конструктора підкласу автоматично викликається конструктор суперкласу.
Перевантаження методів та операторів • Методи, а також конструктори класу, можуть бути перевантажені. Це означає, що в одному класі може бути кілька методів з однаковим іменем; їх розрізнення здійснюється за кількістю і типом аргументів. • На відміну від Java, оператори також можуть перевантажуватися!
Поліморфізм у C#: початок • Об’єктна змінна суперкласу може посилатися на екземпляр підкласу, або посиланню на суперклас може бути надано значення посилання на підклас (розширююче перетворення). • Розширююче перетворення здійснюється автоматично; знижуюче слід робити явно. • “Безпечне” приведення – as. • Перевірка типу – is.
Поліморфізм: віртуальні функції • Класичний приклад: оголошуємо клас Baseз методом Metodта його підклас Sub, в якому теж є метод Metod. Створюємо екземпляр класу Subі викликаємо метод Metod. Метод якого саме класу буде викликаний?
Віртуальні функції: загальні орієнтири • За замовченням методи C# не є віртуальними, але їх можна зробити віртуальними за допомогою модифікатора virtual. • Статичні, а також приватні методи не можуть бути віртуальними. • Важливі модифікатори: override – заміщення; new – затінення.
Демонстрація: Polimorf class Bas { public virtual void Metod() { Console.WriteLine("Base class"); } } class Der : Bas { public void Metod()// new чи override ? { Console.WriteLine("Derived class"); } }
Використання Bas b = new Der();//якщо var – може бути //інакше b.Metod();
Quiz: що виведе програма static void Main(string[] args){ D d = new D(); C c = d; B b = c; A a = b; d.M(); c.M(); b.M(); a.M(); Console.ReadLine(); } } class A { public virtual void M() { Console.Write("A"); } } class B:A { public override void M(){ Console.Write("B");} } class C : B { new public virtual void M() { Console.Write(“С");} } class D : C { public override void M(){Console.Write("D"); } }
Абстрактні методи і класи • Абстрактний метод - метод без реалізації; він супроводжується модифікатором abstract. • Передбачається, що такі методи повинні перевизначатися в підкласах. Тому абстрактні методи автоматично є віртуальними. • Абстрактні методи не мають тіла: public abstract int fun(); • Якщо клас містить хоча б один абстрактний метод, він сам повинен бути оголошений як абстрактний. • Для абстрактного класу не можна створювати екземпляри.
Інтерфейси • Логічний розвиток ідеї абстрактних класів. • Всі методи інтерфейсу є абстрактними і неявно є public. • Інтерфейси не можуть містити полів (на відміну від Java). • Але інтерфейси можуть містити властивості. • В C# прийнято починати назви інтерфейсів з літери I, закінчуються вони часто на able.
Реалізація інтерфейсів • Можна сказати, що інтерфейс - це набір вимог, які висуваються до класу, що його реалізує. • Якщо клас реалізує інтерфейс, то він повинен визначити всі методи, які описані в інтерфейсі. • Синтаксис: class Kl : I {… • Клас може реалізувати декілька інтерфейсів; у цьому випадку вони перелічуються через кому. • Якщо, крім цього, клас Kl спадкує від іншого класу B та реалізує кілька інтерфейсів – class Kl: B,I1,I2,… • Між інтерфейсами може бути відношення спадкування, причому таке спадкування може бути множинним.
Приклад: інтерфейс interface I { int Pole { get; set; } void Metod(); } class Kl : I { public int Pole { get; set; } public void Metod() { Console.WriteLine("Interface implemented with field "+Pole); } }
Явна реалізація інтерфейсів. • Закриття методу інтерфейсу в класі; тоді для звернення до такого методу треба використовувати вказівник інтерфейсу. • Випадок, коли реалізується кілька інтерфейсів, і відповідний метод для кожного інтерфейсу потрібно визначити по-своєму.
Приклад явної реалізації інтерфейсів. Інтерфейси interface I1 { void Metod(); } interface I2 { void Metod(); }
Приклад явної реалізації інтерфейсів. Клас class Kl : I1,I2 { void I1.Metod() { Console.WriteLine("First Interface"); } void I2.Metod() { Console.WriteLine("Second Interface"); } }
Приклад явної реалізації інтерфейсів. Звернення I1 ekz1 = new Kl(); I2 ekz2 = (I2) ekz1; //один об’єкт – різні типи! ekz1.Metod(); ekz2.Metod();
Перевантаження операторів - приклад class Kl { public int Pole; public Kl(int Pole) { this.Pole = Pole; } public static Kl operator +(Kl a, Kl b) { return new Kl(a.Pole + b.Pole); } }
В основній програмі class Program { static void Main(string[] args) { Kl a = new Kl(10) + new Kl(15); Console.WriteLine(a.Pole); Console.ReadLine(); } }
Перевантаження операторів – деякі особливості • Всі оператори мають бути описані як public і static. • Не можна перевантажувати ряд операторів, зокрема =. • Якщо перевантажується +, то += перевантажується автоматично. • Можуть перевантажуватися trueта false (буде робитися приклад на цю тему). • Для операторів приведення типів може бути заданий явний або неявний режим виклику (див. наступний слайд).
Модифікатори explicit та implicit • Використовуються у визначеннях операторів приведення типів для задання того, чи буде перетворення здійснюватися неявно, чи приведення типів слід робити явним чином.
Делегати • Прийнято характеризувати делегати як вказівники на функції. • Типові використання: передача методів як параметрів; організація обробки подій (для цього є спеціальний механізм, тісно пов’язаний з делегатами).
Приклад: непрямий виклик функцій delegate void MyFun(int x); class Program { static void Main(string[] args) { Kl ekz = new Kl(); ekz.Pole = 20; MyFun deleg = ekz.Fun; deleg(3); Console.ReadLine(); } } class Kl { public int Pole {get; set;} public void Fun(int q) { Console.WriteLine(Pole*q); } }
Делегати як параметри delegate double MyFun(double x); class Program { static void DoSth(MyFun f, double y) {//Делегат як параметр Console.WriteLine( f(y) );} static double Kvadrat(double y) {return y * y; }//Власний метод static void Main(string[] args) { DoSth( Math.Sin, Math.PI/2); //Використання існуючих //методів DoSth( Math.Cos, Math.PI/2); DoSth(Program.Kvadrat, 5); //Використання власних методів DoSth( delegate (double x) { return x / 5; //Анонімний метод; в 3.0 – спрощений //синтаксис } , 20 );
Ще один приклад: сортування колекції List<String> ls = new List<string>(); ls.Add("qwerty"); ls.Add("a"); ls.Add("This string is extremely large"); ls.Add("This string is large"); ls.Sort(delegate(String s1, String s2) { return s2.Length - s1.Length; });
Ламбда-вирази: приклад DoSth(x => x/5, 20);