OOP Course
Сьогодні

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

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

Питання для самоконтролю — Розділ 8. Додаткові можливості ООП у C 8.1. Визначення операторів 1. Що таке перевантаження операторів у C ? Яку проблему воно вирішує? Поясніть на прикладі, чому код if tempAfter tem

Питання для самоконтролю — Розділ 8. Додаткові можливості ООП у C#

8.1. Визначення операторів

  1. Що таке перевантаження операторів у C#? Яку проблему воно вирішує? Поясніть на прикладі, чому код if (tempAfter > tempBefore) читабельніший за if (tempAfter.Celsius > tempBefore.Celsius).

  2. Опишіть механізм роботи перевантаженого оператора: що робить компілятор, коли зустрічає вираз t1 + delta, де t1 — об'єкт класу BodyTemperature? Яку сигнатуру шукає компілятор?

  3. Які обов'язкові вимоги до методу-оператора? Назвіть усі: модифікатори, ключові слова, вимоги до параметрів, різниця між унарними і бінарними операторами.

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

    class Temperature
    {
        public double Value { get; }
        public Temperature(double v) => Value = v;
    
        public static Temperature operator +(Temperature t1, Temperature t2)
            => new Temperature(t1.Value + t2.Value);
    
        public static bool operator >(Temperature t1, Temperature t2)
            => t1.Value > t2.Value;
    }
  5. Що означає «парні оператори» у C#? Які пари компілятор вимагає визначати разом? Що трапиться, якщо визначити > без <? Чому це обмеження введено?

  6. При перевантаженні == компілятор рекомендує також перевизначити Equals(object) і GetHashCode(). Чому? Яка непослідовна поведінка виникає, якщо == порівнює значення, а Equals() — посилання?

  7. Оператор t1 + 0.5 компілюється успішно, а 0.5 + t1 — ні. Чому? Що потрібно зробити, щоб обидві форми працювали?

  8. Коли варто перевантажувати оператори, а коли це є антипатерном? Наведіть по два приклади доречного і недоречного перевантаження у контексті реальної предметної галузі.


8.2. Визначення інкременту та декременту

  1. Чим визначення операторів ++ і -- для класу відрізняється від визначення арифметичних операторів? Скільки параметрів приймає оператор ++? Яку кількість перевантажених варіантів потрібно написати для підтримки і t++, і ++t?

  2. Чому наступний підхід є неправильним? Поясніть обидві причини:

    public static BodyTemperature operator ++(BodyTemperature t)
    {
        t.Celsius += 0.1; // безпосередня мутація параметра
        return t;
    }
  3. Що таке «immutable update pattern» при визначенні операторів? Чому він є стандартом у C#? Напишіть правильну реалізацію ++ для класу BodyTemperature, що зберігає температуру в градусах Цельсія.

  4. Поясніть різницю між постфіксним t++ і префіксним ++t. Хоча ми визначаємо один метод — компілятор розрізняє їх поведінку. Що саме відрізняється? Що виведе наступний код:

    BodyTemperature t1 = new BodyTemperature(36.6);
    BodyTemperature t2 = t1++;
    Console.WriteLine($"t1={t1.Celsius:F1}, t2={t2.Celsius:F1}");
  5. Коли різниця між t++ і ++t має практичне значення, а коли — ні? Наведіть конкретний приклад, де вибір між ними впливає на результат.

  6. Що таке оператори true і false у C#? Навіщо вони потрібні? Що відбудеться після їх визначення, коли ви пишете if (device) або if (!device)?

  7. Оператори true і false завжди визначаються парою. Чому? Що відбувається при спробі визначити тільки один із них?

  8. Спроєктуйте клас BloodPressure з полями Systolic і Diastolic. Реалізуйте оператори ++ і -- (збільшення/зменшення систолічного на 1), а також true/false (норма — якщо систолічний до 120 і діастолічний до 80). Покажіть використання у if-умові.


