Why “Finally” Block Gets Executed Even When “Try” Block Throws Exception

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.