字符串对象真的是通过引用?

时间:2009-10-20 23:24:20

标签: c# .net string reference

我正在学习(新手).NET,我有些疑惑。

从书中读到的例子我了解到String是对象,然后是Reference Type。

所以,我做了这个测试,结果与我的预期不同:

我真的很好奇,这是一个例外,因为“字符串”是特殊类型吗?

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
    }
}

输出:

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!

10 个答案:

答案 0 :(得分:19)

String确实是一种引用类型。但是,当您的Main方法调用changeMe(str)时,.NET会在param参数中将str的引用副本传递给changeMe。然后,changeMe修改此副本以引用“我已经改变了你!!!”,但原始的str引用仍然指向“我永远不会改变”。

作为引用类型意味着如果您更改了传递的字符串的状态,则调用者将看到这些更改。 (您不能对字符串执行此操作,因为字符串是不可变的,但您可以将其用于其他引用类型,例如Control。)但重新分配参数不会更改传入的调用方的参数,即使该值是参考。

答案 1 :(得分:13)

字符串是引用。

changeMe不会更改字符串,只是在该函数中重新分配本地引用(指针)。

现在,如果您将字符串作为ref arg传递,您可以获得更多乐趣:

public static string changeMe(ref string param) {
    param = "I have changed you!!!";
    return ""; // no return for test
}

答案 2 :(得分:4)

这与字符串是值类型或引用类型无关。您的changeMe方法的参数未标记为ref,因此如果方法更改它,则调用者将看不到更改。

请尝试改为:

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

    return ""; // no return for test
}

答案 3 :(得分:4)

Program.changeMe(str)不会导致str设置为“我改变了你!!!”的原因是虽然string是引用类型,但引用是按值传递的。

所以param是引用的副本,它在Program.changeMe(param)的范围内被更改,然后在方法结束时被抛弃。

参考str仅被复制,而不是更改为引用“我已经改变了你!!!”

答案 4 :(得分:2)

字符串确实是一种特殊情况。它们是引用类型,但行为类似于值类型。这是为.Net开发团队的“简单性”而做的。

好极了: 它是一种参考类型 字符串是值类型是一种常见的误解。那是因为它的不变性(见下一点)使它有点像一种价值类型。它实际上就像一个普通的引用类型。有关值类型和引用类型之间差异的更多详细信息,请参阅有关参数传递和内存的文章。

来自:http://www.yoda.arachsys.com/csharp/strings.html

答案 5 :(得分:1)

MSDN page可以帮助解释它为什么是参考类型。

因为Stringchar的集合并且继承自各种Collection基类,所以它是一个类,因此是一个引用类型。

答案 6 :(得分:1)

是的,这看起来很奇怪。发生的事情是您在参数中传递了对字符串的引用,但是您将其传递给按值,即您在参数中创建了引用的副本。因此,当您在函数范围内重新分配它时,您只需要更改changeMe()方法的字符串引用的本地副本。

有关这方面的更多信息: http://www.yoda.arachsys.com/csharp/parameters.html

答案 7 :(得分:0)

您按值传递并制作了引用的副本。通过引用传递它将改变。好吧,真的会创建一个新的字符串,你的参考将是一个新的位置。

答案 8 :(得分:0)

如果您打算将参数的值更改为方法,则应该使用“ref”或“out”关键字。否则,您可能会制造令人困惑的方法,这些方法可能会也可能不会达到您的意图。正如其他人指出的那样,你正在改变字符串引用的值而不是对象的实例。要查看其如何与其他类一起使用,我已经编写了一个快速示例

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";
}

答案 9 :(得分:-1)

对于所有其他引用类型,使用byval语义(没有ref或out)传递它意味着您无法更改调用者复制指向的堆上的哪个对象,这并不意味着您可以' t更改变量指向的堆上的对象的内容。因为字符串是不可变的,所以它们确实是一个例外,它们在这方面的行为就像值类型一样,即使它们是引用类型,但这是通过CLR中的特殊附加编码来完成的。