8.3. Перевантаження операцій перетворення типів

  1. Що таке перевантаження операторів перетворення типів у C#? Яку проблему воно вирішує? Наведіть приклад, коли без нього код стає громіздким.

  2. Поясніть різницю між implicit і explicit оператором перетворення. Коли кожен з них доречний? Яку відповідальність несе програміст, коли пише явний cast?

  3. Які обов'язкові вимоги до оператора перетворення: модифікатори, ключові слова, обмеження на типи параметра і результату? Чому не можна визначити перетворення між двома чужими типами в третьому класі?

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

    class Celsius { public double Value { get; } public Celsius(double v) => Value = v; }
    class Fahrenheit { public double Value { get; } public Fahrenheit(double v) => Value = v; }
    class Converter
    {
        public static implicit operator Celsius(Fahrenheit f)
            => new Celsius((f.Value - 32) * 5.0 / 9.0);
    }
  5. Напишіть клас Pulse (пульс у ударах на хвилину). Визначте implicit-перетворення з int у Pulse і explicit-перетворення з Pulse у int. Поясніть свій вибір напрямів.

  6. За яким критерієм обирати implicit або explicit? Заповніть таблицю: «Можлива втрата даних?», «Задіяна нетривіальна формула?», «Перетворення між різними одиницями?» — і вкажіть для кожного рядка, який оператор доречніший.

  7. Що виведе наступний код? Поясніть, яке перетворення відбувається і коли:

    class Kg { public double Value { get; }
               public Kg(double v) => Value = v;
               public static implicit operator Kg(double v) => new Kg(v); }
    Kg weight = 72.5;
    Console.WriteLine(weight.Value);
  8. Чи можна визначити два оператори перетворення у протилежних напрямах між одними й тими самими типами (наприклад, BodyTemperaturedouble)? Чи є якісь обмеження на комбінацію implicit у одному напрямі і explicit в іншому?


8.4. Індексатори

  1. Що таке індексатор у C#? Чим він схожий на властивість і чим від неї відрізняється? Що використовується замість імені властивості в синтаксисі індексатора?

  2. Запишіть синтаксис числового індексатора з блоками get і set для класу Ward, що дає доступ до пацієнтів за позицією. Яку перевірку доречно додати в get і set?

  3. Чим рядковий індексатор відрізняється від числового? Наведіть практичний приклад, де рядковий індексатор є більш виразним рішенням, ніж числовий або набір окремих властивостей.

  4. Що виведе наступний код? Чи є помилка у використанні індексатора?

    class Config
    {
        private string[] _items = { "alpha", "beta", "gamma" };
        public string this[int i]
        {
            get => _items[i];
            set => _items[i] = value;
        }
    }
    var cfg = new Config();
    cfg[1] = "delta";
    Console.WriteLine(cfg[0] + " " + cfg[1]);
  5. Як визначити індексатор лише для читання (readonly)? Коли це доречно? Напишіть клас ImmutableRecord, де індексатор надає доступ до даних за числовим індексом, але не дозволяє їх змінювати.

  6. Чи можна перевантажити індексатор — визначити кілька індексаторів з різними типами або кількістю параметрів? Що визначає, який саме індексатор викликається? Наведіть приклад.

  7. Що таке двопараметровий індексатор? Де і коли його доречно застосовувати? Запишіть синтаксис і покажіть приклад із розкладом прийомів schedule[day, hour].

  8. Порівняйте індексатор і словник Dictionary<TKey, TValue>: обидва надають доступ за ключем. Коли краще використовувати власний клас з індексатором, а коли достатньо словника?


8.5. Змінні-посилання та повернення посилання

  1. Що таке ref-змінна (ref local)? Чим вона відрізняється від звичайного присвоєння? Що означає термін «псевдонім» (alias) у цьому контексті?

  2. Що виведе наступний код? Поясніть крок за кроком:

    double x = 10.0;
    ref double r = ref x;
    r = 20.0;
    Console.WriteLine(x);
    x = 30.0;
    Console.WriteLine(r);
  3. Чи можна оголосити ref-змінну без ініціалізації (ref double r;)? Що відбудеться? Яке значення обов'язково надається при оголошенні ref-змінної?

  4. Що таке ref return? Яку практичну перевагу дає повернення посилання замість значення? Яка синтаксична відмінність у сигнатурі методу і у виклику?

  5. Напишіть метод ref double FindFirst(double[] arr, double value), що повертає посилання на перший елемент масиву, рівний value. Покажіть, як через повернене посилання змінити цей елемент безпосередньо в масиві.

  6. Що таке ref readonly? Яку мету воно переслідує? Чим відрізняється від звичайного ref?

  7. Перелічіть обмеження методів із ref return: що не можна повертати як посилання і чому? Чому не можна повертати посилання на локальну змінну методу?

  8. Коли ref local і ref return є обґрунтованими інструментами, а коли вони лише ускладнюють код? Назвіть конкретний сценарій, де ref return дає реальний виграш у порівнянні зі звичайним поверненням значення.


