Що таке раннє та пізнє зв'язування. Відмінність перевизначення та приховування методів. Конструктори. Зарезервовані слова super і this. Блоки ініціалізації

10.11.2019 Програми

Щоб з'ясувати, у чому різниця між раннім (статичним) і пізнім (динамічним) зв'язуванням у Java, потрібно спочатку зрозуміти, що таке це саме зв'язування . Зв'язування означає наявність зв'язку між посиланням та кодом. Наприклад, змінна, яку ви посилаєтеся, прив'язана до коду, у якому вона визначена. Аналогічно, метод, що викликається, прив'язаний до місця в коді, де він визначений.

Існує два типи зв'язування методів у мові Java: раніше зв'язування (його ще називають статичним) і пізніше (відповідно динамічне) зв'язування . Виклик методу Java означає, що цей метод прив'язується до конкретного коду або в момент компіляції, або під час виконання, при запуску програми і створенні об'єктів. Можна зрозуміти з назви, статичне зв'язування має статичний характер, оскільки відбувається під час компіляції, тобто код «знає», який метод викликати після компіляції вихідного коду Java в файли класів. А оскільки це стосується ранньої стадії життєвого циклупрограми називається також раннім зв'язуванням (early binding). З іншого боку, динамічне зв'язування відбувається під час виконання після запуску програми віртуальною машиною Java. У цьому випадку те, який метод викликати визначається конкретним об'єктом, так що в момент компіляції інформація недоступна, адже об'єкти створюються під час виконання. А оскільки це відбувається на пізній стадії життєвого циклу програми, то називається в мові Java пізнім зв'язуванням (late binding). Давайте розглянемо ще кілька відмінностей, щоб краще розібратися з цим, а, крім того, можемо відповісти на це дуже популярне питання, яке ставлять на співбесідах з Java.

Раннє та пізнє зв'язування в Java

Існує безліч відмінностей статичного та динамічного зв'язування у мові Java, але найважливіше – те, як їх використовує JVM. Чи ви коли-небудь замислювалися, яким чином JVM вирішує, який метод викликати, якщо в області видимості міститься більше одного методу з одним ім'ям? Якщо ви коли-небудь використовували перевантаження або перевизначення методів, то знаєте, що Java може мати кілька методів з одним ім'ям. У випадку з Java віртуальна машина JVM використовує як статичне, і динамічне зв'язування для вибору потрібного методу.

Приклад статичного та динамічного зв'язування в Java

У цій програмі ви побачите, що прив'язка віртуальних методів не відбувається під час компіляції за допомогою статичного зв'язування, оскільки в цьому випадку викликався б метод із суперкласу, як відбувається зі статичними методами, які рано зв'язуються. Якщо буде викликаний метод підкласу, то для зв'язування функції використовувався конкретний об'єкт під час виконання, а, отже, для зв'язування віртуальних функційвикористовується динамічне зв'язування. public class Main ( public static void main (String args) ( // Приклад статичного та динамічного зв'язування в Java Insurance current = new CarInsurance (); //Динамічне зв'язування на основі об'єкта int premium = current. premium (); // Статичне зв'язування з урахуванням класу String category = current. категорії (); System. out. println ("premium:" + premium); System. out. println ("category:" + category); ) ) class Insurance ( public static final int LOW = 100 ; public int premium ( ) ( return LOW ; ) public static String category ( ) ( return " Insurance " ; ) ) class CarInsurance extends Insurance ( public static final int HIGH = ) public int premium () ( return HIGH; ) public static String category () ( return "Car Insurance" ; ) ) Результати виконання: premium : 200 category : Insurance Як ви бачите, виклик методу premium() привів до виконання методу з підкласу, в той час, як виклик методу category() привів до виконання методу суперкласу. Це відбувається через те, що premium() – віртуальний метод, який дозволяється за допомогою пізнього зв'язування, тоді як category() – статичний метод, який дозволяється за допомогою статичного зв'язування під час компіляції на ім'я класу.
Цікаво читати про Java? Вступайте до групи!

Відмінності між раннім та пізнім зв'язуванням у мові Java

