четверг, апреля 12, 2007

Охота за исключениями

(с иллюстрациями и поеданием выловленных трофеев)


Недавно встретил на удивление бессмысленный код:


try
{
//что то там делаем
}
catch
{
throw;
}


Эта конструкция try - catch не делает ровным счетом ничего. Я засомневался и решил проверить, для чего написал такой код:


class Class1
{
[STAThread]
static void Main(string[] args)
{
try
{
CatchException();
}
catch(Exception ex)
{
Console.WriteLine(ex.ToString());
}
Console.ReadLine();
}
// здесь будет exception
public static void RiseException()
{
throw new MyException("Deep exeption");
}
// это просто обертка
public static void Wrap()
{
RiseException();
}
// здесь ловим исключение
public static void CatchException()
{
try
{
Wrap();
}
catch
{
throw;
}
}
}

// это наше собственное исключение
public class MyException : ApplicationException
{
public MyException(string message):base(message){}
}


В методе RiseException мы генерируем наше собственное исключение MyException. В методе CatchException мы воспроизводим эту странную конструкцию try-catch, а в методе Main наблюдаем что из этого всего вышло.
Итак на выходе данной программы мы имеем:


Catch.MyException: Deep exeption
at Catch.Class1.RiseException() in c:\temp\solutions\catch\class1.cs:line 24
at Catch.Class1.Wrap() in c:\temp\solutions\catch\class1.cs:line 30
at Catch.Class1.CatchException() in c:\temp\solutions\catch\class1.cs:line 42

at Catch.Class1.Main(String[] args) in c:\temp\solutions\catch\class1.cs:line
12


Что мы видим? Информация о типе исключения сохранилась. Сообщение на месте. Информация о меcте возникновения исключения и стеке вызовов тоже есть. Для чистоты эксперимента можно вовсе убрать блок try - catch в методе CatchException. Ничего не изменится.
Итак мы убедились что данный код абсолютно бессмысленный. Но в тоже время и вреда от него никакого. Давайте посмотрим на менее безобидные варианты.
Вариант два - "типа обработали исключение"


public static void CatchException()
{
try
{
Wrap();
}
catch(Exception ex)
{
throw ex;
}
}


Выловленное исключение тут же отправляем гулять далее. Посмотрим на результат


Catch.MyException: Deep exeption
at Catch.Class1.CatchException() in c:\temp\solutions\catch\class1.cs:line 42

at Catch.Class1.Main(String[] args) in c:\temp\solutions\catch\class1.cs:line
12


Что мы видим? Мы видим что MyException возник в методе CatchException, строка 42. Т.е. информация о реальном месте возникновения исключения утеряна. Если вы уходите из пректа и хотите насолить своему приемнику, это вариант специально для вас. Вывод. Если вы хотите после обработки исключения отправить гулять его дальше, используйте голый throw; вместо throw exception;
А вот еще одна часто встречающаяся вариация на эту тему:


public static void CatchException()
{
try
{
Wrap();
}
catch(Exception)
{
throw new ApplicationException("Someting wrong in CatchException");
}
}


Этот вариант позволяет еще сильнее запутать того, кто будет разбираться с возникшем исключением. Информация о первоначальной причине и месте исключения теряется полностью.
В этом дурно пахнущем коде достаточно изменить несколько символов, чтобы всем стало хорошо.


public static void CatchException()
{
try
{
Wrap();
}
catch(Exception ex)
{
throw new ApplicationException("Someting wrong in CatchException", ex);
}
}


Всего то делов, добавили в создаваемое исключение ссылку на исходное. Результат разительно отличается в лучшую строну.


System.ApplicationException: Someting wrong in CatchException ---> Catch.MyExcep
tion: Deep exeption
at Catch.Class1.RiseException() in c:\temp\solutions\catch\class1.cs:line 24
at Catch.Class1.Wrap() in c:\temp\solutions\catch\class1.cs:line 30
at Catch.Class1.CatchException() in c:\temp\solutions\catch\class1.cs:line 38

--- End of inner exception stack trace ---
at Catch.Class1.CatchException() in c:\temp\solutions\catch\class1.cs:line 42

at Catch.Class1.Main(String[] args) in c:\temp\solutions\catch\class1.cs:line
12


Просто замечательно, видно все. Сначала был MyException в методе RiseException, а потом его обернули в ApplicationException в методе CatchException.
В общем, все это элементарные вещи, может и не стоило писать все это. Но сколько не смотрю кода, кругом одни и те-же грабли разложены. Поэтому и пишу, может кому то поможет.

3 комментария:

2man комментирует...

а просто catch {} - вовсе не бессмысленно, просто если знаешь, что может быть исключение, а обрабатывать "в лом" :)

Анонимный комментирует...

Серега, это не бессмысленная конструкция. Это защита от luring attack
Правда там еще надо добавить кода в блок catch

Sergey Rozovik комментирует...

> Правда там еще надо добавить кода в блок catch

Ну если кода в catch добавить, то вероятно будет уже не такая бессмысленная конструкция.
Правда, я не представляю что за код туда добавить надо для защиты от luring attack. Если я не ошибаюсь для этих целей в .Net используется CAS.