program tip

try catch 블록의 "when"키워드가 if 문과 동일합니까?

radiobox 2020. 12. 29. 06:56
반응형

try catch 블록의 "when"키워드가 if 문과 동일합니까?


C # 6.0에서 "when"키워드가 도입되었으므로 이제 catch 블록에서 예외를 필터링 할 수 있습니다. 그러나 이것은 catch 블록 내부의 if 문과 같지 않습니까? 만약 그렇다면, 그것은 단지 문법적인 설탕이 아닌가 아니면 내가 뭔가를 놓치고 있는가?

예를 들어 "when"키워드가있는 try catch 블록 :

try { … } 
catch (WebException ex) when ex.Status == WebExceptionStatus.Timeout {
   //do something
}
catch (WebException ex) when ex.Status== WebExceptionStatus.SendFailure {
   //do something
}
catch (Exception caught) {…}

또는

try { … } 
catch (WebException ex) {
   if(ex.Status == WebExceptionStatus.Timeout) {
      //do something
   }
}
catch (WebException ex) {
   if(ex.Status == WebExceptionStatus.SendFailure) {
      //do something
   }
}
catch (Exception caught) {…}

이미 여기에있는 몇 가지 훌륭한 답변 외에도 예외 필터와 catch 블록의 "if"간에는 매우 중요한 차이점 이 있습니다. 필터는 inner finally 블록 이전에 실행 됩니다.

다음을 고려하세요:

void M1()
{
  try { N(); } catch (MyException) { if (F()) C(); }
}
void M2()
{
  try { N(); } catch (MyException) when F() { C(); }
}
void N()
{
  try { MakeAMess(); DoSomethingDangerous(); } 
  finally { CleanItUp(); }
}

호출 순서는 M1과 M2간에 다릅니다 .

M1이 호출되었다고 가정합니다. MakeAMess ()를 호출하는 N ()을 호출합니다. 엉망이됩니다. 그런 다음 DoSomethingDangerous ()에서 MyException이 발생합니다. 런타임은이를 처리 할 수있는 catch 블록이 있는지 확인합니다. finally 블록은 CleanItUp ()을 실행합니다. 엉망이 정리되었습니다. 제어는 catch 블록으로 전달됩니다. 그리고 catch 블록은 F ()를 호출 한 다음 C ()를 호출합니다.

M2는 어떻습니까? MakeAMess ()를 호출하는 N ()을 호출합니다. 엉망이됩니다. 그런 다음 DoSomethingDangerous ()에서 MyException이 발생합니다. 런타임은이를 처리 할 수있는 catch 블록이 있는지 확인합니다. 런타임은 F ()를 호출하여 catch 블록이 처리 할 수 ​​있는지 확인합니다. finally 블록은 CleanItUp ()을 실행하고 제어가 catch로 전달되고 C ()가 호출됩니다.

차이를 느꼈습니까? M1의 경우 F ()는 엉망이 정리 된 후 호출 되고 M2의 경우 엉망이 정리 되기 전에 호출 됩니다. F ()가 정확성에 대한 엉망이 없다는 것에 의존한다면 M1을 M2처럼 보이도록 리팩토링하면 큰 문제가됩니다!

여기에는 정확성 문제 이상의 것이 있습니다. 보안에 대한 영향도 있습니다. 우리가 만들고있는 "메쉬"가 "관리자 가장"이고 위험한 작업에 관리자 액세스가 필요하며 정리가 관리자를 가장하지 않는다고 가정합니다. M2에서 F에 대한 호출 은 관리자 권한을 갖습니다 . M1에서는 그렇지 않습니다. 사용자가 M2를 포함하는 어셈블리에 거의 권한을 부여하지 않았지만 N이 완전 신뢰 어셈블리에 있다고 가정합니다. M2 어셈블리의 잠재적으로 적대적인 코드는이 유인 공격을 통해 관리자 액세스 권한을 얻을 수 있습니다.

연습으로 :이 공격을 방어하기 위해 N을 어떻게 작성 하시겠습니까?

(물론 런타임은 M2와 N 사이에 권한을 부여하거나 거부하는 스택 주석 이 있는지 알 수있을만큼 똑똑 하고 F를 호출하기 전에이를 되돌립니다. 이는 런타임이 만든 엉망이며 올바르게 처리하는 방법을 알고 있습니다. 그러나 런타임은 사용자가 만든 다른 혼란에 대해 알지 못합니다 .)

여기서 중요한 점은 예외를 처리 할 때마다 정의에 따라 무언가 끔찍하게 잘못되었고 세상이 생각한대로되지 않는다는 것입니다. 예외 필터는 예외 조건에 의해 위반 된 불변에 대한 정확성에 의존해서는 안됩니다.

최신 정보:

Ian Ringrose는 우리가 어떻게이 혼란에 빠졌는지 묻습니다.

This portion of the answer will be somewhat conjectural as some of the design decisions described here were undertaken after I left Microsoft in 2012. However I've chatted with the language designers about these issues many times and I think I can give a fair summary of the situation.

The design decision to make filters run before finally blocks was taken in the very early days of the CLR; the person to ask if you want the small details of that design decision would be Chris Brumme. (UPDATE: Sadly, Chris is no longer available for questions.) He used to have a blog with a detailed exegesis of the exception handling model, but I don't know if it is still on the internet.

합리적인 결정입니다. 디버깅 목적으로 우리 는 finally 블록이 실행 되기 전에이 예외가 처리 될 것인지 또는 완전히 처리되지 않은 예외가 프로세스를 파괴하는 "정의되지 않은 동작"시나리오에 있는지 알고 싶습니다 . 프로그램이 디버거에서 실행중인 경우 정의되지 않은 동작에는 finally 블록이 실행 되기 전에 처리되지 않은 예외 지점에서 중단이 포함됩니다 .

