OOP Course
Сьогодні

Підрозділ 7.Q

Питання для самоконтролю

Питання для самоконтролю — Розділ 7. Інтерфейси 7.1. Визначення інтерфейсів 1. Що таке інтерфейс у C ? Чим він принципово відрізняється від класу і від абстрактного класу? Що дозволяє, а що забороняє мати інтер

Питання для самоконтролю — Розділ 7. Інтерфейси

7.1. Визначення інтерфейсів

  1. Що таке інтерфейс у C#? Чим він принципово відрізняється від класу і від абстрактного класу? Що дозволяє, а що забороняє мати інтерфейс серед своїх членів?

  2. Чому за угодою C# назви інтерфейсів починаються з великої літери I? Чи є це вимогою компілятора? Наведіть 3–4 приклади стандартних інтерфейсів .NET, що ілюструють цю конвенцію.

  3. Поясніть різницю у рівнях доступу: чому члени інтерфейсу за замовчуванням public, а не private, як у класах? Що відбудеться, якщо клас спробує реалізувати публічний метод інтерфейсу зі зниженим рівнем доступу (наприклад, protected)?

  4. Що таке «реалізація за замовчуванням» (default interface implementation), доступна з C# 8.0? Яку практичну проблему вона вирішує? Чому без неї додавання нового методу до вже опублікованого інтерфейсу «ламало» б всіх реалізаторів?

  5. Знайдіть помилку у цьому коді та поясніть її:

    interface ILogger
    {
        private int _count = 0; // поле екземпляра
        void Log(string message);
    }
  6. Що таке антипатерн «Бог-інтерфейс»? Наведіть приклад такого інтерфейсу в контексті медичної системи. Які конкретні проблеми він спричиняє при реалізації різними класами? Як правильно його розбити на вузькі інтерфейси?

  7. Порівняйте інтерфейс і абстрактний клас за такими критеріями: стан (поля), конструктор, кількість реалізованих/успадкованих типів, default-реалізація, спадкування. В якому випадку краще обирати інтерфейс, а в якому — абстрактний клас?

  8. Оголошено статичний метод безпосередньо в інтерфейсі. Як до нього звернутися? Чи успадковується він класами-реалізаторами? Поясніть різницю між статичним методом інтерфейсу і звичайним статичним методом класу-утиліти.


7.2. Застосування інтерфейсів

  1. Чому не можна створити екземпляр інтерфейсу напряму через new? Що натомість зберігає змінна типу інтерфейсу? Покажіть, як змінна IDiagnosable d може «вказувати» на об'єкти різних класів.

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

  3. Що виведе наступний код? Поясніть, чому виклик asConcrete.LogResult() може спричинити помилку компіляції:

    interface ILogger
    {
        void LogResult() => Console.WriteLine("[LOG] Збережено");
    }
    class Patient : ILogger { }
    
    ILogger asInterface = new Patient();
    asInterface.LogResult(); // рядок A
    
    Patient asConcrete = new Patient();
    asConcrete.LogResult(); // рядок B
  4. Опишіть різницю між розширювальним (widening) і звужувальним (narrowing) перетворенням типу через інтерфейс. Чому одне відбувається автоматично, а інше потребує явного приведення? Коли при явному приведенні виникне InvalidCastException?

  5. Порівняйте три способи звужувального приведення: явний cast (T), оператор as, і оператор is з pattern matching. Коли кожен з них доречний? Який з них рекомендується як основний і чому?

  6. Клас реалізує кілька інтерфейсів. Напишіть код, де один об'єкт класу Patient зберігається в трьох різних змінних типів інтерфейсів: IDiagnosable, IBillable, INotifiable. Покажіть, які методи доступні через кожну з них.

  7. Структура також може реалізовувати інтерфейси. Чому це особливо важливо для структур? Що відбувається зі значенням структури при присвоєнні до змінної інтерфейсного типу (boxing)? Які наслідки це має для продуктивності?

  8. Проаналізуйте код і поясніть його поведінку:

    interface IRunnable { void Run(); }
    class Base : IRunnable { public void Run() => Console.WriteLine("Base"); }
    class Child : Base { public new void Run() => Console.WriteLine("Child"); }
    
    IRunnable r = new Child();
    r.Run(); // що виведе?

