
- •Ключове слово this
- •Ключове слово static
- •Статичний конструктор
- •Інкапсуляція з використанням методів get і set
- •Інкапсуляція з використанням властивостей
- •Організація робіт при описі класу. Атрибут partial
- •Спадкоємство
- •Int point; // поле
- •Додавання до класу запечатаного класу
- •Вкладеність класів
- •Поліморфізм
- •Абстрактні класи
- •Приховування членів класу
- •Оператори as і is
- •Структури
Інкапсуляція з використанням властивостей
У С# є інший, зручніший спосіб інкапсулювання даних - це використання так званих властивостей. Властивості - це спрощене синтаксичне представлення методів доступу до поля.
Додамо до класу Employee ще одне поле: табельний номер працівника. Визначення властивостей представлене в програмі лістингу 8.3.
Листинг 8.3
// У С# є інший, зручніший спосіб інкапсулювання даних - це використання так званих властивостей.
// Властивості - це спрощене синтаксичне представлення методів доступу до поля. Додамо до класу Employee
// ще одне поле: табельний номер працівника. Визначення властивостей представлене в програмі лістингу 8.3.
using System;
namespace app24_properties
{
class Employee
{
private string empName;
private int empID;
private float empPay;
// Конструктор з полями
public Employee(string Name, int ID, float Pay)
{
empName=Name; empID=ID; empPay=Pay;
}
// Конструктор з властивостями
/* Щоб конструктори відрізнялися, параметр Pay взятий іншого типу. Але поле empPay
оголошено типу float, тому знадобилося примусове приведення параметра Pay до
типу float. this задіяний через однаковість імен параметрів і властивостей */
public Employee(string Name, int ID, double Pay)
{
this.Name=Name; this.ID=ID; this.Pay=(float)Pay;
}
// Так визначаються властивості Name, ID, Pay:
public string Name
{
get { return empName; }
set
{
if (value.Length > 9)
Console.WriteLine ("Помилка довжини: довжина імені > 9. "+
"Ім'я = {0}", value);
else
empName = value;
} // set
} // Name
public int ID
{
get { return empID; }
set { empID = value; }
} // ID
public float Pay
{
get {return empPay;}
set { empPay = value;}
}
} // Employee
class Program
{
public static void Main(string[] args)
{
Employee emp = new Employee("Іванов", 411, 15000);
// Виведення полів об'єкта через виведення властивостей:
Console.WriteLine("empName = {0}", emp.Name);
Console.WriteLine("empID = {0}", emp.ID);
Console.WriteLine("empPay = {0}", emp.Pay);
// emp.SetPay(emp.GetPay() + 1000);
// Без властивостей треба було б так робити
emp.Pay += 1000; // emp.Pay буде дорівнювати 16000
Console.WriteLine("empPay після додавання 1000 до властивості Pay = {0}", emp.Pay);
// Створення об'єкта із конструктора з допомогою задання властивостей
Console.WriteLine("Створення об'єкта через властивості:");
Employee emp1 = new Employee("Петров",001,25000);
// Виведення полій об'єкта через виведення властивостей:
Console.WriteLine("empName = {0}",emp1.Name);
Console.WriteLine("empID = {0}",emp1.ID);
Console.WriteLine("empPay = {0}",emp1.Pay);
Console.Write("Press any key to continue... ");
Console.Read();
}
}
}
Ми бачимо, що властивості мають такі ж імена, як і відповідні поля, для обробки яких створюються властивості. Тип доступу до властивостей - public, бо це фактично методи, які будуть викликатися поза даним класом, тому і повинні бути загальнодоступними. У властивості є тіло, обмежене фігурними дужками, в яке поміщено два спеціальні методи управління відповідним властивості полем. У методів задані в середовищі виконання імена get (отримати значення поля) і set (встановити значення поля). Метод get() повертає значення поля, метод set() присвоює полю значення змінної value, яке визначається в основній програмі, яка виконує метод set(). При цьому тип value завжди співпадає з типом властивості. Властивості хороші не тільки коротшою формою запису, але і також тим, що вони можуть брати участь в операціях усередині класу. Наприклад, якщо в клас додати поле private float emppay (зарплата) якому відповідатиме властивість, визначена як
public float Pay
{
get {return emppay;}
set { emppay = value;}
}
(компілятор потім перетворить цей короткий запис в звичайні функції-методи), то якщо потрібно буде збільшити зарплату, скажімо на 1000, то за відсутності апарату властивостей треба було б писати оператори:
Employee emp = new Employee("Іванов");
emp.SetPay(emp.GetPay() + 1000);
а з використанням властивості Pay можна записати
emp.Pay += 1000;
Набагато простіше.
Як отримувати значення полів, якщо ми ввели поняття властивостей? А ми вже показали частково в операторові
emp.Pay += 1000;
Тобто замість імені поля після крапки треба поставити ім'я властивості. У цьому випадку компілятором буде побудовано звернення до методу get(), який виведе нас на поле. А звернення до поля для виводу його вмісту (раніше ми зверталися через метод GetІм’яПоля()) буде таким: emp.Pay. Чудово! З введенням поняття властивостей ми отримали можливість в основних програмах, які використовують класи навіть не здогадуватися, що існують якісь методи get і set, хоча раніше в основній програмі, як ми бачили, треба було писати звернення до методів SetName(), GetName() і т. п. І ще одна можливість з'явилася після введення поняття властивостей: у конструкторі тепер можна не писати присвоєння параметрів полям класу, а відразу - властивостям. Якщо раніше ми писали, наприклад, конструктор у вигляді:
public Employee(string name)
{
empName=name;
}
то тепер можна писати у вигляді:
public Employee(string name)
{
Name=name;
}
де Name - це ім'я властивості, пов'язаної з полем empName, наприклад. У конструкторі ж можна задавати оператори, які перевіряють на достовірність дані, що вводяться (це і раніше ніхто не заважав робити). Робота з властивостями відображена в програмі лістингу 8.3. Результат представлений на рис. 8.4.
Рис. 8.4. Робота з властивостями класу
Про доступність і статичність властивостей
Коли при визначенні властивості задано обидва методи get, set, то така властивість доступна як для читання, так і для запису (модифікації). Якщо якийсь з методів не вказати, то властивість стане або доступною тільки для читання (присутній тільки метод get), або тільки для запису (присутній тільки метод set).
Якщо поле класу має атрибут static, то цілком природно, що і властивість для цього поля треба оголошувати з таким же атрибутом.
Автоматичні властивості
При визначенні властивості ми бачили, що вона задається парою методів get-set, в тілах яких (у фігурних дужках) тільки прості оператори - повернення і присвоєння. У більшості випадків опис get-set цим і обмежується. Якщо в класі багато властивостей, то записувати майже один і той же вміст у фігурних дужках стає нудно. Тому розробники мови придумали, як обійти цю проблему. Вони дали можливість відмовитися від запису полів, а потім на їх основі - запису властивостей, а ввели можливість відразу визначати властивості без опису полів (фактично була подвійна робота), і властивості писати коротше.
От як це робиться. Скористаємося даними програми з лістінга 8.3. Ми описували поле empPay і властивість до нього Pay таким чином:
private float empPay; // поле
public float Pay // властивість
{
get { return empPay; }
set { empPay = value; }
}
Тепер поле не визначають, а пишуть відразу вигляд властивості:
public float Pay {get; set;}
(без крапки з комою в кінці). Далі у всьому цьому розбирається компілятор, який автоматично створює властивість такою, якою їй потрібно бути з цієї синтаксичної конструкції, і забезпечує роботу програміста тільки з властивістю. Полів немає! Таким чином певні властивості називають автоматичними.
Ініціалізація об'єкта
Ми знаємо, що ініціалізація об'єкта відбувається через конструктор у якому при створенні об'єкта задаються конкретні значення параметрів. Існує ще один спосіб ініціалізації, синтаксис якого - список значень загальнодоступних полів і/або властивостей (властивості за визначенням загальнодоступні), взятий у фігурні дужки. Наприклад, маємо клас р, описаний як
class p
{
// Властивості (автоматичні)
public int x {get; set;}
public int у {get; set;}
// Конструктори
public p()
{
}
public p(int v1, int v2)
{
x=v1;
y=v2;
}
}
З допомогою цих конструкторів можна створити об'єкти звичайним способом:
p ob1=new p(); ob1.x=10; ob1.y=20;
або
p ob2=new p(10,20);
А за допомогою синтаксису ініціалізації можна написати:
p ob=new p {x=10, y=20};
При такому синтаксисі конструктор класу - конструктор за замовчуванням - викликається неявно. Але можна його викликати і явно, записавши
p ob=new p() {x=10, y=20};
При ініціалізації об'єкта таким чином можна викликати не тільки конструктор за замовчуванням, але і будь-який інший визначений в класі. Наприклад, в нашому випадку можна записати:
p ob2=new p(10,20) {x=30, y=40};
Властивості класу набудуть, природно, останніх значень (30, 40).
Застосування синтаксису ініціалізації об'єктів наочніше і зручне при великому числі властивостей, полів і конструкторів у класі. У цьому випадку не треба думати, який конструктор вибрати для даного об'єкта: бери конструктор за замовчуванням і у фігурних дужках пиши назви потрібних властивостей і/або полів і присвоювані їм значення. Програма по прикладах ініціалізації приведена в лістінгу 8.4. Результат її роботи представлений на рис. 8.5.
Лістинг 8.4
// Застосування синтаксису ініціалізації об'єктів наочніше і зручне при великому числі властивостей, полів і
// конструкторів у класі. У цьому випадку не треба думати, який конструктор вибрати для даного об'єкта: бери
// конструктор за замовчуванням і у фігурних дужках пиши назви потрібних властивостей і/або полів і
// присвоювані їм значення. Програма по прикладах ініціалізації приведена в лістінгу 8.4. Результат її роботи
// представлений на рис. 8.5.
using System;
namespace app25_obj_ini
{
class p
{
// Властивості (автоматичні)
public int x {get; set;}
public int y {get; set;}
// Конструктори
public p()
{
}
public p(int v1, int v2)
{
x=v1;
y=v2;
}
}
class Program
{
public static void Main()
{
p ob1=new p(); ob1.x=10; ob1.y=20;
Console.WriteLine("Ініціалізація властивостей через " +
"конструктор за замовчуванням: x={0} y={1}", ob1.x, ob1.y);
p ob2=new p(10,20);
Console.WriteLine("Ініціалізація властивостей через " +
"двухаргументний конструктор: x={0} y={1}", ob2.x, ob2.y);
p ob=new p {x=10, y=20};
Console.WriteLine("Ініціалізація властивостей через " +
"синтаксис ініціалізації: x={0} y={1}", ob.x, ob.y);
Console.Write("Press any key to continue... ");
Console.Read();
}
}
}
Рис. 8.5. Різні види ініціалізації об'єкта