Как стать автором
Обновить

Комментарии 9

Всё достаточно просто: выражение throw; в C# теряет стек исключения (вернее, просто генерирует его заново).
throw; ничего не теряет. Вот если сделать throw e; где e — пойманное исключение, тогда да, стек заменится.
Вообще говоря, здесь я действительно промахнулся, т.к. был введён в заблуждение собственными примерами. Стек при throw; всё же немного модифицируется, но в нём меняется только номер строки в пределах той функции, где было поймано исключение.

Текст статьи исправлен.
Эм. Запустил вот такой код:

static string _originalStrace;
static void Throw ()
{
	throw new Exception ();
}
static void ThrowAndRethrow ()
{
	try {
		Throw ();
	} catch (Exception e) {
		_originalStrace = e.StackTrace;
		throw;
	}
}
public static void Main (string[] args)
{
	try {
		ThrowAndRethrow ();
	} catch (Exception e) {
		Console.WriteLine (e.StackTrace == _originalStrace);
	}
}

Условие e.StackTrace == _originalStrace выполняется, т. е. стектрейс никак не модифицируется
Вы удивитесь, но у меня он выводит False. VS2012, .NET4.5.
Занятно. На Mono true. Видимо, отличия реализаций CLR всё же сказываются даже на таком уровне. Причём .NET ещё и в _originalStackTrace забывает положить информацию о том, что вызов был из Main, в трейсе только два кадра стека.
Проверил, что именно там происходит. Вот два стека, которые генерируются в процессе работы вашей программы:

   at CSharpFilter.Program.Throw() in CSharpFilter\Program.cs:line 40 // throw new Exception();
   at CSharpFilter.Program.ThrowAndRethrow() in CSharpFilter\Program.cs:line 46 // Throw();

   at CSharpFilter.Program.Throw() in CSharpFilter\Program.cs:line 40 // throw new Exception();
   at CSharpFilter.Program.ThrowAndRethrow() in CSharpFilter\Program.cs:line 52 // throw;
   at CSharpFilter.Program.Main(String[] args) in CSharpFilter\Program.cs:line 59 // ThrowAndRethrow();

Помимо разной глубины стеков (ну, это уж точно артефакт несоответствия mono и .NET), видно, что строки действительно разные.
Один из разработчиков Microsoft Roslyn (@KirillOsenkov) недавно рассказывал, что они используют exception filters внутри тестов для одной интересной задачи — автоматического снятие минидампа процесса до непосредственно разворачивания стека при возникновении исключения.

Хотел написать такой же хелпер (если DynamicMethod позволяет делать BeginExceptFilterBlock, конечно, в чем я сомневаюсь), что-нибудь типа MiniDump.Execute(() => { ... }) в топ левеле и чтобы процесс сам себя дампил, да дампами не пользуюсь вообще (процессы слишком жирные).

Еще вроде в статье не упомянуто, что в случае возникновения исключения в filter-блоке, это равнозначно значению 0 на стеке по окончанию исполнения фильтра (то есть предикат фильтрации считается вернувшим false), а само исключение безвозвратно теряется.

В dotPeek не сделано ввиду исключительной редкости данной конструкции, постраюсь запилить на следующую версию ;)
Спасибо за пояснения. А вот вопрос исключений внутри фильтра я попробую на досуге изучить подробнее — тема интересная. Например, что будет, если в фильтре выпадет что-нибудь ужасное, типа StackOverflow или OutOfMemory? А если из нативного кода пробросить? Если где-то и искать баги рантайма, то именно в таких, редко возникающих кейсах.
throw теряет оригинальный стек — на .NET 2.0 — 3.5 подтверждаю.

PS ну где была эта статья, когда мы писали сложные синхронные приложения?..
Зарегистрируйтесь на Хабре, чтобы оставить комментарий

Публикации