Успадкування
У попередньому розділі ви ознайомилися з поняттям похідних класів. Ця властивість класу — наслідувати властивості від іншого класу — називається наслідуванням.
Хоча ви вже знайомі з концепцією наслідування, цього разу розглянемо її більш детально для глибшого розуміння.
Для швидкого повторення наведено приклад наслідування:
index.cs
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(); } }
Директива #pragma warning disable "warning code"
використовується для вимкнення певних попереджень компілятора. Зазвичай не рекомендується вимикати попередження, оскільки їх ігнорування може призвести до неочікуваної поведінки програми.
Наведений вище код містить один батьківський клас під назвою Mammal
та два похідних класи під назвою Cat
і Dog
.
Зверніть увагу, що жоден з класів не має явно визначеного конструктора, що означає використання конструктора за замовчуванням при створенні об'єкта.
Конструктор за замовчуванням автоматично надається мовою програмування, якщо в класі не визначено жодного конструктора явно. Конструктор за замовчуванням — це, по суті, порожній конструктор, який не містить жодного коду, наприклад, public className() {}
— так може виглядати конструктор за замовчуванням. Оскільки він не ініціалізує жоден атрибут явно, усі атрибути отримують значення за замовчуванням — також звані нульовими значеннями.
Створимо конструктор для класу Mammal
вручну, який ініціалізує об'єкт Mammal
певними даними:
index.cs
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
12345public Mammal(int age, float weight) { this.age = age; this.weight = weight; }
Тому при створенні нового об'єкта завжди необхідно передавати обов'язкові аргументи конструктора у правильному порядку:
index.cs
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
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
12345678910111213141516171819202122232425262728293031323334353637383940414243using 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
12345678910class DerivedClassName : ParentClassName { // ... attributes // ... methods public DerivedClassName(int arg1, int arg2, ...) : base(arg1, arg2, ...) { // code here } }
Приклад:
index.cs
1234567891011121314151617181920212223242526272829303132using 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
12345678910111213141516171819202122232425262728293031323334353637383940414243444546474849505152535455565758using 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
12345678910111213141516171819202122232425class 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
1234// All Correct var m1 = new Mammal(); var m2 = new Mammal(10); var m3 = new Mammal(10, 42.5f);
Це також означає, що ви можете викликати будь-який з трьох конструкторів із конструкторів похідного класу. Наприклад, усі ці варіанти є коректними:
index.cs
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
12345678910111213141516171819202122232425262728293031323334353637383940414243444546474849505152535455565758596061626364656667686970717273747576777879808182838485868788899091929394using 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
123456789101112131415161718192021222324252627class 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
123456789101112131415161718192021222324252627282930class 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
123456789101112131415public 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
123456789101112131415161718192021222324252627282930313233343536373839public 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. Уважно перегляньте код і визначте, який ефективний спосіб виправлення цієї помилки:
Дякуємо за ваш відгук!
Запитати АІ
Запитати АІ
Запитайте про що завгодно або спробуйте одне із запропонованих запитань, щоб почати наш чат
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
Успадкування
Свайпніть щоб показати меню
У попередньому розділі ви ознайомилися з поняттям похідних класів. Ця властивість класу — наслідувати властивості від іншого класу — називається наслідуванням.
Хоча ви вже знайомі з концепцією наслідування, цього разу розглянемо її більш детально для глибшого розуміння.
Для швидкого повторення наведено приклад наслідування:
index.cs
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(); } }
Директива #pragma warning disable "warning code"
використовується для вимкнення певних попереджень компілятора. Зазвичай не рекомендується вимикати попередження, оскільки їх ігнорування може призвести до неочікуваної поведінки програми.
Наведений вище код містить один батьківський клас під назвою Mammal
та два похідних класи під назвою Cat
і Dog
.
Зверніть увагу, що жоден з класів не має явно визначеного конструктора, що означає використання конструктора за замовчуванням при створенні об'єкта.
Конструктор за замовчуванням автоматично надається мовою програмування, якщо в класі не визначено жодного конструктора явно. Конструктор за замовчуванням — це, по суті, порожній конструктор, який не містить жодного коду, наприклад, public className() {}
— так може виглядати конструктор за замовчуванням. Оскільки він не ініціалізує жоден атрибут явно, усі атрибути отримують значення за замовчуванням — також звані нульовими значеннями.
Створимо конструктор для класу Mammal
вручну, який ініціалізує об'єкт Mammal
певними даними:
index.cs
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
12345public Mammal(int age, float weight) { this.age = age; this.weight = weight; }
Тому при створенні нового об'єкта завжди необхідно передавати обов'язкові аргументи конструктора у правильному порядку:
index.cs
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
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
12345678910111213141516171819202122232425262728293031323334353637383940414243using 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
12345678910class DerivedClassName : ParentClassName { // ... attributes // ... methods public DerivedClassName(int arg1, int arg2, ...) : base(arg1, arg2, ...) { // code here } }
Приклад:
index.cs
1234567891011121314151617181920212223242526272829303132using 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
12345678910111213141516171819202122232425262728293031323334353637383940414243444546474849505152535455565758using 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
12345678910111213141516171819202122232425class 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
1234// All Correct var m1 = new Mammal(); var m2 = new Mammal(10); var m3 = new Mammal(10, 42.5f);
Це також означає, що ви можете викликати будь-який з трьох конструкторів із конструкторів похідного класу. Наприклад, усі ці варіанти є коректними:
index.cs
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
12345678910111213141516171819202122232425262728293031323334353637383940414243444546474849505152535455565758596061626364656667686970717273747576777879808182838485868788899091929394using 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
123456789101112131415161718192021222324252627class 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
123456789101112131415161718192021222324252627282930class 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
123456789101112131415public 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
123456789101112131415161718192021222324252627282930313233343536373839public 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. Уважно перегляньте код і визначте, який ефективний спосіб виправлення цієї помилки:
Дякуємо за ваш відгук!