views:

497

answers:

10

Hi,

I have being studying (newbie) .NET and I got some doubts.

Reading from a book examples I learnt that String are object then Reference Type.

So, I did this test and the result was different what I expected:

I'm really curious, is this an exception because "string" are special types?

class Program
{
    static void Main(string[] args)
    {
        SByte a = 0;
        Byte b = 0;
        Int16 c = 0;
        Int32 d = 0;
        Int64 e = 0;
        string s = "";
        Exception ex = new Exception();

        object[] types = { a, b, c, d, e, s, ex };

        // C#
        foreach (object o in types)
        {
            string type;
            if (o.GetType().IsValueType)
                type = "Value type";
            else
                type = "Reference Type";
            Console.WriteLine("{0}: {1}", o.GetType(), type);
        }

        // Test if change
        string str = "I'll never will change!";

        Program.changeMe(str);

        Console.WriteLine(str);
    }

    public static string changeMe(string param)
    {
        param = "I have changed you!!!";

        return ""; // no return for test
    }
}

Output:

System.SByte: Value type
System.Byte: Value type
System.Int16: Value type
System.Int32: Value type
System.Int64: Value type
System.String: Reference Type
System.Exception: Reference Type
I'll never will change!
+1  A: 

The MSDN page can help explain why it is a reference type.

Because String is a collection of chars and inherits from various Collection base classes, it is a class, and therefore a reference type.

nasufara
This is not correct. You could easily define a struct which contained a collection of chars.
itowlson
Yea, String is kind of a special reference type, immutable; making use of an intern pool. You might want to read this: http://en.wikipedia.org/wiki/String_interning
o.k.w
Fixed it up a bit.
nasufara
String doesn't inherit from collection base classes. It derives directly from object. It *implements* various collection interfaces, but structs can implement interfaces too (check out Int32 **grin**).
itowlson
+2  A: 

strings are indeed a special case. They are a reference type but behave like value types. This was done for "simplicity's sakes" by the .Net development team.

Great quote: It is a reference type It's a common misconception that string is a value type. That's because its immutability (see next point) makes it act sort of like a value type. It actually acts like a normal reference type. See my articles on parameter passing and memory for more details of the differences between value types and reference types.

From: http://www.yoda.arachsys.com/csharp/strings.html

Paul Sasik
Strings aren't that special: you can easily create immutable reference types with value semantics in your own code. And the reason the poster isn't seeing his original change is nothing to do with immutability: he'd see exactly the same thing if he passed a Control.
itowlson
Yes, the statement "strings are indeed a special case" may be misleading in this context. Strings are ordinary immutable reference types, and behave as reference types and not value types for every purpose (object.Equals behavior, nullability, construction, noncopying, etc). The fact that they are immutable is orthogonal to the whether they are reference or value types: Either reference or value types may be immutable.
Ray Burns
BUT I should mention that *strings are indeed a special case* in another way: The NET Framework runtime (the CLR) optimizes the storage of a string's internal character array in RAM in a unique way. If you're looking at RAM using a low level debugger you'll see strings are indeed stored differently. But there is no way to detect this difference from your user level code unless you carefully measure RAM consumption in certain specific scenarios, for all normal purposes there is nothing at all special about strings.
Ray Burns
+13  A: 

Strings are references.

changeMe doesn't change the string, it just reassigns a local reference (pointer) within that function.

Now, if you passed the string as a ref arg, you can have more fun:

public static string changeMe(ref string param) {
    param = "I have changed you!!!";
    return ""; // no return for test
}
Frank Krueger
+3  A: 

This has nothing to do with string being a value type or a reference type. The parameter of your changeMe method isn't marked ref, so if the method changes it, the caller will not see the changes.

Try that instead :

public static string changeMe(ref string param)
{
    param = "I have changed you!!!";

    return ""; // no return for test
}
Thomas Levesque
I didn't put "ref" because the idea is to discovery why a "reference" type doesn't change. I thought it could be redundant. "Reference type with ref param".
Ismael
I think you're confusing two independant concepts : value/reference types, and passing parameters by value/by reference. Check this link for more details : http://www.yoda.arachsys.com/csharp/parameters.html
Thomas Levesque
-1: for ALL other reference types, passing it using byval semaantics means you cannot change which object on the heap the callers copy points to, it does not mean you can't change the contents of the object on the heap that the variable poitns to. Because strings are immutable, they behave like a value type in this respect, even though they are a reference type, but this is done by exceptional additional coding in the CLR
Charles Bretana
This is not contradictory with my answer... The string itself isn't modified here (it wouldn't be possible anyway), but the `param` variable is changed to reference another string. And no, strings don't "behave like a value type", they behave exactly like any other immutable reference type... See the quote in psasik's answer
Thomas Levesque
@Charles: It is not true that strings behave like a value type "by exception additonal coding in the CLR". The only thing special about strings is that, like any other immutable type, the implementors left out all setter methods and other ways to modify the object outside the contructor. I would not consider omitting methods to be "exceptional additional coding". It's just choosing to leave out methods that would modify the string. That's less coding, not "additional" coding, and certainly not "exceptional."
Ray Burns
A: 

