今天我了解了价值类型和参考类型。
我在下面的代码示例中有一个疑问:
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;
}
此处更新字符串构建器值和类成员变量值,但为什么FuntionString(str);
不更新str
值? (为什么不作为参考传递?)
答案 0 :(得分:6)
区分变量和对象非常重要。
考虑代码:
String str = "";
FuntionString(str);
str
是一个变量。首先,该变量的值是对字符串的引用。假设该引用是数字246.字符串246可以被解析为一个值;它的值是一个空字符数组。
然后我们将该变量的值传递给FuntionString
。我们没有传递对str
变量的引用,我们只是传递了数字246.它是该引用或数字的副本。
在该函数内部,它有一个完全不同的变量,恰好也称为str
。标识符相同的事实并没有改变它恰好具有相同值的不同变量这一事实。
当您更改str
内的FuntionString
变量时,您执行不更改str
变量Main
}。在FuntionString
的正文完成后,str
的{{1}}仍然保留在引用246上,就像之前一样,Main
变量位于str
内}具有值为FuntionString
的字符串新字符串的新引用的值,比如说3。将更改为变量不会反映在"updated value"
。
对于Main
,方法的实施实际上并未更改FunctionSB
变量。它不是改变变量,而是改变变量引用的对象。在这种情况下,sb
指向某个位置的对象,假设为39.主方法中的sb
和其他方法中的两个不同变量带有副本同样的参考。方法不会更改sb
变量,而是更改位置39 处的sb对象。两个sb
对象仍然具有相同的值;它们没有变化,但它们都“指向”一个已经改变的单个对象。因此,可以从sb
观察到用该方法进行的突变。
如果从Main
的定义中你更改了两个变量都指向的字符串对象,而不是更改变量本身,则更改将从呼叫者那里“可观察”。当然,这是不可能的,因为字符串是不可变的。该方法无法以调用者可以观察的方式改变对象。
答案 1 :(得分:4)
要意识到的是,当你编写str = "updated value";
时,实际上是在创建一个新对象。也就是说,你已经完成了相同的工作:
str = new string("updated value");
这意味着,当您编写str = "updated value"
时,您将新对象分配给引用“str”,而不是修改现有对象。 **
因此,关于“客户”类的正确比较点不是:
c.s = "updated class value";
而是:
c = new customer { s = "updated class value" }.
原先由“c”或“str”引用的引用指向的对象因此不变。
在OP案例中您需要做的是使用ref
关键字将引用传递给字符串:
static void FuntionString(ref String str)
{
str = "updated value";
}
这里的区别在于引用本身在FunctionString
内更新,现在指向新的字符串对象。
**请注意,自.Net strings are immutable起,这将永远为真。无法修改字符串对象,只能创建一个新对象并重新分配它。要以稍微不同的方式重新说明:是的,字符串是通过引用传递的,但是,由于字符串类型是不可变的,您仍然无法使用该引用以任何方式更改对象。
答案 2 :(得分:1)
当您使用StringBuilder时,您正在改变StringBuilder实例的实例。它是同一个对象,因为该值是对同一StringBuilder对象的引用。
考虑一下:
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");
}
但是,使用您的字符串示例,您不会修改字符串的相同实例,而是将值本身修改为差异引用。
static void FuntionString(String str)
{
str = "updated value";
}
上面的示例替换了str
引用的值。这不是字符串的唯一行为,StringBuilder的行为方式相同。这表现出与String相同的问题:
private static void FunctionSB(StringBuilder sb)
{
sb = new StringBuilder();
}
上述不会修改传递给FunctionSB
的字符串构建器。正如其他人所指出的那样,解决此问题的方法是使用ref
,因为您想要修改引用本身。
答案 3 :(得分:0)
c#中的字符串是内置数据类型。如果你想传递值,这样你可以重新引用(不能修改,只能替换,字符串是不可变的),你需要out修饰符:
static void FuntionString(out String str)
{
str = "updated value";
}
这与通过引用传递它基本相同。唯一的区别是,当使用时,必须初始化您的字符串。如果你不能确定这一点,请使用ref。