This code is part of an application that reads from and writes to an ODBC connected database. It creates a record in the database and then checks if a record has been successfully created, then returning true
.
My understanding of control flow is as follows:
command.ExecuteNonQuery()
is documented to throw an InvalidOperationException
when "a method call is invalid for the object's current state". Therefore, if that would happen, execution of the try
block would stop, the finally
block would be executed, then would execute the return false;
at the bottom.
However, my IDE claims that the return false;
is unreachable code. And it seems to be true, I can remove it and it compiles without any complaints. However, for me it looks as if there would be no return value for the code path where the mentioned exception is thrown.
private static bool createRecord(String table,
IDictionary<String,String> data,
System.Data.IDbConnection conn,
OdbcTransaction trans) {
[... some other code ...]
int returnValue = 0;
try {
command.CommandText = sb.ToString();
returnValue = command.ExecuteNonQuery();
return returnValue == 1;
} finally {
command.Dispose();
}
return false;
}
What is my error of understanding here?
The last statement
return false
is unreachable, because the try block is missing acatch
part that would handle the exception, so the exception is rethrown after thefinally
block and the execution never reaches the last statement.On your code:
This is the flaw in your logic because the
finally
block won't catch the exception and it will never reach the last return statement.It seems, you are looking for something like this:
Please, note, that
finally
doesn't swallow any exceptionYou don't have a
catch
block, so the exception is still thrown, which blocks the return.This is wrong, because the finally block would be executed, and then there would be an uncaught exception.
finally
blocks are used for cleanup, and they do not catch the exception. The exception is thrown before the return, therefore, the return will never be reached, because an exception is thrown before.Your IDE is correct that it will never be reached, because the exception will be thrown. Only
catch
blocks are able to catch exceptions.Reading from the documentation,
This clearly shows that the finally is not intended to catch the exception, and you would have been correct if there had been an empty
catch
statement before thefinally
statement.Compiler Warning (level 2) CS0162
Which is just saying, the Compiler understands enough through Static Analysis that it cant be reached and completely omits it from the compiled IL (hence your warning)
Note : You can prove this fact to your self by trying to Step on to the Unreachable Code with the debugger, or using an IL Explorer
The
finally
may run on an Exception, (though that aside) it doesn't change the fact (in this case) it will still be an Uncaught Exception. Ergo, the lastreturn
will never get hit regardless.If you want the code to continue onto the last
return
, your only option is to Catch the Exception;If you don't, just leave it the way it is and remove the
return
.Example
To quote the documentation
try-finally (C# Reference)
Lastly
When using anything that supports the
IDisposable
interface (which is designed to release unmanaged resources), you can wrap it in ausing
statement. The compiler will generate atry {} finally {}
and internally callDispose()
on the objectThe warning is because you didn't use
catch
and your method is basically written like this:Since you use
finally
solely to dispose, the preferred solution is to utilizeusing
pattern:That's enough, to ensure what
Dispose
will be called. It's guaranteed to be called either after successful execution of code block or upon (before) somecatch
down in call stack (parent calls are down, right?).If it wouldn't be about disposing, then
is enough, since you will never have to return
false
at the end of method (there is no need for that line). Your method is either return result of command execution (true
orfalse
) or will throw an exception otherwise.Consider also to throw own exceptions by wrapping expected exceptions (check out InvalidOperationException constructor):
This is typically used to say something more meaningful (useful) to the caller than nested call exception would be telling.
Most of times you don't really care about unhandled exceptions. Sometimes you need to ensure that
finally
is called even if exception is unhandled. In this case you simply catch it yourself and re-throw (see this answer):