2016-07-07 7 views
6

См. Правки ниже для воспроизведения поведения, которое я описываю в этой проблеме.Как правильно выполнить метод возврата исключений внутри метода возврата в C#

Следующая программа никогда не закончится, поскольку конструкция yield return в C# вызывает метод GetStrings() неопределенно, когда генерируется исключение.

class Program 
{ 
    static void Main(string[] args) 
    { 
     // I expect the Exception to be thrown here, but it's not 
     foreach (var str in GetStrings()) 
     { 
      Console.WriteLine(str); 
     } 
    } 

    private static IEnumerable<string> GetStrings() 
    { 
     // REPEATEDLY throws this exception 
     throw new Exception(); 
     yield break; 
    } 
} 

Для этого простейшего примера, я мог бы, очевидно, использовать return Enumerable.Empty<string>(); вместо этого, и проблема уходит. Однако в более интересном примере я ожидал бы, что исключение будет выброшено один раз, а затем метод перестанет быть вызванным и выкинет исключение в методе, который «потребляет» IEnumerable.

Есть ли способ произвести такое поведение?

EDIT: хорошо, проблема отличается от того, что я сначала подумал. Программа выше не заканчивается, и цикл foreach ведет себя как бесконечный цикл. Программа ниже завершается, и исключение отображается на консоли.

class Program 
{ 
    static void Main(string[] args) 
    { 
     try 
     { 
      foreach (var str in GetStrings()) 
      { 
       Console.WriteLine(str); 
      } 
     } 
     catch (Exception e) 
     { 
      Console.WriteLine(e); 
     } 
    } 

    private static IEnumerable<string> GetStrings() 
    { 
     throw new Exception(); 
     yield break; 
    } 
} 

Почему try ... catch блок сделать разницу в этом случае? Мне это очень странно. Спасибо @ AndrewKilburn за его ответ уже за то, что он указал мне на это.

EDIT # 2: В командной строке программа выполняет одно и то же в обоих случаях. В Visual Studio Enterprise 2015, обновление 2, будь то компиляция в Debug или Release, поведение выше - это то, что я вижу. С try ... catch программа заканчивается исключением, и без нее Visual Studio никогда не закрывает программу.

EDIT # 3: Исправлено Для меня проблема была решена путем ответа @MartinBrown. Когда я отключу опцию Visual Studio в разделе «Отладка»> «Параметры»> «Отладка»> «Общие»> «Отвяжите стек вызовов при необработанных исключениях», эта проблема исчезнет. Когда я снова поставлю флажок, проблема вернется.

+0

Я попробовал, и он бросает только один раз. В случае обертывания 'foreach' с исключением' try catch' был пойман только один раз, и программа завершает работу. Правильно ли я это понял? – Szer

+0

Хммм, я не вижу этого поведения. Позвольте мне снова проверить, чтобы быть уверенным. Поведение, которое я вижу, это то, что исключение выбрано, и оно каким-то образом зацепилось за кулисами, а цикл 'foreach' ведет себя как бесконечный цикл. –

+0

@Szer См. Мое редактирование, блоки 'try' ...' catch', похоже, имеют значение в том, работает ли цикл foreach как бесконечный цикл. Это имеет какое-то значение для вас? –

ответ

5

Поведение, наблюдаемое здесь, не является ошибкой в ​​коде; скорее это побочный эффект отладчика Visual Studio. Это можно устранить, отключив стирание стека в Visual Studio. Попробуйте войти в опции Visual Studio. Отладка/Общие и снятие флажка «Разморозить стек вызовов на необработанных исключениях». Затем запустите код еще раз.

Что происходит, когда ваш код попадает в совершенно необработанное исключение. Visual Studio отказывается от стека вызовов непосредственно перед строкой вашего кода, вызвавшей исключение. Он делает это так, что вы можете редактировать код и продолжать выполнение с отредактированным кодом.

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

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

+0

Это исправило проблему. Любое объяснение, почему? –

+0

К сожалению, мое знание того, что на самом деле делает, немного ограничено. Дайте мне знать, если вы когда-нибудь это выясните. –

+0

@JohnCarpenter [Здесь] (https://blogs.msdn.microsoft.com/saraford/2008/08/08/did-you-know-what-unwinding-the-call-stack-on-unhandled-exceptions-does -277 /) описывается функциональность. (Вы можете найти описание в комментариях) –

0
class Program { 
     static void Main(string[] args) { 
      try { 
       foreach (var item in GetStrings()) { 
        Console.WriteLine(); 
       } 
      } 
      catch (Exception ex) { 

     } 
    } 
    private static IEnumerable<string> GetStrings() { 
     // REPEATEDLY throws this exception 
     throw new Exception(); 
     yield break; 
    } 
} 

Положив его в попытке поймать его причины, чтобы выйти и делать все, что вы хотите

+0

К сожалению, это не сработает, потому что исключение вообще не бросается в метод 'Main'. –

+0

@JohnCarpenter Прекрасно работает для меня .. –

+0

Это интересно ... ставить попытку поймать делает поведение программы по-разному. Без 'try' ...' catch' программа для вас заканчивается? –

0

Следующая программа никогда не закончится

Это ложь. Программа скоро закончится.

потому что конструкция возврата доходности в C# вызывает метод GetStrings() неопределенно, когда генерируется исключение.

Это неверно. Это не делает этого вообще.

Я бы ожидал, что исключение будет выброшено один раз, а затем метод перестанет быть вызванным и выдаст исключение в методе, который «потребляет» IEnumerable.

Именно это и произошло .

Есть ли способ произвести такое поведение?

Используйте код, который вы уже указали.

+0

Ну, это оказалось не так. По-видимому, поведение в Visual Studio было иным. Или, по крайней мере, оказалось другим. :) –

0
class Program 
{ 
    public static int EnumerableCount; 

    static void Main(string[] args) 
    { 
     EnumerableCount = 0; 
     try 
     { 
      foreach (var str in GetStrings()) 
      { 
       Console.WriteLine(str); 
       Console.Read(); 
      } 
     } 
     catch (Exception e) 
     { 
      Console.WriteLine(e); 
      Console.Read(); 
     } 
    } 

    private static IEnumerable<string> GetStrings() 
    { 
     EnumerableCount++; 
     var errorMessage = string.Format("EnumerableCount: {0}", EnumerableCount); 
     throw new Exception(errorMessage); 
     yield break; 
    } 
} 

имеет следующий вывод:

System.Exception: EnumerableCount: 1 
    at {insert stack trace here} 

поток выполнения переходит в GetStrings() Метод для первой итерации, исключение брошено и поймано в методе Main(). После входа в программу программа выйдет.

Удаление попытки try в методе Main() приводит к тому, что исключение не обрабатывается. Выходной сигнал затем:

Unhandled Exception: System.Exception: EnumerableCount: 1 
    at {insert stack trace here} 

и программа аварий.