Чем отличается throw от throw ex при перебросе исключения? Почему throw ex сбрасывает stack trace и усложняет поиск первопричины?

C# / .NET Основы языка Junior

Что это: Оператор throw без операнда внутри catch перебрасывает текущее исключение с сохранением исходного stack trace. throw ex создаёт новое место возникновения исключения и перезаписывает StackTrace, скрывая реальную строку, где ошибка произошла впервые.

Почему важно: Это базовая тема диагностики: на собеседовании проверяют, умеет ли кандидат не ломать логирование и расследование инцидентов в production.

Под капотом: CLR хранит stack trace в поле _stackTraceString объекта Exception. throw в catch использует инструкцию rethrow, которая не сбрасывает контекст. throw ex фактически бросает тот же объект заново из текущего кадра, и Runtime обновляет точку возникновения.

Типичная ошибка: Разработчики пишут catch (Exception ex) { Logger.Log(ex); throw ex; } и теряют исходную строку кода в логах, тратя часы на поиск настоящего источника ошибки в верхнем слое обёртки.

try
{
    throw new InvalidOperationException('root');
}
catch (Exception ex)
{
    // throw ex; // ПЛОХО: stack trace начнётся здесь
    throw; // ХОРОШО: сохраняет исходный stack trace
}