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
123
Dog myVar = new Dog(); // or even in case of implicit declaration, 'myVar' will be of type 'Dog' var myVar = new Dog();

Однак також можна створити змінну базового класу та зберігати в ній об'єкт похідного класу.

Наприклад:

index.cs

index.cs

copy
1234567891011121314151617181920212223242526272829303132
using System; class Mammal { public int age; public Mammal(int age) { this.age = age; } } class Dog : Mammal { public string breed; public Dog(int age, string breed) : base(age) { this.breed = breed; } } class ConsoleApp { static void Main() { Mammal example = new Dog(10, "Labrador"); Console.WriteLine(example.age); Console.WriteLine(example.breed); // Error because it's not accessible (read explanation below) } }

breed атрибут об'єкта Dog недоступний, оскільки при збереженні об'єкта Dog у змінну типу Mammal він неявно перетворюється на Mammal. Це означає, що можна отримати доступ лише до атрибутів і методів, які присутні у класі Mammal. Це не означає, що значення, збережене у класі breed, втрачено — воно лише недоступне, тобто фактично приховане.

Можна просто явно перетворити example у Dog за допомогою явного приведення типу, і атрибут breed знову стане доступним:

index.cs

index.cs

copy
12345678910111213141516171819202122232425262728293031323334353637383940
using System; class Mammal { public int age; public Mammal(int age) { this.age = age; } public void speak() { // Empty for now } } class Dog : Mammal { public string breed; public Dog(int age, string breed) : base(age) { this.breed = breed; } } class ConsoleApp { static void Main() { Mammal example = new Dog(10, "Labrador"); Console.WriteLine(example.age); Dog casted = (Dog)example; Console.WriteLine(casted.breed); } }

Однак для методів існує додаткова можливість, яка називається перевизначення методу (method overriding), що певною мірою схожа на перевантаження методу (method overloading). У цьому випадку ви фактично перевизначаєте метод із базового класу в похідному класі.

Наприклад, ви можете захотіти, щоб у класу Mammal був метод speak(), який виводить Woof!, якщо його викликає об'єкт Dog, і Meow!, якщо його викликає об'єкт Cat:

index.cs

index.cs

copy
1234567891011121314151617181920212223242526272829303132333435363738394041424344454647
using System; class Mammal { public void speak() { Console.WriteLine("Mammal Speaks"); } } class Dog : Mammal { public void speak() { Console.WriteLine("Woof!"); } } class Cat : Mammal { public void speak() { Console.WriteLine("Meow!"); } } class ConsoleApp { static void Main() { // The following execute the overriden version of the methods Dog d1 = new Dog(); d1.speak(); Cat c1 = new Cat(); c1.speak(); Mammal m1 = new Mammal(); Mammal m2 = new Cat(); Mammal m3 = new Dog(); // All of these execute the 'Mammal' version of the method m1.speak(); m2.speak(); m3.speak(); } }

Попередження, які ви отримуєте під час компіляції, виникають через співпадіння імен методів у батьківському та дочірніх класах. У цьому випадку є співпадіння між методами speak класів Cat і Mammal, а також між Dog і Mammal, тому ви отримуєте два попередження.

Зверніть увагу, що в кінці m1, m2 і m3 виконують метод, визначений у класі Mammal, оскільки вони приведені до об'єктів типу Mammal.

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

Щоб зробити метод віртуальним, достатньо додати ключове слово virtual перед його типом повернення:

index.cs

index.cs

copy
1234567
class Mammal { public virtual void speak() { Console.WriteLine("Mammal Speaks"); } }

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

index.cs

index.cs

copy
123456789101112131415
class Dog : Mammal { public override void speak() { Console.WriteLine("Woof!"); } } class Cat : Mammal { public override void speak() { Console.WriteLine("Meow!"); } }

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

index.cs

index.cs

copy
1234567891011121314151617181920212223242526272829303132333435363738394041424344454647
using System; class Mammal { public virtual void speak() { Console.WriteLine("Mammal Speaks"); } } class Dog : Mammal { public override void speak() { Console.WriteLine("Woof!"); } } class Cat : Mammal { public override void speak() { Console.WriteLine("Meow!"); } } class ConsoleApp { static void Main() { Mammal m1 = new Mammal(); Mammal m2 = new Cat(); Mammal m3 = new Dog(); m1.speak(); // Since it stores a Mammal() object, it executes the Mammal's implementation m2.speak(); // Output: Meow! m3.speak(); // Output: Woof! // The following behave the same as they did in the previous example: Dog d1 = new Dog(); d1.speak(); Cat c1 = new Cat(); c1.speak(); } }

У результаті видно, що m2 та m3 використовують реалізації методу з класів Cat та Dog, оскільки ці об'єкти спочатку були створені як Cat та Dog. Цей процес перевизначення методів, коли вони поводяться відповідно до свого початкового типу, називається поліморфізмом.

Одна з ситуацій, коли це може бути дуже корисно — можливість зберігати об'єкти різних типів даних в одному масиві або списку.

Наприклад:

index.cs

index.cs

copy
123456789
List<Shape> shapes = new List<Shape>(); shapes.Add(new Rectangle(25, 50)); shapes.Add(new Square(50)); shapes.Add(new Circle(10)); shapes[0].getArea(); // Output: 1250.0f shapes[1].getArea(); // Output: 2500.0f shapes[2].getArea(); // Output: 314.15f

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

2. Який правильний спосіб зробити метод таким, що може бути перевизначений?

question mark

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

Select the correct answer

question mark

Який правильний спосіб зробити метод таким, що може бути перевизначений?

Select the correct answer

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

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

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

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

Запитати АІ

expand

Запитати АІ

ChatGPT

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

Suggested prompts:

Can you explain more about how polymorphism works in arrays or lists?

What are some real-world examples where polymorphism is useful?

Can you show how to implement polymorphism in another programming language?

Awesome!

Completion rate improved to 2.04

bookПоліморфізм

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

Термін Поліморфізм означає "існування у кількох різних формах". У контексті об'єктно-орієнтованого програмування поліморфізм стосується здатності класу надавати спільний інтерфейс для своїх похідних класів, дозволяючи кожному похідному класу реалізовувати специфічну поведінку для успадкованих методів.

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

Наприклад:

index.cs

index.cs

copy
123
Dog myVar = new Dog(); // or even in case of implicit declaration, 'myVar' will be of type 'Dog' var myVar = new Dog();

Однак також можна створити змінну базового класу та зберігати в ній об'єкт похідного класу.

Наприклад:

index.cs

index.cs

copy
1234567891011121314151617181920212223242526272829303132
using System; class Mammal { public int age; public Mammal(int age) { this.age = age; } } class Dog : Mammal { public string breed; public Dog(int age, string breed) : base(age) { this.breed = breed; } } class ConsoleApp { static void Main() { Mammal example = new Dog(10, "Labrador"); Console.WriteLine(example.age); Console.WriteLine(example.breed); // Error because it's not accessible (read explanation below) } }

breed атрибут об'єкта Dog недоступний, оскільки при збереженні об'єкта Dog у змінну типу Mammal він неявно перетворюється на Mammal. Це означає, що можна отримати доступ лише до атрибутів і методів, які присутні у класі Mammal. Це не означає, що значення, збережене у класі breed, втрачено — воно лише недоступне, тобто фактично приховане.

Можна просто явно перетворити example у Dog за допомогою явного приведення типу, і атрибут breed знову стане доступним:

index.cs

index.cs

copy
12345678910111213141516171819202122232425262728293031323334353637383940
using System; class Mammal { public int age; public Mammal(int age) { this.age = age; } public void speak() { // Empty for now } } class Dog : Mammal { public string breed; public Dog(int age, string breed) : base(age) { this.breed = breed; } } class ConsoleApp { static void Main() { Mammal example = new Dog(10, "Labrador"); Console.WriteLine(example.age); Dog casted = (Dog)example; Console.WriteLine(casted.breed); } }

Однак для методів існує додаткова можливість, яка називається перевизначення методу (method overriding), що певною мірою схожа на перевантаження методу (method overloading). У цьому випадку ви фактично перевизначаєте метод із базового класу в похідному класі.

Наприклад, ви можете захотіти, щоб у класу Mammal був метод speak(), який виводить Woof!, якщо його викликає об'єкт Dog, і Meow!, якщо його викликає об'єкт Cat:

index.cs

index.cs

copy
1234567891011121314151617181920212223242526272829303132333435363738394041424344454647
using System; class Mammal { public void speak() { Console.WriteLine("Mammal Speaks"); } } class Dog : Mammal { public void speak() { Console.WriteLine("Woof!"); } } class Cat : Mammal { public void speak() { Console.WriteLine("Meow!"); } } class ConsoleApp { static void Main() { // The following execute the overriden version of the methods Dog d1 = new Dog(); d1.speak(); Cat c1 = new Cat(); c1.speak(); Mammal m1 = new Mammal(); Mammal m2 = new Cat(); Mammal m3 = new Dog(); // All of these execute the 'Mammal' version of the method m1.speak(); m2.speak(); m3.speak(); } }

Попередження, які ви отримуєте під час компіляції, виникають через співпадіння імен методів у батьківському та дочірніх класах. У цьому випадку є співпадіння між методами speak класів Cat і Mammal, а також між Dog і Mammal, тому ви отримуєте два попередження.

Зверніть увагу, що в кінці m1, m2 і m3 виконують метод, визначений у класі Mammal, оскільки вони приведені до об'єктів типу Mammal.

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

Щоб зробити метод віртуальним, достатньо додати ключове слово virtual перед його типом повернення:

index.cs

index.cs

copy
1234567
class Mammal { public virtual void speak() { Console.WriteLine("Mammal Speaks"); } }

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

index.cs

index.cs

copy
123456789101112131415
class Dog : Mammal { public override void speak() { Console.WriteLine("Woof!"); } } class Cat : Mammal { public override void speak() { Console.WriteLine("Meow!"); } }

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

index.cs

index.cs

copy
1234567891011121314151617181920212223242526272829303132333435363738394041424344454647
using System; class Mammal { public virtual void speak() { Console.WriteLine("Mammal Speaks"); } } class Dog : Mammal { public override void speak() { Console.WriteLine("Woof!"); } } class Cat : Mammal { public override void speak() { Console.WriteLine("Meow!"); } } class ConsoleApp { static void Main() { Mammal m1 = new Mammal(); Mammal m2 = new Cat(); Mammal m3 = new Dog(); m1.speak(); // Since it stores a Mammal() object, it executes the Mammal's implementation m2.speak(); // Output: Meow! m3.speak(); // Output: Woof! // The following behave the same as they did in the previous example: Dog d1 = new Dog(); d1.speak(); Cat c1 = new Cat(); c1.speak(); } }

У результаті видно, що m2 та m3 використовують реалізації методу з класів Cat та Dog, оскільки ці об'єкти спочатку були створені як Cat та Dog. Цей процес перевизначення методів, коли вони поводяться відповідно до свого початкового типу, називається поліморфізмом.

Одна з ситуацій, коли це може бути дуже корисно — можливість зберігати об'єкти різних типів даних в одному масиві або списку.

Наприклад:

index.cs

index.cs

copy
123456789
List<Shape> shapes = new List<Shape>(); shapes.Add(new Rectangle(25, 50)); shapes.Add(new Square(50)); shapes.Add(new Circle(10)); shapes[0].getArea(); // Output: 1250.0f shapes[1].getArea(); // Output: 2500.0f shapes[2].getArea(); // Output: 314.15f

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

2. Який правильний спосіб зробити метод таким, що може бути перевизначений?

question mark

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

Select the correct answer

question mark

Який правильний спосіб зробити метод таким, що може бути перевизначений?

Select the correct answer

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

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

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

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