Try, Catch, and Finally are the keywords used for exception handling. There are certain rules as to how we should write all these three blocks together. Usually, a "try" block is followed by "catch" block and "catch" block is followed by "finally" block. Whenever any exception is thrown by the code written inside the "try" block, that exception is handled by the respective "catch" block. However, the execution of "finally" block is little bit different. Irrespective of any exception thrown or not, the "finally" block gets executed unless we abort the system forcibly before the execution. So, this blog will explain the functionality behind the code which makes "finally" block executed by default.

Here is the code snippet that explains the use of "try", "catch", and "finally" block.
  1. using System;
  2. using System.Collections.Generic;
  3. using System.Linq;
  4. using System.Text;
  5. using System.Threading.Tasks;
  6. namespace ConsoleApplication1 {
  7. class Program {
  8. static void Main(string[] args) {
  9. try {
  10. var i = 0;
  11. var j = 10;
  12. Console.WriteLine(j / i);
  13. } catch (ArithmeticException ex) {
  14. Console.WriteLine("Catch block");
  15. } catch (Exception ex) {
  16. Console.WriteLine("Catch block");
  17. // throw new Exception ( "catch" );
  18. } finally {
  19. Console.WriteLine("finally block");
  20. }
  21. }
  22. }
  23. }
If we get the exception due to the code inside the "try" block, in that case, "catch" block gets executed.

Let's know why "finally" block gets executed even though there is no exception. In order to analyze this, I have gone through the IL code for the above code, as given below.
  1. .method private hidebysig static void Main(string[] args) cil managed {.entrypoint
  2. // Code size 70 (0x46)
  3. .maxstack 2.locals init([0] int32 i, [1] int32 j, [2] class[mscorlib] System.ArithmeticException ex, [3] class[mscorlib] System.Exception V_3)
  4. IL_0000: nop.try {.try {
  5. IL_0001: nop
  6. IL_0002: ldc.i4 .0
  7. IL_0003: stloc .0
  8. IL_0004: ldc.i4.s 10
  9. IL_0006: stloc .1
  10. IL_0007: ldloc .1
  11. IL_0008: ldloc .0
  12. IL_0009: div
  13. IL_000a: call void[mscorlib] System.Console::WriteLine(int32)
  14. IL_000f: nop
  15. IL_0010: nop
  16. IL_0011: leave.s IL_0033
  17. } // end .try
  18. catch [mscorlib] System.ArithmeticException {
  19. IL_0013: stloc .2
  20. IL_0014: nop
  21. IL_0015: ldstr "Catch block"
  22. IL_001a: call void[mscorlib] System.Console::WriteLine(string)
  23. IL_001f: nop
  24. IL_0020: nop
  25. IL_0021: leave.s IL_0033
  26. } // end handler
  27. catch [mscorlib] System.Exception {
  28. IL_0023: stloc .3
  29. IL_0024: nop
  30. IL_0025: ldstr "Catch block"
  31. IL_002a: call void[mscorlib] System.Console::WriteLine(string)
  32. IL_002f: nop
  33. IL_0030: nop
  34. IL_0031: leave.s IL_0033
  35. } // end handler
  36. IL_0033: nop
  37. IL_0034: leave.s IL_0044
  38. } // end .try
  39. finally {
  40. IL_0036: nop
  41. IL_0037: ldstr "finally block"
  42. IL_003c: call void[mscorlib] System.Console::WriteLine(string)
  43. IL_0041: nop
  44. IL_0042: nop
  45. IL_0043: endfinally
  46. } // end handler
  47. IL_0044: nop
  48. IL_0045: ret
  49. } // end of method Program::Main
As per the above IL code, when we write "try", "catch" and "finally" blocks, then "try" and "catch" blocks are considered as one "try" block and the "finally" block follows that "try" block.

That’s the reason even though whether we get any exception or not, the "finally" block still gets executed almost all the time.

However, we can skip the execution of "finally" block. We can write "Environment.Exit(0)" inside the "catch" block so that the "finally" block will never get executed.