연결할 수없는 코드이지만 예외가있는 경우
이 코드는 ODBC 연결 데이터베이스에서 읽고 쓰는 응용 프로그램의 일부입니다. 데이터베이스에 레코드를 작성한 다음 레코드가 성공적으로 작성되었는지 확인한 다음을 리턴 true
합니다.
제어 흐름에 대한 나의 이해는 다음과 같습니다.
command.ExecuteNonQuery()
InvalidOperationException
"객체의 현재 상태에 대해 메소드 호출이 유효하지 않은 경우" 가 발생 하도록 문서화되어 있습니다. 따라서 이런 일이 발생하면 try
블록 실행 이 중지되고 finally
블록이 실행 된 다음 return false;
맨 아래에서 실행 됩니다.
그러나 내 IDE return false;
는 도달 할 수없는 코드 라고 주장합니다 . 그리고 그것은 사실 인 것 같습니다. 나는 그것을 제거 할 수 있으며 불만없이 컴파일됩니다. 그러나 나를 위해 언급 된 예외가 발생하는 코드 경로에 대한 반환 값이없는 것처럼 보입니다.
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;
}
여기 내 이해의 오류는 무엇입니까?
연결할 수없는 코드가 감지되었습니다
컴파일러가 절대 실행되지 않는 코드를 감지했습니다.
즉, 컴파일러는 정적 분석 을 통해 도달 할 수 없다는 것을 이해 하고 컴파일 된 IL 에서 완전히 생략합니다 (따라서 경고)
참고 : 디버거를 사용하여 연결할 수없는 코드로 이동하거나 IL 탐색기를 사용하여이 사실을 스스로 증명할 수 있습니다.
는 finally
온 실행할 수 있습니다 예외 가 여전히있을 것입니다 (옆으로하지만)가 (이 경우) 사실을 변경하지 않습니다, 캐치되지 않는 예외 . Ergo, 마지막 return
은 절대로 맞지 않을 것입니다.
코드가 마지막 코드로 계속
return
되도록 하려면 예외 를 잡아야 합니다 .그렇지 않은 경우 그대로 유지하고를 제거하십시오
return
.
예
try
{
command.CommandText = sb.ToString();
returnValue = command.ExecuteNonQuery();
return returnValue == 1;
}
catch(<some exception>)
{
// do something
}
finally
{
command.Dispose();
}
return false;
설명서를 인용하려면
By using a finally block, you can clean up any resources that are allocated in a try block, and you can run code even if an exception occurs in the try block. Typically, the statements of a finally block run when control leaves a try statement. The transfer of control can occur as a result of normal execution, of execution of a break, continue, goto, or return statement, or of propagation of an exception out of the try statement.
Within a handled exception, the associated finally block is guaranteed to be run. However, if the exception is unhandled, execution of the finally block is dependent on how the exception unwind operation is triggered. That, in turn, is dependent on how your computer is set up.
Usually, when an unhandled exception ends an application, whether or not the finally block is run is not important. However, if you have statements in a finally block that must be run even in that situation, one solution is to add a catch block to the try-finally statement. Alternatively, you can catch the exception that might be thrown in the try block of a try-finally statement higher up the call stack. That is, you can catch the exception in the method that calls the method that contains the try-finally statement, or in the method that calls that method, or in any method in the call stack. If the exception is not caught, execution of the finally block depends on whether the operating system chooses to trigger an exception unwind operation.
Lastly
When using anything that supports the IDisposable
interface (which is designed to release unmanaged resources), you can wrap it in a using
statement. The compiler will generate a try {} finally {}
and internally call Dispose()
on the object
the finally block would be executed, then would execute the return false; at the bottom.
Wrong. finally
doesn't swallow the exception. It honors it and the exception will be thrown as normal. It will only execute the code in the finally before the block ends (with or without an exception).
If you want the exception to be swallowed, you should use a catch
block with no throw
in it.
The warning is because you didn't use catch
and your method is basically written like this:
bool SomeMethod()
{
return true;
return false; // CS0162 Unreachable code detected
}
Since you use finally
solely to dispose, the preferred solution is to utilize using
pattern:
using(var command = new WhateverCommand())
{
...
}
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) some catch
down in call stack (parent calls are down, right?).
If it wouldn't be about disposing, then
try { ...; return true; } // only one return
finally { ... }
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
or false
) or will throw an exception otherwise.
Consider also to throw own exceptions by wrapping expected exceptions (check out InvalidOperationException constructor):
try { ... }
catch(SomeExpectedException e)
{
throw new SomeBetterExceptionWithExplanaition("...", e);
}
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):
try { ... }
catch { ...; throw; } // re-throw
finally { ... }
It seems, you are looking for something like this:
private static bool createRecord(string table,
IDictionary<String,String> data,
System.Data.IDbConnection conn,
OdbcTransaction trans) {
[... some other code ...]
// Using: do not call Dispose() explicitly, but wrap IDisposable into using
using (var command = ...) {
try {
// Normal flow:
command.CommandText = sb.ToString();
// True if and only if exactly one record affected
return command.ExecuteNonQuery() == 1;
}
catch (DbException) {
// Exceptional flow (all database exceptions)
return false;
}
}
}
Please, note, that finally
doesn't swallow any exception
finally {
// This code will be executed; the exception will be efficently re-thrown
}
// And this code will never be reached
You don't have a catch
block, so the exception is still thrown, which blocks the return.
the finally block would be executed, then would execute the return false; at the bottom.
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,
Usually, when an unhandled exception ends an application, whether or not the finally block is run is not important. However, if you have statements in a finally block that must be run even in that situation, one solution is to add a catch block to the try-finally statement. Alternatively, you can catch the exception that might be thrown in the try block of a try-finally statement higher up the call stack. That is, you can catch the exception in the method that calls the method that contains the try-finally statement, or in the method that calls that method, or in any method in the call stack. If the exception is not caught, execution of the finally block depends on whether the operating system chooses to trigger an exception unwind operation.
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 the finally
statement.
When the exception is thrown, the stack will unwind (execution will move out of the function) without returning a value, and any catch block in the stack frames above the function will catch the exception instead.
Hence, return false
will never execute.
Try manually throwing an exception to understand the control flow:
try {
command.CommandText = sb.ToString();
returnValue = command.ExecuteNonQuery();
// Try this.
throw new Exception("See where this goes.");
return returnValue == 1;
} finally {
command.Dispose();
}
On your code:
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; // You return here in case no exception is thrown
} finally {
command.Dispose(); //You don't have a catch so the exception is passed on if thrown
}
return false; // This is never executed because there was either one of the above two exit points of the method reached.
}
the finally block would be executed, then would execute the return false; at the bottom
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.
The last statement return false
is unreachable, because the try block is missing a catch
part that would handle the exception, so the exception is rethrown after the finally
block and the execution never reaches the last statement.
You have two return paths in your code, the second of which is unreachable because of the first. The last statement in your try
block return returnValue == 1;
provides your normal return, so you can never reach the return false;
at the end of the method block.
FWIW, order of exection related to the finally
block is: the expression supplying the return value in the try block will be evaluated first, then the finally block will be executed, and then the calculated expression value will be returned (inside the try block).
Regarding flow on exception... without a catch
, the finally
will be executed upon exception before the exception is then rethrown out of the method; there is no "return" path.
참고URL : https://stackoverflow.com/questions/55116849/unreachable-code-but-reachable-with-an-exception
'IT story' 카테고리의 다른 글
Textview에서 문자 간격을 변경하는 방법은 무엇입니까? (0) | 2020.08.04 |
---|---|
몽구스 (mongodb) 배치 삽입물? (0) | 2020.08.04 |
Node.js는 everyauth와 함께 사용할 환경 별 구성 설정 (0) | 2020.08.04 |
Angular 2의 패시브 링크- (0) | 2020.08.04 |
Java 인터페이스의 선택적 메소드 (0) | 2020.08.04 |