"catch, when"을 사용하여 예외 잡기
C #에서 특정 조건이 충족 될 때 catch 핸들러를 실행할 수있는이 새로운 기능을 발견했습니다.
int i = 0;
try
{
throw new ArgumentNullException(nameof(i));
}
catch (ArgumentNullException e)
when (i == 1)
{
Console.WriteLine("Caught Argument Null Exception");
}
나는 이것이 언제 유용 할 수 있는지 이해하려고 노력하고 있습니다.
하나의 시나리오는 다음과 같을 수 있습니다.
try
{
DatabaseUpdate()
}
catch (SQLException e)
when (driver == "MySQL")
{
//MySQL specific error handling and wrapping up the exception
}
catch (SQLException e)
when (driver == "Oracle")
{
//Oracle specific error handling and wrapping up of exception
}
..
그러나 이것은 다시 동일한 핸들러 내에서 수행 할 수 있고 드라이버 유형에 따라 다른 메소드에 위임 할 수 있습니다. 코드를 더 쉽게 이해할 수 있습니까? 틀림없이 아니오.
제가 생각할 수있는 또 다른 시나리오는 다음과 같습니다.
try
{
SomeOperation();
}
catch(SomeException e)
when (Condition == true)
{
//some specific error handling that this layer can handle
}
catch (Exception e) //catchall
{
throw;
}
다시 이것은 내가 할 수있는 일입니다.
try
{
SomeOperation();
}
catch(SomeException e)
{
if (condition == true)
{
//some specific error handling that this layer can handle
}
else
throw;
}
Does using the 'catch, when' feature make exception handling faster because the handler is skipped as such and the stack unwinding can happen much earlier as when compared to handling the specific use cases within the handler? Are there any specific use cases that fit this feature better which people can then adopt as a good practice?
Catch blocks already allow you to filter on the type of the exception:
catch (SomeSpecificExceptionType e) {...}
The when
clause allows you to extend this filter to generic expressions.
Thus, you use the when
clause for cases where the type of the exception is not distinct enough to determine whether the exception should be handled here or not.
A common use case are exception types which are actually a wrapper for multiple, different kinds of errors.
Here's a case that I've actually used (in VB, which already has this feature for quite some time):
try
{
SomeLegacyComOperation();
}
catch (COMException e) when (e.ErrorCode == 0x1234)
{
// Handle the *specific* error I was expecting.
}
Same for SqlException
, which also has an ErrorCode
property. The alternative would be something like that:
try
{
SomeLegacyComOperation();
}
catch (COMException e)
{
if (e.ErrorCode == 0x1234)
{
// Handle error
}
else
{
throw;
}
}
which is arguably less elegant and slightly breaks the stack trace.
In addition, you can mention the same type of exception twice in the same try-catch-block:
try
{
SomeLegacyComOperation();
}
catch (COMException e) when (e.ErrorCode == 0x1234)
{
...
}
catch (COMException e) when (e.ErrorCode == 0x5678)
{
...
}
which would not be possible without the when
condition.
From Roslyn's wiki (emphasis mine):
Exception filters are preferable to catching and rethrowing because they leave the stack unharmed. If the exception later causes the stack to be dumped, you can see where it originally came from, rather than just the last place it was rethrown.
It is also a common and accepted form of “abuse” to use exception filters for side effects; e.g. logging. They can inspect an exception “flying by” without intercepting its course. In those cases, the filter will often be a call to a false-returning helper function which executes the side effects:
private static bool Log(Exception e) { /* log it */ ; return false; } … try { … } catch (Exception e) when (Log(e)) { }
The first point is worth demonstrating.
static class Program
{
static void Main(string[] args)
{
A(1);
}
private static void A(int i)
{
try
{
B(i + 1);
}
catch (Exception ex)
{
if (ex.Message != "!")
Console.WriteLine(ex);
else throw;
}
}
private static void B(int i)
{
throw new Exception("!");
}
}
If we run this in WinDbg until the exception is hit, and print the stack using !clrstack -i -a
we'll see the just the frame of A
:
003eef10 00a7050d [DEFAULT] Void App.Program.A(I4)
PARAMETERS:
+ int i = 1
LOCALS:
+ System.Exception ex @ 0x23e3178
+ (Error 0x80004005 retrieving local variable 'local_1')
However, if we change the program to use when
:
catch (Exception ex) when (ex.Message != "!")
{
Console.WriteLine(ex);
}
We'll see the stack also contains B
's frame:
001af2b4 01fb05aa [DEFAULT] Void App.Program.B(I4)
PARAMETERS:
+ int i = 2
LOCALS: (none)
001af2c8 01fb04c1 [DEFAULT] Void App.Program.A(I4)
PARAMETERS:
+ int i = 1
LOCALS:
+ System.Exception ex @ 0x2213178
+ (Error 0x80004005 retrieving local variable 'local_1')
That information can be very useful when debugging crash dumps.
When an exception is thrown, the first pass of exception handling identifies where the exception will get caught before unwinding the stack; if/when the "catch" location is identified, all "finally" blocks are run (note that if an exception escapes a "finally" block, processing of the earlier exception may be abandoned). Once that happens, code will resume execution at the "catch".
If there is a breakpoint within a function that's evaluated as part of a "when", that breakpoint will suspend execution before any stack unwinding occurs; by contrast, a breakpoint at a "catch" will only suspend execution after all finally
handlers have run.
Finally, if lines 23 and 27 of foo
call bar
, and the call on line 23 throws an exception which is caught within foo
and rethrown on line 57, then the stack trace will suggest that the exception occurred while calling bar
from line 57 [location of the rethrow], destroying any information about whether the exception occurred in the line-23 or line-27 call. Using when
to avoid catching an exception in the first place avoids such disturbance.
BTW, a useful pattern which is annoyingly awkward in both C# and VB.NET is to use a function call within a when
clause to set a variable which can be used within a finally
clause to determine whether the function completed normally, to handle cases where a function has no hope of "resolving" any exception that occurs but must nonetheless take action based upon it. For example, if an exception is thrown within a factory method which is supposed to return an object that encapsulates resources, any resources that were acquired will need to be released, but the underlying exception should percolate up to the caller. The cleanest way to handle that semantically (though not syntactically) is to have a finally
block check whether an exception occurred and, if so, release all resources acquired on behalf of the object that is no longer going to be returned. Since cleanup code has no hope of resolving whatever condition caused the exception, it really shouldn't catch
it, but merely needs to know what happened. Calling a function like:
bool CopySecondArgumentToFirstAndReturnFalse<T>(ref T first, T second)
{
first = second;
return false;
}
within a when
clause will make it possible for the factory function to know that something happened.
참고URL : https://stackoverflow.com/questions/38497774/catching-exceptions-with-catch-when
'Programing' 카테고리의 다른 글
두 변수가 파이썬에서 동일한 객체를 참조하는지 비교 (0) | 2020.09.22 |
---|---|
WebView 리소스 요청에 사용자 지정 헤더 추가-Android (0) | 2020.09.22 |
특히 malloc의 결과를 캐스팅하는 것이 위험한 것은 무엇입니까? (0) | 2020.09.22 |
MySQL에 PHP 배열을 저장 하시겠습니까? (0) | 2020.09.22 |
TFS에서 비난 기능을 사용하는 방법은 무엇입니까? (0) | 2020.09.22 |