Exceptions vs. No Exceptions
When I moved onto the tools team, I was shocked to find out that the codebase is written not to throw or expect exceptions. I withheld judgement for a little while (because it wasn’t going to change and I have respect for the people who made the decision), but as I get more and more used to it, and discuss it with more and more people, I am more and more convinced it is an archaic and inappropriate convention. Instead of rehashing existing arguments, I’ll tell you a summary of my findings from research and experience:
C++ programmers have brought over this convention into C#. Not having programmed in C++ or C, I don’t know why, just that throwing is not something you do. Not a single argument against throwing exceptions I read was from the last few years or from someone at the forefront of managed language design.
Not throwing exceptions results in verbosity with error codes, or ambiguity by returning a ‘default’ value (null for reference types) if something didn’t work. I won’t talk about managing error codes because I think everyone understands it is a way of the past. A default return value is ok for simple functions with no question about why something fails, (List.IndexOf), but completely unacceptable for complex methods (did I pass in an invalid argument? Was something null that shouldn’t be?).
Not throwing exceptions makes it impossible to enforce contracts. If you don’t use error codes (and I don’t think anyone in a managed environment does, as mentioned above), it is impossible to know whether something failed for valid reasons, or for invalid arguments. If a method cannot run with a null argument, it should check for it and throw a NullReferenceException as the first line of code in the method. Without exceptions, a default value is returned- the same, usually, as if it didn’t work for any other reason! This is a completely unacceptable ambiguity in my eyes.
Every .NET authority I could find was in favor of using exceptions intelligently. This includes the designers of C#, and the authors of the Framework Design and Guidelines, and uncounted numbers of unofficial and respected opinions.
Bad design reinforced bad design. Early on, our game and engine were incredibly slow to start up. So an exception caused you to restart the game to continue debugging (and lose work if you were a content developer). Instead of developing a robust solution to this problem (making more modular code with cleaner interfaces, which could catch and recover from errors in modules), no exceptions were to be thrown at all.
And probably most importantly- the framework is throwing exceptions anyway! I can’t count how many times I’ve seen this in our codebase:
MyClass mc = someObject as MyClass;
mc.Frob();
Our code is imperfect and it is going to throw exceptions because the framework is designed that way (and in this case, a NullReferenceException if someObject cannot be cast to MyClass). And that’s completely aside from things like this:
if (System.IO.File.Exists(filename))
myXmlDoc.Load(filename);
Guess what. Even though you check if it exists, there’s no guarantee it does after the check. So you can still throw an exception. So you still need to develop some way to deal with exceptions.
I felt compelled to write this post based on my experience, but I can’t imagine people are still creating new systems and codebases that don’t throw exceptions- are they?