It will be more honest to answer your question - "it is impossible to predict, take and measure on the real code." And that's why:
Here is the IL into which your examples are compiled:
IL1:
Main IL_0006: ldc.i4.5 IL_0007: newobj instance void ConsoleApplication15.MyClass::.ctor(int32) IL_000c: ldfld int32 ConsoleApplication15.MyClass::a IL_0011: call void ConsoleApplication15.Program::Work(int32) Work IL_0000: ldarg.0 IL_0001: ldc.i4.5 IL_0002: add IL_0003: call void [mscorlib]System.Console::WriteLine(int32) IL_0008: ret
IL2:
Main IL_0006: ldc.i4.5 IL_0007: newobj instance void ConsoleApplication15.MyClass::.ctor(int32) IL_000c: call void ConsoleApplication15.Program::Work(class ConsoleApplication15.MyClass) Work IL_0000: ldarg.0 IL_0001: ldfld int32 ConsoleApplication15.MyClass::a IL_0006: ldc.i4.5 IL_0007: add IL_0008: call void [mscorlib]System.Console::WriteLine(int32) IL_000d: ret
Ok, what is clear from this code? The compiler has already slightly optimized execution. For example, in IL1.Main, it uses a successful coincidence - the parameters in the function are passed through the stack, the result of the constructor call is also added to the stack - which means that you don't need to make any gestures to pass myClass as the Work parameter. Those. what is visible in C # code:
You access the myClass variable and copy its value, i.e. reference to the memory area where the object is stored
IL does not occur. But all the same - it is clearly visible that in the first code example it is less. So it can be concluded that it works faster. But in fact, it is impossible to draw conclusions from IL. And that's why:
IL code is not executed directly. It is compiled into machine code for a specific platform. And in a specific platform, in addition to the stack, other mechanisms for passing parameters can be used.
In reality, parameters may not be passed through an iron stack. Moreover, in the CLR on x86 / x64, the first two parameters are transmitted through the rcx / rdx registers (ecx / edx). And the result from the method is returned via rax / eax. That's what really compiles your x64 code (on my machine):
ASM1:
Main MyClass myClass = new MyClass(5); 00007FF8F6F20489 mov rcx,7FF8F6E15A88h 00007FF8F6F20493 call 00007FF956512510 00007FF8F6F20498 mov dword ptr [rax+8],5 Work(myClass.a); 00007FF8F6F2049F mov ecx,dword ptr [rax+8] 00007FF8F6F204A2 call 00007FF8F6F20080 Work: 00007FF8F6F204D4 add ecx,5 00007FF8F6F204D7 call 00007FF954497BE0
ASM2:
Main: MyClass myClass = new MyClass(5); 00007FF8F6EF0489 mov rcx,7FF8F6DE5A98h 00007FF8F6EF0493 call 00007FF956512510 00007FF8F6EF0498 mov rcx,rax 00007FF8F6EF049B mov dword ptr [rcx+8],5 Work(myClass); 00007FF8F6EF04A2 call 00007FF8F6EF0088 Work 00007FF8F6F204D4 mov ecx,dword ptr [rcx+8] 00007FF8F6F204D7 add ecx,5 00007FF8F6F204DA call 00007FF954497BE0
Ok, how does this differ from IL? That the stack is not used. Totally. The optimizer stuffed everything into registers.
What is the difference between the options? One instruction:
00007FF8F6EF0498 mov rcx,rax
Mov from register to register takes 1 processor clock. Those. if any of the examples will work faster on x64 - you probably won't notice this.
Performance depends on too many parameters — the platform, the JIT version, the type of compilation — and it is impossible to take all of them into account when writing code. Do not bother. Solve problems as they arise.