Подтвердить что ты не робот

Почему нельзя "возвращать" и "возвращать доход" в том же методе?

Почему мы не можем использовать return и yield return в том же методе?

Например, мы можем иметь 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;
  }
}
4b9b3361

Ответ 1

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

Ответ 2

Нет, вы не можете этого сделать - блок итератора (что-то с 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;
}

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

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

Ответ 3

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

Я бы посоветовал прочитать главу 6 Jon Skeet С# в глубине, из которых глава 6 доступна бесплатно - она ​​охватывает итератор блокирует довольно красиво.

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

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

Ответ 4

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

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

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

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

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

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

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

Ответ 5

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

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

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

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