Miłosz Orzeł

.net, js, html, arduino, java... no rants or clickbaits.

Ref modifier for reference types and a bit of SOS

Take a look at the following code and think what value will be displayed on the console (note that string is a reference type)?

using System;
               
class Program
{
    static void Test(string y)
    {
        y = "bbb";
    }

    static void Main()
    {
        string x = "aaa";
        Test(x);
        Console.WriteLine(x);
    }
}

The correct answer (aaa) is not all that obvious. You will see the words aaa, because without a ref modifier, a program written in C# provides a copy of the parameter value (for value types) or a copy of a reference (for reference types).

When parameter y in method Test receives a new text value, CLR does not modify the array of chars. Instead, a new string is created and a reference to it is assigned to variable y (more info here). Variable y contained in method Test is, however, just a copy of a reference hold under x variable from method named Main.

To actually change the text hidden under x variable, use the ref modifier (you have to set it both in the method declaration and its invocation - C# enforces such behavior for clarity):

using System;
               
class Program
{
    static void Test(ref string y)
    {
        y = "bbb";
    }

    static void Main()
    {
        string x = "aaa";
        Test(ref x);
        Console.WriteLine(x);
    }
}

After this change, console will show bbb text.

 

SOS

Way in which parameters are passed to a method can be examined by using tool called SOS (Son of Strike). We will use CLRStack -a command, which displays information about parameters and local variables on managed code stack (if you don't know how to use SOS look here and here, if you wonder where the name "Son of Strike" came from, click here)...

Below are the results of CLRStack -a command executed at the time of entry to the Test method.

For code without ref modifier:

!CLRStack -a
OS Thread Id: 0x176c (5996)
Child SP IP       Call Site
0031f114 00390104 Program.Test(System.String)
    PARAMETERS:
        y (0x0031f114) = 0x025cb948

0031f158 003900af Program.Main()
    LOCALS:
        0x0031f158 = 0x025cb948

0031f3c0 656721bb [GCFrame: 0031f3c0]

For code with ref modifier:

!CLRStack -a
OS Thread Id: 0x934 (2356)
Child SP IP       Call Site
001dee34 002f00f4 Program.Test(System.String ByRef)
    PARAMETERS:
        y (0x001dee34) = 0x001dee78

001dee78 002f00aa Program.Main()
    LOCALS:
        0x001dee78 = 0x027fb948

001df0ec 656721bb [GCFrame: 001df0ec]

An important difference that is exhibited by these results is the value of y parameter. In the case of code without ref modifier, it is the address of aaa string (0x025cb948). For the code with ref modifier, the value of y parameter is the address of x variable (0x001dee78) from Main method (that variable points to aaa string).