Partial-класс (разделяемый, частичный класс) в C# — это класс, определение которого разбито на несколько объявлений с ключевым словом partial, обычно в разных файлах. При компиляции компилятор собирает все части в один тип: для программы это обычный класс, «частей» в собранной сборке нет.
Содержание
Важно различать два уровня. Partial-тип — класс, структура, интерфейс или record, разнесенные по частям. Partial-член — метод, свойство, конструктор или событие, у которого объявление лежит в одной части, а реализация в другой. Ниже — минимальный пример, правила объединения, частичные члены и типичные ошибки. Все примеры проверены 23.09.2026 на .NET SDK 10.0.401 (C# 14).
Минимальный пример: один класс в двух файлах
Проект из трех файлов. Первая часть класса хранит данные, вторая — поведение.
// Record.Data.cs
namespace HeightWidthInfo;
public partial class Record
{
private readonly int _height;
private readonly int _width;
public Record(int height, int width)
{
_height = height;
_width = width;
}
}
// Record.Print.cs
namespace HeightWidthInfo;
public partial class Record
{
public void Print()
{
Console.WriteLine($"Высота: {_height}");
Console.WriteLine($"Ширина: {_width}");
}
}
// Program.cs
using HeightWidthInfo;
var rec = new Record(10, 15);
rec.Print();
dotnet run печатает:
Высота: 10
Ширина: 15
Метод Print из второго файла свободно читает приватные поля из первого: после сборки это один класс, а private ограничивает доступ пределами типа, а не файла. Мелкая ловушка из прогона: переменную в top-level коде не стоит называть record — строка record.Print(); разбирается как объявление record-типа, и сборка падает с CS1001: Identifier expected.
Правила объединения частей
Компилятор склеивает части по полному имени типа (пространство имен + имя) в пределах одной сборки. Части в разных проектах или в разных пространствах имен — это уже разные классы, и ошибка проявится как «имя не найдено» (CS0103) при обращении к члену другой части. Остальные правила — в таблице, ошибки взяты из реального прогона.
| Ситуация | Что делает компилятор |
|---|---|
У одной из частей нет partial |
CS0260: Missing partial modifier on declaration of type 'Order' |
Части объявлены public и internal |
CS0262: ... have conflicting accessibility modifiers |
| У части модификатор доступа не указан | Разрешено: берется из других частей (public у одной части делает публичным весь класс) |
| В частях разные базовые классы | CS0263: ... must not specify different base classes |
| Базовый класс указан только в одной части | Весь тип наследует его |
| Интерфейсы и атрибуты в разных частях | Объединяются |
abstract или sealed хотя бы в одной части |
Действует на весь тип: new Order() дает CS0144 |
partial enum или partial delegate |
CS0267: partial допустим только перед class, record, struct, interface и частичными членами |
Упрощенная мнемоника «части — это просто куски одного файла» верна для членов и модификаторов, но не для директив using: они действуют только в своем файле, поэтому каждая часть подключает нужные пространства имен сама (если не используются глобальные using).
Частичные методы: классические и расширенные
Классический частичный метод — «крючок»: одна часть объявляет сигнатуру, другая может дать реализацию, а может и не дать. Если реализации нет, компилятор удаляет и объявление, и все вызовы — вместе с вычислением аргументов.
var o = new Order(42);
Console.WriteLine($"Заказ {o.Id} создан");
partial class Order
{
public int Id { get; }
public Order(int id)
{
Id = id;
OnCreated(Describe()); // вызов частичного метода
}
// Объявление без реализации
partial void OnCreated(string info);
private string Describe()
{
Console.WriteLine("Describe() вызван");
return $"id={Id}";
}
}
Вывод — одна строка Заказ 42 создан: Describe() не вызвался, потому что вызов OnCreated удален целиком. Если добавить вторую часть с реализацией, поведение меняется:
partial class Order
{
partial void OnCreated(string info) => Console.WriteLine($"OnCreated: {info}");
}
Describe() вызван
OnCreated: id=42
Заказ 42 создан
Такие методы неявно private, возвращают void и не имеют out-параметров. С C# 9 есть расширенные частичные методы: с модификатором доступа, возвращаемым значением или out-параметрами. Для них реализация обязательна, иначе ошибка:
error CS8795: Partial method 'Order.Validate()' must have an implementation part because it has accessibility modifiers.
Расширенная форма нужна не для «необязательных крючков», а для связки «человек объявляет — генератор реализует».
Partial-свойства, конструкторы и события (C# 13 и 14)
Частичные свойства и индексаторы появились в C# 13, частичные конструкторы экземпляра и события — в C# 14. Схема та же: одна часть объявляет член, другая обязательно реализует.
using System.Text.RegularExpressions;
var c = new Customer(" Anna ");
Console.WriteLine($"Name = '{c.Name}'");
Console.WriteLine(Validators.Email.IsMatch("user@example.com"));
public partial class Customer
{
public partial string Name { get; set; } // C# 13
public partial Customer(string name); // C# 14
}
public partial class Customer
{
private string _name = "";
public partial string Name
{
get => _name;
set => _name = value.Trim();
}
public partial Customer(string name) => Name = name;
}
public static partial class Validators
{
// Реализацию свойства пишет генератор исходного кода
[GeneratedRegex(@"^[^@\s]+@[^@\s]+\.[^@\s]+$")]
public static partial Regex Email { get; }
}
Name = 'Anna'
True
[GeneratedRegex] — типичный сценарий: разработчик пишет только объявление, а генератор исходного кода добавляет вторую часть класса с реализацией на этапе компиляции. На C# 13 (<LangVersion>13</LangVersion>) этот же файл не собирается, среди ошибок — CS9260: Feature 'partial events and constructors' is not available in C# 13.0. Регулярное выражение здесь учебное, для полноценной проверки email его недостаточно.
Где partial встречается на практике
Главный сценарий — отделить код, который пишет инструмент, от кода, который пишет человек. Тогда повторная генерация не затирает ручные правки.
| Задача | Как используется partial | Когда иначе |
|---|---|---|
| Формы WinForms | Дизайнер пишет Form1.Designer.cs с InitializeComponent(), обработчики — в Form1.cs |
Designer-файл руками не правят |
| WPF | Из XAML при сборке генерируется часть MainWindow.g.cs |
— |
| Blazor | Разметка в .razor, логика в .razor.cs (partial-класс) |
Можно держать код в блоке @code |
| Генераторы исходного кода | [GeneratedRegex], [LibraryImport], генераторы MVVM |
— |
| Большой класс «на двоих» | Можно разнести по файлам | Часто лучше разбить на несколько классов |
Про последнюю строку: partial не уменьшает ответственность класса, а только прячет ее по файлам. Если класс разросся до тысяч строк, обычно правильнее выделить отдельные классы. Partial также не заменяет наследование и методы расширения: дописать чужой класс из другой сборки через partial нельзя.
Если не компилируется
CS0260— у одной из частей пропущенpartial: добавьте его во все объявления.CS0103: The name '_id' does not exist in the current contextпри обращении к полю из другой части — проверьте, что namespace и имя класса совпадают буквально и файлы входят в один проект.CS8795— у расширенного partial-метода нет реализации: допишите ее или, если реализацию дает генератор, проверьте, что он подключен.CS8703илиCS9260про версию языка — проект собирается старой версией C#: проверьтеTargetFrameworkиLangVersionв.csproj.
Выводы
- Partial-класс — один тип, объявленный частями; компилятор объединяет их в пределах одной сборки и одного полного имени.
- Модификаторы доступа частей не должны конфликтовать, базовый класс — один;
abstract,sealed, интерфейсы и атрибуты распространяются на весь тип. - Классический partial-метод без реализации удаляется вместе с вызовами и вычислением аргументов; расширенный требует реализацию.
- Partial-свойства и индексаторы доступны с C# 13, конструкторы и события — с C# 14.
- Основная ценность partial — разделение сгенерированного и ручного кода, а не способ спрятать слишком большой класс.
Где применяется / связь с практикой
Освойте тему на практике
С partial-классами начинающий C#-разработчик сталкивается в первом же проекте на WinForms, WPF или Blazor, а в современных библиотеках — через генераторы исходного кода. Чтобы уверенно читать такой код и понимать, что делает компилятор, нужна база по типам, ООП и сборке проектов. Системно ее дает курс C# Developer. Basic, а отдельные темы по .NET удобно посмотреть на открытых уроках Otus.
FAQ
Можно ли сделать partial статический или generic-класс?
Да. У generic-класса все части должны объявлять одинаковые параметры типа с одинаковыми именами, а ограничения where, если указаны в нескольких частях, должны совпадать.
Влияет ли partial на производительность?
Нет. Разделение существует только в исходном коде; после компиляции получается обычный тип.
Можно ли объявить вложенный partial-класс?
Да, вложенный тип тоже может быть partial. Если части вложенного класса разнесены по файлам, внешний класс в каждом из них тоже объявляется как partial.



