Защо ни е това
Досега създавахме обект и после му запълвахме атрибутите ред по ред. Работи, но е досадно и лесно се забравя нещо. Конструкторът позволява обектът да се роди вече готов.
Ключово
Конструкторът е специфичен метод: носи името на класа и се извиква от new. Един клас може да има няколко конструктора — стига да се различават по сигнатура.
Конструктор по премълчаване
Най-простите конструктори вече ги използвахме: функциите Car() и Student(). Тези конструктори, поставени автоматично от средата, само отделят памет за обекта, но не дават на атрибутите стойности, зададени от програмиста.
Всъщност стандартните конструктори задават и стойности по премълчаване — празен низ за променливите от тип string и нулеви стойности за целочислените типове. Ето как изглежда стандартният конструктор на класа Car:
public Car()
{
this.brand = null;
this.model = null;
this.engineVolume = 0;
this.yearProd = 0;
}Възможно е сами да предефинираме стандартния конструктор, като поставим по-подходящи стойности по премълчаване. Например вместо празен низ да зададем текст, който подсказва, че на атрибута не е зададена стойност:
public Car()
{
this.brand = "Не е въведена";
this.model = "Не е въведен";
this.engineVolume = 0;
this.yearProd = 0;
}Бележка
Ключовата дума this означава „текущата инстанция“. Тя позволява да различиш атрибута на обекта от едноименен параметър — много удобно, когато параметърът се казва точно като атрибута.
Конструктор с аргументи
Сега ще дефинираме алтернативен конструктор, с помощта на който можем да създаваме инстанции на клас, който освен да заделя памет, може и да задава стойности на всички атрибути:
public Car(string b, string m, int e, int y)
{
this.brand = b;
this.model = m;
this.engineVolume = e;
this.yearProd = y;
}Създаването на обект вече е един ред:
| Без конструктор с аргументи | С конструктор с аргументи | |
|---|---|---|
| Създаване | Car myCar = new Car(); | Car myCar = new Car("FlyingCar", "Mark1", 3000, 2010); |
| После | Четири реда присвоявания | Нищо повече — обектът е готов |
| Риск | Забравен атрибут остава с нулева стойност | Компилаторът иска всички аргументи |
Ако данните идват от конзолата, частта за създаване на инстанцията изглежда така:
string b, m; int e, y;
Console.Write("Въведете марка:"); b = Console.ReadLine();
Console.Write("Въведете модел:"); m = Console.ReadLine();
Console.Write("Въведете обем на двигателя:");
e = int.Parse(Console.ReadLine());
Console.Write("Въведете година на производство:");
y = int.Parse(Console.ReadLine());
Car myCar = new Car(b, m, e, y);Внимание
Всички конструктори на един клас трябва да са с едно и също име — то е името на класа. Затова, съгласно политиката за претоварване на имената, те трябва да се различават по сигнатурата си — броя и типовете на аргументите.
Не е задължително конструкторът да има аргументи за инициализиране на всички атрибути. Например класът Rational (правилна дроб) може да има два конструктора:
public Rational(int n, int d)
{ num = n; denum = d; }
// Цяло число — представя се като дроб със знаменател 1.
public Rational(int n)
{ num = n; denum = 1; }Копиращ конструктор
Когато работим с инстанции на един клас, съвсем естествено се налага да присвоим на една инстанция стойностите на атрибутите на друга, както правим това с обикновените променливи. При класове с много атрибути да се извършва такова присвояване за всеки от атрибутите може да е много трудоемко.
Копиращ конструктор
Конструктор, който създава нова инстанция на класа и присвоява на атрибутите ѝ стойностите на атрибутите на съществуваща инстанция.
public Rational(Rational r)
{ this.num = r.num; this.denum = r.denum; }Сега копирането е един оператор: Rational a = new Rational(x);
Съвет
Точно това позволява задача 1 по-долу: правим четири копия на дробта x и всяко от тях изпълнява различна операция, без оригиналът да пострада.
Задачи
Задача 1 — Rational с методи
Напиши конзолно приложение с класа Rational, което въвежда две дробни числа, пресмята сбора, разликата, произведението и частното им, както и сравнява двете дроби.
static void Main(string[] args)
{
int xnum, xdenum, ynum, ydenum;
xnum = int.Parse(Console.ReadLine());
xdenum = int.Parse(Console.ReadLine());
ynum = int.Parse(Console.ReadLine());
ydenum = int.Parse(Console.ReadLine());
Rational x = new Rational(xnum, xdenum);
Rational y = new Rational(ynum, ydenum);
Rational a = new Rational(x); a.Add(y); a.Print();
Rational s = new Rational(x); s.Sub(y); s.Print();
Rational m = new Rational(x); m.Mult(y); m.Print();
Rational d = new Rational(x); d.Div(y); d.Print();
int c = x.Compare(y);
if (c == 1) Console.WriteLine("X > Y");
else if (c == -1) Console.WriteLine("X < Y");
else Console.WriteLine("X = Y");
}Забележи колко по-четимо е това от версията с голи формули от урок 8. Формулите не са изчезнали — просто са се преместили в методите на класа.
Упражнения
Задача 2. Създай други разумни конструктори с параметри за класовете Car и Student. Указание: не забравяй, че не може да имаме два едноименни конструктора с еднакви сигнатури.
Задача 3. Създай копиращи конструктори за Car и Student.
Задача 4. Модифицирай конзолните приложения, които вече сме създавали и за които това е възможно, като използваш новите версии на класовете.
Какво трябва да запомниш
- Конструкторът носи името на класа и няма тип на връщана стойност.
- Стандартният конструктор дава нулеви стойности; можеш да го предефинираш с по-смислени.
- Няколко конструктора в един клас се различават само по сигнатура — брой и типове аргументи.
- Копиращият конструктор приема инстанция на същия клас и пренася атрибутите ѝ.
thisсочи текущата инстанция и позволява параметърът да се казва като атрибута.