OOP Course
Сьогодні

Підрозділ 8.7

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

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

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

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

Навіщо потрібні часткові класи

Найважливіший практичний сценарій — генератори коду. Коли середовище розробки або фреймворк автоматично генерує частину класу (наприклад, WinForms-дизайнер генерує ініціалізацію UI-компонентів, а Entity Framework — опис таблиць бази даних), зручно тримати цей згенерований код окремо від коду, написаного вручну. При кожному оновленні інструмент перегенеровує «свій» файл, не торкаючись «вашого». Без partial це було б неможливо без постійних конфліктів злиття.

Другий сценарій — великі класи, в яких логічно виділити окремі «зони відповідальності»: базові властивості, медична інформація, допоміжні методи. Замість одного файлу на 2000 рядків ми розбиваємо клас на кілька файлів — кожен відповідає за свою ділянку. При цьому з точки зору мови це все ще один тип.

Третій сценарій — розподіл роботи в команді: різні розробники можуть паралельно додавати функціональність до одного класу у різних файлах, не отримуючи конфліктів.

Синтаксис і механізм компіляції

Для визначення часткового класу перед ключовим словом class (або struct, interface, record) додається модифікатор partial:

public partial class Patient { ... } // у першому файлі
public partial class Patient { ... } // у другому файлі

Слово partial — це виключно директива для компілятора, а не частина метаданих типу. У скомпільованому IL-коді існує один-єдиний тип Patient з усіма членами з усіх частин. Жодного накладу у виконанні немає.

Часткові класи: два визначення — один тип після компіляції

Правила сумісності часткових визначень. Всі частини одного partial типу повинні:

  • Перебувати в одному просторі імен (namespace).
  • Перебувати в одному assembly (проєкті).
  • Мати однакове ім'я і однакові загальні модифікатори доступу (public, internal тощо).
  • Якщо тип успадковує інший — базовий клас можна вказати лише в одному partial-визначенні, або в усіх, але однаково.

Якщо одна частина оголошує partial class Patient : Person, а інша просто partial class Patient — це допустимо. Але якщо дві частини вказують різні базові класи — помилка компіляції.

Що може бути partial

Модифікатор partial можна застосувати до:

Тип Синтаксис
Клас public partial class Patient { }
Структура public partial struct BloodPressure { }
Інтерфейс public partial interface IPatientRecord { }
Record (C# 9+) public partial record Patient(string Name, int Age);

Вкладені типи також можуть бути partial. Але методи, поля, властивості — ні; partial застосовується тільки до типів (і до окремої категорії часткових методів, про яку йдеться далі).

Клінічний приклад: розподіл класу Patient

У реальному проєкті ці два partial блоки знаходились би у різних файлах. У runnable-прикладі вони в одному файлі — C# це допускає, оскільки partial є лише підказкою компілятору про дозвіл на розподіл, а не вимогою.

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

Часткові класи можуть містити часткові методи (partial methods). Ідея полягає в тому, що одна частина класу оголошує сигнатуру методу (без тіла), а інша — надає реалізацію. Це корисно саме в контексті code generation: генератор може оголосити «точки розширення», а розробник заповнює їх реалізацією.

// Частина 1 (згенерована автоматично):
public partial class Patient
{
    partial void OnDiagnosisChanged(string newDiagnosis); // тільки оголошення

    public void SetDiagnosis(string diagnosis)
    {
        Diagnosis = diagnosis;
        OnDiagnosisChanged(diagnosis); // викликаємо — можливо порожній виклик
    }
}

// Частина 2 (написана вручну розробником):
public partial class Patient
{
    partial void OnDiagnosisChanged(string newDiagnosis)
    {
        Console.WriteLine($"Діагноз змінено: {newDiagnosis}");
        // тут можна надіслати сповіщення, зробити логування тощо
    }
}

Ключова особливість: якщо реалізація часткового методу відсутня, компілятор просто видаляє виклик OnDiagnosisChanged(diagnosis) з коду. Жодної помилки компіляції, жодних витрат на виконання — виклик зникає повністю. Це фундаментально відрізняє часткові методи від абстрактних: абстрактний метод без реалізації — помилка компіляції; частковий — тиха відмова.

Обмеження базових часткових методів

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

Обмеження Причина
Немає модифікатора доступу (за замовчуванням private) public метод без реалізації — порушення контракту типу
Тип результату — лише void Не можна видалити виклик, якщо код очікує повернуте значення
Немає out-параметрів Невизначені out після видаленого виклику — помилка
Немає virtual, override, sealed, new, extern Ці модифікатори вимагають реальної реалізації у типі

Розширені часткові методи (C# 9+)

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

// Оголошення з public і non-void:
public partial class PatientRecord
{
    public partial string GenerateSummary(); // C# 9 — реалізація обов'язкова
}

// Реалізація — ОБОВ'ЯЗКОВО в іншій частині:
public partial class PatientRecord
{
    public partial string GenerateSummary()
        => $"Пацієнт: {Name}, вік: {Age}, діагноз: {Diagnosis}";
}

Розширені часткові методи корисні у Source Generators (C# 10+), де генератор оголошує метод-«заглушку», а розробник надає конкретну реалізацію, яка може повертати значення і бути публічною.

Підсумок: базові vs розширені часткові методи

Характеристика Базовий partial (C# 3) Розширений partial (C# 9)
Модифікатор доступу Тільки private (implicit) Будь-який
Тип результату Тільки void Будь-який
out-параметри Заборонені Дозволені
Реалізація Необов'язкова Обов'язкова
Без реалізації Виклик видаляється Помилка компіляції
Розроблено Tomka Yurii · © 2026 ·