See edits below for reproducing the behavior that I describe in this problem.
The following program will never end, because the yield return
construct in C# calls the GetStrings()
method indefinitely when an exception is thrown.
class Program
{
static void Main(string[] args)
{
// I expect the Exception to be thrown here, but it's not
foreach (var str in GetStrings())
{
Console.WriteLine(str);
}
}
private static IEnumerable<string> GetStrings()
{
// REPEATEDLY throws this exception
throw new Exception();
yield break;
}
}
For this trivial example, I could obviously use return Enumerable.Empty<string>();
instead, and the problem goes away. However in a more interesting example, I'd expect the exception to be thrown once, then have the method stop being called and throw the exception in the method that's "consuming" the IEnumerable
.
Is there a way to produce this behavior?
EDIT: ok, the problem is different than I first thought. The program above does NOT end, and the foreach
loop behaves like an infinite loop. The program below DOES end, and the exception is displayed on the console.
class Program
{
static void Main(string[] args)
{
try
{
foreach (var str in GetStrings())
{
Console.WriteLine(str);
}
}
catch (Exception e)
{
Console.WriteLine(e);
}
}
private static IEnumerable<string> GetStrings()
{
throw new Exception();
yield break;
}
}
Why does the try
... catch
block make a difference in this case? This seems very strange to me. Thanks to @AndrewKilburn for his answer already for pointing me to this.
EDIT #2:
From a Command Prompt, the program executes the same in both cases. In Visual Studio Enterprise 2015, Update 2, whether I compile in Debug or Release, the behavior above is what I am seeing. With the try
... catch
, the program ends with an exception, and without it Visual Studio never closes the program.
EDIT #3: Fixed For me, the issue was resolved by the answer by @MartinBrown. When I uncheck Visual Studio's option under Debug > Options > Debugging > General > "Unwind the call stack on unhandled exceptions" this problem goes away. When I check the box again, then the problem comes back.
After throwing an exception, you do not need to return because throw returns for you. Throwing will bubble up the call stack to the next exception handler so returning is not required.
If you return something from inside a finally block, it still gets returned despite the exception being thrown.
"yield break" breaks the Coroutine (it's similar as "return"). "yield return null" means that Unity will wait the next frame to finish the current scope. "yield return new" is similar to "yield return null" but this is used to call another coroutine.
In short: You should throw an exception if a method is not able to do the task it is supposed to do.
The behaviour being seen here is not a fault in the code; rather it is a side effect of the Visual Studio debugger. This can be resolved by turning off stack unwinding in Visual Studio. Try going into Visual Studio options Debugging/General and unchecking "Unwind the call stack on unhandled exceptions". Then run the code again.
What happens is that when your code hits a completely unhandled exception Visual Studio is unwinding the call stack to just before the line in your code that caused the exception. It does this so that you can edit the code and continue execution with the edited code.
The issue seen here looks like an infinite loop because when you re-start execution in the debugger the next line to run is the one that just caused an exception. Outside the debugger the call stack would be completely unwound on an unhandled exception and thus would not cause the same loop that you get in the debugger.
This stack unwinding feature can be turned off in the settings, it is enabled by default. Turning it off however will stop you being able to edit code and continue without first unwinding the stack yourself. This however is quite easy to do either from the call stack window or by simply selecting 'Enable Editing' from the Exception Assistant.
The following program will never end
That's false. The program is going to end quite quickly.
because the yield return construct in C# calls the
GetStrings()
method indefinitely when an exception is thrown.
This is false. It doesn't do this at all.
I'd expect the exception to be thrown once, then have the method stop being called and throw the exception in the method that's "consuming" the
IEnumerable
.
That's exactly what does happen.
Is there a way to produce this behavior?
Use the code you already provided.
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