views:

76

answers:

5

Hi.

I have a class with an API that allows me to ask for objects until it throws an IndexOutOfBoundsException.

I want to wrap it into an iterator, to be able to write cleaner code. However, I need to catch the exception to stop iterating:

static IEnumerable<object> Iterator( ExAPI api ) {
    try {
       for( int i = 0; true; ++i ) {
          yield return api[i]; // will throw eventually
       }
    } 
    catch( IndexOutOfBoundsException ) {
       // expected: end of iteration.
    }
}

But...

When used with expression, a yield return statement cannot appear in a catch block or in a try block that has one or more catch clauses. For more information, see Exception Handling Statements (C# Reference).Statements (C# Reference). (from the msdn)

How can I still wrap this api?

+8  A: 

You simply need to move the yield return statement outside of the try block, like this:

static IEnumerable<object> Iterator( ExAPI api ) {
   for( int i = 0; true; ++i ) {
        object current;
        try {
            current = api[i];
        } catch(IndexOutOfBoundsException) { yield break; }

        yield return current;
    } 
}
SLaks
although wouldnt you need an "if (current != null) yield return current"?
Joshua Evensen
oh wait im dumb i missed the yield break.
Joshua Evensen
The `yield break` statement documentation is left away on the msdn... Thanks for pointing out!
xtofl
A: 

Just reorder the code:

static IEnumerable<object> Iterator( ExAPI api ) {
    bool exceptionThrown = false;
    object obj = null;
    for( int i = 0; true; ++i ) {
        try {
            obj = api[i];
        }
        catch( IndexOutOfBoundsException ) {
            exceptionThrown = true;
            yield break;
        }

        if (!exceptionThrown) {
            yield return obj;
        }
    }
}
AHM
This will never stop.
SLaks
No, I wrote too fast I guess.... Anyway, your answer below is cleaner and better :-)
AHM
Now you have a useless variable.
SLaks
Yeah - but if I changed that, my answer would be a direct copy of your answer, and that would be kind of lame, even if it's correct - you wrote the right code, you should get the credit!
AHM
+3  A: 

You can wrap the simple operation of getting the object into a separate function. You can catch the exception in there:

bool TryGetObject( ExAPI api, int idx, out object obj )
{
    try
    {
        obj = api[idx];
        return true;
    }
    catch( IndexOutOfBoundsException )
    {
        return false;
    }        
}

Then, call that function and terminate if necessary:

static IEnumerable<object> Iterator( ExAPI api )
{
   bool abort = false;

    for( int i = 0; !abort; ++i )
    {
        object obj;
        if( TryGetObject( api, i, out obj ) )
        {
            yield return obj;
        }
        else
        {
            abort = true;
        }
    }
}
Timbo
I'm not really a fan of this approach. If the api is only going to be accessed in this method, a separate method to get the object is kinda redundant.
Joshua Evensen
@Joshua: but a lambda could work...
xtofl
A: 

If you can't check the bounds of the object at all, you could do something like this

static IEnumerable<object> Iterator( ExAPI api )
{
 List<object> output = new List<object>();
    try
 {
  for( int i = 0; true; ++i )
   output.Add(api[i]);
    } 
    catch( IndexOutOfBoundsException )
 {
  // expected: end of iteration.
    }
 return output;
}

although now that im looking here, the answer above is better i believe. The one SLaks posted.

Joshua Evensen
Indeed: my intention is to not fill a container, but rather provide an iterator.
xtofl
A: 
    static IEnumerable<object> Iterator(ExAPI api)
    {
        int i = 0;
        while (true)
        {
            Object a;
            try
            {
                a = api[i++];
            }
            catch (IndexOutOfBoundsException)
            {
                yield break;
            }
            yield return a;
        }
    }
QrystaL