이러한 의미 체계가 보안 및 정확성 문제를 야기한다는 사실은 CLR 팀에 의해 매우 잘 이해되었습니다. 사실 나는 나의 첫 번째 책에서 그것에 대해 논의했다. 그것은 지금 아주 오래전에 출간되었고 12 년 전에 나의 블로그에서 나왔다.

https://blogs.msdn.microsoft.com/ericlippert/2004/09/01/finally-does-not-mean-immediately/

CLR 팀이 원하더라도 지금 의미 체계를 "수정"하는 것은 큰 변화가 될 것입니다.

이 기능은 항상 CIL 및 VB.NET에 존재 해 왔으며 공격자는 필터를 사용하여 코드의 구현 언어를 제어하므로 C #에 기능을 도입해도 새로운 공격 표면이 추가되지 않습니다.

그리고 보안 문제를 야기하는이 기능이 수십 년 동안 "야생"되어 왔으며 내가 아는 한 심각한 보안 문제의 원인이 된 적이 없다는 사실은 공격자에게 그다지 유익한 방법이 아니라는 증거입니다.

Why then was the feature in the first version of VB.NET and took over a decade to make it into C#? Well, "why not" questions like that are hard to answer, but in this case I can sum it up easily enough: (1) we had a great many other things on our mind, and (2) Anders finds the feature unattractive. (And I'm not thrilled with it either.) That moved it to the bottom of the priority list for many years.

How then did it make it high enough on the priority list to be implemented in C# 6? Many people asked for this feature, which is always points in favour of doing it. VB already had it, and C# and VB teams like to have parity when possible at a reasonable cost, so that's points too. But the big tipping point was: there was a scenario in the Roslyn project itself where exception filters would have been really useful. (I do not recall what it was; go diving in the source code if you want to find it and report back!)

As both a language designer and compiler writer, you want to be careful to not prioritize the features that make only compiler writer's lives easier; most C# users are not compiler writers, and they're the customers! But ultimately, having a collection of real-world scenarios where the feature is useful, including some that were irritating the compiler team itself, tipped the balance.


But isn't this the same as a if statement inside a catch block?

No, because your second approach without when won't reach the second Catch if the ex.Status== WebExceptionStatus.SendFailure. With when the first Catch would have been skipped.

So the only way to handle the Status without when is to have the logic in one catch:

try { … } 
catch (WebException ex) {
   if(ex.Status == WebExceptionStatus.Timeout) {
      //do something
   }
   else if(ex.Status == WebExceptionStatus.SendFailure) {
      //do something
   }
   else
      throw; // see Jeppe's comment 
}
catch (Exception caught) {…}

The else throw will ensure that only WebExceptions with status=Timeout or SendFailure are handled here, similar to the when approach. All others will not be handled and the exception will be propagated. Note that it won't be caught by the last Catch, so there's still a difference to the when. This shows one of the advantages of when.


isn't this the same as a if statement inside a catch block?

No. It acts more as a "discriminator" for the benefit of the Exception-throwing system.

Remember how Exceptions are thrown twice?

The first "throw" (those "first-chance" Exceptions that 'Studio goes on about) tells the Run-Time to locate the nearest Exception Handler that can deal with this Type of Exception and to collect up any "finally" blocks between "here" and "there".

The second "throw" unwinds the call stack, executing each of those "finally" blocks in turn and then delivers the execution engine to the entry point of the located Exception handling code.

Previously, we could only discriminate between different Types of Exception. This decorator gives us finer-grain control, only catching a particular Type of Exception that happens to be in a state that we can do something about.
For example (out of thin air) you might want to handle a "Database Exception" that indicates a broken connection and, when that happens, try to reconnect automatically.
Lots of database operations throw a "Database Exception", but you're only interested in a particular "Sub-Type" of them, based on the properties of the Exception object, all of which are available to the exception-throwing system.

An "if" statement inside the catch block will achieve the same end result, but it will "cost" more at run-time. Because this block will catch any and all "Database Exceptions", it will be invoked for all of them, even if it can only do something useful for a [very] small fraction of them. It also means that you then have to re-throw [all] the Exceptions that you can't do anything useful with, which is just repeating the whole, two-pass, handler-finding, finally-harvesting, Exception-throwing farago all over again.

Analogy: A [very strange] Toll bridge.

By default, you have to "catch" every car in order for them to pay the toll. If, say, cars driven by city employees are exempt from the toll (I did say it was strange), then you only need to stop cars driven by anybody else.

You could stop every car and ask:

catch( Car car ) 
{ 
   if ( car.needsToPayToll() ) 
      takePayment( car ); 
} 

Or, if you had some way of "interrogating" the car as it approached, then you could ignore those driven by city employees, as in:

catch( Car car ) when car.needsToPayToll() 
{ 
   takePayment( car ); 
} 

Extending the answer by Tim.

C# 6.0 introduces a new feature exception filter and a new keyword when.

Is the "when" keyword in a try catch block the same as a if statement?

The when keyword works like if. A when condition is a predicate expression, which can be appended to a catch block. If the predicate expression is evaluated to be true, the associated catch block is executed; otherwise, the catch block is ignored.

A wonderful explanation is given on C# 6.0 Exception Filter and when Keyword

ReferenceURL : https://stackoverflow.com/questions/39506949/is-the-when-keyword-in-a-try-catch-block-the-same-as-an-if-statement

반응형