Notice: This page requires JavaScript to function properly.
Please enable JavaScript in your browser settings or update your browser.
Вивчайте Успадкування | Принципи ООП
C# Понад Базовий Рівень

bookУспадкування

У попередньому розділі ви ознайомилися з поняттям похідних класів. Ця властивість класу — наслідувати властивості від іншого класу — називається наслідуванням.

Хоча ви вже знайомі з концепцією наслідування, цього разу розглянемо її більш детально для глибшого розуміння.

Для швидкого повторення наведено приклад наслідування:

index.cs

index.cs

copy
1234567891011121314151617181920212223242526272829303132333435363738394041424344
#pragma warning disable CS0169 // To disable some unnecessary compiler warnings for this example. Using this is not a recommended practice. using System; class Mammal { int age; float weight; // kilogram (1 kg = 2.2 pounds) } class Dog : Mammal { string breed; public void speak() { Console.WriteLine("Woof!"); } } class Cat : Mammal { string furPattern; public void speak() { Console.WriteLine("Meow!"); } } class ConsoleApp { static void Main() { Cat myCat = new Cat(); Dog myDog = new Dog(); myCat.speak(); myDog.speak(); } }
Note
Примітка

Директива #pragma warning disable "warning code" використовується для вимкнення певних попереджень компілятора. Зазвичай не рекомендується вимикати попередження, оскільки їх ігнорування може призвести до неочікуваної поведінки програми.

Наведений вище код містить один батьківський клас під назвою Mammal та два похідних класи під назвою Cat і Dog.

Зверніть увагу, що жоден з класів не має явно визначеного конструктора, що означає використання конструктора за замовчуванням при створенні об'єкта.

Note
Примітка

Конструктор за замовчуванням автоматично надається мовою програмування, якщо в класі не визначено жодного конструктора явно. Конструктор за замовчуванням — це, по суті, порожній конструктор, який не містить жодного коду, наприклад, public className() {} — так може виглядати конструктор за замовчуванням. Оскільки він не ініціалізує жоден атрибут явно, усі атрибути отримують значення за замовчуванням — також звані нульовими значеннями.

Створимо конструктор для класу Mammal вручну, який ініціалізує об'єкт Mammal певними даними:

index.cs

index.cs

copy
12345678910111213141516171819202122232425262728293031323334353637383940414243444546
#pragma warning disable CS0169 // To disable some unnecessary compiler warnings for this example. Using this is not a recommended practice. using System; class Mammal { int age; float weight; // kg public Mammal(int age, float weight) { this.age = age; this.weight = weight; } } class Dog : Mammal { string breed; public void speak() { Console.WriteLine("Woof!"); } } class Cat : Mammal { string furPattern; public void speak() { Console.WriteLine("Meow!"); } } class ConsoleApp { static void Main() { // Creating a "Mammal" object with some data Mammal m1 = new Mammal(10, 42.0f); } }

Якщо спробувати скомпілювати цю програму, у консолі з’являться деякі помилки. Щоб зрозуміти ці помилки, спочатку потрібно ознайомитися з двома важливими поняттями, пов’язаними з конструкторами.

Перше полягає в тому, що після явного визначення конструктора для класу, цей клас більше не має конструктора за замовчуванням, і тому явно визначений конструктор стає основним конструктором цього класу, як у цьому випадку:

index.cs

index.cs

copy
12345
public Mammal(int age, float weight) { this.age = age; this.weight = weight; }

Тому при створенні нового об'єкта завжди необхідно передавати обов'язкові аргументи конструктора у правильному порядку:

index.cs

index.cs

copy
1234567
// Incorrect ways to create 'Mammal', will show an error Mammal m1 = new Mammal(); Mammal m1 = new Mammal(10); Mammal m1 = new Mammal(42.0f); // Correct way to create 'Mammal', will execute fine. Mammal m1 = new Mammal(10, 42.0f);

По-друге, похідні класи також можуть мати конструктори, однак перед викликом конструктора похідного класу викликається конструктор базового (батьківського) класу:

index.cs

index.cs