Тепер, коли ви розібралися і розумієте, як у мові Java зв'язуються виклики методів і як функціонує статичне та динамічне зв'язування, давайте ще раз перерахуємо ключові відмінності між раннім та пізнім зв'язуванням у мові Java:
  1. Статичне зв'язування відбувається під час компіляції, динамічне – під час виконання.

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

  3. Статичне зв'язування використовується в мові Java для розв'язання перевантажених методів, в той час як динамічне зв'язування використовується в мові Java для вирішення перевизначених методів.

  4. Аналогічно, приватні, статичні та термінальні методи дозволяються за допомогою статичного зв'язування, оскільки їх не можна перевизначати, а всі віртуальні методи дозволяються за допомогою динамічного зв'язування.

  5. У разі статичного зв'язування використовуються не конкретні об'єкти, а інформація про тип, тобто виявлення потрібного методу використовується тип посилальної змінної. З іншого боку, при динамічному зв'язуванні для знаходження потрібного методу Java використовується конкретний об'єкт.
Ось непогана вправа, заснована на поняттях статичного та динамічного зв'язування у мові Java. Чи зможете ви відповісти на запитання: "Що буде виведено під час виконання наступної програми?" Що виведе ця програма? Collection, Set або HashSet?Ось і все, що ми хотіли розповісти вам про відмінності між раннім (статичним) і пізнім (динамічним) зв'язуванням у мові Java. Це один із найкращих питаньдля телефонної співбесіди з мови Java, оскільки вона надає багато можливостей перевірки глибини знань кандидата. Завжди пам'ятайте, що приватні , статичні і final-методи зв'язуються за допомогою статичного зв'язування , а віртуальні – динамічного . Аналогічно, найкращий приклад статичного зв'язування – навантаження методів, а перевизначення – динамічного.

2

Say не було ніякої функції Доброго дня, і ми просто називаємо ob.display в основному, то вона викликає функцію відображення класу B, а не клас А.

Виклик функції display() встановлюється компілятором один раз як версія, визначена в базовому класі. Це називається статичною роздільною здатністю виклику функції або статичною прив'язкою - виклик функції фіксується перед виконанням програми. Це іноді називають раннім зв'язуванням, тому що функція display() задається під час компіляції програми.

