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

The "when" keyword was introduced in C # 6.0, now you can filter the exception in the catch block. But isn't it like the if statement inside a catch block? if so, is it not just syntactic sugar or am I missing something?

For example, a catch try block with the keyword "when":

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

or

 try { … } catch (WebException ex) { if(ex.Status == WebExceptionStatus.Timeout) { //do something } } catch (WebException ex) { if(ex.Status == WebExceptionStatus.SendFailure) { //do something } } catch (Exception caught) {…} 
+53
c # try-catch
Sep 15 '16 at 8:59
source share
4 answers

In addition to the few subtle answers you already have, there is a very important difference between the exception filter and the “if” in the catch block: filters run before the internal finally blocks .

Consider the following:

 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(); } } 

The order of calls differs between M1 and M2 .

Suppose that M1 is called. It calls N (), which calls MakeAMess (). A mess is being made. Then DoSomethingDangerous () throws a MyException. Runtime checking checks to see if there is any catch block that can handle this. The finally block runs CleanItUp (). The confusion is being cleared. Control passes to the catch block. And the catch block calls F (), and then possibly C ().

How about M2? It calls N (), which calls MakeAMess (). A mess is being made. Then DoSomethingDangerous () throws a MyException. Runtime checking checks to see if there is any catch block that can handle this, and there is - it is possible. Runtime calls F () to see if the catch block can handle it, and it can. The finally block runs CleanItUp (), control passes to catch, and C () is called.

Did you notice the difference? In the case of M1, F () is called after the mess is cleared, and in the case of M2, it is called before the mess is cleared. If F () depends on the lack of clutter for its correctness, then you have big problems if you reorganize M1 to look like M2!

There are more than problems with correctness; there are security implications. Suppose the “mess” we create represents the administrator, a dangerous operation requires administrator access, and cleaning does not represent the administrator. In M2, calling F has administrator privileges. In M1, this is not so. Suppose the user has granted several privileges to an assembly containing M2, but N is in the full trust assembly; potentially hostile code in the M2 assembly can gain administrator access through this bait.

As an exercise: how would you write N so that it defends itself from this attack?

(Of course, the runtime is smart enough to know if there are stack annotations that grant or deny privileges between M2 and N and return them before calling F. This is the mess that the executable was executing, and it knows how to handle it correctly but the runtime is not aware of any other mess you made.)

The key conclusion here is that at any time when you handle the exception, by definition, something went terribly wrong and the world is not what you think. Exclusion filters should not depend on their correctness from invariants that were violated by an exceptional condition.

UPDATE:

Ian Rinrose asks how we got into this mess.

This part of the answer will be somewhat hypothetical, as some of the design decisions described here were taken after I left Microsoft in 2012. However, I have repeatedly talked with the developers of the language about these problems, and I think I can give a fair presentation of the situation.

The decision to create filters is made before the final blocks were made in the earliest days of the CLR; person to ask if you want the fine details of this design decision to be Chris Brumme. (UPDATE: Unfortunately, Chris is no longer available for questions.) He had a blog with a detailed exegesis of the exception handling model, but I don't know if he is still on the Internet.

This is a smart decision. For debugging purposes, we want to know before the final blocks are triggered, whether this exception will be handled, or if we are in the "undefined behavior" scenario of a completely unhandled exception that destroys the process. Because if the program is running in the debugger, the undefined behavior will include a break at the point of the unhandled exception before finally blocks are run.

The fact that this semantics introduces security and correctness issues has been well understood by the CLR team; I actually discussed this in my first book, which was posted many years ago and twelve years ago on my blog:

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

And even if the CLR team wanted to, it would be a terrific change to “fix” semantics.

This function has always existed in CIL and VB.NET, and the attacker controls the language for implementing the code using a filter, so the introduction of this function in C # does not add any new attack surface.

And the fact that this feature, which introduces the security problem, has been “in the wild” for several decades and, as far as I know, has never been the cause of a serious security problem, indicates that it is not a very fruitful opportunity for attackers.

Why then was there a function in the first version of VB.NET and took more than a decade to turn it into C #? Well, “why not” such questions are difficult to answer, but in this case I can do it quite easily: (1) we had many other things on our mind, and (2) Anders considers this feature unattractive. (And I, too, am not enthusiastic about this.) For many years, this has moved him to the list of priorities.

How did he make it high enough in the priority list to be implemented in C # 6? Many people have requested this feature, which always indicates this. VB already had this, and the C # and VB teams wanted to have parity when possible, at a reasonable price, so that is the point. But there was a big turning point: there was a scenario in the Roslyn project where exclusivity filters would be really useful. (I don’t remember what it was: go to the source code if you want to find it and report!)

As a language developer as well as a compiler developer, you should be careful not to give priority to functions that make life easier for a compiler writer; most C # users are not compilers, and they are clients! But ultimately, having a set of real-world scenarios where this function is useful, including some that annoyed the compiler team, balanced the balance.

+70
Sep 15 '16 at 15:32
source share

But isn't it like the if statement inside a catch block?

No, because your second approach without when will not reach the second Catch if ex.Status== WebExceptionStatus.SendFailure . With when first Catch would be skipped.

Thus, the only way to handle Status without when is through logic in a single Catch :

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

else throw ensures that only WebExceptions are processed here using status=Timeout or SendFailure , similar to when . All others will not be processed and the exception will be thrown. Please note that it will not be caught by the last Catch , so the difference with when remains. This shows one of the benefits of when .

+46
Sep 15 '16 at 9:01
source share

Does this match the if statement inside the catch block?

No. It acts more like a “discriminator” in the interests of an exception throwing system.

Remember how an exception is thrown twice?

The first “throw” (those “first chances” of Exceptions referred to by Studio) indicate to Run-Time to find the closest exception handler that can handle this type of exception and collect all “finally”, blocks between “here” and "there".

The second “roll” expands the call stack, executing each of these “final” blocks in turn, and then delivers the execution mechanism to the entry point of the located exception handling code.

Previously, we could only distinguish between different types of exceptions. This decorator gives us finer control over the grain, but only catches a particular type of exception that is in a state of that we can do something about.
For example, (from over the air), you might want to handle a “Database Exception” that indicates a broken connection, and when that happens, try connecting again.
Many database operations call "Database Exception", but you are only interested in the specific "subtype" of them, based on the properties of the Exception object, all of which are available to the exception system.

The if if statement inside the catch block will achieve the same end result, but it will "cost" more at runtime. Since this block will catch all and all of the “Database Exceptions”, it will be called for all of them, even if it can only do something useful for the [very] small part. It also means that you need to rethrow [all] Exceptions that you cannot handle, which just repeats the whole, two-pass, handler, final harvest, exception-throwing farago all again.

Analogy: [very strange] toll bridge.

By default, you need to “catch” every car so that they pay a fee. If, say, cars driven by city employees are exempt from duty (I really said that this is strange), then you need to stop cars driven by someone else.

You can stop each car and ask:

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

Or, if you had some way of “interrogating” a car as you got closer to it, you could ignore those driven by city employees, for example:

 catch( Car car ) when car.needsToPayToll() { takePayment( car ); } 
+11
Sep 15 '16 at 10:59
source share

Extension response to Tim.

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

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

When the keyword works like if. A, when the condition is a predicate expression that can be added to the catch block. If the predicate expression evaluates to true, the corresponding catch block is executed; otherwise, the catch block is ignored.

A wonderful explanation is given on the C # 6.0 exception filter and keyword

0
Sep 15 '16 at 9:10
source share



All Articles