Briefly explaining Try...catch...finally?

VitzzViperzz

Well-known member
Joined
Jan 16, 2017
Messages
75
Location
United Kingdom
Programming Experience
1-3
Hello,

So I am following this book and we are at a point where we are covering Try...catch...finally. But I don't understand the last application we made. The code is also pretty long. I have personal never write this much code in my life. It sounds bad, this is some complicated stuff.

C#:
namespace Ch07Ex02
{
   class Program
   {
      static string[] eTypes = { "none", "simple", "index",
                                 "nested index", "filter" };


      static void Main(string[] args)
      {
         foreach (string eType in eTypes)
         {
                try
                {
                    WriteLine("Main() try block reached.");        // Line 21
                    WriteLine($"ThrowException(\"{eType}\") called.");
                    ThrowException(eType);
                    WriteLine("Main() try block continues.");      // Line 24
                }
                catch (System.IndexOutOfRangeException e) when (eType == "filter")            // Line 26
                {
                    BackgroundColor = ConsoleColor.Red;
                    WriteLine($"Main() FILTERED System.IndexOutOfRangeException catch block reached. Message:\n\"{e.Message}\"");
                    ResetColor();
                }
                catch (System.IndexOutOfRangeException e)              // Line 32
                {
                    WriteLine($"Main() System.IndexOutOfRangeException catch block reached. Message:\n\"{e.Message}\"");
                }
                catch                                                    // Line 36
                {
                    WriteLine("Main() general catch block reached.");
                }
                finally
                {
                    WriteLine("Main() finally block reached.");
                }
                WriteLine();
            }
            ReadKey();
        }


      static void ThrowException(string exceptionType)
      {
         WriteLine($"ThrowException(\"{exceptionType}\") reached.");


         switch (exceptionType)
         {
            case "none":
               WriteLine("Not throwing an exception.");
               break;                                               // Line 57
            case "simple":
               WriteLine("Throwing System.Exception.");
               throw new System.Exception();                        // Line 60
            case "index":
               WriteLine("Throwing System.IndexOutOfRangeException.");
               eTypes[5] = "error";                                 // Line 63
               break;
            case "nested index":
               try                                                  // Line 66
               {
                  WriteLine("ThrowException(\"nested index\") " +
                             "try block reached.");
                  WriteLine("ThrowException(\"index\") called.");
                  ThrowException("index");                          // Line 71
               }
               catch                                                // Line 66
               {
                  WriteLine("ThrowException(\"nested index\") general"
                                    + " catch block reached.");
                  throw;
               }
               finally
               {
                  WriteLine("ThrowException(\"nested index\") finally"
                                    + " block reached.");
               }
               break;
            case "filter":
               try                                                  // Line 86
               {
                  WriteLine("ThrowException(\"filter\") " +
                            "try block reached.");
                  WriteLine("ThrowException(\"index\") called.");
                  ThrowException("index");                          // Line 91
               }
               catch                                                // Line 93
               {
                   WriteLine("ThrowException(\"filter\") general"
                                    + " catch block reached.");
                   throw;
               }
               break;
            }
      }
   }
}

I do understand that we have started using the Try and catch to help minimize the exception potential as much as possible. But I really don't understand anything that the code is trying to teach.

In my eyes, we are essentially writing to test to the console and pretending that we have errors and how we would handle them. But that's about it.

Could someone just give a quick explanation?

Thanks
 
The point of a try...catch block is to attempt to safely execute code that may throw an exception. It is generally better to avoid exceptions being thrown if possible, e.g. if you're converting a string to a number then validate it first. Sometimes exceptions are unavoidable though, because you can't know that code will fail until you try it, e.g. opening files or connecting to databases. In the case of a file, you can check that it exists but you can't be guaranteed that you'll be able to open it and there is always a chance, however small, that it will be deleted between checking that it exists and trying to open it. For that reason, actions like opening files should ALWAYS be wrapped in an exception handler.

An exception handler has three parts, two of which are conditionally optional. The 'try' block, which is required, contains the code that you want to execute but don't know whether it will work or not. The 'catch' block contains code that you want to execute if and only if an exception is thrown in the 'try' block. That may include notifying the user, logging the error clean-up code. The 'catch' block is option if you have a 'finally' block and you can have multiple 'catch' blocks as long as none specify the same type of exception to catch. The 'finally' block is optional if you have a 'catch' block and contains code that you want to run whether or not an exception is thrown. That usually means releasing resources, e.g. closing files or database connections. The last thing you want is a resource leak due to an exception.