Тепер, як може викликати функцію відображення похідного класу без використання віртуального ключового слова (пізнього зв'язування) перед функцією відображення в базовому класі?

Тепер у цій програмі, передаючи об'єкт як дзвінок за значенням, дзвінок за вказівником і дзвінок за посиланням на функцію Hello відмінно працюють. Тепер, якщо ми використовуємо поліморфізм і хочемо відобразити функцію-член похідного класу, якщо він викликається, ми повинні додати ключове слово virtual перед функцією відображення у базі. Якщо передати значення об'єкта під час виклику за допомогою покажчика та виклику за посиланням це виклик функції у похідному класі, але якщо передати об'єкт за значенням він не чому це так?>

Class A (public: void display(); // virtual void display() (cout<< "Hey from A" <display() ) int main() ( B obj; Hello(obj); // obj //&ob return 0; )

  • 2 відповіді
  • Сортування:

    Активність

4

зараз як він може викликати функцію відображення похідного класу без використання ключового слова virtual (пізніша прив'язка) перед функцією відображення в базовому класі?

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

B b; A & a = b;

ви отримаєте різні результати від виклику невіртуальної функції:

B.display(); // Called as B a.display(); // Called as A

Якщо ви знаєте, речовий тип, то ви можете вказати, що ви хочете назвати цю версію:

Static_cast (a).display(); // Called as B

але що жахливо неправильно, якщо об'єкт a відноситься до не має типу B .

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

Виправити. Якщо ви зробите віртуальну функцію, вона буде дозволена під час виконання відповідно до динамічного типу об'єкта, навіть якщо ви використовуєте інший тип посилання або покажчик для доступу до неї. Таким чином, обидва вищезгадані приклади назвали б його B .

Якщо ми передаємо значення об'єкта на виклик покажчиком і викликаємо за посиланням, він викликає функцію у похідному класі, але якщо ми передаємо об'єкт за значенням, це не означає, чому це так?

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

0

Wikipedia каже, що нарізка об'єктів відбувається тому, що немає місця для зберігання додаткових членів похідного класу в суперкласі, тому він зрізаний. Чому нарізка об'єктів не відбувається, якщо ми передаємо її посиланням чи покажчиком? Чому суперклас отримує додатковий простір для його зберігання? -

ВІРТУАЛЬНІ ФУНКЦІЇ______________________________________________________________ 1

Раннє та пізнє зв'язування. Динамічний поліморфізм ___________________________________ 1

Віртуальні функції___________________________________________________________________ 1 Віртуальні деструктори _______________________________________________________________ 4 Абстрактні класи і суто віртуальні функції___________________________________________ 5

ВІРТУАЛЬНІ ФУНКЦІЇ

Раннє та пізнє зв'язування. Динамічний поліморфізм

У C++ поліморфізм підтримується двома способами.

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

ня програми, шляхомраннього зв'язування ідентифікаторів функцій із фізичними адресами на стадії компіляції та компонування.

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

Віртуальна функція – це функція, виклик якої (і дії, що виконуються при цьому) залежить від типу об'єкта, для якого вона викликана. Об'єкт визначає, яку функцію потрібно викликати під час виконання програми. Цей вид поліморфізму називається динамічним поліморфізмом.

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

Для кожного поліморфного типу даних компілятор створює таблицю віртуальних функцій і вбудовує у кожен об'єкт такого класу прихований покажчик цієї таблиці. Вона містить адреси віртуальних функцій відповідного об'єкта. Ім'я покажчика на таблицю віртуальних функцій та назва таблиці залежить від реалізації у конкретному компіляторі. Наприклад, Visual C++ 6.0 цей покажчик має ім'я vfptr , а таблиця називається vftable (від англійського Virtual Function Table). Компілятор автоматично вбудовує на початок конструктора поліморфного класу фрагмент коду, який ініціалізує покажчик таблиці віртуальних функцій. Якщо викликається віртуальна функція, код, згенерований компілятором, знаходить покажчик таблицю віртуальних функцій, потім переглядає цю таблицю і витягує з неї адресу відповідної функції. Після цього здійснюється перехід на вказану адресу та виклик функції.

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

У зв'язку з цим потрібно усвідомлювати тій ціні, яку доводиться платити за можливість використання пізнього зв'язування.

Оскільки об'єкти з віртуальними функціями повинні підтримувати і таблицю віртуальних функцій, їх використання завжди веде до деякого підвищення витрат пам'яті і зниження швидкодії програми. Якщо ви працюєте з невеликим класом, який не збираєтеся робити базовим для інших класів, то в цьому випадку немає сенсу у використанні віртуальних функцій.

Віртуальні функції

Функції, у яких відомий інтерфейс виклику (тобто прототип), але реалізація може бути задана у випадку, і може бути визначено лише конкретних випадків, називаються віртуальними (термін, що означає, що функція може бути перевизначена у похідному класі).

Віртуальні функції – це функції, які гарантують, що буде викликано правильну функцію для об'єкта безвідносно до того, який вираз використовується для здійснення виклику.

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

class Coord

Базовий клас координат

// базовий клас координат

protected:

// Захищені члени класу

double x, y;

// координати

public:

// Відкриті члени класу

Coord () (x = 0; y = 0;)

// Конструктор базового класу

void Input();

// Оголошує невіртуальну функцію

virtual void Print();

// Оголошує віртуальну функцію

void Coord:: Input ()

// дозволяє вводити координати з клавіатури

cout<<"\tx=";

// Вводить значення x з клавіатури

cout<<"\ty=";

// Вводить значення y з клавіатури

void Coord :: Print ()

// Виводить значення координат на екран

cout<<"\tx="<

Похідний клас точки

class Dot: publicCoord

// спадкоємець класу координат

char name;

// ім'я точки

public:

// Відкриті члени класу

Dot (ch ar N): Coord () (name = N;)

// Викликає конструктор базового класу

void Input();

void Print();

void Dot:: Input ()

// дозволяє вводити координати крапки з клавіатури

char S = "Введіть координати точки";

CharToOem (S, S);

cout<

Coord:: Input();

void Dot:: Print()

// виводить значення координат точки на екран

char S = "Координати точки";

CharToOem (S, S);

// Перетворює символи рядка на кирилицю

cout<

// виводить на екран заголовок та ім'я точки

Coord:: Print ();

// Викликає функцію базового класу

class Vec: publicCoord

Похідний клас вектор

// спадкоємець класу координат

char name [3];

// ім'я вектора

public:

// Відкриті члени класу

Vec (char * pName) : Coord () (strncpy (name, pName, 3); name [2] = "\0";)

void Input();

// Перевизначає невіртуальну функцію

void Print();

// Перевизначає віртуальну функцію

void Vec:: Input()

// дозволяє вводити векторні проекції з клавіатури

Лекція 9 Віртуальні функції 3

char S = "Введіть проекції вектора";// оголошує та ініціалізує рядок запрошення

CharToOem (S, S);

// Перетворює символи рядка на кирилицю

cout<

// виводить на екран запрошення та ім'я вектора

Coord:: Input();

// Викликає функцію базового класу

void Vec:: Print ()

// Виводить значення проекцій вектора на екран

char S = "Проекції вектора";

// оголошує та ініціалізує рядок заголовка

CharToOem (S, S);

// Перетворює символи рядка на кирилицю

cout<

// виводить на екран заголовок та ім'я вектора

Coord:: Print ();

// Викликає функцію базового класу

У наведеному прикладі оголошено базовий клас Coord і два похідні класи Dot і Vec. Функція Print () у похідних класах є віртуальною, оскільки вона оголошена віртуальною в базовому класі Coord. Функція Print () у похідних класах Dot і Vec перевизначає функцію базового класу. Якщо похідний клас не надає перевизначеної реалізації функції Print () , використовується стандартна реалізація з базового класу.

Функція Input () оголошена невіртуальною у базовому класі Coord і перевизначена у похідних класах Dot і Vec.

void main()

Coord * pC = New Coord ();

// Оголошує покажчик на координати та виділяє пам'ять

Dot * pD = New Dot ("D");

// Оголошує покажчик на точку та виділяє пам'ять

Vec * pV = new Vec ("V");

// Оголошує покажчик на вектор і виділяє пам'ять

pC->Input ();

pC->Print ();

// Викликає віртуальну функцію Coord:: Print ()

// покажчик координати отримує адресу об'єкта типу точки

pC->Input ();

// Викликає невіртуальну функцію Coord:: Input ()

pC->Print ();

// Викликає віртуальну функцію Dot:: Print ()

// покажчик координати отримує адресу об'єкта типу вектора

pC->Input ();

// Викликає невіртуальну функцію Coord:: Input ()

pC->Print ();

// Викликає віртуальну функцію Vec:: Print ()

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

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

Необхідно відзначити, що операція присвоєння pC = pD , яка використовує операнди різних типів (Coord* і Dot* ) без перетворення, можлива тільки для покажчика на базовий клас в лівій частині. Зворотна операція присвоєння pD = pC неприпустима та викликає помилку синтаксису.

Під час виконання програма виводить на екран:

Координати точки D:

Проекції вектора V:

При виклику функції за допомогою покажчиків та посилань, застосовуються такі правила:

виклик віртуальної функції дозволяється відповідно до типу об'єкта, адресу якого зберігає покажчик чи посилання;

виклик невіртуальної функції дозволяється відповідно до типу вказівника чи посилання.

Віртуальні функції викликаються лише об'єктів, які належать деякому класу. Тому

не можна оголосити глобальну чи статичну функцію віртуальної. Ключове слово virtual може

Пізніше зв'язування зкомпонентами COM

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

Програми з раннім зв'язуванням дізнаються адреси на ранній стадії процесу компіляції/виконання під час компіляції. Коли програма (раннім зв'язуванням компілюється, компілятор використовує бібліотеку типів компонента для включення адрес методів і властивостей компонента в клієнтську програму, щоб до адрес можна було дуже швидко і безпомилково звертатися. Технології взаємодії COM, які були розглянуті до цих пір, використовують раннє зв'язування.

Зі свого боку, програми з пізнім зв'язуванням дізнаються адреси властивостей та методів на пізній стадії процесу компіляції/виконання, в той самий момент, коли ці властивості та методи викликаються. Код з пізнім зв'язуванням зазвичай звертається до клієнтських об'єктів через базові типи даних, такі як object і використовує середовище часу виконання для динамічного визначення адрес методів. Хоча код з пізнім зв'язуванням дозволяє використовувати деякі складні технології програмування, такі як поліморфізм, він вимагає деяких витрат, які ми незабаром побачимо.

Але спочатку перевіримо, як пізніше зв'язування виконується за допомогою відображення в C# (Відображення є способом, який використовується кодом під час виконання для визначення інформації про інтерфейси серверних класів; див. розділ 5.)

При пізньому зв'язуванні з об'єктом COM у програмі C# не потрібно створювати RCW для компонента COM. Натомість викликається метод класу GetTypeFromProgID класу Type для створення екземпляра об'єкта, що представляє тип об'єкта COM. Клас Type є членом простору імен System.Runtime.InteropServices і в коді нижче конфігуруємо об'єкт Type для того ж компонента COM доступу до даних, який використовувався в попередніх прикладах:


Type objCustomerTableType;

Коли є об'єкт Type, що інкапсулює інформацію про тип об'єкта COM, він використовується для створення екземпляра самого об'єкта COM. Це реалізується передачею об'єкта Type метод класу CreateInstance класу Activator.CreateInstance створює екземпляр об'єкта COM і повертає на нього посилання пізнього зв'язування, яку можна зберегти в посиланні типу object.

об'єкт objCustomerTable;
objCustomerTable = Activator.CreateInstance(objCustomerTableType);

На жаль, неможливо викликати методи безпосередньо на засланні типу object . Щоб звернутися до об'єкта COM, необхідно використовувати метод InvokeMember об'єкта Type , який був створений спочатку. При виклику методу InvokeMember йому передається посилання на об'єкт COM разом з ім'ям методу COM, що викликається, а також масив типу object всіх вхідних аргументів методу.

ObjCustomerTableType.InvokeMember("Delete", BindingFlags.InvokeMethod, null, objCustomerTable, aryInputArgs);

Нагадаємо ще раз послідовність дій:

1. Створити об'єкт Type для об'єкта COM за допомогою методу класу Type.GetTypeFromProgID() .

2. Використовувати цей об'єкт Type для створення об'єкта COM за допомогою Activator.CreateInstance().

3. Методи викликаються на об'єкті COM, викликаючи метод InvokeMember на об'єкті Type і передаючи до нього посилання object як вхідний аргумент. Нижче наведено приклад коду, що поєднує все це в один блок:

використовуючи System.Runtime.InteropServices;
Type objCustomerTableType;
об'єкт objCustomerTable;
objCustomerTableType=Type.GetTypeFromProgID("DataAccess.CustomerTable");
objCustomerTable=Activator.CreateInstance(ObjCustomerTableType);
objCustomerTableType.InvokeMember("Delete", BindingFlags, InvokeMethod, null, objCustomerTable, aryInputArgs);
objCustomerTableType = Type.GetTypeFromProgID("DataAccess.CustomerTable");

Хоча засоби пізнього зв'язування C# дозволяють уникнути труднощів RCW, необхідно знати про деякі, пов'язані з цим недоліки.

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

Останнє оновлення: 04.02.2019

Раніше було розглянуто два способи зміни функціональності методів, успадкованих від базового класу - приховування та перевизначення. У чому різниця між цими двома способами?

Перевизначення

Візьмемо приклад із перевизначенням методів:

Class Person ( public string FirstName ( get; set; ) public string LastName ( get; set; ) public Person(string firstName, string lastName) ( FirstName = firstName; LastName = lastName; ) ($"(FirstName) (LastName)"); ) ) class Employee: Person ( public string Company ( get; set; ) public Employee(string firstName, string lastName, string company) : base(firstName, lastName) ( Company = company; ) public override void Display() ( Console.WriteLine($"(FirstName) (LastName) працює в (Company)"); ) )

Також створимо об'єкт Employee та передамо його змінній типу Person:

Person tom = new Employee("Tom", "Smith", "Microsoft"); tom.Display(); // Tom Smith працює в Microsoft

Тепер ми отримуємо інший результат, ніж при прихованні. А при викликі tom.Display() виконується реалізація методу Display із класу Employee.

p align="justify"> Для роботи з віртуальними методами компілятор формує таблицю віртуальних методів (Virtual Method Table або VMT). До неї записуються адреси віртуальних методів. До кожного класу створюється своя таблиця.

Коли створюється об'єкт класу, компілятор передає в конструктор об'єкта спеціальний код, який пов'язує об'єкт і таблицю VMT.

А за виклику віртуального методу з об'єкта береться адреса його таблиці VMT. Потім з VMT витягується адреса методу і передається управління. Тобто процес вибору реалізації методу провадиться під час виконання програми. Власне, так і виконується віртуальний метод. Слід враховувати, що оскільки середовищі виконання спочатку необхідно отримати з таблиці VMT адресу потрібного методу, це трохи уповільнює виконання програми.

Приховування

Тепер візьмемо ті ж класи Person та Employee, але замість перевизначення використовуємо приховування:

Class Person ( public string FirstName ( get; set; ) public string LastName ( get; set; ) public Person(string firstName, string lastName) ( FirstName = firstName; LastName = lastName; ) public void Display() ( Console.WriteLine( $"(FirstName) (LastName)"); ) ) class Employee: Person (public string Company (get; set;) ; ) public new void Display() ( Console.WriteLine($"(FirstName) (LastName) працює в (Company)"); ) )

І подивимося, що буде у наступному випадку:

Person tom = new Employee("Tom", "Smith", "Microsoft"); tom.Display(); // Tom Smith

Змінна tom являє собою тип Person, але зберігає посилання на об'єкт Employee. Однак під час виклику методу Display буде виконуватися та версія методу, яка визначена саме у класі Person, а не у класі Employee. Чому? Клас Employee не перевизначає метод Display, успадкований від базового класу, а фактично визначає новий метод. Тому при викликі tom.Display() викликається метод Display із класу Person.