57

I use the yield return keyword quite a bit, but I find it lacking when I want to add a range to the IEnumerable. Here's a quick example of what I would like to do:

IEnumerable<string> SomeRecursiveMethod()
{
    // some code 
    // ...
    yield return SomeRecursiveMethod();
}

Naturally this results in an error, which can be resolved by doing a simple loop. Is there a better way to do this? A loop feels a bit clunky.

André Haupt
  • 3,294
  • 5
  • 32
  • 57
  • 1
    Still a loop, but if C# supported it, you could sweeten the syntax by implementing a `ForEach` extension method for `IEnumerable`. See the accepted answer for this question: http://stackoverflow.com/questions/200574. Unfortunately, it seems you can't `yield return` from a lambda... – Merlyn Morgan-Graham Mar 24 '11 at 07:26
  • Possible duplicate of [Yield Return Many?](https://stackoverflow.com/questions/3851997/yield-return-many) – StayOnTarget Jun 12 '19 at 18:49

3 Answers3

62

No, there isn't I'm afraid. F# does support this with yield!, but there's no equivalent in C# - you have to use the loop, basically. Sorry... I feel your pain. I mentioned it in one of my Edulinq blog posts, where it would have made things simpler.

Note that using yield return recursively can be expensive - see Wes Dyer's post on iterators for more information (and mentioning a "yield foreach" which was under consideration four years ago...)

Jon Skeet
  • 1,421,763
  • 867
  • 9,128
  • 9,194
  • I hope they add something like `yield foreach` in a future version of C#. If you have already accepted the overhead of using an iterator method, the `yield foreach` does not add any more overhead than a simple foreach you would have to write yourself. Though, I think I can hear Eric in the background chiming in with a `implement, test, document, localize` point so I doubt it will ever come (and I agree with Eric, though I still would like for it to magically just appear free in their compiler.) – Lasse V. Karlsen Mar 24 '11 at 09:15
  • 8
    @Lasse: I would also like it to magically appear! The COmega research version of C# had this feature. However I have heard a rumour that the authors have since discovered that the stack-efficient code transformation proposed in that language is not correct for some cases involving nested iterators that throw exceptions. There might not be an easy way to do this feature that is still efficient. It's on the list, but we have lots of higher priorities. – Eric Lippert Mar 24 '11 at 14:23
  • @EricLippert where did this feature go? :8 – Mafii Jan 30 '17 at 08:33
  • @Mafii: To the Roslyn repo, of course: https://github.com/dotnet/roslyn/issues/15 – Jon Skeet Jan 30 '17 at 08:40
  • @JonSkeet thanks a lot - always forget to look there. – Mafii Jan 30 '17 at 10:22
  • Moved to the csharplang repo now: https://github.com/dotnet/csharplang/issues/378 – Dan Jagnow Apr 06 '18 at 17:57
6

If you already have an IEnumerable to loop over, and the return type is IEnumerable (as is the case for functions that could use yield return), you can simply return that enumeration.

If you have cases where you need to combine results from multiple IEnumerables, you can use the IEnumerable<T>.Concat extension method.

In your recursive example, though, you need to terminate the enumeration/concatenation based on the contents of the enumeration. I don't think my method will support this.

Merlyn Morgan-Graham
  • 58,163
  • 16
  • 128
  • 183
  • 2
    This changes the behaviour significantly though - iterator blocks execute lazily; if you don't have any `yield return` statements, it will all execute eagerly. – Jon Skeet Mar 24 '11 at 07:39
  • @Jon: Definitely, and I believe this will break recursion, which was the specific example he gave (though not the only example of where this functionality would be useful). You will still get a level of lazy evaluation, though, if your enumerations are based off the results of `yield return` functions. I'm pretty sure (at least, I hope :) you aren't enumerating when you call `Concat`. – Merlyn Morgan-Graham Mar 24 '11 at 07:44
0

The yield keyword is indeed very nice. But nesting it in a for loop will cause more glue code to be generated and executed.

If you can live with a less functional style of programming, you can pass a List around to which you append:

 void GenerateList(List<string> result)
 {

      result.Add("something")

      // more code.

      GenerateList(result);

 }