7.3. Явна реалізація інтерфейсів

  1. Що таке явна реалізація інтерфейсу? Чим вона синтаксично відрізняється від звичайної (неявної) реалізації? Який рівень доступу має явно реалізований метод і чому?

  2. Через яку змінну доступний явно реалізований метод — через змінну класу чи через змінну інтерфейсу? Покажіть три різних способи викликати явно реалізований метод RunDiagnostics() для об'єкта Patient.

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

  4. Два інтерфейси IDiagnosable і ITreatment мають метод void RunProcedure(). Клас реалізує обидва. Що відбудеться, якщо метод реалізовано без явної реалізації (один спільний метод)? Як явна реалізація вирішує конфлікт? Напишіть обидва варіанти.

  5. Проаналізуйте чотири варіанти зміни реалізації інтерфейсу у похідному класі: override, new, new + повторна реалізація, явна реалізація + new. Що виведе кожен варіант для змінних типів BaseClass, IDiagnosable і DerivedClass?

  6. Є абстрактний клас MedicalRecord, що реалізує IDiagnosable, але оголошує RunDiagnostics() абстрактним. Поясніть механізм: чи виконує MedicalRecord контракт інтерфейсу? Що відбувається при спробі створити екземпляр MedicalRecord напряму?

  7. Спроєктуйте клас SmartDevice, який реалізує два інтерфейси: IReadable з методом string Read() і IWriteable з методом string Write(). Реалізуйте Read() явно (доступно тільки через IReadable), а Write() — неявно (доступно також напряму). Покажіть різницю у доступі.

  8. Чому не можна написати public void IDiagnosable.RunDiagnostics()? Що відбудеться, якщо поставити public перед явною реалізацією? Поясніть логіку цього обмеження.


7.4. Успадкування інтерфейсів

  1. Що таке успадкування інтерфейсів? Яку вимогу ставить компілятор до класу, що реалізує похідний інтерфейс, стосовно методів батьківського інтерфейсу?

  2. Є ланцюжок: IExaminableIDiagnosableITreatable. Клас Doctor реалізує ITreatable. Скільки методів зобов'язаний реалізувати Doctor? Які «зрізи» контракту доступні через змінні різних типів інтерфейсів?

  3. Чому інтерфейс може успадковувати кілька базових інтерфейсів одночасно, а клас — лише один базовий клас? Яка концептуальна різниця між «успадкуванням реалізації» і «успадкуванням контракту»?

  4. Що відбувається, коли в похідному інтерфейсі оголошується new void Examine(), а базовий вже має void Examine() з реалізацією за замовчуванням? Як поведінка залежить від типу змінної (IDerived чи IBase)? Чим це відрізняється від override?

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

    internal interface IBase { void Act(); }
    public interface IDerived : IBase { void ExtendedAct(); }
  6. Чому не можна застосувати sealed або abstract до інтерфейсу? Поясніть, чому ці модифікатори суперечать природі інтерфейсів.

  7. Спроєктуйте систему інтерфейсів для медичної клініки: ISchedulable (запис пацієнта), IPayable (оплата), IClinicAppointment : ISchedulable, IPayable (прийом). Реалізуйте клас ConsultationAppointment і покажіть, як через різні змінні типу інтерфейсу отримати доступ до різних підмножин методів.

  8. Два інтерфейси — ISchedulable і IPayable — обидва оголошують string GetSummary(). Клас реалізує обидва. Як правильно надати різну реалізацію для кожного інтерфейсу? Напишіть повний приклад.


7.5. Інтерфейси в узагальненнях

  1. У чому полягають два різні способи взаємодії інтерфейсів та узагальнень у C#? Поясніть різницю між «інтерфейс як обмеження» і «узагальнений інтерфейс».

  2. Оголошено class Repository<T> where T : IExaminable, IPrintable. Що саме гарантує компілятор всередині цього класу щодо об'єкта типу T? Чому це безпечніше, ніж перевірка через is у runtime?

  3. Скільки інтерфейсних обмежень можна вказати у where? Чи можна поєднати обмеження-клас і обмеження-інтерфейси в одному where? Запишіть синтаксис для узагальненого класу, що вимагає базовий клас BaseMedical і одночасно реалізацію IExaminable, IPrintable.

  4. Оголошено interface IEntity<TId> { TId Id { get; } }. Як клас Patient може реалізувати цей інтерфейс з int як типом ID, а клас InsurancePolicy — з string? Покажіть реалізацію обох класів.

  5. Клас Cardiologist реалізує IClinicWorker, де IClinicWorker : IExaminable, IPrintable. Чи задовольняє Cardiologist обмеження where T : IExaminable, IPrintable? Поясніть логіку: чому «успадкована реалізація» інтерфейсу рахується.

  6. Напишіть узагальнений метод PrintAll<T>(T[] items) where T : IPrintable, який викликає Print() для кожного елемента масиву. Покажіть, що цей метод прийме масив будь-якого класу, що реалізує IPrintable, але відмовить у компіляції для класу без цього інтерфейсу.

  7. Порівняйте: interface IRepo<T> (без параметра типу) і interface IRepo<TId> (узагальнений). В яких сценаріях узагальнений інтерфейс є явно кращим рішенням? Назвіть принаймні два конкретних приклади.

  8. Спроєктуйте узагальнений клас MedicalRegistry<T> where T : IEntity<int>, IExaminable, що зберігає колекцію медичних сутностей і надає метод T FindById(int id). Поясніть, чому подвійне обмеження тут доречне.