It is generally considered back practice to have a 'catch' that doesn't specify a type of exception to catch. That's because you should only be writing an explicit exception handler if you know that a specific type of exception can be thrown. If an unexpected exception is thrown then you don't really know what state the application is in so, strictly speaking, it's not really safe to continue running the app. For unexpected exceptions, you should have a global exception handler in your app that will log the error and shutdown gracefully. You might give the user the option to continue running the app but, in that case, you should warn them that it is in an unknown state so, while unlikely, data loss cannot be guaranteed to not occur.

As for your code, I think the reason that you don't understand it is that it is very contrived. Code to demonstrate specific features often is very contrived because the author is trying to isolate one specific feature to avoid clouding the issue when that feature would rarely be seen in isolation is real-world code. In your case, you have that ThrowException method that is intended specifically to throw an exception, which is obviously something that you wouldn't put in a real-world app. You should just think of that as standing for any method that could throw an exception. For instance, that 'try' block could call a method that tries to open a file or connect to a database and those methods could throw an exception, so they would need to be called in a similar manner. Just think of your ThrowException method as a method that you're trying to call but may or may not throw one of several different exceptions.

So, in your Main method, you are trying to call a method that may or may not throw one of several different types of exceptions. That method call goes in the 'try' block. You want to perform a different action when an exception is thrown depending on the specifics so there are several different 'catch' blocks. The first 'catch' block is executed if and only if an IndexOutOfRangeException is thrown and the 'eType' variable is set to "filter". the second 'catch' block is executed if and only if an IndexOutOfRangeException is thrown and the 'eType' variable is NOT set to "filter". The third 'catch' block will be executed in all other cases.

The ThrowException method itself is throwing exceptions in a number of different ways, which allows them to be caught in different in different ways. Let's look at each case in the switch individually:
            case "none":
               WriteLine("Not throwing an exception.");
               break;

In this case, no exception is thrown, so this simulates code completing successfully.
            case "simple":
               WriteLine("Throwing System.Exception.");
               throw new System.Exception();

In this case, an Exception is explicitly thrown. Exception is the base class for all exceptions. You should rarely throw an Exception but rather throw something more specific that inherits Exception. That way, you know something more specific happened when you catch it. This is basically how you throw your own exceptions though. Exceptions are classes and you invoke a constructor as you do for other classes and then use a 'throw' statement to throw the exception.

Note also that there is no need for a 'break' in a case that throws an exception, because a 'throw' statement ends the current method.
            case "index":
               WriteLine("Throwing System.IndexOutOfRangeException.");
               eTypes[5] = "error";
               break;

This code is implicitly throwing an IndexOutOfRangeException. It tries to set the element at index 5 in the 'eTypes' array but there is no such element so the exception gets thrown. There's no 'throw' statement in your code but inside the system code for the array element setter there is.
            case "nested index":
               try                                                  // Line 66
               {
                  WriteLine("ThrowException("nested index") " +
                             "try block reached.");
                  WriteLine("ThrowException("index") called.");
                  ThrowException("index");                          // Line 71
               }
               catch                                                // Line 66
               {
                  WriteLine("ThrowException("nested index") general"
                                    + " catch block reached.");
                  throw;
               }
               finally
               {
                  WriteLine("ThrowException("nested index") finally"
                                    + " block reached.");
               }
               break;

This code catches and rethrows an exception. The method calls another method (in this case it's the ThrowException method calling itself, but it might be any two methods) that throws an exception and it catches that exception and then retrows it. You might do this if you want to execute a bit more code of your own. If you didn't catch and rethrow the exception then control would be passed immediately back to the calling method but by catching the exception you get control back, so you can execute some more code before rethrowing and passing control back to the caller. The most common extra code would be logging.
            case "filter":
               try                                                  // Line 86
               {
                  WriteLine("ThrowException("filter") " +
                            "try block reached.");
                  WriteLine("ThrowException("index") called.");
                  ThrowException("index");                          // Line 91
               }
               catch                                                // Line 93
               {
                   WriteLine("ThrowException("filter") general"
                                    + " catch block reached.");
                   throw;
               }
               break;

This is basically the same as the previous case but the fact that it uses a different value of 'eType' allows the example to demonstrate using a filter on the 'catch' blocks with the 'when' clause, performing more than one action for the same type of exception.
 
You did a great job at explaining this. This book does have its pros and cons and the way that some things are explained is very bad. When I read another C# book the explanation was so much more simple and understandable.

But once again, thank you for explaining that code. I can see how the thought process works now.
 
Back
Top Bottom