As usr explains in his answer, ReSharper tries to find known patterns in your code it can offer refactorings for.
But let's take a look at the generated code for both cases.
The first function:
private static string GetLineA()
{
string s = Console.ReadLine();
if (s == null)
{
return GetLineA();
}
return s;
}
Gives this (x64, Release):
00007FFB34AC43EE add byte ptr [rax],al
00007FFB34AC43F0 sub rsp,28h
00007FFB34AC43F4 call 00007FFB8E56F530 // <-- Console.ReadLine
00007FFB34AC43F9 test rax,rax
00007FFB34AC43FC jne 00007FFB34AC440F
00007FFB34AC43FE mov rax,7FFB34AC0F60h
00007FFB34AC4408 add rsp,28h
00007FFB34AC440C jmp rax
00007FFB34AC440F add rsp,28h
00007FFB34AC4413 ret
You can clearly see it's tail recursive, since the only call
instruction is for Console.ReadLine
.
The second version:
private static string GetLineB()
{
string s = Console.ReadLine();
if (s == null)
{
s = GetLineB();
}
return s;
}
Gives this:
00007FFB34AC44CE add byte ptr [rax],al
00007FFB34AC44D0 sub rsp,28h
00007FFB34AC44D4 call 00007FFB8E56F530 // <-- Console.ReadLine
00007FFB34AC44D9 test rax,rax
00007FFB34AC44DC jne 00007FFB34AC44E3
00007FFB34AC44DE call 00007FFB34AC0F68 // <-- Not good.
00007FFB34AC44E3 nop
00007FFB34AC44E4 add rsp,28h
00007FFB34AC44E8 ret
There's a second call
there, so you don't get tail recursion, and the stack will grow, eventaully leading to a stack overflow if it grows large enough.
Well, it looks like the JIT didn't optimize the code into a tail recursive call.
Anyway, beware, since you're at the mercy of the JIT.
Here's GetLineA
in x86:
00F32DCA in al,dx
00F32DCB call 72A209DC // <-- Console.ReadLine
00F32DD0 test eax,eax
00F32DD2 jne 00F32DDC
00F32DD4 call dword ptr ds:[12B8E94h] // <-- Ouch
00F32DDA pop ebp
00F32DDB ret
00F32DDC pop ebp
00F32DDD ret
See? You can't really depend on it, and the language provides no guarantee.