Модификаторы доступа, static, sealed, partial
↑ 1.4 ООП в C♯ · ← Предыдущая · → Следующая
Зачем это на собесе
Модификаторы задают границы инкапсуляции и наследования. Спросят, чем
protected internalотличается отprivate protected, что такоеstatic-класс, зачемsealed, для чегоpartialи как это связано с кодогенерацией.
Объяснение
Модификаторы доступа
| Модификатор | Кто видит |
|---|---|
public | все, в том числе другие сборки |
internal | только своя сборка (по умолчанию для типов верхнего уровня) |
protected | сам класс и наследники (в любой сборке) |
private | только сам тип (по умолчанию для членов класса) |
protected internal | наследники или любой код своей сборки (объединение) |
private protected | наследники и только внутри своей сборки (пересечение, C# 7.2) |
file (C# 11) | только внутри одного файла (типы-хелперы, кодогенерация) |
public class Patient
{
private int _age; // по умолчанию для полей и методов
protected virtual void Validate() { }
internal string Snils { get; set; } = ""; // доступно в своей сборке (и через InternalsVisibleTo)
}- Для типов верхнего уровня допустимы только
publicиinternal. - Наследник не может быть доступнее базового класса, метод не может возвращать менее доступный тип (иначе CS0050/CS0051).
- InternalsVisibleTo открывает
internalконкретной сборке — обычно проекту тестов.
static
- Статический класс: нельзя создать экземпляр и наследовать; только статические члены. Для утилит и extension-методов.
- Статический член принадлежит типу, а не экземпляру. Статические поля живут всё время работы приложения (по домену приложения).
- Статический конструктор выполняется один раз до первого использования типа.
public static class DateExtensions
{
public static bool IsWeekend(this DateOnly d) => d.DayOfWeek is DayOfWeek.Saturday or DayOfWeek.Sunday;
}staticлокальные функции и лямбды не захватывают внешние переменные — дешевле и без скрытых аллокаций.
sealed
Запрещает наследование класса или дальнейшее переопределение метода.
public sealed class ReportRenderer { } // от него нельзя наследоваться
public class Base { public virtual void Run() { } }
public class Mid : Base { public sealed override void Run() { } } // дальше переопределять нельзяЗачем: явно ограничить контракт расширения, безопасность, небольшая оптимизация (JIT может девиртуализировать вызовы). Хорошая практика: классы по умолчанию sealed, пока наследование не спроектировано.
partial
Разбивает определение типа (или метода) на несколько файлов. Все части компилируются как один тип.
// Patient.cs
public partial class Patient { public string Name { get; set; } = ""; }
// Patient.Validation.cs
public partial class Patient { public bool IsValid() => !string.IsNullOrWhiteSpace(Name); }Где используется: сгенерированный код (Designer-файлы, gRPC/Protobuf, Source Generators, [GeneratedRegex], LoggerMessage) отделяется от рукописного. partial-методы (C# 9+) объявляются в одной части, а реализуются в другой.
readonly, const, abstract, virtual — рядом
readonly— поле присваивается только в конструкторе (см. тему про константы).abstract— без реализации, только в абстрактном классе.virtual— можно переопределить в наследнике.override,new— переопределение и сокрытие (см. тему про наследование).
Нюансы и подводные камни
protected internalчасто путают сprivate protected: первый расширяет доступ (ИЛИ), второй сужает (И).- Неявные модификаторы: без указания у члена класса —
private, у типа верхнего уровня —internal, у члена интерфейса —public. - Публичные
staticизменяемые поля — глобальное состояние: гонки данных и трудность тестирования. Статическое состояние допустимо только для неизменяемых данных или потокобезопасных кэшей. partialне «наследование»: это один тип. Модификаторы (public,sealed, базовые типы) должны согласовываться между частями.- Статические классы нельзя подменить в тесте: логику с побочными эффектами лучше выносить в сервис за интерфейсом.
Вопросы с ответами
В чём разница между
protected internalиprivate protected?
protected internal— доступ из наследников (в любой сборке) или из своей сборки.private protected— только из наследников внутри той же сборки: пересечение условий.
Какой модификатор доступа по умолчанию у члена класса и у класса верхнего уровня?
У члена класса —
private, у типа верхнего уровня —internal.
Зачем делать класс
sealed?Чтобы запретить наследование: явный контракт, защита от хрупкого базового класса и небольшая выгода для производительности за счёт девиртуализации.
Что такое
partialи зачем он нужен?Разделение одного типа на несколько файлов. Основное применение — отделить сгенерированный код от рукописного (Source Generators, gRPC, designer).
Можно ли создать экземпляр static-класса и унаследоваться от него?
Нет ни того, ни другого. Static-класс содержит только статические члены.
Связанные темы
- Предыдущая: Классы, конструкторы, свойства, индексаторы · Следующая: Наследование, virtual, override, new
- Extension-методы: 2.1.6 Extension methods и fluent API
- Source Generators: 2.1.10 Source generators и Roslyn (как в analytics_generator)
- InternalsVisibleTo в тестах: 2.4 Тестирование в .NET — основы