为什么必须通过引用传递String对象?

时间:2015-02-04 18:41:15

标签: c# pass-by-reference pass-by-value

在C#中,据我所知,方法调用期间参数的传递是按值进行的。但是当您使用对象作为参数时,您传递的是对象本身的引用。这意味着如果您访问(并修改)对象内部字段的值,则一旦完成方法调用,也会看到更新。

因此,如果我在方法中修改String的值,则应在方法调用终止时修改它(因为String是C#中的对象)。但事实并非如此,如果我写的话:

public static void main (String []args){
  String s= "hello";
  method(s);
  System.Console.Writeline(s);
}

public void method (String s)
{s = "world";}

它将打印“你好”,而不是“世界”。打印“世界”的唯一方法是在方法签名中添加关键字ref并调用。

为什么会这样?我的答案(我希望你确认或纠正)是在C#中String对象是不可变的,所以如果我使s =“world”实际上编译器正在创建一个新的字符串对象,但是对象的引用是不会改变(因为通过是值)。

事实上,如果我在s.getHashCode()来电之前和之后打印method(),则这两个值会有所不同。

您如何看待我的解释?

3 个答案:

答案 0 :(得分:6)

  

因此,如果我在方法中修改String的值,则应在方法调用终止时修改它(因为String是C#中的对象)。但这不是真的,事实上如果我写:

您没有修改String对象。您正在修改参数以引用其他String。这只是改变了一个局部变量,而且调用者看不到

String引用按值传递,就像正常一样。您需要区分更改参数的值,以及修改参数值引用的对象。即使它是可变的,您也会在自己的班级中看到完全相同的行为:

class Person
{
    public string Name { get; set; }
}

class Test
{
    static void Main()
    {
        var p = new Person { Name = "Tom" };
        Method(p);
        Console.WriteLine(p.Name);
    }

    static void Method(Person parameter)
    {
        parameter = new Person { Name = "Robin" };
    }
}

现在,如果在Method中您对对象进行了更改,例如

static void Method(Person parameter)
{
    parameter.Name = "Robin";
}

... 然后您会看到输出的更改。但那并没有修改参数。字符串不变性相关的唯一原因是,当参数是字符串时,它意味着上面的Method的第二个版本没有等效(在安全代码中)。

有关详细信息,请参阅my article on parameter passing

答案 1 :(得分:2)

  

因此,如果我在方法中修改String的值,则应在方法调用终止时修改它(因为String是C#中的对象)。

是的,这是100%正确的。如果您能够修改实际的string实例,那么调用者将能够观察到该更改。

当然,因为string是不可变的,所以无法修改字符串实例。这就是它不可变的意义。

由于您实际上并未实际修改string实例,因此无法对调用者进行实际观察。

您在代码中所做的不是修改字符串实例,而是修改保存对字符串的引用的变量。该变量与main方法中的局部变量完全不同。修改一个不会影响另一个(除非您使用ref)。

你拥有完全不同的变量,每个变量都带有一个字符串的引用。如果更改了两个引用的字符串,则可以从任一位置观察到该更改。更改任一变量以引用新内容不是其他变量可以观察到的内容。

答案 2 :(得分:1)

它与字符串不变性无关。这是引用类型变量的默认行为。分配新引用时,您将更改变量的引用值。引用同一位置的其他变量不受该分配的影响。在您的情况下,当您不使用ref时,s的参考值将被复制到parameter,如下所示:

string s = "hello"
string parameter = s;

之后,如果您为parameter分配新的引用,则只会更改parameter指向的位置,并且不会影响s。当你使用ref时,没有复制。在这种情况下,你实际传递引用而不是引用的副本。这就是为什么你可以改变它s指向的地方。 再次对所有引用类型都是如此,而不仅仅是字符串。