← Назад к вопросам

В чём разница между throw и re-throw?

2.0 Middle🔥 111 комментариев
#Основы C# и .NET

Комментарии (1)

🐱
deepseek-v3.2PrepBro AI6 апр. 2026 г.(ред.)

Ответ сгенерирован нейросетью и может содержать ошибки

Разница между throw и re-throw в C#

В C# обработка исключений представляет собой важный механизм управления ошибками, и понимание различий между throw и re-throw (повторным выбросом) критически для написания надёжного и поддерживаемого кода.

Базовое определение

throw — это оператор, который инициирует исключение, прерывая нормальный поток выполнения программы и передавая управление ближайшему блоку catch.

Re-throw (повторный выброс) — это повторное возбуждение уже перехваченного исключения без создания нового объекта исключения, что позволяет сохранить исходную информацию об ошибке.

Ключевые различия

1. Сохранение стека вызовов

Наиболее важное различие заключается в сохранении исходной трассировки стека:

// Пример с throw (теряет стек вызовов)
try
{
    SomeMethod();
}
catch (Exception ex)
{
    // Создаётся НОВОЕ исключение, стек будет указывать на эту строку
    throw new Exception("Произошла ошибка", ex);
    // Исходный стек вызовов будет во внутреннем исключении (ex)
}

// Пример с re-throw (сохраняет стек вызовов)
try
{
    SomeMethod();
}
catch (Exception)
{
    // Повторно выбрасывается ТО ЖЕ САМОЕ исключение
    throw; // Стек сохраняется!
}

2. Поведение при пустом throw

С версии C# 7.0 появилась возможность использовать throw без параметров в блоке catch:

try
{
    await ProcessDataAsync();
}
catch (IOException ex)
{
    LogError(ex);
    throw; // Это re-throw - сохраняет полную информацию об исключении
}

3. Изменение информации об исключении

  • При throw ex; (что является антипаттерном) вы создаёте новую трассировку стека, начиная с точки повторного выброса
  • При throw; (re-throw) сохраняется оригинальная трассировка стека
  • При throw new Exception(...) создаётся совершенно новое исключение

Рекомендуемые сценарии использования

Когда использовать re-throw (throw;):

public void ProcessFile(string path)
{
    try
    {
        // Чтение и обработка файла
        var content = File.ReadAllText(path);
        TransformContent(content);
    }
    catch (FileNotFoundException)
    {
        // Логируем, но позволяем исключению всплывать выше
        _logger.LogWarning($"Файл {path} не найден");
        throw; // Сохраняем оригинальную информацию об исключении
    }
}

Когда использовать throw с новым исключением:

public User GetUser(int id)
{
    try
    {
        return _repository.GetUser(id);
    }
    catch (SqlException ex)
    {
        // Обёртываем низкоуровневое исключение в более осмысленное
        throw new DataAccessException(
            $"Не удалось получить пользователя с ID {id}", 
            ex); // Исходное исключение сохраняется как InnerException
    }
}

Важные нюансы

  1. Производительность: Re-throw более эффективен, так как не создаёт новый объект исключения
  2. Отладка: Re-throw сохраняет полную трассировку стека, что упрощает отладку
  3. Антипаттерн: Использование throw ex; внутри блока catch считается плохой практикой, так как теряется информация о первоначальной точке возникновения ошибки
  4. Асинхронный код: В асинхронных методах поведение сохраняется, но нужно быть внимательным с контекстом синхронизации

Практический пример

public class DataProcessor
{
    public void Process()
    {
        try
        {
            FirstStep();
            SecondStep();
        }
        catch (InvalidOperationException ex)
        {
            // Логируем детали
            _logger.LogError(ex, "Ошибка при обработке данных");
            
            // Re-throw для сохранения стека вызовов
            throw;
        }
    }
    
    private void FirstStep()
    {
        // Может вызвать InvalidOperationException
    }
    
    private void SecondStep()
    {
        // Может вызвать ArgumentException
    }
}

Заключение

Throw используется для создания и возбуждения новых исключений, тогда как re-throw предназначен для повторного выброса уже перехваченного исключения с сохранением всей оригинальной информации.

Правильное использование этих конструкций позволяет:

  • Сохранять полную диагностическую информацию для отладки
  • Создавать уровни абстракции исключений при необходимости
  • Реализовывать стратегию "логируй и пробрасывай" (log and re-throw)
  • Писать более чистый и поддерживаемый код обработки ошибок

Всегда предпочитайте throw; простому throw ex; внутри блоков catch, если не требуется оборачивать исключение в другой тип с дополнительной контекстной информацией.

В чём разница между throw и re-throw? | PrepBro