copy
1234567891011121314151617181920212223242526272829303132333435
#pragma warning disable CS0169 // To disable some unnecessary warnings, using this is not a recommended practice. using System; class Mammal { int age; float weight; // kg // No attribute is initialized explicitly in this constructor // Hence, all attributes will take up "zero" values // It is similar to a "default" constructor except it outputs a message public Mammal() { Console.WriteLine("Mammal Constructor Called"); } } class Dog : Mammal { string breed; public Dog() { Console.WriteLine("Dog Constructor Called"); } } class ConsoleApp { static void Main() { Dog myDog = new Dog(); } }

Під час виконання цього коду видно, що метод WriteLine() з конструктора 'Mammal', який є батьківським класом, викликається автоматично. Це означає, що існує правило: конструктор базового класу (також називається базовим конструктором) завжди викликається перед конструктором похідного класу.

Це правило також діє у випадку багаторівневого наслідування:

На діаграмі вище конструктор Kitten викликає конструктор Cat перед власним, однак оскільки Cat також є похідним класом, він викликає конструктор Mammal перед собою, а Mammal викликає конструктор Animal перед своїм конструктором. Таким чином, першим виконується конструктор базового класу — тобто конструктор класу Animal, після чого виконання спускається вниз по ієрархії.

Якщо конструктор батьківського класу не приймає жодних аргументів, він автоматично викликається компілятором. Саме тому конструктор 'Mammal' у наведеному вище прикладі був викликаний автоматично. Однак розглянемо ще раз некоректний код:

index.cs

index.cs

