Living with unchecked exceptions, part two

Thanks everyone who contributed to my earlier post about living with unchecked exceptions. There were over fifty comments in there that directly or indirectly addressed my questions.

The major takeaway here is: exceptions are a bit of a mess in C#. The language semantics and the organization (or lack thereof) of the exception hierarchy makes it hard to know what exceptions you should be catching and which you should be letting go. A lot of people left a lot of great comments but the one that resonated most strongly with me was

I think the whole notion of “handling” exceptions is a bit of a fool’s game. I can probably count on the fingers of one hand the times where I’ve been able to catch a specific exception and then do something intelligent with it. 99% of the time you should either catch everything or catch nothing. When an exception of any type occurs, rewind to a stable state and then either abort or continue.

That’s harsh but I think fair.

This comment suggests that Pokemon handling — gotta catch ‘em all! — is the way to go. I was surprised that almost a third of commenters expressed some support for catch(Exception) as this has historically been described as a bad practice by Microsoft. C# was, as I often say, designed to be a “pit of success” language, where the right thing and the easy thing are the same. That noble goal seems to have not been met here; if catch(Exception) is both the wrong thing to do and the easy thing to do, that’s because the right thing to do is too hard.

The majority of commenters said they had fixed bugs caused by failing to catch a specific exception, though that ranged in a spectrum from “one or two” to “rarely”, to “often”.

A third said that they used MSDN and other forms of documentation (including XML doc comments) to determine what exceptions ought to be caught. MSDN came in for both praise and criticism here; some parts of it are excellent, some are unclear. Third party library documentation came in for a thorough drubbing; no one believes it.

A quarter said that they used some kind of trial-and-error — debugging, testing, reading log files, getting crash dumps — to figure out what exceptions needed to be handled.

Several commenters said that they used decompilers to check what exceptions a library method could throw. And several more expressed the desire for a static analysis tool that would assist them when calling code that could throw an exception.

Again, the frustration was summed up by a comment:

Every try/catch block is an exercise in frustration because you ever know if you’ve caught the right types until it blows up in production.

Quite a few commenters noted that the exception catching system treats the type of the exception as the most important information about it, but a type alone is not enough information to make a good judgment about how to handle an exception.

I asked whether consistency was important. That is, whether seeing that 9 times out of 10 that Frob() is called, it is wrapped in a try{...}catch(FooException), is good evidence that your next call to Frob() should similarly be wrapped. Opinions varied widely. The responses were pretty evenly distributed across a spectrum that went

  • absolutely not
  • probably not but at least give it some thought
  • it’s hard to say, I’d take it on a case by case basis
  • probably yes, follow the pattern unless there is good reason to deviate
  • absolutely yes

And a great many people pointed out that if nine times out of ten a method call is in a block that handles a specific exception then the method in question might better be refactored into a helper method that did the catch for you.

Thanks again all, this was very helpful!

About these ads

