TryParse considered harmful….. sometimes.
It’s a common enough wisdom in programming that you should never catch an exception that you can’t handle.
The reasons for this are well-known.
- The exception might be indicating that your system is unable to function correctly, and having your own code continue regardless is a recipe for disaster.
- There may be code above yours in the stack that does know how to handle the exception, if only you’d let it.
- And, in my view, perhaps the reason closest to a programmer’s heart: the exception may have information that you could have used to diagnose a problem.
There are various strategies that you can choose from to deal with exceptions correctly:
- be very fussy about what you catch
- handle it if you can
- log and re-throw
- throw your own (more informative) exception, but pass the original as an inner exception
And perhaps there are more, depending on your circumstances.
But on to the subject of my story. Today I came across a trap(!) that you can fall into even if you are keenly aware of all this. We generally try to avoid using exceptions as part of our day-to-day programme flow. Sometimes you want to test if something’s going to succeed, and depending on the outcome, go into one or other code branch. In terms of performance, an exception can be very expensive way of doing this. This is one of the reasons why it’s very common for types that offer a .Parse() method also to have a .TryParse() So instead of writing:
…. you’d probably write it like this:
The big assumption here is that you are processing input data of some sort, which may or may not successfully parse to a double. In a big loop, with the chance of many exceptions, you really, really do want to choose the second approach over the first.
By contrast, today I was kicking the tyres on a popular web content management system, and reached a point where I had to decorate a class with an attribute and provide a GUID parameter. I used the “Create GUID” tool in Visual Studio, and pasted the result in. Some time later, after taking care of some other things, I tried running the code, and was faced with an exception.
[ArgumentOutOfRangeException: Specified argument was out of the range of valid values.]
Being unfamiliar with the system, it took me a while to figure out what had happened. It turned out that my finger had slipped while pasting the GUID, and there was an extra character at the end. (I don’t know about you, but I could stare at a GUID all day without spotting that.) The exception was coming up from the setter for the GUID attribute property on the attribute, so I decompiled the code and found something like this:
So the programmer who’d written this was trying to do the right thing. By using TryParse, they were avoiding using an exception. However, circumstances alter cases, and in this case, I’d have preferred the exception. Once I’d realised it was one of my GUIDs, I was using the PowerShell to check them, and this is what I saw when I tested the culprit.
PS C:\Users\dominicc> [GUID]::Parse("2B48BBEE-202E-4B67-A18E-9B032111D7CCO") Exception calling "Parse" with "1" argument(s): "Guid should contain 32 digits with 4 dashes (xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx)."
So if they had used GUID.Parse() and just let it throw, I’d have had a much better message than the generic “out of range” text.
All in all, I think we should forgive the mystery programmer their error. It’s easily done, and I’ve written much worse code myself. The point I’m trying to make is that sometimes the right choice isn’t obvious. In this case, the GUID to be parsed would be checked in to my code repository, and once my own code is debugged, it’s not going to change. The exception would be truly exceptional. This is a very different circumstance to processing input data in a big loop, and so a different technique is called for. Exceptions are useful. They are there for a reason, and sometimes an exception is what you want.