Підрозділ 7.Q
Питання для самоконтролю
Питання для самоконтролю — Розділ 7. Інтерфейси 7.1. Визначення інтерфейсів 1. Що таке інтерфейс у C ? Чим він принципово відрізняється від класу і від абстрактного класу? Що дозволяє, а що забороняє мати інтер
Питання для самоконтролю — Розділ 7. Інтерфейси
7.1. Визначення інтерфейсів
Що таке інтерфейс у C#? Чим він принципово відрізняється від класу і від абстрактного класу? Що дозволяє, а що забороняє мати інтерфейс серед своїх членів?
Чому за угодою C# назви інтерфейсів починаються з великої літери
I? Чи є це вимогою компілятора? Наведіть 3–4 приклади стандартних інтерфейсів .NET, що ілюструють цю конвенцію.Поясніть різницю у рівнях доступу: чому члени інтерфейсу за замовчуванням
public, а неprivate, як у класах? Що відбудеться, якщо клас спробує реалізувати публічний метод інтерфейсу зі зниженим рівнем доступу (наприклад,protected)?Що таке «реалізація за замовчуванням» (default interface implementation), доступна з C# 8.0? Яку практичну проблему вона вирішує? Чому без неї додавання нового методу до вже опублікованого інтерфейсу «ламало» б всіх реалізаторів?
Знайдіть помилку у цьому коді та поясніть її:
interface ILogger { private int _count = 0; // поле екземпляра void Log(string message); }Що таке антипатерн «Бог-інтерфейс»? Наведіть приклад такого інтерфейсу в контексті медичної системи. Які конкретні проблеми він спричиняє при реалізації різними класами? Як правильно його розбити на вузькі інтерфейси?
Порівняйте інтерфейс і абстрактний клас за такими критеріями: стан (поля), конструктор, кількість реалізованих/успадкованих типів, default-реалізація, спадкування. В якому випадку краще обирати інтерфейс, а в якому — абстрактний клас?
Оголошено статичний метод безпосередньо в інтерфейсі. Як до нього звернутися? Чи успадковується він класами-реалізаторами? Поясніть різницю між статичним методом інтерфейсу і звичайним статичним методом класу-утиліти.
7.2. Застосування інтерфейсів
Чому не можна створити екземпляр інтерфейсу напряму через
new? Що натомість зберігає змінна типу інтерфейсу? Покажіть, як зміннаIDiagnosable dможе «вказувати» на об'єкти різних класів.Поясніть, що таке поліморфізм через інтерфейс. Напишіть метод
RunAll(IDiagnosable[] items), який послідовно викликаєRunDiagnostics()для кожного елемента. Покажіть, що цей метод однаково добре працює з об'єктами різних класів, що реалізуютьIDiagnosable.Що виведе наступний код? Поясніть, чому виклик
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Опишіть різницю між розширювальним (widening) і звужувальним (narrowing) перетворенням типу через інтерфейс. Чому одне відбувається автоматично, а інше потребує явного приведення? Коли при явному приведенні виникне
InvalidCastException?Порівняйте три способи звужувального приведення: явний cast
(T), операторas, і операторisз pattern matching. Коли кожен з них доречний? Який з них рекомендується як основний і чому?Клас реалізує кілька інтерфейсів. Напишіть код, де один об'єкт класу
Patientзберігається в трьох різних змінних типів інтерфейсів:IDiagnosable,IBillable,INotifiable. Покажіть, які методи доступні через кожну з них.Структура також може реалізовувати інтерфейси. Чому це особливо важливо для структур? Що відбувається зі значенням структури при присвоєнні до змінної інтерфейсного типу (boxing)? Які наслідки це має для продуктивності?
Проаналізуйте код і поясніть його поведінку:
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. Явна реалізація інтерфейсів
Що таке явна реалізація інтерфейсу? Чим вона синтаксично відрізняється від звичайної (неявної) реалізації? Який рівень доступу має явно реалізований метод і чому?
Через яку змінну доступний явно реалізований метод — через змінну класу чи через змінну інтерфейсу? Покажіть три різних способи викликати явно реалізований метод
RunDiagnostics()для об'єктаPatient.Назвіть дві головні ситуації, коли явна реалізація є необхідною, а не лише зручною. Наведіть код, що ілюструє кожну із ситуацій.
Два інтерфейси
IDiagnosableіITreatmentмають методvoid RunProcedure(). Клас реалізує обидва. Що відбудеться, якщо метод реалізовано без явної реалізації (один спільний метод)? Як явна реалізація вирішує конфлікт? Напишіть обидва варіанти.Проаналізуйте чотири варіанти зміни реалізації інтерфейсу у похідному класі:
override,new,new+ повторна реалізація, явна реалізація +new. Що виведе кожен варіант для змінних типівBaseClass,IDiagnosableіDerivedClass?Є абстрактний клас
MedicalRecord, що реалізуєIDiagnosable, але оголошуєRunDiagnostics()абстрактним. Поясніть механізм: чи виконуєMedicalRecordконтракт інтерфейсу? Що відбувається при спробі створити екземплярMedicalRecordнапряму?Спроєктуйте клас
SmartDevice, який реалізує два інтерфейси:IReadableз методомstring Read()іIWriteableз методомstring Write(). РеалізуйтеRead()явно (доступно тільки черезIReadable), аWrite()— неявно (доступно також напряму). Покажіть різницю у доступі.Чому не можна написати
public void IDiagnosable.RunDiagnostics()? Що відбудеться, якщо поставитиpublicперед явною реалізацією? Поясніть логіку цього обмеження.
7.4. Успадкування інтерфейсів
Що таке успадкування інтерфейсів? Яку вимогу ставить компілятор до класу, що реалізує похідний інтерфейс, стосовно методів батьківського інтерфейсу?
Є ланцюжок:
IExaminable→IDiagnosable→ITreatable. КласDoctorреалізуєITreatable. Скільки методів зобов'язаний реалізуватиDoctor? Які «зрізи» контракту доступні через змінні різних типів інтерфейсів?Чому інтерфейс може успадковувати кілька базових інтерфейсів одночасно, а клас — лише один базовий клас? Яка концептуальна різниця між «успадкуванням реалізації» і «успадкуванням контракту»?
Що відбувається, коли в похідному інтерфейсі оголошується
new void Examine(), а базовий вже маєvoid Examine()з реалізацією за замовчуванням? Як поведінка залежить від типу змінної (IDerivedчиIBase)? Чим це відрізняється відoverride?Проаналізуйте наступне оголошення і поясніть, чи є воно коректним та чому:
internal interface IBase { void Act(); } public interface IDerived : IBase { void ExtendedAct(); }Чому не можна застосувати
sealedабоabstractдо інтерфейсу? Поясніть, чому ці модифікатори суперечать природі інтерфейсів.Спроєктуйте систему інтерфейсів для медичної клініки:
ISchedulable(запис пацієнта),IPayable(оплата),IClinicAppointment : ISchedulable, IPayable(прийом). Реалізуйте класConsultationAppointmentі покажіть, як через різні змінні типу інтерфейсу отримати доступ до різних підмножин методів.Два інтерфейси —
ISchedulableіIPayable— обидва оголошуютьstring GetSummary(). Клас реалізує обидва. Як правильно надати різну реалізацію для кожного інтерфейсу? Напишіть повний приклад.
7.5. Інтерфейси в узагальненнях
У чому полягають два різні способи взаємодії інтерфейсів та узагальнень у C#? Поясніть різницю між «інтерфейс як обмеження» і «узагальнений інтерфейс».
Оголошено
class Repository<T> where T : IExaminable, IPrintable. Що саме гарантує компілятор всередині цього класу щодо об'єкта типуT? Чому це безпечніше, ніж перевірка черезisу runtime?Скільки інтерфейсних обмежень можна вказати у
where? Чи можна поєднати обмеження-клас і обмеження-інтерфейси в одномуwhere? Запишіть синтаксис для узагальненого класу, що вимагає базовий класBaseMedicalі одночасно реалізаціюIExaminable,IPrintable.Оголошено
interface IEntity<TId> { TId Id { get; } }. Як класPatientможе реалізувати цей інтерфейс зintяк типом ID, а класInsurancePolicy— зstring? Покажіть реалізацію обох класів.Клас
CardiologistреалізуєIClinicWorker, деIClinicWorker : IExaminable, IPrintable. Чи задовольняєCardiologistобмеженняwhere T : IExaminable, IPrintable? Поясніть логіку: чому «успадкована реалізація» інтерфейсу рахується.Напишіть узагальнений метод
PrintAll<T>(T[] items) where T : IPrintable, який викликаєPrint()для кожного елемента масиву. Покажіть, що цей метод прийме масив будь-якого класу, що реалізуєIPrintable, але відмовить у компіляції для класу без цього інтерфейсу.Порівняйте:
interface IRepo<T>(без параметра типу) іinterface IRepo<TId>(узагальнений). В яких сценаріях узагальнений інтерфейс є явно кращим рішенням? Назвіть принаймні два конкретних приклади.Спроєктуйте узагальнений клас
MedicalRegistry<T> where T : IEntity<int>, IExaminable, що зберігає колекцію медичних сутностей і надає методT FindById(int id). Поясніть, чому подвійне обмеження тут доречне.
7.6. Копіювання об'єктів. Інтерфейс ICloneable
Що відбувається при простому присвоєнні
b = aдля об'єкта класу (reference type)? Чому змінаb.Nameпісля такого присвоєння також змінюєa.Name? Чим ця поведінка відрізняється від присвоєння значимих типів (struct)?Що таке інтерфейс
ICloneable? Яку сигнатуру має його єдиний метод? Чому тип поверненняobject, а не конкретний тип? Як правильно викликатиClone()і отримати об'єкт конкретного типу?Поясніть різницю між поверхневим (shallow) і глибоким (deep) копіюванням. Що робить
MemberwiseClone()? Коли поверхневого копіювання достатньо, а коли потрібне глибоке?Знайдіть помилку у наступному прикладі та поясніть, що відбуватиметься при виконанні коду:
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); // що виведе?Реалізуйте глибоке копіювання для класу
Patient, що містить полеDiagnosisRecord Diagnosis. Покажіть, що після клонування змінаDiagnosis.Codeв копії не впливає на оригінал.Що таке інтерфейс
IComparable<T>? Який контракт він накладає на клас? Які значення повинен повертати методCompareToі що кожне з них означає?Порівняйте
IComparable<T>іIComparer<T>: у чому їх різниця? Коли кожен доречний? Чому один клас не може мати два різніCompareToдля різних критеріїв сортування — і якIComparer<T>вирішує цю проблему?Напишіть клас
Appointment(прийом у лікаря) з полямиDateTime Dateіstring DoctorName. РеалізуйтеIComparable<Appointment>для сортування за датою. Також напишіть окремий компараторAppointmentByDoctorComparer : IComparer<Appointment>для сортування за ім'ям лікаря.
7.7. Коваріантність та контраваріантність узагальнених інтерфейсів
Що таке інваріантність узагальнених інтерфейсів? Чому за замовчуванням
IRepository<Cardiologist>іIRepository<Doctor>несумісні навіть приCardiologist : Doctor? Яку небезпеку усуває ця суворість?Поясніть різницю між трьома формами варіантності: коваріантністю (
out), контраваріантністю (in) та інваріантністю. У якому напрямі дозволяється присвоєння в кожному випадку?Оголошено
interface IFactory<out T> { T Create(); }. Що означаєout? ЧомуIFactory<Cardiologist>можна безпечно присвоїти змінній типуIFactory<Doctor>? Яке обмеження накладаєoutна використанняTу методах інтерфейсу?Оголошено
interface IHandler<in T> { void Handle(T item); }. Що означаєin? ЧомуIHandler<Doctor>можна присвоїти зміннійIHandler<Cardiologist>? Це інтуїтивно здається «зворотнім» — поясніть логіку цього явища.Що виведе наступний код? Поясніть, яка форма варіантності тут задіяна і чому присвоєння у рядку 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();Поясніть, чому
outне можна використовувати як тип параметра методу, аin— як тип результату методу. Яку безпеку гарантують ці обмеження?Стандартний інтерфейс
IEnumerable<out T>є коваріантним, аIComparer<in T>— контраваріантним. Поясніть, чому такі рішення прийняті для кожного з них: яким чиномTвикористовується в кожному інтерфейсі?Спроєктуйте інтерфейс
IClinicMessenger<in TRequest, out TResponse>для системи медичних запитів. Покажіть, яке присвоєння стає можливим завдяки обом модифікаторам, і поясніть, чому таке присвоєння є безпечним.