4

I need to do some heavy, somewhat fragile logic in a method that I'm implementing as an iterator (using yield):

public IEnumerable<Things> GetMoreThings() {
    while (goodStuffHappens()) {
        Things moreThingsIWant = TemptFateAgain();
        if (moreThingsIWant.Any())
            yield return moreThingsIWant;
    }
}

Out in the calling method, I need to wrap the call to GetMoreThings in try/catch and yield return the result:

try {
    foreach (Things thing in Helpful.GetMoreThings())
        yield return thing;
}

catch (Exception e) {
    //crash, burn
}

The initiated will immediately realize that this is impossible - there is no such thing as a yield inside a try/catch block (only try/finally).

Any recommendations?

Community
  • 1
  • 1
sq33G
  • 3,320
  • 1
  • 23
  • 38

3 Answers3

8

Both the answers here were correct. There's no built-in shortcut for this one, you need to tease apart the iterator in a while rather than a for loop in order to separate between the call to Enumerator.MoveNext() and the use of Enumerator.Current.

IEnumerator<Things> iterator = Helpful.GetMoreThings.GetEnumerator();
bool more = true;

while (more) {
    try {
        more = iterator.MoveNext();
    }
    catch (Exception e) {
        //crash, burn
    }

    if (more)
        yield return iterator.Current;
}
sq33G
  • 3,320
  • 1
  • 23
  • 38
1

Put the Helpful.GetMoreThings() call and enumeration separately from the yield:

try {
    var results = Helpful.GetMoreThings().ToList();
}
catch (Exception e) {
    //crash, burn
}

foreach (Things thing in results)
    yield return thing;

Something like that.

If you need this to be lazy the code gets really nasty. You can no longer use foreach. You need to write the iteration loop manually and the code size explodes to like 20 lines of illegible mess. I know because I did this yesterday.

usr
  • 168,620
  • 35
  • 240
  • 369
  • Yes, I need it to be lazy. Otherwise I have trouble coming up with a good reason to design it this way. – sq33G Feb 11 '16 at 12:25
  • Then, do what the last paragraph says: Expand the foreach into a manually written loop. You can then call MoveNext() with a catch guard and yield without catch. – usr Feb 11 '16 at 12:27
1

You can use the Catch() extension method from Ix.Net, or just copy its Apache-licensed source code. The code could look like this:

return Helpful.GetMoreThings().Catch((Exception e) =>
{
    // crash, burn
    return null;
}
svick
  • 236,525
  • 50
  • 385
  • 514
  • The source code is actually the 20 illegible mess lines referred to above in the answer from @usr. – sq33G Feb 11 '16 at 20:26