Поиск блока catch при обработке исключений

Последнее обновление: 30.12.2021

Если код, который вызывает исключение, не размещен в блоке try или помещен в конструкцию try..catch, которая не содержит соответствующего блока catch для обработки возникшего исключения, то система производит поиск соответствующего обработчика исключения в стеке вызовов.

Например, рассмотрим следующую программу:

try
{
    TestClass.Method1();
}
catch (DivideByZeroException ex)
{
    Console.WriteLine($"Catch в Main : {ex.Message}");
}
finally
{
    Console.WriteLine("Блок finally в Main");
}
Console.WriteLine("Конец метода Main");

class TestClass
{
    public static void Method1()
    {
        try
        {
            Method2();
        }
        catch (IndexOutOfRangeException ex)
        {
            Console.WriteLine($"Catch в Method1 : {ex.Message}");
        }
        finally
        {
            Console.WriteLine("Блок finally в Method1");
        }
        Console.WriteLine("Конец метода Method1");
    }
    static void Method2()
    {
        try
        {
            int x = 8;
            int y = x / 0;
        }
        finally
        {
            Console.WriteLine("Блок finally в Method2");
        }
        Console.WriteLine("Конец метода Method2");
    }
}

В данном случае стек вызовов выглядит следующим образом: метод Main вызывает метод Method1, который, в свою очередь, вызывает метод Method2. И в методе Method2 генерируется исключение DivideByZeroException. Визуально стек вызовов можно представить следующим образом:

Поиск блока catch при обработке исключения в C#

Внизу стека метод Main, с которого началось выполнение, и на самом верху метод Method2.

Что будет происходить в данном случае при генерации исключения?

  1. Метод Main вызывает метод Method1, а тот вызывает метод Method2, в котором генерируется исключение DivideByZeroException.

  2. Система видит, что код, который вызывал исключение, помещен в конструкцию try..catch

    try
    {
    	int x = 8;
    	int y = x / 0;
    }
    finally
    {
    	Console.WriteLine("Блок finally в Method2");
    }
    

    Система ищет в этой конструкции блок catch, который обрабатывает исключение DivideByZeroException. Однако такого блока catch нет.

  3. Система опускается в стеке вызовов в метод Method1, который вызывал Method2. Здесь вызов Method2 помещен в конструкцию try..catch

    try
    {
    	Method2();
    }
    catch (IndexOutOfRangeException ex)
    {
    	Console.WriteLine($"Catch в Method1 : {ex.Message}");
    }
    finally
    {
    	Console.WriteLine("Блок finally в Method1");
    }
    

    Система также ищет в этой конструкции блок catch, который обрабатывает исключение DivideByZeroException. Однако здесь также подобный блок catch отсутствует.

  4. Система далее опускается в стеке вызовов в метод Main, который вызывал Method1. Здесь вызов Method1 помещен в конструкцию try..catch

    try
    {
    	TestClass.Method1();
    }
    catch (DivideByZeroException ex)
    {
    	Console.WriteLine($"Catch в Main : {ex.Message}");
    }
    finally
    {
    	Console.WriteLine("Блок finally в Main");
    }
    

    Система снова ищет в этой конструкции блок catch, который обрабатывает исключение DivideByZeroException. И в данном случае такой блок найден.

  5. Система наконец нашла нужный блок catch в методе Main, для обработки исключения, которое возникло в методе Method2 - то есть к начальному методу, где непосредственно возникло исключение. Но пока данный блок catch НЕ выполняется. Система поднимается обратно по стеку вызовов в самый верх в метод Method2 и выполняет в нем блок finally:

    finally
    {
    	Console.WriteLine("Блок finally в Method2");
    }
    
  6. Далее система возвращается по стеку вызовов вниз в метод Method1 и выполняет в нем блок finally:

    finally
    {
    	Console.WriteLine("Блок finally в Method1");
    }
    
  7. Затем система переходит по стеку вызовов вниз в метод Main и выполняет в нем найденный блок catch и последующий блок finally:

    catch (DivideByZeroException ex)
    {
    	Console.WriteLine($"Catch в Main : {ex.Message}");
    }
    finally
    {
    	Console.WriteLine("Блок finally в Main");
    }
    
  8. Далее выполняется код, который идет в методе Main после конструкции try..catch:

    Console.WriteLine("Конец метода Main");
    

    Стоит отметить, что код, который идет после конструкции try...catch в методах Method1 и Method2, не выполняется, потому что обработчик исключения найден именно в методе Main.

Консольный вывод программы:

Блок finally в Method2
Блок finally в Method1
Catch в Main: Attempted to divide by zero
Блок finally в Main
Конец метода Main
Помощь сайту
Юмани:
410011174743222
Перевод на карту
Номер карты:
4048415020898850