35 thoughts on “Living with unchecked exceptions, part two

  1. Perhaps the root of the problem is that the C# exception mechanism is quite heavily overloaded. When I receive an exception, I know only that a thing has happened that wasn’t considered part of the “normal” flow of the method by its designer. I need to consult the documentation (if there is any) to determine how to handle each error on a case-by-case basis.

    We’ve seen in .NET the Parse methods of the common types evolving to have TryParse counterparts – acknowledgement that sometimes a failure is anticipated: it’s not exceptional. In this scenario (if we ignore the addition of TryParse) it would be quite suitable to catch exceptions from Parse. Except, now I have to think about which exceptions I should try catching. Are all the possible exceptions something I should catch, or just ones which are about the format of the input?

    What we lack is some way to clearly indicate what each exception means in the context of the method throwing it. Having a common family of exceptions adds an implicit constraint (or perhaps best-practice) that we should avoid creating new types and re-use what’s already available. This means we have to translate what that exception type meant to the developer at the time they wrote the method. In some cases, the same error can be expressed in subtly different ways. I answered this StackOverflow question years ago http://stackoverflow.com/questions/1453200/exception-for-missing-data; thinking about it now it seems somehow wrong that there should be multiple choices about which exception to throw based on some subtlety in the implementation details.

    Most of the time, what I care about is whether the exception is a failure in a precondition or is failure from some external component or underlying piece of hardware.

    In the case of a precondition failure, it’s either my fault for getting the system in a bad state (failure to open a connection, etc.), or some failure in the inputs, and those are both reasonable sub-classes to handle differently. Sometimes the inputs are from a user and could be invalid and sometimes they really should be good.

    In the case of an external component failure, I’m either going to retry the operation, or try to revert to a safe prior state. Much of the time, it will be a combination of both.

  2. Pokemon exception handling is ugly, but I’m not sure what alternative there is in cases where a caller cannot possibly anticipate all the reasons that a method might fail without damaging system state. I’ve sometimes wished for more virtual methods in exceptions to ask “do you contain a X exception” and “are you resolved yet” [allowing the creation of composite exceptions which would be processed by handlers that are interested in any constituent exception, but continuing to bubble up until all constituent exceptions have been handled]. Such things would probably not be a bad idea, but the more about things, the more I think what’s most needed is a means of letting cleanup code know of any exception preceding it, e.g. by supporting a `finally(Exception ex)` syntax and allowing `using` to use an interface `IDisposableEx.Dispose(Exception ex)` (in either case, `ex` would be null if there is no exception). Such a feature would allow robust code to be written as something like:

    using(this.NewFaultGuard(Invalidate))
    {
    methods which would leave object
    in a corrupt state if they don’t all succeed
    }

    rather than:

    try
    {
    methods which would leave object
    in a corrupt state if they don’t all succeed
    }
    catch(Exception ex)
    {
    Invalidate(ex);
    throw;
    }

    The former pattern would require one line of code and one pair of scoping braces [assuming NewFaultGuard is an extension method which returns a struct encapsulating a T and an Action(T,Exception)]. The latter would require four lines of code and two pairs of scoping braces, and also alters the semantics of stack unwinding.

  3. Interesting! This is my take on this:

    I would categorize exceptions in two categories: error exceptions and information exceptions. (This depends on the “catching” logic rather than the exception’s class.)
    I think that 99% of the exceptions that can be thrown in an application are in the former category – exceptions that are thrown when an error is detected (bad arguments, wrong state, etc). Good practice will be to keep a valid state and let them pass on to the message queue to be displayed to user (or logged). Never catch and ignore those!
    The other 1% of the exceptions are the ones that provide information and might be caught. For example, a WebException can provide information about the connection that you want to handle. Or maybe if you want to use an XXX method as a TryXXX (where a TryXXX method is not provided).

    If we’re thinking about checked exceptions (or some other exceptions framework), these are pretty much useless for the first category, and might be useful for the second. Even the second category is not consistent – if a “Parse” method throws an exception when it fails, then sometimes you need to handle it and sometimes just let it pass on, depending on the use case.

  4. It’s an important and interesting discussion and I agree with a lot you say Eric, especially the quote from the previous comment. Assuming that an exception really IS exceptional, there are basically three things you want exception systems for, as I see it. In order:

    1. An ability to handle a priori known conditions that the programmer accounted for. These exceptions aren’t really ‘exceptional’, since they were expected. Validation exceptions and ‘DB deadlock in transaction’ exceptions are typical examples. Exception types (and codes) work fairly well for this, although it clearly requires that documentation is good.

    2. A possibility to isolate the exception to the smallest possible section of your application, and if needed shut that part down or rewind it to a stable state. This is currently very hard and highly manual/intuitive! How can we automatically isolate the exception to any part? We trust ValidationException to be ok to handle and just ask the user to correct input, but we die on StateCorruptedException. How about ArgumentException? Should the app die or not? We often have nothing but good guesses, tradeoffs and pragmatism. Simply put, exception types aren’t very good for this task, as a type isn’t per se associated with an isolated part of an application or application state.

    3. Having the exception mechanisms help programmers defer information about the exceptional situation in order to to various “good things”: display messages, start helper agents that fix the situation etc. Specific types of exceptions can be good for this purpose, but simple types are very crude. It would be much easier to build powerful helper tools if there was more context information associated with the exception, such as logs of actions leading up to the situation, suggested ways to remedy the problem, collected statistics, information texts in different languages and if they’re appropriate to show for different users. Clearly a complex problem.

  5. The advice against catch(Exception) infuriates me; there are plenty of cases where there is no other option. I work on Office add-ins fairly frequently and if you ever once let an exception bubble up then your add-in gets disabled with no clear indication to the user why (except a tiny error prompt that no real user ever reads) and all they know is “my button disappeared!” If my add-in genuinely did something bad this would be appropriate, but since I have no way of verifying which exceptions I “should” expect I have no choice but to catch all of them.

    • Yes, that’s a good example: it’s all about the user perception. If they see a scary ‘technical’ system error message, they would say, ‘the program crashed, I don’t want this rubbish’. If, on the other hand, it’s some nice, friendly message from the program itself, and it keeps running, they’ll just report an issue, but will keep using it.
      And there are other situations: a service, an online store, or any kind of web application that the users expect to be able to check periodically (a weather report?). If you release any of those into production, by all means log all the exceptions that happen under the bonnet, but you dear not let it crash, especially if you’re on leave at the time it does. :-)

  6. I haven’t written a ton of Java, but when I have, the existence of checked exceptions made me much more likely to handle exceptions where they were thrown. And by “handle”, I mean wrap in some other exception type that was already in the list of checked exceptions thrown by my method and then throw that.

    When writing C#, I am much more likely to write pokemon handlers (if I do any handling whatsoever) because I usually do the handling two or three levels back in the call stack, where all I have to do is tell the user that there was an error and then clean up nicely.

  7. “That noble goal seems to have not been met here; if catch(Exception) is both the wrong thing to do and the easy thing to do, that’s because the right thing to do is too hard.”
    The noble goal hasn’t been met, but I’m not sure how much difference checked exceptions in the original C# implementation would have made. I also can’t think of anything that should be done by C# now to correct the problem.
    For me it’s really been a problem only when using certain libraries/APIs and I tend to blame the designers of those libraries/APIs, not the language. For popular libraries, especially the BCL, static analysis could really help flag problems, but I believe it belongs in tools outside the compiler. Of course that could be good for Coverity.

    • Well I must say that I trust any Java library that uses checked exceptions *way* more than I trust Java libraries that use RuntimeExceptions or third party .NET libraries.

      In .NET if you use your average third party application and you don’t want to go through the complete source code (if that was even possible) of each release the only way to be sure that you won’t have some unexpected exception turns up is to use “pokemon handling”.

      Yes in theory it’d be awesome if libraries documented all the exceptions they throw, but in my experience if you don’t force people to document it, they won’t – or at least they won’t keep it up to date.

      The problem on the other side is that Java’s exception system was implemented before they came up with generics and it sadly shows (the throws clause allows union types, but that’s the *only* place they’re valid, no way to specify that something doesn’t actually throw an exception, and so on – the problems show especially badly in Java8 with lambdas).

  8. I’ve used catch (Exception ex) in two ways:

    1) At the top level of a program. I almost never want a program to completely die – I want it to report what happened. Usually this means collecting the exception details and putting them somewhere the user will find it (STDOUT, a log file, etc). That means a general catch handler.

    2) When I don’t trust the code under the exception. A recent example is a host that would run some PowerShell scripts. If the script throws an error, I want it to (again) report the error in a helpful way rather than crashing the host. Again, this is a general catch handler.

    Both of these cases are the minority in terms of lines of code affected, but they do happen.

  9. I’m going to agree with the sentiment that there are really two different things going on with exceptions: One is “An error happened – no way to recover” vs. “Something unexpected happened – do you care?” The 2nd category is the interesting one. Something like “Hey, this data set that you gave me contains some unexpected stuff at the end, but I was able to process most of that. Here is the work I was able to do stuffed into a field on the custom exception. Here’s where things went awry. You (the caller) can decide what to do next” is interesting. Something like “Hey, you called Math.Sqrt on a negative number” would ideally be more of a compile-time or analysis thing, although I understand that it’s more complicated than that in a non functional language.

  10. Not sure if related, I’d like to share my experience about “vexing exceptions” and making it easy to do the right thing.
    A while ago I had to design a return type for some function which could either be successful and return some data, either fail in a particular way, either fail in a different way (with some other data explaining why if failed).

    The easy way would have been to create two exception classes and have the plain “success” type as a return type. But I knew that the failures would have to be caught, and I wanted to avoid creating those vexing exceptions, so I created a hierarchy of three classes representing the success case and the two error cases. The return type of the function was their base class.

    The problem is, it took an awfully big number of lines of code to do such a simple thing! C# doesn’t make it easy to take the right decision here, at least when compared to more functional languages like F# that could easily have defined such a non-vexing return type in three LoC with the help of discriminated unions. Such a feature would definitely be a big help for avoiding to create vexing exceptions.

    Oh and I just got an idea: rethrowing should really be the default, and there should be a ‘swallow’ keyword that would be required to prevent rethrowing. This would make it a lot easier to search an unknown codebase for swallowed exceptions.

    • I think that the biggest problem with vexing exceptions is the word “vexing.”

      The fact is that often you get to a point in an operation where you just realize “this operation just isn’t going to work, I want out.” Throwing a custom exception is a great way to do that (especially if you’re n levels deep in a recursive descent parser.)

      The problem is some very early guidance, coming largely from the performance team that exceptions are evil, and not to use exceptions for flow control. Rico Mariani once said to only throw an exception when it would be ok for the computer to beep three times.

      The problem is because of this guidance, people write lots of code, like you did, just to avoid using exceptions; as though avoiding exceptions was a goal in itself. Not only is it a lot of work, it makes error handling inconsistent, and error handling invades all the code — the problems that exceptions are supposed to fix.

      The solution is to disregard anyone who’s advice is “Don’t use language feature X.” (I’ve heard this applied to recursion, tertiary operators, postincrement operators, exceptions and more.) Exceptions aren’t vexing, they’re expensive. Using an expensive operation to do something simple is vexing. Using something expensive to do something hard is why I have 8 cores on my machine, I’ll just pay the tax.

      • Not sure if you both are talking about the same thing, if is the case, yes, there is a problem with the “vexing” word, another problem with exceptions, and another problem with “vexing workarounds”, let’s get, for example, the int.Parse and int.TryParse, the first throw vexing exceptions, the second presents a vexing workaround, well, a string may fail to be converted to int because: 1) Not a number; 2) Too big or too small; 3) Not na integer, int.Parse gives a separate exception for overflow and invalid format, the 3 was forgotten, the TryParse don’t give clue about what went wrong, vexing, don’t you think? Now let’s suppose someone decides to “fix” TryParse by, instead of returning bool returning a enum or another custom class with details about what went wrong, isn’t this just reinventing exceptions?

        Oh, BTW, in my code int.TryParse is more common than int.Parse, TryParse is just more practical for most cases, exception handling doesn’t fit well in formulas, but I use int.Parse in code when I shall throw na exception if the number is not in correct format.

        Now let’s go with a mroe complicated example, XML parsing, there is a lot of code in a parser, sometimes, a lot of levels and a lot of thing wich may go wrong, how is it supposed to behave? Throwing a generic exception at first error like XmlDocument.Load does? Don’t think so… Maybe, throwing a custom exception saying the error, the line and column? Don’t think so too, if I present this to the user he will think it is a pain to fix what the program said, try again, fix the next error and so. The last option would be, somehow, know every detectable error at once, like, invalid character “0x9087″ at line 56, missing bracket at line 80, missing required atribute “xyz” at line 98, etc, wich don’t fits well in the exception model.

        To be honest I don’t know if .Net have any class to validade XML like that, what I am actually doing in one app is to do the XmlDocument.Load catch exeptions, tell the user “something is wrong with the file you uploaded, revise everything you did and try again”, users hate it and often contact user support instead of revising by themselves, but I don’t have time (or better, this particular feature isn’t important enough for my boss to give me time) for improving and already realized how frustrating improving it will be, there are a lot of errors from user support wich I didn’t think were possible.

        • So you don’t tell them where the first error appeared that actually caused the parser to throw up? Yeah I’d be annoyed as hell from that too. On the other hand if it doesn’t take minutes to get the answer if there’s an error I wouldn’t have any problem repeatedly fixing one after the other (but if you don’t tell me where to start, it gets a whole lot harder!)

          And parsing XML documents after an error is fraught with peril (lots of ambiguities and possible false positives there). Any parser intended for websites may work, because they’re used to deal with broken data and may work reasonably well at finding a good new parsing point.

  11. My general rule is that there are a few reasons to catch an exception where it occurs:

    1. You can actually do something to fix the problem. In this case, however, you are kind of using the exception mechanism as a flow-of-control and the code should probably be rewritten.

    2. You need to add context to the exception. Maybe you’re processing a bunch of widgets in a loop and you want to know which widget caused the exception so you catch it and wrap it in a more specific exception with contextual information. Once again though it seems like you should be able to handle this with error checking and flow-of-control.

    3. To prevent the exception from leaking up to the user. In this case it’s catch(Exception), log it, and show a friendly error page/screen/whatever to the user.

    In my experience (14 years now of .NET) #3 is by far the most common case, and it really only needs to be done at the top of the call stack. The “don’t catch Exception” advice has always seemed stupid to me in light of the above philosophy which, quite frankly, has always served me pretty well.

    • Well, to be honest, exceptions are a flow-of-control tool. And that reminds me of a song^H^H^H^H story from a couple of years ago. There was some hand-written parsing code of this sort:

      void ParseInput(string s) {
      ParseAsFirst(s);
      ParseAsSecond(s);
      throw new CantParseException(“Can’t parse!”);
      }

      void ParseAsFirst(string s) {
      // some parsing code
      if (ok) { throw new ResultFirstException(result); } else { return; }
      }

      void ParseAsSecond(string s) {
      // some parsing code
      if (ok) { throw new ResultSecondException(result); } else { return; }
      }

      so in the end, it had to be used like this:

      try { ParseInput(s); }
      catch (ResultFirstException ex) { /* ex.data is the parsing result, and is of type First */ }
      catch (ResultSecondException ex) { /* ex.data is still the parsing result, but of type Second */ }
      catch (CantParseException ex) { /* well, can’t parse as neither First or Second value */ }

      This story has a good ending: when the author was asked about this… quite unorthodox control flow (which, besides being “weird”, is actually only guilty of being “slow”) he said that he wanted to have a base class ParsingResult, derive from it ResultFirst, ResultSecond, ResultCantParse classes, but couldn’t write a type-driven switch (he didn’t find instanceof), so that’s what he came up with. He then was taught about instanceof, about returning null and when it’s ok, and also of the existence of Haskell with its algebraic types. And yes, the code was re-written in more traditional style.

  12. It seems to me that exceptions as implemented in Java and C# simply do a bad job when it comes to failure handling. They work well for *error* handling – that is, the case where something unexpected has happened and you want to unwind the entire stack until you reach a generic error logging / reporting facility. However, for expected failures (e.g. number can’t be parsed, network connection broke) trying to use exceptions gives me headaches, and I don’t think that the problem is with the fact that the exceptions are unchecked (though that does not mean that I like unchecked exceptions).

    One of the main problems I see is that you have to catch such failure exceptions immediately, or risk losing the context that you would need to sensibly handle or even identify the failure. If your try-block spans more code than just the call to e.g. int.Parse, can you still be sure that the OverflowException was actually thrown there, and not some other code in that block? But if you have to catch failure exceptions immediately, you have to clutter your code with try-catch blocks that break up your happy-path code and make it hard to follow – exactly what exceptions were supposed to prevent.

    • Addendum to clarify: I think that cancelling out of a “deep” operation would be useful for failures, too, not just errors. I would love to see a way to do this without losing the precious context information (which part of the operation failed) and without requiring lots of catching and rethrowing.

    • One good thing was the introduction (starting in v2.0) of the TryXxx operations (for example, Int32.TryParse). Whoever wrote the original Double type (which had Double.TryParse from the get go) should be thanked. His/her code became the model.

      Using exceptions to handle malformed user input is an example of using a sledge hammer where a stapler makes more sense.

  13. While much of my code falls into the catch everything and log it pattern there are specific cases that analyse exceptions to determine whether they indicate transient, retryable conditions e.g. SQLExceptions with number 1205 indicating a deadlock or -2 indicating a connection error such as during a failover. Similarly Entity Framework indicates optimistic concurrency clashes and it sometimes happens with data communications when you can’t tell if a connection is stale until you try to use it but a new connection might work. Azure has similar issues. My systems have to run continuously and robustly without human intervention for months at a time so the software has to decide what is transient and what is fatal and, in the former case, limit the number of retries to avoid infinite loops. Experience and trial and error have been the way to distinguish the errors. At times I’ve even had to resort to analysing the text of the exception message which isn’t great for localisation.

  14. The major takeaway here is: exceptions are a bit of a mess in C#.

    I heartily agree.

    Quite a few commenters noted that the exception catching system treats the type of the exception as the most important information about it, but a type alone is not enough information to make a good judgment about how to handle an exception.

    My feeling here is that a key part of this perception is that the type hierarchy of exceptions isn’t well enough thought through. Since the type doesn’t tell you enough by itself, you look for other information about the exception, sometimes in vain.

    But the type could and should. At least a broad categorisation into “execution environment is stuffed, give up” or “app-level error, recovery is possible” is possible.

    However, I suppose type doesn’t have to be the key – I suppose a root exception class that just declared a bunch of properties that provided that same info, and a catch that filtered by those properties, could work pretty well. People could no doubt think of other schemes.

    Either way, what I do think is key is that the exception handling has to be thought through. Everyone writing a new exception or throwing one needs to consider how to advertise to the person catching it what they should do with it. They need to consider whether they are notifying about a fatal, exogenous, etc, etc problem; whether a retry is at all worthwile; whether continued use of the objects involved is advisable.

  15. “Quite a few commenters noted that the exception catching system treats the type of the exception as the most important information about it, but a type alone is not enough information to make a good judgment about how to handle an exception.”

    Let’s take a C++ wrapper around a C library. Almost always it introduces UnderlyingApiException which wraps around error codes from the library — and that’s it. That tends to lead to this:

    catch (UnderlyingApiException& e) {
    if (e.err_code() == FILE_NOT_FOUND) {
    // that’s ok, we are just probing, continue going
    }
    else {
    throw;
    }
    }

    Obviously, one could make a separate class derived from UnderlyingApiException for every error code, but jeez. Writing 100 classes is way more boring than writing an enum with 100 values, or 100 integer constants, because there is more bolerplate.

    • I’d suggest that one exception class per error code is too fine-grained and not a great idea, even if you could be bothered.

      What I’d look for in that situation would be broad categories of error codes that each had similar error-handling needs, and make one class per category. All would preserve the original code for logging.

      So there would be RetryableApiException, YouCalledMeWrongApiException, ExternalSystemIsntWorkingApiException and IveSmashedTheHeapHaHaHaApiException. Or something.

  16. To make something inteligent in the error handler the programmer needs information, sometimes (actually, almost always) this information doesn’t fit in a generic class, so, to include enough information one would create almost one exception type per error code.

    I mean, if redesigned, currently exceptions misses a lot of importante information.

    • I’ve got to say I really don’t see this in practice. Ever. If a different exception class is required for every error code in your underlying API, that implies that every error code requires completely disjoint error handling. You said you had 100 error codes. I do not believe there are 100 usefully different ways to handle the failure of an API call, nor do I believe anyone would or should spend their time coding them all.

      As other people above have quite correctly said, most error handlers tend to log the error and either swallow it or rethrow it. Why on earth would you write 100 catch clauses after your API call that mostly do the same thing? That’s not sensible.

      You do realise that exception classes can contain properties themselves, right? The particular error code can be stored in a property, along with messages and other useful details for logging or user feedback. The motivation for distinguishing errors with different exception classes is simply that they require disjoint or mostly disjoint handling in catch clauses. Hence, the four or so different classes I describe above.

  17. Use a relevant base class when a common handling is needed… Of course, a decent hierarchy is required.

    And I said “intelligent” handling, wich, by my understanding means, if you got a access denied the exception should tell what is missing (this single feature would save me a lot of googling a few days ago) so the app can prompt the user for the required permission, a file in use (or locked, or whatever) exception would help if providing who is using it, etc.

    The general “error logging” doesn’t fall in the “intelligent handling” case, in fact, I think if this logging is required it is either a fault of the framework in question because it doesn’t already provide a smart way of logging or a programmer fault wich didn’t studied the said framework logging features.

    • I agree about the types of intelligent handling an app should provide. For those examples, especially if there are many others of the same ilk, I personally would re-use the same exception class, but ensure that it had smarts enough to carry all the relevant bits of information to the user and the log. The code that gathers the information about who has locked the file would not be in the exception class itself, since it’s specific to one particular error condition, but would be called when creating the error instance to be thrown. Maybe in a factory method, if many places needed to perform his check, or just inline if not.

      Moving well off-topic onto logging, I currently spend a lot of time writing secure WCF web services. schannel “logs” error conditions to the Windows event log, WCF logs to a formatted XML file, and my code logs using nlog. It’s all over the place and there’s not really much I can do about it. I’ve written pages of documentation telling the support people where to go to diagnose problems, and I really really wish these respective bits of the technology stack would just leave the logging to me, so I could put it all in one place.

  18. When writing some complex async Socket code, I had to go with the “debug it until it works” approach, because that was the only way of finding out all the different things that can throw. When it was done, it really looked like I should have just “catch (Exception)”, because the recovery code was invariably identical.

    I’ve pondered an alternative approach to exception types. Specifically, if my method calls something, and that something throws FooException which I don’t handle, this is an internal error in my method, and so what propagates is an InternalErrorException, *not* FooException.

    In addition to this, I would have the option to declare that a specific call is allowed to propagate the specific exception (only really useful to those building APIs), and of course I can always throw my own exceptions. None of these would need to be listed in the method declaration header by the programmer, but they would be inferred by the compiler and listed in the method’s metadata. The CLR would then use this metadata to decide whether to propagate an exception as InternalErrorException or not.

    The biggest problem with this is probably performance, as it is more or less like having a try/catch around the whole body of every single method. A secondary problem is deciding whether it’s actually worth it, which is difficult until one implements a full-blown system and tries to write real-life code in it. A mini-problem is convincing people that the current approach is a mess that requires replacement (but now I can just point them here!)

    Performance aside, this would fix one huge problem with the usefulness of exception types. Currently, if I receive a FileNotFoundException, it could have originated literally anywhere. Not just directly in the methods I called, but ten levels deep inside those methods in completely unrelated, unexpected situations. To me, ten levels up, this is not a FileNotFoundException. This is a “you’ve called a method that has a bug” exception. I cannot recover from it like I can from a FNFE I expect. This change fixes that.


    • When writing some complex async Socket code, I had to go with the “debug it until it works” approach, because that was the only way of finding out all the different things that can throw. When it was done, it really looked like I should have just “catch (Exception)”, because the recovery code was invariably identical.

      Does the recovery happens to be “ignore the exception and retry”?

    • What you describe sounds like my big beef with checked exceptions–they’d be useful *if* there were an easy way for a block of code to say “I’m not prepared to handle any exceptions of type XX which I do not throw myself; mark any such exceptions to indicate that they’ve passed through code that was not prepared to handle them”. If such a feature were combined with a form of “catch” that would only catch exceptions which had not passed through layers that couldn’t handle them, then it would be possible to distinguish exceptions which were thrown for reasons the caller expected, from those which were thrown for other reasons.

      Additionally, there should be mechanisms to let code easily invalidate objects when updates to them exit abruptly because of exceptions. Code’s need to take action when an exception occurs is somewhat orthogonal to code’s ability to resolve the exceptional condition. Often code which can resolve an exception will need to take action, but in some cases, the mere act of unwinding the stack will suffice; code which takes action upon an exception will often resolve it, but in many cases an abrupt exit from a block of code should cause any object that might have been corrupted thereby to be expressly invalidated. The invalidation code would have no pretext of resolving the exception, so it should still percolate up the call stack, but would protect against the spread of corrupt data.

Leave a Reply

Fill in your details below or click an icon to log in:

WordPress.com Logo

You are commenting using your WordPress.com account. Log Out / Change )

Twitter picture

You are commenting using your Twitter account. Log Out / Change )

Facebook photo

You are commenting using your Facebook account. Log Out / Change )

Google+ photo

You are commenting using your Google+ account. Log Out / Change )

Connecting to %s