7

今天我学习了值类型和引用类型。

我对下面的代码示例有一个疑问:

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值呢?(为什么不作为参考传递?)

4

4 回答 4

6

区分变量对象很重要。

考虑代码:

String str = "";
FuntionString(str);

str是一个变量。起初,该变量的值是对字符串的引用。假设该引用是数字 246。字符串 246 可以解析为一个值;它的值是一个空字符数组。

然后我们将该变量的传递给FuntionString. 我们没有传递对str变量的引用,我们只是传递数字 246。它是该引用或数字的副本

在该函数内部,它有一个完全不同的变量,恰好也被调用str。标识符相同的事实并没有改变它是一个恰好具有相同值的不同变量的事实。

当您更改内部str 变量FuntionString时,请不要变量strMain. 在 of 的主体FuntionString完成后, strfromMain仍然像以前一样持有引用 246,并且str内部的变量FuntionString具有一些新引用的值,比如说 3,一个带有 value 的字符串 new string "updated value"对变量的更改不会反映在Main.

FunctionSB实现方法的情况下实际上并没有改变sb变量。它不会改变变量,而是改变变量引用的对象。在这种情况下sb,它指向某个位置的对象,比如说 39。sbmain 方法和另一个方法中的 The 是两个不同的变量,具有相同引用的副本。该方法不会更改sb变量,而是更改位置 39 的 sb 对象。他们俩sb对象仍然具有相同的值;它们没有改变,但它们都“指向”一个已经改变的对象。因此,可以从 中观察到使用该方法执行的突变Main

如果根据定义,FuntionString更改了两个变量指向的字符串对象,而不是更改变量本身,那么更改将是调用者“可观察到的”。当然,这是不可能的,因为字符串是不可变的。该方法无法以调用者可以观察到的方式改变对象。

于 2013-10-09T18:34:23.427 回答
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 字符串是不可变的,因此这始终是正确的。无法修改字符串对象,只能创建一个新对象并重新分配它。以稍微不同的方式重新陈述这一点:是的,字符串是通过引用传递的,但是,由于字符串类型是不可变的,您仍然不能使用该引用以任何方式更改对象。

于 2013-10-09T18:27:53.477 回答
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 的行为方式相同。这表现出与字符串相同的问题:

private static void FunctionSB(StringBuilder sb)
{
    sb = new StringBuilder();
}

以上不会修改传递给FunctionSB. 正如其他人所指出的,解决此问题的方法是使用ref,因为您想修改引用本身。

于 2013-10-09T18:34:44.617 回答
0

c# 中的字符串是一种内置数据类型。如果你想传递值以便可以重新引用(不能修改,只能替换,字符串是不可变的),你需要 out 修饰符:

    static void FuntionString(out String str) 
    {
        str = "updated value";
    }

这基本上与通过引用传递它相同。唯一的区别是当使用 out 时,你的字符串必须被初始化。如果您不能确定这一点,请改用 ref。

于 2013-10-09T18:25:48.910 回答