7.6. Копіювання об'єктів. Інтерфейс ICloneable

  1. Що відбувається при простому присвоєнні b = a для об'єкта класу (reference type)? Чому зміна b.Name після такого присвоєння також змінює a.Name? Чим ця поведінка відрізняється від присвоєння значимих типів (struct)?

  2. Що таке інтерфейс ICloneable? Яку сигнатуру має його єдиний метод? Чому тип повернення object, а не конкретний тип? Як правильно викликати Clone() і отримати об'єкт конкретного типу?

  3. Поясніть різницю між поверхневим (shallow) і глибоким (deep) копіюванням. Що робить MemberwiseClone()? Коли поверхневого копіювання достатньо, а коли потрібне глибоке?

  4. Знайдіть помилку у наступному прикладі та поясніть, що відбуватиметься при виконанні коду:

    class Patient : ICloneable
    {
        public string Name { get; set; }
        public DiagnosisRecord Diagnosis { get; set; }
        public object Clone() => MemberwiseClone();
    }
    var p1 = new Patient { Name = "Іван", Diagnosis = new DiagnosisRecord("J18") };
    var p2 = (Patient)p1.Clone();
    p2.Diagnosis.Code = "I10"; // змінюємо через p2
    Console.WriteLine(p1.Diagnosis.Code); // що виведе?
  5. Реалізуйте глибоке копіювання для класу Patient, що містить поле DiagnosisRecord Diagnosis. Покажіть, що після клонування зміна Diagnosis.Code в копії не впливає на оригінал.

  6. Що таке інтерфейс IComparable<T>? Який контракт він накладає на клас? Які значення повинен повертати метод CompareTo і що кожне з них означає?

  7. Порівняйте IComparable<T> і IComparer<T>: у чому їх різниця? Коли кожен доречний? Чому один клас не може мати два різні CompareTo для різних критеріїв сортування — і як IComparer<T> вирішує цю проблему?

  8. Напишіть клас Appointment (прийом у лікаря) з полями DateTime Date і string DoctorName. Реалізуйте IComparable<Appointment> для сортування за датою. Також напишіть окремий компаратор AppointmentByDoctorComparer : IComparer<Appointment> для сортування за ім'ям лікаря.


7.7. Коваріантність та контраваріантність узагальнених інтерфейсів

  1. Що таке інваріантність узагальнених інтерфейсів? Чому за замовчуванням IRepository<Cardiologist> і IRepository<Doctor> несумісні навіть при Cardiologist : Doctor? Яку небезпеку усуває ця суворість?

  2. Поясніть різницю між трьома формами варіантності: коваріантністю (out), контраваріантністю (in) та інваріантністю. У якому напрямі дозволяється присвоєння в кожному випадку?

  3. Оголошено interface IFactory<out T> { T Create(); }. Що означає out? Чому IFactory<Cardiologist> можна безпечно присвоїти змінній типу IFactory<Doctor>? Яке обмеження накладає out на використання T у методах інтерфейсу?

  4. Оголошено interface IHandler<in T> { void Handle(T item); }. Що означає in? Чому IHandler<Doctor> можна присвоїти змінній IHandler<Cardiologist>? Це інтуїтивно здається «зворотнім» — поясніть логіку цього явища.

  5. Що виведе наступний код? Поясніть, яка форма варіантності тут задіяна і чому присвоєння у рядку 3 є коректним:

    interface IProducer<out T> { T Produce(); }
    class AnimalProducer : IProducer<Animal> { public Animal Produce() => new Animal(); }
    IProducer<object> p = new AnimalProducer(); // рядок 3 — Animal : object
    object obj = p.Produce();
  6. Поясніть, чому out не можна використовувати як тип параметра методу, а in — як тип результату методу. Яку безпеку гарантують ці обмеження?

  7. Стандартний інтерфейс IEnumerable<out T> є коваріантним, а IComparer<in T> — контраваріантним. Поясніть, чому такі рішення прийняті для кожного з них: яким чином T використовується в кожному інтерфейсі?

  8. Спроєктуйте інтерфейс IClinicMessenger<in TRequest, out TResponse> для системи медичних запитів. Покажіть, яке присвоєння стає можливим завдяки обом модифікаторам, і поясніть, чому таке присвоєння є безпечним.

Розроблено Tomka Yurii · © 2026 ·