8.6. Методи розширення

  1. Що таке метод розширення у C#? Яку практичну проблему він вирішує? Наведіть приклад: коли метод розширення є єдиним варіантом, а успадкування — неможливим.

  2. Опишіть синтаксис методу розширення: де він визначається, які модифікатори потрібні, що означає this перед першим параметром?

  3. Що насправді відбувається при виклику 38.5.IsFever()? Як компілятор транслює цей виклик? Поясніть, що мають на увазі терміном «синтаксичний цукор».

  4. Що виведе наступний код? Поясніть, що відбувається при конфлікті між методом розширення і власним методом типу:

    public static class Ext { public static string Info(this string s) => "extension"; }
    // string вже має метод, але не 'Info' — в реальному коді, але уявімо:
    string s = "hello";
    Console.WriteLine(s.Info());
  5. Напишіть статичний клас DateTimeExtensions з методом розширення для DateTime, що визначає, чи є дата вихідним днем (IsWeekend()). Покажіть виклик.

  6. Поясніть: якщо методи розширення для типу визначені в просторі імен MyApp.Extensions, а у файлі використовується using MyApp, чи будуть вони доступні? Що потрібно вказати?

  7. LINQ-методи (Where, Select, OrderBy) реалізовані як методи розширення. Поясніть: для якого типу вони є розширеннями? Що це означає для будь-якого класу, що реалізує IEnumerable<T>?

  8. Порівняйте методи розширення і статичні методи-утиліти (helper methods): обидва досягають схожого результату. Яка головна перевага методів розширення з точки зору читабельності та стилю fluent API?


