I don't really understand how the instructions flow in the following code.
The body of finally
is guaranteed to be executed before the method returns. If so, the return value should be 0 rather than 1.
Could you explain the internal mechanism why the return value is still 1 even though the finally
has reset it to 0?
class Container
{
int data = 0;
public int Retrieve()
{
try
{
Inc();
return data;
}
finally
{
Reset();
//return data;
}
}
void Reset()
{
data = 0;
WriteLine("Reset");
}
void Inc() => data++;
}
class ReturnInTry
{
static void Main()
{
Clear();
WriteLine("Start");
WriteLine(new Container().Retrieve());
WriteLine("End");
}
}
Because when the return instruction is executed, it PUSHs in the CPU Stack the value to be returned.
Then the finally block is executed but it does not modify the value already pushed.
Thus after the method PROC RET, the caller POPs the value and has what it has been PUSHed, but the data itself has been reseted.
Therefore, calling the method again will return 0.
This means that the return statement is executed first, and the code in finally
is executed after, so the result is previously stored and changing data
does not change this stored in the stack result.
We can check that using for example ILSpy:
.method public hidebysig
instance int32 Retrieve () cil managed
{
// Method begins at RVA 0x4cf4
// Code size 30 (0x1e)
.maxstack 1
.locals init ( [0] int32 )
.try
{
// Inc();
IL_0002: ldarg.0
IL_0003: call instance void ConsoleApp.Container::Inc()
// return data;
IL_0009: ldarg.0
IL_000a: ldfld int32 ConsoleApp.Container::data
IL_000f: stloc.0
IL_0010: leave.s IL_001c
} // end .try
finally
{
// Reset();
IL_0013: ldarg.0
IL_0014: call instance void ConsoleApp.Container::Reset()
// }
IL_001b: endfinally
} // end handler
IL_001c: ldloc.0
IL_001d: ret
} // end of method Container::Retrieve
// Console.WriteLine(new Container().Retrieve());
IL_000c: newobj instance void ConsoleApp.Container::.ctor()
IL_0011: call instance int32 ConsoleApp.Container::Retrieve()
IL_0016: call void [mscorlib]System.Console::WriteLine(int32)