7

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?)

Servy
  • 202,030
  • 26
  • 332
  • 449
Saurabh Mahajan
  • 2,937
  • 7
  • 25
  • 32
  • A string is a simple type. – Maess Oct 09 '13 at 18:23
  • 1
    http://www.yoda.arachsys.com/csharp/parameters.html http://www.yoda.arachsys.com/csharp/references.html – SLaks Oct 09 '13 at 18:23
  • 3
    You aren't mutating the same object like you are with string builder, you are replacing one reference for another. You can replicate this with the string builder by trying to replace it with a new instance of a string builder. – vcsjones Oct 09 '13 at 18:24
  • http://www.codeproject.com/Articles/406046/Why-strings-are-immutable-and-what-are-the-implica – Preston Guillot Oct 09 '13 at 18:24

4 Answers4

6

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.

Servy
  • 202,030
  • 26
  • 332
  • 449
4

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.

McGarnagle
  • 101,349
  • 31
  • 229
  • 260
  • 4
    I don't see how string immutability is relevant here. The thing to consider is whether you are referencing the object itself or just modifying the reference. – vcsjones Oct 09 '13 at 18:35
  • @vcsjones because the OP is asking why a string, which is an object, doesn't behave like `customer` -- in other words, why you can't modify it inside a function call. Immutability is the reason why you can't. – McGarnagle Oct 09 '13 at 18:44
  • @vcsjones I see what you mean though, changing the object vs changing the reference is the key point. – McGarnagle Oct 09 '13 at 18:47
  • @McGarnagle Comparing `Customer` with `String` is irrelevant here. Then the comparison code should look something like this. `c = new customer();` – Sriram Sakthivel Oct 09 '13 at 18:48
  • 1
    I find your notation at the end highly unhelpful/wrong. Understanding the difference between immutability and value semantics is important, especially when someone is learning about both concepts for the first time. Say that they're the same is both wrong and actively harmful for someone who doesn't understand the underlying concepts. For example, if a string were a value type then passing one to a function would involve copying the entire string's values, but it's a reference type, so that doesn't happen. Interestingly, strings *can* be mutated (StringBuilder actually mutates strings). – Servy Oct 09 '13 at 19:30
  • @Servy point taken, I've changed the wording -- I can see how the "value type" analogy could confuse things. Do you have a source for strings being mutable? I don't believe that's true -- StringBuilder has its own representation of strings that is mutable, it's not magically able to mutate an immutable type. – McGarnagle Oct 09 '13 at 20:01
  • 3
    @McGarnagle Well, the reason it can is because it exposes means of mutating a string that are `internal` rather than `public`. Because they're a part of the same library, it can access such mutable functionality. It's not terribly relevant to know that implementation detail though; just an interesting tidbit. You can also mutate a string using unsafe code and pointers, but then again you can do anything when you do that. [reference](http://stackoverflow.com/questions/3564906/how-the-stringbuilder-class-is-implemented-does-it-internally-create-new-string) – Servy Oct 09 '13 at 20:14
1

When you are using the StringBuilder, you are mutating an instance of an instance of a StringBuilder. It is the same object because the value is a reference to the same StringBuilder object.

Consider this:

StringBuilder sb = new StringBuilder(); //sb is a variable to a string builder

//cat is a difference reference than sb, but both values are references that point to the same string builder.
private static void FunctionSB(StringBuilder cat)
{
    cat.Append("sb updated");
}

With your string example however, you aren't modifying the same instance of the string, you are modifying the value itself to be a difference reference.

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

This above example replaces the value that str references. This isn't a unique behavior of string, StringBuilder behaves in the same way. This exhibits the same problem as String:

private static void FunctionSB(StringBuilder sb)
{
    sb = new StringBuilder();
}

The above won't modify the string builder that is passed into FunctionSB. As others have noted, the way to address this issue is to use ref, since you want to modify the reference itself.

vcsjones
  • 138,677
  • 31
  • 291
  • 286
0

A string in c# is a built-in data type. If you want to do pass the value so you can rereference(can't modify, only replace, strings are immutable), you need the out modifier:

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

This is essentially the same as passing it by reference. The only difference is that when using out, your string must be initialized. If you can't be sure of this, use a ref instead.

Captain Skyhawk
  • 3,499
  • 2
  • 25
  • 39