copy
12345678910111213141516171819202122232425262728293031323334353637383940414243
using System; class Mammal { int age; float weight; // kg public Mammal(int age, float weight) { this.age = age; this.weight = weight; } } class Dog : Mammal { string breed; public void speak() { Console.WriteLine("Woof!"); } } class Cat : Mammal { string furPattern; public void speak() { Console.WriteLine("Meow!"); } } class ConsoleApp { static void Main() { // Creating a "Mammal" object with some data Mammal m1 = new Mammal(10, 42.0f); } }

У наведеному вище коді виникають дві помилки, які по суті означають, що ви не викликали конструктори базового класу вручну — оскільки вони потребують певних аргументів, їх потрібно викликати вручну. Базовий синтаксис ручного виклику конструктора батьківського класу виглядає так:

index.cs

index.cs

copy
12345678910
class DerivedClassName : ParentClassName { // ... attributes // ... methods public DerivedClassName(int arg1, int arg2, ...) : base(arg1, arg2, ...) { // code here } }

Приклад:

index.cs

index.cs

copy
1234567891011121314151617181920212223242526272829303132
using System; class ExampleParentClass { int value1; int value2; public ExampleParentClass(int value1, int value2) { this.value1 = value1; } } class ExampleDerivedClass : ExampleParentClass { int value3; // The value1 and value2 arguments are passed to the base class's contructor public ExampleDerivedClass(int value1, int value2, int value3) : base (value1, value2) { this.value3 = value3; } } class ConsoleApp { static void Main() { var testObject = new ExampleDerivedClass(5, 7, 9); } }

Використовуючи цей синтаксис, можна передати всі необхідні дані до конструктора Mammal через конструктори Cat та Dog, щоб виправити помилку, яка виникала раніше:

index.cs

index.cs

copy
12345678910111213141516171819202122232425262728293031323334353637383940414243444546474849505152535455565758
using System; class Mammal { int age; float weight; // kg public Mammal(int age, float weight) { this.age = age; this.weight = weight; } } class Dog : Mammal { string breed; public Dog(int age, float weight, string breed) : base(age, weight) { this.breed = breed; } public void speak() { Console.WriteLine("Woof!"); } } class Cat : Mammal { string furPattern; public Cat(int age, float weight, string furPattern) : base(age, weight) { this.furPattern = furPattern; } public void speak() { Console.WriteLine("Meow!"); } } class ConsoleApp { static void Main() { // Creating a "Mammal" object with some data Mammal m1 = new Mammal(10, 42.0f); // Creating a "Dog" object with some data Dog d1 = new Dog(10, 42.5f, "Dobermann"); Console.WriteLine("Executed Successfully"); } }

Ще одна важлива властивість конструкторів полягає в тому, що їх можна перевантажувати так само, як і будь-який інший метод. Можна створити декілька конструкторів із різною кількістю аргументів:

index.cs

index.cs

copy
12345678910111213141516171819202122232425
class Mammal { int age; float weight; // kg // 1st constructor public Mammal() { // We leave it empty for this example // Since it's empty, it mimics the "default" constructor } // 2nd constructor public Mammal(int age) { this.age = age; } // 3rd constructor public Mammal(int age, float weight) { this.age = age; this.weight = weight; } }

У цьому випадку клас Mammal має 3 конструктори. Таким чином, можна ініціалізувати або створити об'єкт mammal трьома різними способами, і компілятор вибере, який конструктор викликати, залежно від кількості та типу аргументів:

index.cs

index.cs

copy
1234
// All Correct var m1 = new Mammal(); var m2 = new Mammal(10); var m3 = new Mammal(10, 42.5f);

Це також означає, що ви можете викликати будь-який з трьох конструкторів із конструкторів похідного класу. Наприклад, усі ці варіанти є коректними:

index.cs

index.cs

copy
123456789101112131415161718
// Using 3rd base constructor public Dog(int age, float weight, string breed) : base(age, weight) { this.breed = breed; } // Using 2nd base constructor public Dog(int age, string breed) : base(age) { this.breed = breed; } // Using 1st base constructor // If the base constructor has no arguments then it is automatically called (similar to the default constructor), so we don't necessarily need to write 'base()' public Dog(string breed) { this.breed = breed; }

Давайте об'єднаємо два наведені вище фрагменти та додамо кілька операторів Console.WriteLine, щоб побачити, в якому порядку виконуються конструктори, і на практиці переконатися в результатах:

index.cs

index.cs

copy
12345678910111213141516171819202122232425262728293031323334353637383940414243444546474849505152535455565758596061626364656667686970717273747576777879808182838485868788899091929394
using System; class Mammal { int age; float weight; // kg // 1st Constructor public Mammal() { // We leave it empty for this example // Since it's empty, it mimics the "default" constructor // The attributes are initialized with zero values Console.WriteLine("Mammal - Constructor 1 Called"); } // 2nd Constructor public Mammal(int age) { this.age = age; Console.WriteLine("Mammal - Constructor 2 Called"); } // 3rd Constructor public Mammal(int age, float weight) { this.age = age; this.weight = weight; Console.WriteLine("Mammal - Constructor 3 Called"); } } class Dog : Mammal { string breed; public Dog() { Console.WriteLine("Dog - Constructor 1 Called"); } // Using 1st Mammal constructor // We don't necessarily need to write 'base()' in this case // It automatically finds and calls the constructor with no arguments public Dog(string breed) { this.breed = breed; Console.WriteLine("Dog - Constructor 2 Called"); } // Using 2nd Mammal constructor public Dog(int age, string breed) : base(age) { this.breed = breed; Console.WriteLine("Dog - Constructor 3 Called"); } // Using 3rd Mammal constructor public Dog(int age, float weight, string breed) : base(age, weight) { this.breed = breed; Console.WriteLine("Dog - Constructor 4 Called"); } public void speak() { Console.WriteLine("Woof!"); } } class ConsoleApp { static void Main() { // Creating a "Mammal" object using different constructors Mammal m1 = new Mammal(10, 42.0f); Mammal m2 = new Mammal(10); Mammal m3 = new Mammal(); Console.WriteLine("----------"); // Seperator, for ease of reading output // Creating a "Dog" object using different constructors Dog d1 = new Dog(10, 42.0f, "Dobermann"); Console.WriteLine(""); Dog d2 = new Dog(10, "Dobermann"); Console.WriteLine(""); Dog d3 = new Dog("Dobermann"); Console.WriteLine(""); Dog d4 = new Dog(); } }

Тепер, коли ви ознайомилися з різними можливостями наслідування, важливо також знати, як і коли їх правильно використовувати. Ось кілька аспектів, які слід враховувати при побудові класів на основі наслідування:

Баланс між простотою та гнучкістю: Перевантаження конструкторів дозволяє створювати багато різних конструкторів з різними типами аргументів, але надмірне використання цієї можливості може ускладнити код і зробити його важким для підтримки. Рекомендується тримати код класу коротким, лаконічним і зручним. Уникайте створення занадто великої кількості конструкторів для одного класу, щоб зберегти баланс між простотою та гнучкістю.

Простота конструкторів: Конструктори повинні головним чином відповідати за ініціалізацію об'єкта базовими даними. Рекомендується уникати зайвої обробки та складної логіки всередині конструктора. Якщо потрібні обчислення або логіка, краще винести їх у окремий метод.

;Погана практика:;

index.cs

index.cs

copy
123456789101112131415161718192021222324252627
class Customer { string name; string accountType; double balance; public Customer (string name, string accountType, double balance) { this.name = name; this.accountType = accountType; if (accountType == "Savings") { // Plus 1 Percent this.balance = balance + balance * 0.01; } else if (accountType == "HighYieldSavings") { // Plus 5 percent this.balance = balance + balance * 0.05; } else { this.balance = balance; } } }

Хороша практика:

index.cs

index.cs

copy
123456789101112131415161718192021222324252627282930
class Customer { string name; string accountType; double balance; public Customer (string name, string accountType, double balance) { this.name = name; this.accountType = accountType; this.balance = balance; monthlyInterest(); } // This method might be used in other places too private void monthlyInterest() { if(accountType == "Savings") { // Plus 1 Percent balance += balance * 0.01; } else if(accountType == "HighYieldSavings") { // Plus 5 percent balance += balance * 0.05; } } }

Ініціалізація важливих атрибутів: необхідно ініціалізувати всі важливі атрибути об'єкта коректними значеннями, щоб забезпечити їх правильну роботу — навіть якщо це конструктор без аргументів.

Погана практика:

index.cs

index.cs

copy
123456789101112131415
public class Car { private string brand; private string model; private int year; private double price; // Constructor does not initialize important attributes // It is also generally not a good idea to have constructors without any arguments if they're not needed. public Car() { // No initialization of attributes Console.WriteLine("Car Created"); } }

Хороша практика:

index.cs

index.cs

copy
123456789101112131415161718192021222324252627282930313233343536373839
public class Car { private string brand; private string model; private int year; private double price; // Good: Constructor initializes important attributes // It also checks if the values are correct // In this case the if-else statements are not unnecessary since they are important for ensuring that the object functions correctly. public Car(string brand, string model, int year, double price) { this.brand = brand; this.model = model; // Validate and set the year // The first public car was created in 1886 :) if (year > 1886) { this.year = year; } else { Console.WriteLine("Invalid year. Setting year to default."); this.year = DateTime.Now.Year; // Set to current year as default } // Validate and set the price if (price >= 0) { this.price = price; } else { Console.WriteLine("Invalid price. Setting price to default."); this.price = 0; // Set to a default value } } }

1. Яка властивість дозволяє створювати декілька конструкторів для одного класу?

2. Можливо, для цього питання вам знадобиться один із концептів із попередніх розділів. У наведеному нижче коді виникає помилка на рядках 15 та 16. Уважно перегляньте код і визначте, який ефективний спосіб виправлення цієї помилки:

question mark

Яка властивість дозволяє створювати декілька конструкторів для одного класу?

Select the correct answer

question mark

Можливо, для цього питання вам знадобиться один із концептів із попередніх розділів. У наведеному нижче коді виникає помилка на рядках 15 та 16. Уважно перегляньте код і визначте, який ефективний спосіб виправлення цієї помилки:

Select the correct answer

Все було зрозуміло?

Як ми можемо покращити це?

Дякуємо за ваш відгук!

Секція 5. Розділ 2

Запитати АІ

expand

Запитати АІ

ChatGPT

Запитайте про що завгодно або спробуйте одне із запропонованих запитань, щоб почати наш чат

Suggested prompts:

Can you explain more about constructor overloading?

What happens if I don't call the base constructor in a derived class?

Can you give an example of multilevel inheritance with constructors?

Awesome!

Completion rate improved to 2.04

bookУспадкування

Свайпніть щоб показати меню

У попередньому розділі ви ознайомилися з поняттям похідних класів. Ця властивість класу — наслідувати властивості від іншого класу — називається наслідуванням.

Хоча ви вже знайомі з концепцією наслідування, цього разу розглянемо її більш детально для глибшого розуміння.

Для швидкого повторення наведено приклад наслідування:

index.cs

index.cs

copy
1234567891011121314151617181920212223242526272829303132333435363738394041424344
#pragma warning disable CS0169 // To disable some unnecessary compiler warnings for this example. Using this is not a recommended practice. using System; class Mammal { int age; float weight; // kilogram (1 kg = 2.2 pounds) } class Dog : Mammal { string breed; public void speak() { Console.WriteLine("Woof!"); } } class Cat : Mammal { string furPattern; public void speak() { Console.WriteLine("Meow!"); } } class ConsoleApp { static void Main() { Cat myCat = new Cat(); Dog myDog = new Dog(); myCat.speak(); myDog.speak(); } }
Note
Примітка

Директива #pragma warning disable "warning code" використовується для вимкнення певних попереджень компілятора. Зазвичай не рекомендується вимикати попередження, оскільки їх ігнорування може призвести до неочікуваної поведінки програми.

Наведений вище код містить один батьківський клас під назвою Mammal та два похідних класи під назвою Cat і Dog.

Зверніть увагу, що жоден з класів не має явно визначеного конструктора, що означає використання конструктора за замовчуванням при створенні об'єкта.

Note
Примітка

Конструктор за замовчуванням автоматично надається мовою програмування, якщо в класі не визначено жодного конструктора явно. Конструктор за замовчуванням — це, по суті, порожній конструктор, який не містить жодного коду, наприклад, public className() {} — так може виглядати конструктор за замовчуванням. Оскільки він не ініціалізує жоден атрибут явно, усі атрибути отримують значення за замовчуванням — також звані нульовими значеннями.

Створимо конструктор для класу Mammal вручну, який ініціалізує об'єкт Mammal певними даними:

index.cs

index.cs

copy
12345678910111213141516171819202122232425262728293031323334353637383940414243444546
#pragma warning disable CS0169 // To disable some unnecessary compiler warnings for this example. Using this is not a recommended practice. using System; class Mammal { int age; float weight; // kg public Mammal(int age, float weight) { this.age = age; this.weight = weight; } } class Dog : Mammal { string breed; public void speak() { Console.WriteLine("Woof!"); } } class Cat : Mammal { string furPattern; public void speak() { Console.WriteLine("Meow!"); } } class ConsoleApp { static void Main() { // Creating a "Mammal" object with some data Mammal m1 = new Mammal(10, 42.0f); } }

Якщо спробувати скомпілювати цю програму, у консолі з’являться деякі помилки. Щоб зрозуміти ці помилки, спочатку потрібно ознайомитися з двома важливими поняттями, пов’язаними з конструкторами.

Перше полягає в тому, що після явного визначення конструктора для класу, цей клас більше не має конструктора за замовчуванням, і тому явно визначений конструктор стає основним конструктором цього класу, як у цьому випадку:

index.cs

index.cs

copy
12345
public Mammal(int age, float weight) { this.age = age; this.weight = weight; }

Тому при створенні нового об'єкта завжди необхідно передавати обов'язкові аргументи конструктора у правильному порядку:

index.cs

index.cs

copy
1234567
// Incorrect ways to create 'Mammal', will show an error Mammal m1 = new Mammal(); Mammal m1 = new Mammal(10); Mammal m1 = new Mammal(42.0f); // Correct way to create 'Mammal', will execute fine. Mammal m1 = new Mammal(10, 42.0f);

По-друге, похідні класи також можуть мати конструктори, однак перед викликом конструктора похідного класу викликається конструктор базового (батьківського) класу:

index.cs

index.cs

copy
1234567891011121314151617181920212223242526272829303132333435
#pragma warning disable CS0169 // To disable some unnecessary warnings, using this is not a recommended practice. using System; class Mammal { int age; float weight; // kg // No attribute is initialized explicitly in this constructor // Hence, all attributes will take up "zero" values // It is similar to a "default" constructor except it outputs a message public Mammal() { Console.WriteLine("Mammal Constructor Called"); } } class Dog : Mammal { string breed; public Dog() { Console.WriteLine("Dog Constructor Called"); } } class ConsoleApp { static void Main() { Dog myDog = new Dog(); } }

Під час виконання цього коду видно, що метод WriteLine() з конструктора 'Mammal', який є батьківським класом, викликається автоматично. Це означає, що існує правило: конструктор базового класу (також називається базовим конструктором) завжди викликається перед конструктором похідного класу.

Це правило також діє у випадку багаторівневого наслідування:

На діаграмі вище конструктор Kitten викликає конструктор Cat перед власним, однак оскільки Cat також є похідним класом, він викликає конструктор Mammal перед собою, а Mammal викликає конструктор Animal перед своїм конструктором. Таким чином, першим виконується конструктор базового класу — тобто конструктор класу Animal, після чого виконання спускається вниз по ієрархії.

Якщо конструктор батьківського класу не приймає жодних аргументів, він автоматично викликається компілятором. Саме тому конструктор 'Mammal' у наведеному вище прикладі був викликаний автоматично. Однак розглянемо ще раз некоректний код:

index.cs

index.cs

copy
12345678910111213141516171819202122232425262728293031323334353637383940414243
using System; class Mammal { int age; float weight; // kg public Mammal(int age, float weight) { this.age = age; this.weight = weight; } } class Dog : Mammal { string breed; public void speak() { Console.WriteLine("Woof!"); } } class Cat : Mammal { string furPattern; public void speak() { Console.WriteLine("Meow!"); } } class ConsoleApp { static void Main() { // Creating a "Mammal" object with some data Mammal m1 = new Mammal(10, 42.0f); } }

У наведеному вище коді виникають дві помилки, які по суті означають, що ви не викликали конструктори базового класу вручну — оскільки вони потребують певних аргументів, їх потрібно викликати вручну. Базовий синтаксис ручного виклику конструктора батьківського класу виглядає так:

index.cs

index.cs

copy
12345678910
class DerivedClassName : ParentClassName { // ... attributes // ... methods public DerivedClassName(int arg1, int arg2, ...) : base(arg1, arg2, ...) { // code here } }

Приклад:

index.cs

index.cs

copy
1234567891011121314151617181920212223242526272829303132
using System; class ExampleParentClass { int value1; int value2; public ExampleParentClass(int value1, int value2) { this.value1 = value1; } } class ExampleDerivedClass : ExampleParentClass { int value3; // The value1 and value2 arguments are passed to the base class's contructor public ExampleDerivedClass(int value1, int value2, int value3) : base (value1, value2) { this.value3 = value3; } } class ConsoleApp { static void Main() { var testObject = new ExampleDerivedClass(5, 7, 9); } }

Використовуючи цей синтаксис, можна передати всі необхідні дані до конструктора Mammal через конструктори Cat та Dog, щоб виправити помилку, яка виникала раніше:

index.cs

index.cs

copy
12345678910111213141516171819202122232425262728293031323334353637383940414243444546474849505152535455565758
using System; class Mammal { int age; float weight; // kg public Mammal(int age, float weight) { this.age = age; this.weight = weight; } } class Dog : Mammal { string breed; public Dog(int age, float weight, string breed) : base(age, weight) { this.breed = breed; } public void speak() { Console.WriteLine("Woof!"); } } class Cat : Mammal { string furPattern; public Cat(int age, float weight, string furPattern) : base(age, weight) { this.furPattern = furPattern; } public void speak() { Console.WriteLine("Meow!"); } } class ConsoleApp { static void Main() { // Creating a "Mammal" object with some data Mammal m1 = new Mammal(10, 42.0f); // Creating a "Dog" object with some data Dog d1 = new Dog(10, 42.5f, "Dobermann"); Console.WriteLine("Executed Successfully"); } }

Ще одна важлива властивість конструкторів полягає в тому, що їх можна перевантажувати так само, як і будь-який інший метод. Можна створити декілька конструкторів із різною кількістю аргументів:

index.cs

index.cs

copy
12345678910111213141516171819202122232425
class Mammal { int age; float weight; // kg // 1st constructor public Mammal() { // We leave it empty for this example // Since it's empty, it mimics the "default" constructor } // 2nd constructor public Mammal(int age) { this.age = age; } // 3rd constructor public Mammal(int age, float weight) { this.age = age; this.weight = weight; } }

У цьому випадку клас Mammal має 3 конструктори. Таким чином, можна ініціалізувати або створити об'єкт mammal трьома різними способами, і компілятор вибере, який конструктор викликати, залежно від кількості та типу аргументів:

index.cs

index.cs

copy
1234
// All Correct var m1 = new Mammal(); var m2 = new Mammal(10); var m3 = new Mammal(10, 42.5f);

Це також означає, що ви можете викликати будь-який з трьох конструкторів із конструкторів похідного класу. Наприклад, усі ці варіанти є коректними:

index.cs

index.cs

copy
123456789101112131415161718
// Using 3rd base constructor public Dog(int age, float weight, string breed) : base(age, weight) { this.breed = breed; } // Using 2nd base constructor public Dog(int age, string breed) : base(age) { this.breed = breed; } // Using 1st base constructor // If the base constructor has no arguments then it is automatically called (similar to the default constructor), so we don't necessarily need to write 'base()' public Dog(string breed) { this.breed = breed; }

Давайте об'єднаємо два наведені вище фрагменти та додамо кілька операторів Console.WriteLine, щоб побачити, в якому порядку виконуються конструктори, і на практиці переконатися в результатах:

index.cs

index.cs

copy
12345678910111213141516171819202122232425262728293031323334353637383940414243444546474849505152535455565758596061626364656667686970717273747576777879808182838485868788899091929394
using System; class Mammal { int age; float weight; // kg // 1st Constructor public Mammal() { // We leave it empty for this example // Since it's empty, it mimics the "default" constructor // The attributes are initialized with zero values Console.WriteLine("Mammal - Constructor 1 Called"); } // 2nd Constructor public Mammal(int age) { this.age = age; Console.WriteLine("Mammal - Constructor 2 Called"); } // 3rd Constructor public Mammal(int age, float weight) { this.age = age; this.weight = weight; Console.WriteLine("Mammal - Constructor 3 Called"); } } class Dog : Mammal { string breed; public Dog() { Console.WriteLine("Dog - Constructor 1 Called"); } // Using 1st Mammal constructor // We don't necessarily need to write 'base()' in this case // It automatically finds and calls the constructor with no arguments public Dog(string breed) { this.breed = breed; Console.WriteLine("Dog - Constructor 2 Called"); } // Using 2nd Mammal constructor public Dog(int age, string breed) : base(age) { this.breed = breed; Console.WriteLine("Dog - Constructor 3 Called"); } // Using 3rd Mammal constructor public Dog(int age, float weight, string breed) : base(age, weight) { this.breed = breed; Console.WriteLine("Dog - Constructor 4 Called"); } public void speak() { Console.WriteLine("Woof!"); } } class ConsoleApp { static void Main() { // Creating a "Mammal" object using different constructors Mammal m1 = new Mammal(10, 42.0f); Mammal m2 = new Mammal(10); Mammal m3 = new Mammal(); Console.WriteLine("----------"); // Seperator, for ease of reading output // Creating a "Dog" object using different constructors Dog d1 = new Dog(10, 42.0f, "Dobermann"); Console.WriteLine(""); Dog d2 = new Dog(10, "Dobermann"); Console.WriteLine(""); Dog d3 = new Dog("Dobermann"); Console.WriteLine(""); Dog d4 = new Dog(); } }

Тепер, коли ви ознайомилися з різними можливостями наслідування, важливо також знати, як і коли їх правильно використовувати. Ось кілька аспектів, які слід враховувати при побудові класів на основі наслідування:

Баланс між простотою та гнучкістю: Перевантаження конструкторів дозволяє створювати багато різних конструкторів з різними типами аргументів, але надмірне використання цієї можливості може ускладнити код і зробити його важким для підтримки. Рекомендується тримати код класу коротким, лаконічним і зручним. Уникайте створення занадто великої кількості конструкторів для одного класу, щоб зберегти баланс між простотою та гнучкістю.

Простота конструкторів: Конструктори повинні головним чином відповідати за ініціалізацію об'єкта базовими даними. Рекомендується уникати зайвої обробки та складної логіки всередині конструктора. Якщо потрібні обчислення або логіка, краще винести їх у окремий метод.

;Погана практика:;

index.cs

index.cs

copy
123456789101112131415161718192021222324252627
class Customer { string name; string accountType; double balance; public Customer (string name, string accountType, double balance) { this.name = name; this.accountType = accountType; if (accountType == "Savings") { // Plus 1 Percent this.balance = balance + balance * 0.01; } else if (accountType == "HighYieldSavings") { // Plus 5 percent this.balance = balance + balance * 0.05; } else { this.balance = balance; } } }

Хороша практика:

index.cs

index.cs

copy
123456789101112131415161718192021222324252627282930
class Customer { string name; string accountType; double balance; public Customer (string name, string accountType, double balance) { this.name = name; this.accountType = accountType; this.balance = balance; monthlyInterest(); } // This method might be used in other places too private void monthlyInterest() { if(accountType == "Savings") { // Plus 1 Percent balance += balance * 0.01; } else if(accountType == "HighYieldSavings") { // Plus 5 percent balance += balance * 0.05; } } }

Ініціалізація важливих атрибутів: необхідно ініціалізувати всі важливі атрибути об'єкта коректними значеннями, щоб забезпечити їх правильну роботу — навіть якщо це конструктор без аргументів.

Погана практика:

index.cs

index.cs

copy
123456789101112131415
public class Car { private string brand; private string model; private int year; private double price; // Constructor does not initialize important attributes // It is also generally not a good idea to have constructors without any arguments if they're not needed. public Car() { // No initialization of attributes Console.WriteLine("Car Created"); } }

Хороша практика:

index.cs

index.cs

copy
123456789101112131415161718192021222324252627282930313233343536373839
public class Car { private string brand; private string model; private int year; private double price; // Good: Constructor initializes important attributes // It also checks if the values are correct // In this case the if-else statements are not unnecessary since they are important for ensuring that the object functions correctly. public Car(string brand, string model, int year, double price) { this.brand = brand; this.model = model; // Validate and set the year // The first public car was created in 1886 :) if (year > 1886) { this.year = year; } else { Console.WriteLine("Invalid year. Setting year to default."); this.year = DateTime.Now.Year; // Set to current year as default } // Validate and set the price if (price >= 0) { this.price = price; } else { Console.WriteLine("Invalid price. Setting price to default."); this.price = 0; // Set to a default value } } }

1. Яка властивість дозволяє створювати декілька конструкторів для одного класу?

2. Можливо, для цього питання вам знадобиться один із концептів із попередніх розділів. У наведеному нижче коді виникає помилка на рядках 15 та 16. Уважно перегляньте код і визначте, який ефективний спосіб виправлення цієї помилки:

question mark

Яка властивість дозволяє створювати декілька конструкторів для одного класу?

Select the correct answer

question mark

Можливо, для цього питання вам знадобиться один із концептів із попередніх розділів. У наведеному нижче коді виникає помилка на рядках 15 та 16. Уважно перегляньте код і визначте, який ефективний спосіб виправлення цієї помилки:

Select the correct answer

Все було зрозуміло?

Як ми можемо покращити це?

Дякуємо за ваш відгук!

Секція 5. Розділ 2
some-alt