You passed by value and made a copy of the reference. Pass it by reference it will change. Well, really a new string will be created and your reference will be to a new spot.

Nathan Z
+16  A: 

String is indeed a reference type. However, when your Main method calls changeMe(str), .NET passes a copy of the reference to str to changeMe in the param argument. changeMe then modifies this copy to refer to "I have changed you!!!", but the original str reference still points to "I will never change".

Being a reference type means that if you changed the state of the passed string, the caller would see those changes. (You can't do this to a string because strings are immutable, but you can do it to other reference types e.g. Control.) But reassigning a parameter doesn't change the value the caller passed in that parameter, even if that value is a reference.

itowlson
+3  A: 

The reason that Program.changeMe(str) does not cause str to be set to "I have changed you!!!" is that although string is a reference type, references are passed by value.

So param is a copy of the reference, it gets changed inside the scope of Program.changeMe(param) and then thrown away at the end of the method.

The reference str is only copied, not changed to reference "I have changed you!!!"

richj
+1  A: 

Yes, this seems strange. What's happening is that you're passing a reference to your string in the parameter, but you're passing it by value, i.e. you're creating a copy of the reference in the parameter. So when you reassign it within the scope of you're function, you're only changing the changeMe() method's local copy of your string reference.

Lots more info about this here: http://www.yoda.arachsys.com/csharp/parameters.html

cxfx
A: 

If you are planning on changing the value of an argument to a method you should really use the "ref" or "out" keywords. Otherwise you risk making confusing methods that may or may not do what you intend. As other have pointed out you are changing the value of the string reference and not the instance of the object. To see how this works with other classes I have written a quick example

static void Main(string[] args)
{
    var str = "String";
    var obj = new MyObject() { Value = "Object" };

    Console.WriteLine(str); //String
    Console.WriteLine(obj); //Object

    ChangeMe(str);
    ChangeMe(obj);

    Console.WriteLine(str); //String
    Console.WriteLine(obj); //Object

    ChangeMeInner(obj);
    // this is where it can get confusing!!!
    Console.WriteLine(obj); //Inner 

    ChangeMe(ref str);
    ChangeMe(ref obj); 

    Console.WriteLine(str); // Changed
    Console.WriteLine(obj); // Changed

    Console.Read();
}
class MyObject
{
    public string Value { get; set; }
    public override string ToString()
    {
        return Value;
    }
}
static void ChangeMe(MyObject input)
{
    input = new MyObject() { Value = "Changed" };
}
static void ChangeMeInner(MyObject input)
{
    input.Value = "Inner";
}
static void ChangeMe(string input)
{
    input = "Changed";
}
static void ChangeMe(ref MyObject input)
{
    input = new MyObject() { Value = "Changed" };
}
static void ChangeMe(ref string input)
{
    input = "Changed";
}
Matthew Whited
A: 

For all other reference types, passing it using byval semantics (without ref or out ), means you cannot change which object on the heap the callers copy points to, it does not mean you can't change the contents of the object on the heap that the variable points to. Because strings are immutable, they are truly an exception, they behave like a value type in this respect, even though they are a reference type, but this is done by exceptional additional coding in the CLR.

Charles Bretana
Incorrect. 1. There is no "exceptional additional coding in the CLR" to make string a value type. 2. The word "other" in the first sentence makes it deceptive ("For all other reference types..."). In fact, the truth is that for *all* reference types, *including* string, the rest of the sentence applies. In fact, you can't (easily) change a string that is pointed to, but if you want to interpret the statement this way, it also applies to all immutable types so "For all other reference types" would need to be "for mutable reference types". I just fear people will be mislead by this.
Ray Burns
Typo in previous comment: First numbered item should be: 1. There is no "exceptional additional coding in the CLR" to make string *behave like* a value type.
Ray Burns
Puzzle: There actually is a way to modify a string in NET Framework. What is it?
Ray Burns
There IS additional code in the CLR (or CTS) that makes strings BEHAVE like a value type. For strings thjat are NOT interned, it's the code that creates a NEW object on the heap with the new value you are 'assigning' to the string, and replaces the pointer value in the variable with the address to this new object instead of the old one. For interned strings, all the code responsible for managing the intern table, and the levels of indirection between the string 'variable' and the entry in the intern table is ADDITIONAL code not used by any other type in the CTS.
Charles Bretana
But @Ray, your second point about being able to change the contents vs which object, again, because of the way strings work, when you pass a string by reference, it only SEEMS like you are able to chnage the contents of the string. Much is going on beneath the surface. that no other CTS type does, to create this behavior. So I'm not sure how anyone could be mislead by this...
Charles Bretana