Как в C# запретить наследование от класса?

Ответ

Чтобы полностью запретить наследование от класса, используется модификатор sealed.

Синтаксис:

public sealed class ApiResponse
{
    public string Data { get; set; }
    public int StatusCode { get; set; }
}

// Попытка наследования вызовет ошибку компиляции CS0509:
// 'DerivedResponse': cannot derive from sealed type 'ApiResponse'
public class DerivedResponse : ApiResponse { }

Зачем это нужно?

  1. Защита инвариантов и дизайна: Когда класс реализует специфичную логику, нарушаемую при наследовании (например, кэширование, синглтон, безопасность).
  2. Оптимизация производительности: Компилятор JIT может применять агрессивные оптимизации (девиртуализацию вызовов), зная, что метод не будет переопределен в производном классе.
  3. Контроль над иерархией: Явное указание, что класс является конечным в иерархии.

Частичное запрещение: Можно запретить переопределение отдельных методов в незапечатанном классе, используя sealed для метода.

public class PaymentProcessor
{
    // Базовый виртуальный метод
    public virtual void Validate() { /* общая логика */ }
}

public class CreditCardProcessor : PaymentProcessor
{
    // Переопределяем и ЗАПЕЧАТЫВАЕМ метод.
    // Дальнейшее переопределение в наследниках CreditCardProcessor будет невозможно.
    public sealed override void Validate()
    {
        base.Validate();
        // + специфичная проверка карты
    }
}

public class PremiumCardProcessor : CreditCardProcessor
{
    // Ошибка CS0239: нельзя переопределить запечатанный метод.
    // public override void Validate() { }
}

Ответ 18+ 🔞

Ну ты смотри, какой прикол! Вот есть у тебя класс, и ты такой: "Всё, ребята, это шедевр, идеально, трогать — только портить". Чтобы какой-нибудь умник не наследовался от него и не наделал в коде таких дел, что потом разгребать — овердохуища, нужно его запечатать. Кидаешь модификатор sealed и спишь спокойно.

Как это выглядит, блядь:

public sealed class ApiResponse
{
    public string Data { get; set; }
    public int StatusCode { get; set; }
}

// А вот тут уже попытка наследования вызовет такую ошибку компиляции, что мало не покажется:
// 'DerivedResponse': cannot derive from sealed type 'ApiResponse'
public class DerivedResponse : ApiResponse { }

А нахуя это вообще надо, спросишь?

  1. Чтобы дизайн не сломали, епта. Бывает класс с такой хитрой внутренней кухней (кэш там, синглтон, безопасность какая), что любое наследование — это гарантированный пиздец. Запечатал — и нет проблем.
  2. Чтобы быстрее работало, ядрёна вошь! Компилятор, видя sealed, понимает: "А, так тут переопределений точно не будет!" И начинает вытворять такие оптимизации, что производительность взлетает.
  3. Просто чтобы поставить точку. Чётко говоришь миру: "Эта ветка эволюции закончена, плодиться тут нечего".

А ещё есть фишка — можно не весь класс душить, а только отдельные методы, хитрая жопа! Допустим, базовый метод виртуальный, ты его переопределил, но дальше хочешь запретить мудохаться с ним.

public class PaymentProcessor
{
    // Ну, обычный виртуальный метод, можно переопределять
    public virtual void Validate() { /* общая логика */ }
}

public class CreditCardProcessor : PaymentProcessor
{
    // Переопределили, но ВОТ ТУТ СТАВИМ ПЛОМБУ.
    // Дальше никто в наследниках этот метод трогать не сможет.
    public sealed override void Validate()
    {
        base.Validate();
        // + своя специфичная проверка карты
    }
}

public class PremiumCardProcessor : CreditCardProcessor
{
    // А вот тут уже получишь по рукам: CS0239 — sealed метод переопределять низя!
    // public override void Validate() { }
}

Вот и весь секрет. Хочешь полный контроль — запечатывай класс. Хочешь точечную защиту — запечатывай критические методы. И никаких распиздяйств!