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]...
}
}
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.
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.
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.
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.
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.
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.
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With