Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Getting reference to an exception in finally block in an iterator

Tags:

c#

ienumerable

Is there any way to get a reference to an exception inside a finally block in an iterator function or property that allow try..finally but not try..catch?

I'm not going to use it to change or mess with the control flow, but would like to be able to get a reference to the exception in the finally block anyway (if one was thrown), in order to read from it and possibly add stuff to the Data member.

I understand that due to nature of the compiler generated classes from iterators, it is probably not possible/allowed for the same reason why try..catch around a yield statement is not allowed in the first place. But I'm still hoping that there is maybe be some way (or even ugly trick) to get hold of the exception anyway.

Simplified example:

IEnumerable<SomeClass> Something
get
{
  try
  {
    throw new SomeException();
    yield return new SomeClass();
  }
  finally
  {
    Exception ex = ... // <= TODO - get hold of the exception here [if one was thrown]...
  }
}
like image 648
KristoferA Avatar asked Jan 17 '11 07:01

KristoferA


People also ask

What happens if there is an exception inside finally block?

The "finally" block execution stops at the point where the exception is thrown. Irrespective of whether there is an exception or not "finally" block is guaranteed to execute. Then the original exception that occurred in the try block is lost.

Can exception be thrown in finally block?

Some resource cleanup, such as closing a file, needs to be done even if an exception is thrown. To do this, you can use a finally block. A finally block always executes, regardless of whether an exception is thrown. The following code example uses a try / catch block to catch an ArgumentOutOfRangeException.

What happens to exception in try finally?

An exception occurring in finally block behaves in the same way as any other exception. Even if the try block contains a return statement or branching statements like break and continue, then the finally block will still be executed.

How do you use finally block for catching exceptions?

The finally block in java is used to put important codes such as clean up code e.g. closing the file or closing the connection. The finally block executes whether exception rise or not and whether exception handled or not. A finally contains all the crucial statements regardless of the exception occurs or not.


2 Answers

This is a very interesting question.

Recall that in Linq, a lot of standard operators are provided that effectively chain together. There currently isn't one that lets you wrap custom exception handling around an inner sequence.

So my suggestion is to write a new one that allows you to specify an action that handles any exception that occurs during the execution of IEnumerator.MoveNext:

public static class EnumerableExceptions
{
    public static IEnumerable<TItem> Catch<TItem, TEx>(
        this IEnumerable<TItem> source, 
        Action<TEx> handler) where TEx : Exception
    {
        using (var enumerator = source.GetEnumerator())
        {
            for (; ; )
            {
                try
                {
                    if (!enumerator.MoveNext())
                        yield break;
                }
                catch (TEx x)
                {
                    handler(x);
                    yield break;
                }

                yield return enumerator.Current;
            }
        }
    }
}

So now supposing we had this:

public class NastyException : Exception { }

public static IEnumerable<String> StringYielder()
{
    yield return "apple";
    yield return "banana";

    throw new NastyException();

    yield return "oracle";
    yield return "grapefruit";
    yield return "microsoft";
}

We'd like to be able to wrap all the body in a try/catch, which is sadly illegal. But what we can do is wrap the generated sequence:

public static IEnumerable<String> LoggingStringYielder()
{
    return StringYielder().Catch(
        (NastyException ex) => 
            Console.WriteLine("Exception caught: " + ex.StackTrace));
}

That is, I get a sequence by calling the "raw" StringYielder method, and then I apply the new Catch operator to it, specifying what to do if a certain exception type occurs. Here I'm just going to print the stack trace.

So if I do this:

foreach (var str in LoggingStringYielder())
    Console.WriteLine(str);

The program completes without crashing, and the output is:

apple
banana
Exception caught:    at ConsoleApplication7.Program.<StringYielder>.. blah

So although you can't put a try catch around the code inside the original iterator method, you can now "wrap" it around the outside of that iterator method. It's like a non-intrusive way of injecting exception handling around the code between each yield return.

Bonus update!

To be really picky about the way I worded that last sentence:

  • Firstly you can throw before the first yield return and it is treated the same way, as that code executes in the first call to MoveNext. So "... the code before each..." would have been more accurate than "... the code between each...".

  • Secondly, a yield return may accept an expression that has to be evaluated, and which may throw during evaluation. That should be regarded as code that executes before the yield return occurs, even though syntactically it appears after it.

like image 168
Daniel Earwicker Avatar answered Oct 13 '22 09:10

Daniel Earwicker


How about moving all the code which could generate an exception into a nested try/catch:

IEnumerable<int> GetFoo()
{
    for (int i = -10; i < 10; i++)
    {
        Exception ex = null;
        try
        {
            int result = 0;
            try
            {
                result = 10 / i;
            }
            catch (Exception e) // Don't normally do this!
            {
                ex = e;
                throw;
            }
            yield return result;
        }
        finally
        {
            if (ex != null)
            {
                // Use ex here
            }
        }
    }
}

With the above pattern, however, you may be able to do everything you need just within the catch block instead, which would be simpler - you may be able to get rid of the surrounding try/finally block.

like image 26
Jon Skeet Avatar answered Oct 13 '22 09:10

Jon Skeet