Следующее нормально:
try
{
Console.WriteLine("Before");
yield return 1;
Console.WriteLine("After");
}
finally
{
Console.WriteLine("Done");
}
finally
Блок работает , когда все , что закончил выполнение ( IEnumerator<T>
опоры , IDisposable
чтобы обеспечить способ обеспечить это даже тогда , когда перечисление прекращается до его завершения).
Но это не нормально:
try
{
Console.WriteLine("Before");
yield return 1; // error CS1626: Cannot yield a value in the body of a try block with a catch clause
Console.WriteLine("After");
}
catch (Exception e)
{
Console.WriteLine(e.Message);
}
Предположим (ради аргумента), что исключение вызвано тем или иным WriteLine
вызовом внутри блока try. В чем проблема с продолжением выполнения в catch
блоке?
Конечно, часть yield return (в настоящее время) не может ничего выбросить, но почему это должно мешать нам иметь закрывающую try
/ catch
для обработки исключений, созданных до или после yield return
?
Обновление: здесь есть интересный комментарий от Эрика Липперта - похоже, у них уже достаточно проблем с правильной реализацией поведения try / finally!
РЕДАКТИРОВАТЬ: страница MSDN с этой ошибкой: http://msdn.microsoft.com/en-us/library/cs1x15az.aspx . Однако это не объясняет почему.
источник
Ответы:
Я подозреваю, что это вопрос практичности, а не осуществимости. Я подозреваю , что есть очень, очень мало раз , когда это ограничение является на самом деле это вопрос , который не может работаться вокруг - но дополнительные сложности в компилятор будут весьма значительными.
Я уже сталкивался с некоторыми подобными вещами:
В каждом из этих случаев можно было бы получить немного больше свободы за счет дополнительной сложности компилятора. Команда сделала прагматичный выбор, за что я им аплодирую - я бы предпочел иметь немного более строгий язык с компилятором с точностью 99,9% (да, есть ошибки; я столкнулся с одной на SO буквально на днях), чем более гибкий язык, который не может правильно компилироваться.
РЕДАКТИРОВАТЬ: Вот псевдо-доказательство того, почему это возможно.
Считают, что:
Теперь трансформируем:
в (своего рода псевдокод):
Единственное дублирование заключается в настройке блоков try / catch - но это определенно может сделать компилятор.
Возможно, я что-то здесь пропустил - если да, дайте мне знать!
источник
using
иforeach
. Например:try{foreach (string s in c){yield return s;}}catch(Exception){}
yield
, на мой взгляд, это все еще сильно ограничивает полезность из-за спагетти-кода, который вам нужно написать, чтобы обойти это.yield
IMO - это далеко не серьезно .Все
yield
операторы в определении итератора преобразуются в состояние в конечном автомате, который эффективно используетswitch
оператор для продвижения состояний. Если бы он действительно сгенерировал код дляyield
операторов в try / catch, ему пришлось бы дублировать все вtry
блоке для каждогоyield
оператора, исключая все остальныеyield
операторы для этого блока. Это не всегда возможно, особенно если однаyield
инструкция зависит от более ранней.источник
Я бы предположил, что из-за того, как стек вызовов запускается / раскручивается, когда вы передаете return из перечислителя, становится невозможным для блока try / catch на самом деле «поймать» исключение. (поскольку блок возврата yield не находится в стеке, даже если он создал блок итерации)
Чтобы получить представление о том, о чем я говорю, настройте блок итератора и foreach с использованием этого итератора. Проверьте, как выглядит стек вызовов внутри блока foreach, а затем проверьте его внутри блока итератора try / finally.
источник
Я принимаю ответ НЕУБЕДИТЕЛЬНОГО СКИТА, пока кто-нибудь из Microsoft не придет и не обольет эту идею холодной водой. Но я не согласен с основополагающей частью - конечно, правильный компилятор важнее, чем полный, но компилятор C # уже очень умен в этом преобразовании для нас, насколько это возможно. Немного больше полноты в этом случае сделало бы язык более простым в использовании, обучении, объяснении, с меньшим количеством крайних случаев или ошибок. Так что я думаю, что это стоит дополнительных усилий. Несколько ребят из Редмонда пару недель чешут затылки, и в результате миллионы программистов в течение следующего десятилетия могут немного расслабиться.
(Я также питаю ужасное желание, чтобы был способ создать
yield return
исключение, которое было вставлено в конечный автомат «извне», кодом, управляющим итерацией. Но мои причины, по которым я этого хотел, довольно неясны.)На самом деле у меня есть один вопрос об ответе Джона, связанный с выдачей выражения yield return.
Очевидно, доходность 10 не так уж и плоха. Но это было бы плохо:
Так что не имеет смысла оценивать это в предыдущем блоке try / catch:
Следующей проблемой будут вложенные блоки try / catch и повторные исключения:
Но я уверен, что это возможно ...
источник