8.7. Часткові класи та методи

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

  2. Назвіть три практичних сценарії використання часткових класів. Який із них є найважливішим у промисловій розробці (підказка: генератори коду)?

  3. Які правила сумісності повинні виконуватися для всіх частин одного partial типу? Що станеться, якщо одна частина оголошує public partial class Patient, а інша — internal partial class Patient?

  4. Що таке частковий метод (partial method)? Яку роль відіграє частина з оголошенням і яку — частина з реалізацією? Що відбувається з викликом часткового методу, якщо реалізація відсутня?

  5. Визначте обмеження базових часткових методів (C# 3). Чому кожне з обмежень пов'язане з можливістю компілятора «видалити виклик»: відсутність public, тільки void, заборона out?

  6. Знайдіть помилку у наступному коді:

    public partial class Report
    {
        partial string GenerateSummary(); // C# 3 стиль
    }
    public partial class Report
    {
        partial string GenerateSummary() => "Summary text";
    }
  7. Чим розширені часткові методи (C# 9) відрізняються від базових? Які нові можливості вони надають і яку обов'язкову умову вводять натомість?

  8. Спроєктуйте частковий клас PatientRecord у двох частинах: перша зберігає базові дані (ім'я, вік, id) і оголошує partial void OnDataUpdated(string field), друга — медичні дані (діагноз, алергії) і надає реалізацію OnDataUpdated, що логує зміну в консоль.


8.8. Анонімні типи

  1. Що таке анонімний тип у C#? Де і навіщо він використовується? Який синтаксис для його визначення? Чому обов'язково потрібне var?

  2. Що компілятор генерує для анонімного типу «під капотом»: клас, модифікатор, властивості, методи? Чому ім'я генерованого класу містить символи <>, і що це практично означає?

  3. Чому властивості анонімних типів є readonly (тільки для читання)? Що відбудеться при спробі змінити значення після ініціалізації? Яку альтернативу слід використовувати, якщо потрібна мутабельність?

  4. Що виведе наступний код і чому? Поясніть механізм структурної рівності анонімних типів:

    var a = new { Name = "Іван", Age = 45 };
    var b = new { Name = "Іван", Age = 45 };
    var c = new { Age = 45, Name = "Іван" }; // інший порядок!
    Console.WriteLine(a.Equals(b));
    Console.WriteLine(a.Equals(c));
    Console.WriteLine(a.GetType() == b.GetType());
    Console.WriteLine(a.GetType() == c.GetType());
  5. Поясніть «ініціалізатор з проекцією» (projection initializer): що означає new { diagnosis, roomNumber } замість new { Diagnosis = diagnosis, RoomNumber = roomNumber }? Звідки беруться імена властивостей?

  6. Чому анонімний тип не можна передати як параметр методу або повернути як результат методу зі збереженням типізації? Які два «обходи» існують і які їхні недоліки?

  7. Де анонімні типи є незамінними? Поясніть їх роль у LINQ-запитах, зокрема в операторі select. Чому саме тут анонімний тип краще, ніж повноцінний клас?

  8. Порівняйте анонімний тип, кортеж і record: коли кожен з них є правильним вибором? Складіть таблицю або опишіть по 1–2 конкретних сценарії для кожного.


8.9. Кортежі

  1. Яку проблему вирішують кортежі у C#? Які альтернативи існували до C# 7.0 і чим вони незручні? Наведіть типовий сценарій, де кортеж є найлаконічнішим рішенням.

  2. Поясніть принципову різницю між System.Tuple<T1, T2> і System.ValueTuple<T1, T2>: де розміщується в пам'яті кожен, яка рівність, чи є мутабельність, як виглядає синтаксис?

  3. Що таке «іменовані поля» кортежу? Чи існують вони у скомпільованому IL-коді? Що це означає при передачі кортежу в зовнішню бібліотеку або збереженні у object?

  4. Що виведе наступний код? Поясніть, як відбувається структурна рівність і чому назви полів не враховуються:

    var a = (Name: "Іван", Age: 45);
    var b = (PatientName: "Іван", Years: 45);
    Console.WriteLine(a == b);
  5. Що таке декомпозиція кортежу? Що означає discard (_)? Напишіть метод, що повертає (double Min, double Max, double Average), і покажіть два варіанти його виклику: зі збереженням усіх значень і з відкиданням непотрібного.

  6. Порівняйте out-параметри і кортежі для повернення кількох значень. Який підхід є кращим у сучасному C# і чому?

  7. Кортежі є мутабельними (на відміну від анонімних типів). Покажіть трюк обміну двох змінних через кортеж (a, b) = (b, a). Чому цей запис є лаконічнішим за традиційний обмін через тимчасову змінну?

  8. Спроєктуйте метод AnalyzeBloodPressure(int[] systolicReadings), що повертає іменований кортеж (int Min, int Max, double Average, bool HasHypertension). Покажіть виклик методу з декомпозицією та відкиданням непотрібного поля.


8.10. Records

  1. Що таке record у C# 9? Яку проблему він вирішує порівняно зі звичайним класом? Назвіть три–чотири члени, які компілятор генерує автоматично для record.

  2. Порівняйте рівність для record і для звичайного класу. Що виведе наступний код і чому?

    public record Patient(string Name, int Age);
    public class Doctor { public string Name { get; } public int Age { get; }
                          public Doctor(string n, int a) { Name = n; Age = a; } }
    var p1 = new Patient("Іван", 45); var p2 = new Patient("Іван", 45);
    var d1 = new Doctor("Іван", 45);  var d2 = new Doctor("Іван", 45);
    Console.WriteLine(p1 == p2);
    Console.WriteLine(d1 == d2);
  3. Що таке модифікатор init? Чим він відрізняється від set і від readonly-поля? Які два місця у коді дозволяють встановити init-властивість?

  4. Поясніть оператор with. Що він робить? Чому він важливий для незмінних об'єктів? Що відбувається з оригінальним об'єктом після var updated = original with { Age = 46 }?

  5. Є ієрархія records: MedicalPerson(Name, Age) і ClinicPatient(Name, Age, Diagnosis) : MedicalPerson. Чому new MedicalPerson("Іван", 45).Equals(new ClinicPatient("Іван", 45, "Норма")) поверне false, хоча поля Name і Age однакові? Що таке EqualityContract?

  6. Порівняйте позиційний і повний синтаксис record. Яка різниця між ними? Коли потрібно обирати повний синтаксис? Покажіть, як до позиційного record додати обчислювану властивість і звичайну мутабельну властивість.

  7. Поясніть різницю між record class, record struct і readonly record struct. Де розміщується кожен у пам'яті? Яка різниця у мутабельності позиційних властивостей між record struct і record class?

  8. Спроєктуйте record DiagnosisRecord(string Code, string Description, DateTime Date) для медичної системи. Покажіть: структурну рівність двох однакових записів, операцію with для оновлення дати, декомпозицію через var (code, desc, date) = record, та автоматичний ToString.

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