2012-03-09 2 views
17

Почему мы не можем использовать возврат и доходность возврата в том же методе?Почему нельзя использовать «возврат» и «возврат дохода» в том же методе?

Например, у нас могут быть GetIntegers1 и GetIntegers2 ниже, но не GetIntegers3.

public IEnumerable<int> GetIntegers1() 
{ 
    return new[] { 4, 5, 6 }; 
} 

public IEnumerable<int> GetIntegers2() 
{ 
    yield return 1; 
    yield return 2; 
    yield return 3; 
} 

public IEnumerable<int> GetIntegers3() 
{ 
    if (someCondition) 
    { 
    return new[] {4, 5, 6}; // compiler error 
    } 
    else 
    { 
    yield return 1; 
    yield return 2; 
    yield return 3; 
    } 
} 
+11

Подождите секунду, jon skeet придет сейчас. – Juvanis

+0

Я добавлю, что если вам это действительно нужно, вы можете создать GetIngegers4, который вызывает GetIntegers1 ИЛИ GetIntegers2 в зависимости от состояния. – xanatos

+0

Это, вероятно, очевидно, но в таких случаях вы всегда можете развернуть свою коллекцию и вернуть доходность: foreach (элемент в новой позиции [] {4,5,6}) return return item; – Foo42

ответ

16

return нетерпеливо. Он возвращает весь набор результатов сразу. yield return строит перечислитель. За кулисами компилятор C# испускает необходимый класс для перечислителя при использовании yield return. Компилятор не ищет условия выполнения, такие как if (someCondition) при определении того, должен ли он испускать код для перечислимого или иметь метод, который возвращает простой массив. Он обнаруживает, что в вашем методе вы используете оба варианта, что невозможно, поскольку он не может испускать код для счетчика и в то же время метод возвращает нормальный массив и все это для того же метода.

+0

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

7

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

Я бы рекомендовал прочитать главу 6 книги Джона Скита C# in Depth, из которой глава 6 доступна бесплатно - она ​​отлично закрывает итераторные блоки.

Я не вижу причин, почему это было бы невозможно в будущих версиях компилятора C#. Другие языки .Net поддерживают нечто подобное в виде оператора «выход из» (See F# yield!). Если такой оператор существует в C# не позволит вам написать код в виде:

public IEnumerable<int> GetIntegers() 
{ 
    if (someCondition) 
    { 
    yield! return new[] {4, 5, 6}; 
    } 
    else 
    { 
    yield return 1; 
    yield return 2; 
    yield return 3; 
    } 
} 
10

Нет, вы не можете сделать это - блок итератора (что-то с yield) не использовать регулярные (non-yield) return. Вместо этого вам нужно использовать 2 метода:

public IEnumerable<int> GetIntegers3() 
{ 
    if (someCondition) 
    { 
    return new[] {4, 5, 6}; // compiler error 
    } 
    else 
    { 
    return GetIntegers3Deferred(); 
    } 
} 
private IEnumerable<int> GetIntegers3Deferred() 
{ 
    yield return 1; 
    yield return 2; 
    yield return 3; 
} 

или так в данном конкретном случае код для обоих уже существует в 2-х других методов:

public IEnumerable<int> GetIntegers3() 
{ 
    return (someCondition) ? GetIntegers1() : GetIntegers2(); 
} 
3

Теоретически я думаю, что нет никаких оснований почему возвращение и возврат выхода не могут быть смешаны: Это было бы легким дело для компилятора в первый синтаксический превратить любое return (blabla()); предложения в:

var myEnumerable = blabla(); 
foreach (var m in myEnumerable) 
    yield return m; 
yield break; 

, а затем продолжить (чтобы преобразовать весь метод в ... все, что они преобразуют сейчас; внутренний анонимный класс IEnumerator ?!)

Так почему же они не решили реализовать его, вот два предположения:

  • они могли бы решили, что это было бы ввести в заблуждение пользователей, чтобы иметь как возвращение и доходность дохода сразу,

  • Возврат всего перечислимого быстрее и дешевле, но также и нетерпелив; построение через доходность доходности немного дороже (особенно если вызывается рекурсивно, см. предупреждение Эрика Липперта об обходах в бинарных деревьях с предложениями возврата доходности здесь: https://stackoverflow.com/a/3970171/671084 например), но ленив. Поэтому пользователь обычно не хотел бы смешивать их: если вам не нужна лень (т.вы знаете всю последовательность) не страдают от снижения эффективности, просто используйте обычный метод. Возможно, они хотели заставить пользователя подумать об этом.

С другой стороны, похоже, что бывают ситуации, когда пользователь может извлечь выгоду из некоторых синтаксических расширений; вы можете прочитать этот вопрос и ответы в качестве примера (не тот же вопрос, но, возможно, с похожим мотивом): Yield Return Many?

+0

На самом деле это ответ на вопрос «Какими могут быть причины, по которым Microsoft решила не поддерживать ...», что было бы лучше вопрос в первую очередь. –

0

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

Что именно сделал бы ваш код? Будет ли он напрямую возвращать массив или он будет перебирать его?

Если он будет напрямую возвращать массив, тогда вам придется придумывать сложные правила, при каких условиях допустимо return, потому что return после yield return не имеет смысла. И вам, вероятно, потребуется создать сложный код, чтобы решить, будет ли метод возвращать пользовательский итератор или массив.

Если вы хотите итерировать коллекцию, вы, вероятно, хотите получить лучшее ключевое слово. Something like yield foreach. Это было фактически рассмотрено, но в конечном счете не было выполнено. Я думаю, что я помню, что главная причина в том, что на самом деле очень сложно заставить его работать хорошо, если у вас несколько вложенных итераторов.

+1

Трудно заставить его хорошо работать с вложенными итераторами, но эта проблема может быть решена с некоторой ловкостью; они сделали это в C-Omega. Однако, если вы это сделаете, вы начнете сталкиваться с проблемами правильности, когда эти вложенные итераторы содержат обработку исключений. Это прекрасная идея, и я хотел бы, чтобы мы могли это сделать, но соотношение боли и усиления слишком велико. –

Смежные вопросы