Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Strings not behaving as a reference type

Today I learned about value types and reference types.

I am having one doubt in the code sample below:

class Program
{
    static void Main(string[] args)
    {
        StringBuilder sb = new StringBuilder();
        FunctionSB(sb);
        Console.WriteLine(sb); //sb updated

        customer c = new customer();
        FunctionClass(c);
        Console.WriteLine(c.s);//updated class value 

        String str = "";
        FuntionString(str);
        Console.WriteLine(str);//

    }

    private static void FunctionSB(StringBuilder sb)
    {
        sb.Append("sb updated");
    }

    private static void FunctionClass(customer c)
    {
        c.s = "updated class value ";
    }

    static void FuntionString(String str)
    {
        str = "updated value";
    }
}
class customer
{
    public string s;
}

Here the string builder value and class member variable value is updated, but why is FuntionString(str); not updating the str value? (Why is it not passed as a reference?)

like image 608
Saurabh Mahajan Avatar asked Oct 09 '13 18:10

Saurabh Mahajan


2 Answers

I's important to distinguish between a variable and an object.

Consider the code:

String str = "";
FuntionString(str);

str is a variable. At first the value of that variable is a reference to a string. Let's say that that reference is the number 246. String 246 can be resolved to a value; it's value is an empty character array.

We then pass the value of that variable to FuntionString. We're not passing a reference to the str variable, we're just passing the number 246. It's a copy of that reference, or number.

Inside of that function it has an entirely different variable that happens to also be called str. The fact that the identifier is the same doesn't change the fact that it's a different variable that just happens to have the same value.

When you change the str variable inside of FuntionString you do not change the str variable from Main. After the body of FuntionString finishes the str from Main is still holding onto the reference 246, just like it was before, and the str variable inside of FuntionString has the value to some new reference, let's say 3, of a string new string with the value "updated value". That change to the variable is not reflected in Main.

In the case of FunctionSB the implementation of the method doesn't actually change the sb variable. Instead of changing the variable it mutates the object that the variable is referencing. In this case sb is pointing to the object at some location, let's say 39. The sb in the main method and in this other method are two different variables with a copy of that same reference. The method doesn't change the sb variable, instead it changes the sb object at location 39. The two sb objects still both have the same value; they are unchanged, but they both "point" to a single object that has changed. As such the mutation performed with the method can be observed from Main.

If, from the definition of FuntionString you changed the string object that both variables pointed to, rather than changing the variable itself, then the change would be "observable" from the caller. Of course, that's not possible because strings are immutable. There is no way for the method to mutate the object in a way that the caller can observe.

like image 180
Servy Avatar answered Sep 19 '22 21:09

Servy


The thing to realize is that, when you write str = "updated value";, you are actually creating a new object. That is, you've done the equivalent of:

str = new string("updated value");

This means that, when you write str = "updated value", you're assigning a new object to the reference "str", not modifying the existing object. **

So, the correct point of comparison with regards to the "customer" class is not:

c.s = "updated class value";

But rather:

c = new customer { s = "updated class value" }.

The original object, pointed to by the reference previously held by "c" or "str", is therefore unchanged.


What you need to do in the OP case is pass the reference to the string, by using the ref keyword:

static void FuntionString(ref String str)
{
    str = "updated value";
}

The difference here is that the reference itself gets updated inside FunctionString, and now points to the new string object.


**Note that, since .Net strings are immutable, this will always be true. It's not possible to modify a string object, only to create a new one and re-assign it. To re-state this in a slightly different way: yes, the string is passed by reference, but, since the string type is immutable, you still can't use that reference to change the object in any way.

like image 39
McGarnagle Avatar answered Sep 19 '22 21:09

McGarnagle