在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()
,则这两个值会有所不同。
您如何看待我的解释?
答案 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
指向的地方。 再次对所有引用类型都是如此,而不仅仅是字符串。