06.09.09
Checked Exceptions
Interview with Anders Hejlsberg
This seems to be somewhat of a controversial subject.
On the one hand, we have Java, which forces exceptions to be caught and potentially rethrown. This is, certainly, something of a pain.
On the other hand, C# doesn’t require anything, and any method can potentially throw any kind of exception.
I can see the points on both sides. Nothing is uglier than a bunch of arbitrary try/catch statements in code that do nothing more than rethrow exceptions. And just blindly swallowing exceptions is even worse.
On the other hand, not really knowing what a method might throw in C# can be really, really annoying at times.
Let’s start with versioning, because the issues are pretty easy to see there. Let’s say I create a method
foothat declares it throws exceptionsA,B, andC. In version two offoo, I want to add a bunch of features, and nowfoomight throw exceptionD. It is a breaking change for me to addDto the throws clause of that method, because existing caller of that method will almost certainly not handle that exception.
Well, that’s certainly reasonable. But, I have to wonder if it’s the right answer? If you add a bunch of functionality to a class, is it perhaps better to make some new ReallySpiffyFoo class that contains the new functionality, and leave the existing class as it is?
Then again, I’m not a huge fan of growing classes over time – in most cases, I believe you’re better off leaving a well-defined class as-is except for bug fixes, and putting new functionality into a new class (which might internally use the old one).
Now, each time you walk up the ladder of aggregation, you have this exponential hierarchy below you of exceptions you have to deal with. You end up having to declare 40 exceptions that you might throw. And once you aggregate that with another subsystem you’ve got 80 exceptions in your throws clause. It just balloons out of control.
Another reasonable point. However, I’d tend to believe that in a case like that, you’ve got a bigger design issue at play. Why in the world would a business object throw a FileNotFoundException or the like? At most, it should throw something like a CouldNotLoadDataException. The fact that the data was to be loaded from a file is completely irrelevant at that level.
I also suspect that Anders is looking at this mostly from the viewpoint of a language and framework developer. As a framework developer, he expects code he writes to be called by other people, and they can certainly look up what exceptions are being thrown. That’s reasonable.
However, if I’m using an interface as an extensibility point, it’s a slightly different story. Now I’m importing someone else’s code into my application, and I have no idea of what it might throw when I call it. If that’ doesn’t sound scary, I don’t know what would. At this point my options are either let my app crash when I make any arbitrary call, or catch Exception directly. Neither of those are, in my mind, really good solutions.
What I’d like to see is defined exceptions, but not necessarily checked exceptions. I’d like to know what exceptions a method may throw, but I don’t want to necessarily be forced to catch them. In my mind, the exceptions you throw are effectively part of your API, especially when looked at from role-based interfaces for extensibility rather than header-style interfaces.
If I define an operation in an interface, I’m basically saying that I expect to be able to make this call, with certain parameters, and get a certain type of result back. As part of that, saying that I expect to throw (or will throw) certain exceptions is part of the definition of my API.
What I don’t see much value in is checked exceptions as in Java. To me, there is absolutely no value in putting in boilerplate code to just rethrow exceptions that I’ve caught just to satisfy a compiler restriction. But, knowing what exceptions can be thrown is, to me, extremely valuable.