Try catch return bool
I was recently writing a lot of method that talked to 3rd party API. And after small refactoring a lot of methods was like try to call some method, if it throws exception return false, if not return true. And I was wondering, what’s the best (in terms of code being generated, speed, efficiency but also readability) way to write it.
I found basically three ways to write it (sure there are other ways):
try
{
FooBar();
return true;
}
catch (SomeException ex)
{
LogException(ex);
return false;
}
try
{
FooBar();
return true;
}
catch (SomeException ex)
{
LogException(ex);
}
return false;
bool result = false;
try
{
FooBar();
result = true;
}
catch (SomeException ex)
{
LogException(ex);
}
return result;
No magic. The third way is probably something you might recognize, if you were programming couple of years in Delphi/(Object)Pascal. Anyway, back to basics. My friend ildasm is going to help us with initial review, what we have actually done.
Everything compiled with full optimization turned on.
.method private hidebysig static bool Test1() cil managed
{
// Code size 28 (0x1c)
.maxstack 1
.locals init (class TryCatchTest.SomeException V_0,
bool V_1)
IL_0000: nop
.try
{
IL_0001: nop
IL_0002: call void TryCatchTest.Program::FooBar()
IL_0007: nop
IL_0008: ldc.i4.1
IL_0009: stloc.1
IL_000a: leave.s IL_0019
} // end .try
catch TryCatchTest.SomeException
{
IL_000c: stloc.0
IL_000d: nop
IL_000e: ldloc.0
IL_000f: call void TryCatchTest.Program::LogException(class [mscorlib]System.Exception)
IL_0014: nop
IL_0015: ldc.i4.0
IL_0016: stloc.1
IL_0017: leave.s IL_0019
} // end handler
IL_0019: nop
IL_001a: ldloc.1
IL_001b: ret
} // end of method Program::Test1
.method private hidebysig static bool Test2() cil managed
{
// Code size 32 (0x20)
.maxstack 1
.locals init (class TryCatchTest.SomeException V_0,
bool V_1)
IL_0000: nop
.try
{
IL_0001: nop
IL_0002: call void TryCatchTest.Program::FooBar()
IL_0007: nop
IL_0008: ldc.i4.1
IL_0009: stloc.1
IL_000a: leave.s IL_001d
} // end .try
catch TryCatchTest.SomeException
{
IL_000c: stloc.0
IL_000d: nop
IL_000e: ldloc.0
IL_000f: call void TryCatchTest.Program::LogException(class [mscorlib]System.Exception)
IL_0014: nop
IL_0015: nop
IL_0016: leave.s IL_0018
} // end handler
IL_0018: nop
IL_0019: ldc.i4.0
IL_001a: stloc.1
IL_001b: br.s IL_001d
IL_001d: nop
IL_001e: ldloc.1
IL_001f: ret
} // end of method Program::Test2
.method private hidebysig static bool Test3() cil managed
{
// Code size 34 (0x22)
.maxstack 1
.locals init (bool V_0,
class TryCatchTest.SomeException V_1,
bool V_2)
IL_0000: nop
IL_0001: ldc.i4.0
IL_0002: stloc.0
.try
{
IL_0003: nop
IL_0004: call void TryCatchTest.Program::FooBar()
IL_0009: nop
IL_000a: ldc.i4.1
IL_000b: stloc.0
IL_000c: nop
IL_000d: leave.s IL_001b
} // end .try
catch TryCatchTest.SomeException
{
IL_000f: stloc.1
IL_0010: nop
IL_0011: ldloc.1
IL_0012: call void TryCatchTest.Program::LogException(class [mscorlib]System.Exception)
IL_0017: nop
IL_0018: nop
IL_0019: leave.s IL_001b
} // end handler
IL_001b: nop
IL_001c: ldloc.0
IL_001d: stloc.2
IL_001e: br.s IL_0020
IL_0020: ldloc.2
IL_0021: ret
} // end of method Program::Test3
The third way is longest and I think also least readable (taking into account it’s C#). Also you can easily break the code. The first and second are really close in terms of code. The first one I’m using if I like to state, that after the exception is processed, nothing is going on further – “do, return or catch, return, done”. Nor in catch block, nor after it. The second one I’m using if my code is like “do something and return something” and then “else recover based on error and return fallback result”.
And what’s your preference?



There's 6 Comments So Far
January 24th, 2012 at 12:49
Everything is wrong. You should not use exceptions for control flow simply because it is bad design. It doesn’t make sense.
January 24th, 2012 at 13:01
Yes, sometimes the API isn’t clean. But you can do nothing about it.
January 24th, 2012 at 13:26
I use version 3
January 24th, 2012 at 19:27
This is my gut reaction: http://pastebin.com/4VCtpFja
I’ve moved the return true to the end, making the catch block an early exit. Has the benefit that the returns follow each other, and the useful method call gets its own block.
January 25th, 2012 at 08:31
@Douglas That’s nice too.
February 16th, 2012 at 21:44
If you repeat this (bad) pattern in many places, I’d go for a functional approach which is more readable IMO.
Write a TryExecute method which takes an Action delegate, executes it, and return your bool result:
http://pastebin.com/epy2hPCW
If you need to customize your logging, you can have an overload which also accepts a Func delegate.
Share your thoughts, leave a comment!