3

I know why I shouldn't use open catch blocks like so:

int x = 0;
try
{
    x = GetXFromSomeplaceThatCanFail();
}
catch //Possibly (Exception) or (Exception e)
{
    //Ignore The Failure Because We Don't Care If It Fails
}
if (x != 0) //Yes I know I can use finally blocks to do something similar, but this is just an example case
{
    //Do Things With x
}

I'm fully aware that this will "swallow" things like OutOfMemoryException, which is bad practice and can cause undetected failures/subtle errors, which are awful things.

That's why I'm going through my code and making sure there are no things like this. Normally you'd go to the documentation of whatever you're using in the try block and catch the expected exceptions, or else know that certain operations generate certain exceptions (like an IndexOutOfRangeException when accessing an array with an index, etc.).

However, there is no documentation to check in odd situations to see what exceptions may be thrown (or it's hard to find). A specific case from my own project (variable names made generic and code simplified) uses the dynamic type to grab a string field only if it exists or else fails gracefully by providing "N/A" as the result. Again, I remind you that I know that this is bad code:

string theString = "Some Old Value From Previous Run/etc.";
try
{
    theString = (placeWhereValuesComeFrom as dynamic).TheString;
}
catch
{
    theString = "N/A";
}

In this context, placeWhereValuesComeFrom inherits from BaseClass which doensn't (nor should it) provide TheString.

I realize that I could create an intermediate class that offers TheString and inherits from BaseClass, and then inherit from that. However, the dynamic solution was really fast to put in place and works well. Unless a better solution is put forth for my specific scenario I plan to add an intermediate class and make only the relevant classes inherit from it, then test like so:

theString = placeWhereValuesComeFrom is Subclass ? ((Subclass)placeWhereValuesComeFrom).TheString : "N/A";

However, under the assumption that I don't want to refactor for whatever reason to use an intermediate class, what should I do here? How can I discover what possible exceptions I should safely ignore in the catch block(s)? What about other similar situations where there's no real way to just "look up" what exceptions can be thrown?

Uwe Keim
  • 39,551
  • 56
  • 175
  • 291
Yushatak
  • 741
  • 1
  • 5
  • 15
  • 1
    Usually if you can handle an exception because you know how to recover from it, you can catch it (and log it out). If you don't know how to recover from an exception then you should just let your application crash, and log all the details of that crash out. – Callum Linington Aug 15 '16 at 15:19
  • 2
    You don't "discover" what exceptions should be ignored. Instead, you only catch the exceptions that you're actually writing code to handle. If you don't know what the exceptions are, then by definition you cannot handle them and shouldn't be catching them. – Cody Gray - on strike Aug 15 '16 at 15:20
  • I know that if the casted dynamic doesn't have the relevant field it will throw an exception. I want to handle that exception. I do not know what that exception type is, however, and the documentation for the dynamic type ***DOES NOT TELL ME***. There are other situations where this applies - what does one do in them? – Yushatak Aug 15 '16 at 15:22
  • You either find better documentation (documentation should *always* indicate what caller-handleable exceptions a method throws), or you test it to see (which sucks because it's fragile and subject to break if the library changes, but so is any other reflection-based approach). – Cody Gray - on strike Aug 15 '16 at 15:24
  • When you catch the exception, in the `(Exception ex) { ex.GetType().Name }` will give you the most derived class name for this exception!! – Callum Linington Aug 15 '16 at 15:25
  • Or if you are using C#6, try [exception filters](https://stackoverflow.com/documentation/c%23/24/c-sharp-6-0-features/46/exception-filters#t=201608151526108424841). – ryanyuyu Aug 15 '16 at 15:26
  • @Yushatak, you can set up a scenario where you know the code will fail, and literally look at the exception that is thrown. Unit testing is a great way to achieve this too. But the answer to your general question, "how does one know about X?", is to test the thing in question! – Chima Osuji Aug 15 '16 at 15:27
  • @ChimaOsuji I know I can test it, but when the documentation doesn't provide a list of exceptions what if I don't think of an error condition and it comes up in production? I'd rather be able to plan for and handle others that could come up. – Yushatak Aug 15 '16 at 15:29
  • @ryanyuyu That's very cool, but I am unfortunately stuck on C#5 in my work environment (way out of my control). – Yushatak Aug 15 '16 at 15:29
  • @CodyGray It's official Microsoft documentation on a Microsoft type and it doesn't. What would "better documentation" be in this case? I could read the source, sure, but I shouldn't be relying on internal knowledge for the same reasons of fragility. – Yushatak Aug 15 '16 at 15:38
  • What other exception are you expecting apart from a runtime binding exception? Either the dynamic object has the property `TheString` or it doesn't. The only exception you need to handle is the binding failure which is always the same; the exact type is easy to figure out simply running a case that will fail and inspecting the exception type. I'm not sure what's the issue here. – InBetween Aug 15 '16 at 15:46
  • @InBetween The documentation doesn't tell me that RuntimeBindingException can be thrown, which means to figure that out I have to manually test what happens when I pass the wrong object in. Imagine you have a large library with similarly undocumented exceptions - would you want to test every mode of failure you can think of? I sure wouldn't. Was hoping for a solution to that situation. – Yushatak Aug 15 '16 at 15:53

1 Answers1

2

The only exception you should be handling here is a runtime binding failure; when the dynamic object does not implement TheString. The type of the exception thrown is Microsoft.System.CSharp.RuntimeBinder.RuntimeBinderException.

So your code should be the following:

try
{
    str = myDynamicObject.TheString;
}
catch (Microsoft.System.CSharp.RuntimeBinder.RuntimeBinderException)
{
    //Binding failure
    str = "N/A"
}
catch ( ... //exceptions you know TheString can throw, if any...)
{
    //Handle
}
// any other exception you don't know how To handle...don't handle it
InBetween
  • 32,319
  • 3
  • 50
  • 90
  • This is valid code to solve the specific case above, but doesn't address the general situation when the documentation doesn't tell you anything and you don't want to manually check failures. – Yushatak Aug 15 '16 at 16:00
  • 3
    @Yushatak Then I simply think the premise of your question is wrong. You should be catching exceptions you know how to handle, nothing else. If you don't know what exceptions you might be getting then you most definitely don't know how to handle them. Either you are using a library woefully documented (use another) or you are using a library you don't fully understand (study it, and unit test your code to death). – InBetween Aug 15 '16 at 16:03
  • That's the closest I'll get to an answer, I guess. If the documentation doesn't tell you, you either have to investigate for yourself or move along to an alternative.. I wish you could just highlight code and then have a right click option for "List Possible Exceptions" that would run through the code layer by layer and enumerate the exceptions that could be thrown (and if you clicked one it would show you where it found it). At that point you could also put some buttons for adding handlers for selected or all exceptions possible for scenarios where you can handle each in its own way properly. – Yushatak Aug 15 '16 at 16:10