24

If I step through the following code the call to ReturnOne() is skipped.

static IEnumerable<int> OneThroughFive()
{
    ReturnOne();
    yield return 2;
    yield return 3;
    yield return 4;
    yield return 5;
}

static IEnumerator<int> ReturnOne()
{
    yield return 1;
}

I can only assume the compiler is stripping it out because what I'm doing is not valid. I'd like the ability to isolate my enumeration into various methods. Is this possible?

David
  • 1,052
  • 1
  • 8
  • 19

1 Answers1

41

You're not actually using the result of ReturnOne. You're calling the method, and ignoring the return value... which means you'd never actually see any of your code being run. You can do it like this:

static IEnumerable<int> OneThroughFive()
{
    foreach (int x in ReturnOne())
    {
        yield x;
    }
    yield return 2;
    yield return 3;
    yield return 4;
    yield return 5;
}

C# doesn't (currently at least :) have a sort of "yield all" construct.

The fact that you're not getting to step into it has nothing to do with the fact that you've got a call within an iterator block - it's just that until you start using the result of an iterator block, none of the code runs. That's why you need to separate out argument validation from yielding. For example, consider this code:

public IEnumerator<string> ReturnSubstrings(string x)
{
    if (x == null)
    {
         throw ArgumentNullException();
    }
    for (int i = 0; i < x.Length; i++)
    {
         yield return x.Substring(i);
    }
}
...
ReturnSubstring(null); // No exception thrown

You need to write it like this:

public IEnumerator<string> ReturnSubstrings(string x)
{
    if (x == null)
    {
         throw ArgumentNullException();
    }
    return ReturnSubstringsImpl(x);
}

private IEnumerator<string> ReturnSubstringsImpl(string x)
{
    for (int i = 0; i < x.Length; i++)
    {
         yield return x.Substring(i);
    }
}

For more details, read chapter 6 of C# in Depth - which happens to be a free chapter in the first edition :) Grab it here.

Jon Skeet
  • 1,421,763
  • 867
  • 9,128
  • 9,194
  • 1
    @Jon: What will happen if you try to actually use the results of the first `ReturnSubstrings`? Will you get a `NullPointerException`, or an `ArgumentNullException`? – BlueRaja - Danny Pflughoeft Mar 23 '10 at 23:39
  • 2
    i love how, when reading this thread, i knew that jon skeet wrote this answer way before i'd scrolled down to the bottom and seen the author. @jon, you really should get out more. but for now, thanks again for teaching me somthing new. – fearofawhackplanet Mar 23 '10 at 23:56
  • 3
    @BlueRaja: You'd get an ArgumentNullException - the check will still execute, but only on the first call to MoveNext() of the iterator returned by GetEnumerator(). – Jon Skeet Mar 24 '10 at 06:29