1
void Method(ref int refArgument)
{
    refArgument = refArgument + 44;
}

int number = 1;
Method(ref number);
Console.WriteLine(number);

Under the hood, what is really happening?

It passes the memory address to the method/function and changes the value stored in that memory address?

OR

It creates a new memory address and stores the value in that newly created address and points the variable(number) to the new memory address?

Which is which?

My hunch is the first one since primitive data types in C# are struct and therefore they will always be pass by value

5
  • 3
    Why not look at the generated assembly and see? Also, why the question? Is it purely academic or are you thinking of using the knowledge for 'performance' or other reasons?
    – Neil
    Commented Nov 15, 2018 at 10:37
  • 1
    ldloca, ldind. LINQPad is your friend. Commented Nov 15, 2018 at 10:40
  • 4
    Possible duplicate of How does the ref keyword work (in terms of memory)
    – Milster
    Commented Nov 15, 2018 at 10:41
  • If you are asking 'is number updated when you assign to refArgument, or only when Method finishes?' the answer is the former.
    – mjwills
    Commented Nov 15, 2018 at 11:04
  • Thanks ya'll I thought it was creating a new memory address and stores the result to that memory address and changes the address pointed at by variable(number), just like in C/C++ it changes the pointer, I guess I was wrong Commented Nov 15, 2018 at 11:11

1 Answer 1

6

If we have a look at the IL code of your snippet:

IL_0000:  nop         
IL_0001:  nop         
IL_0002:  ldc.i4.1    
IL_0003:  stloc.0     // number
// Loads the address of the local variable at a specific index onto the evaluation stack, short form. see: https://learn.microsoft.com/en-us/dotnet/api/system.reflection.emit.opcodes.ldloca_s?view=netframework-4.7.2
IL_0004:  ldloca.s    00 // number
IL_0006:  call        g__Method|0_0
IL_000B:  nop         
IL_000C:  ldloc.0     // number
IL_000D:  call        System.Console.WriteLine
IL_0012:  nop         
IL_0013:  ret         

g__Method|0_0:
IL_0000:  nop   
// ldarg.0 called twice: 1. for ldind.i4 and 2. to store the result back to the memory location in stind.i4      
IL_0001:  ldarg.0     
IL_0002:  ldarg.0     
// Loads a value of type int32 as an int32 onto the evaluation stack indirectly. see: https://learn.microsoft.com/en-us/dotnet/api/system.reflection.emit.opcodes.ldind_i4?view=netframework-4.7.2
// --> It used the passed intptr
IL_0003:  ldind.i4    
IL_0004:  ldc.i4.s    2C 
IL_0006:  add        
// Stores a value of type int32 at a supplied address. see: https://learn.microsoft.com/en-us/dotnet/api/system.reflection.emit.opcodes.stind_i4?view=netframework-4.7.2
IL_0007:  stind.i4    
IL_0008:  ret         

So it:

  1. ldind.i4 loads the value from the supplied memory address and Pushes it onto the stack
  2. ldc.i4.s 2C loads 44 onto the stack
  3. add Does your addition with the two elements of the stack
  4. stind.i4 Stores the result of the addition back to the memory address
2
  • I thought it was something like this 1. Loads the value from the supplied memory address 2. Pushes it onto the stack 3. Does your addition on the stack 4. Creates a new memory address 5. Stores the result to that new memory address 6. Points the variable to the new memory address or this thinking does not make any sense at all? thank you for explaining the answer you posted because I cant seem to understand it without your explaination Commented Nov 15, 2018 at 11:00
  • Therefore in the ldarg.0 is called twice in the biginning of the method, so the memroy address is still present for stind.i4 Commented Nov 15, 2018 at 11:03

Not the answer you're looking for? Browse other questions tagged or ask your own question.