Обработка исключений в C # - PullRequest
0 голосов
/ 26 октября 2018

Я совершенно не понимаю, как возникают исключения в C #. Если возникает исключение, в блоке try 1. это брошено в блок захвата, 2. Если и только если блок catch его перехватит, будет выполнен блок finally. 3. Блок finally выполняется последним при условии, что оператор catch его перехватил.

Однако, когда я пытаюсь запустить программу ниже, вывод A, B не BA. Есть ли что-то не так с моим пониманием? Спасибо.

class Program
 {
     public static void Main(string[] args)
     {
         try
         {
             int a = 2;
             int b = 10 / a;
             try
             {
                 if (a == 1)
                     a = a / a - a;
                 if (a == 2)
                 {
                     int[] c = { 1 };
                     c[8] = 9;
                 }
             }
             finally
             {
                 Console.WriteLine("A");
             }
        }
        catch (IndexOutOfRangeException e)
        {
             Console.WriteLine("B");
        }
        Console.ReadLine();
    }
 }

Исключение возникает в == 2, и я знаю, что внешний улов поймает это исключение. Тем не менее, наконец выполняется в первую очередь? Любая причина, почему это показывает?

изм

Из документов C # мы знаем, наконец, блок выполняется независимо от того, произошло ли исключение.

Однако мой блок finally никогда не выполняется, а взамен я получаю ошибку времени выполнения

class Program
{
    public static void Main(string[] args)
    {
        try
        {
            int a = 2;
            int b = 10 / a;
            try
            {
                if (a == 1)
                    a = a / a - a;
                if (a == 2)
                {
                    int[] c = { 1 };
                    c[8] = 9;
                }
            }
            finally
            {
                Console.WriteLine("A");
            }
        }

        finally{
            Console.WriteLine("finally");
        }

        Console.ReadLine();
    }
}

Ответы [ 5 ]

0 голосов
/ 26 октября 2018

В вашем первом коде причина того, что finally (A) запускается до catch (B), заключается в том, что при возникновении исключения вы выходите из внутреннего блока (вызывая запуск finally) доcatch внешнего блока вступает в игру.Рассмотрим этот код:

private void TestTryCatchFinally()
{
    try
    {
        Debug.WriteLine("Start Outer Try");
        try
        {
            Debug.WriteLine("Start Inner Try");
            throw new Exception("Exception from inner try");
            Debug.WriteLine("End of Inner Try - never reaced");
        }
        //remove this catch block for second test
        catch (Exception)
        {
            Debug.WriteLine("In inner catch");
        }
        //end of code to remove
        finally
        {
            Debug.WriteLine("In Inner Finally");
        }
    }
    catch (Exception)
    {
        Debug.WriteLine("In outer catch");
    }
    finally
    {
        Debug.WriteLine("In outer finally");
    }
}

Если я запусту этот код, я получу следующий вывод:

Start Outer Try
Start Inner Try
Exception thrown: 'System.Exception' in MyTestApp.exe
In inner catch
In Inner Finally
In outer finally

Что вы и ожидаете.Но если я удаляю внутренний блок catch (как отмечено в коде), я получаю следующий вывод:

Start Outer Try
Start Inner Try
Exception thrown: 'System.Exception' in MyTestApp.exe
In Inner Finally
In outer catch
In outer finally

В этом случае, как только выполнение выходит из внутреннего блока try, выполняется код finally.Затем внешний блок захвата имеет свою очередь.

0 голосов
/ 26 октября 2018

Я могу что-то упустить. Разве вы не хотите следующее?

try
{          
}
catch (Exception ex)
{
}
finally
{  
}

Используя блок finally{}, вы можете быть уверены, что некоторые операторы всегда будут выполняться

0 голосов
/ 26 октября 2018

finally выполняется, когда элемент управления покидает блок try, к которому он присоединен по любой причине ; не только если бы был блок catch на том же уровне - это был бы обработчик fault (в терминах CLR), который, я думаю, мы все еще не можем сделать в C #.

Таким образом, он входит в finally, который печатает A, потому что контроль покидает этот блок try. Он покидает его из-за исключения, которое попадает во внешний блок catch. Однако это не меняет времени / порядка.


Обратите внимание, что есть некоторые странности, которые возможны, если исключение полностью не обнаружено в вашем коде. Обработка исключений происходит в два этапа. На первом этапе он пытается найти соответствующее предложение catch для исключения, которое может включать выполнение защитных предложений (when, C # 6 и более поздние версии). На втором этапе он разматывает стек и выполняет все предложения finally в соответствии с требованиями вложенности, прежде чем достигнет правильного уровня, на котором определено условие catch, которое будет обрабатывать исключение 1 .

Я считаю, что в некоторых средах, если на первом этапе не удается найти соответствующий обработчик исключений (предложение catch) на всех , вся управляемая среда может быть разрушена. При таких обстоятельствах пункты finally не выполняются.

Все, что требуется для стандарта, соответствующего CLR, описано в документе MS Partition I.pdf, который можно найти в ECMA C # и стандартах инфраструктуры общего языка . Раздел 12.4.2.5 гласит:

Когда возникает исключение, CLI ищет в массиве первый защищенный блок, который

  • Защищает область, включая указатель текущей инструкции и

  • Является ли блок обработчика захвата и

  • Чей фильтр хочет обработать исключение

Если совпадение не найдено в текущем методе, выполняется поиск вызывающего метода и т. Д. Если совпадений не найдено CLI выведет трассировку стека и прервет программу.

(Мой Акцент )


1 Иллюстрировано с использованием варианта Пример Flydog57 , а также локальные функции C # 7:

using System;

namespace PlayAreaCSCon
{
    internal class Program
    {
        static void Main(string[] args)
        {
            TestTryCatchFinally();
            Console.WriteLine("Complete");
            Console.ReadLine();
        }

        private static void TestTryCatchFinally()
        {
            try
            {
                Console.WriteLine("Start Outer Try");
                try
                {
                    Console.WriteLine("Start Inner Try");
                    throw new Exception("Exception from inner try");
                }
                finally
                {
                    Console.WriteLine("In Inner Finally");
                }
            }
            catch (Exception ex) when (GuardHelper(ex))
            {
                Console.WriteLine("In outer catch");
            }
            finally
            {
                Console.WriteLine("In outer finally");
            }

            bool GuardHelper(Exception ex)
            {
                Console.WriteLine("In outer guard");
                return true;
            }
        }
    }
}

Это печатает:

Start Outer Try
Start Inner Try
In outer guard
In Inner Finally
In outer catch
In outer finally
Complete

Иллюстрирование двухпроходного характера обработки исключений (при этом In outer guard печатается до In Inner Finally)

0 голосов
/ 26 октября 2018

Это должно выглядеть примерно так (зависит от того, что вы на самом деле пытаетесь сделать)

class Program
{
 public static void Main(string[] args)
 {
     try
     {
         int a = 2;
         int b = 10 / a;

        if (a == 1)
            a = a / a - a;
        if (a == 2)
        {
            int[] c = { 1 };
            c[8] = 9;
        }
    }
    catch (IndexOutOfRangeException e)
    {
         //Do something when something in try block throws error
         Console.WriteLine("B");
    }
    finally
    {
        //This code will ALWAYS execute
        //Even when there is no error, A will be written to console
        Console.WriteLine("A");
    }
    Console.ReadLine();
}
}
0 голосов
/ 26 октября 2018

Если у вас есть один блок try-catch-finally, это правда, что catch предшествует, наконец,.Но здесь блоки try-finally и try-catch запускаются в порядке от внутреннего к внешнему.Поэтому, наконец, запускается первым.

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...