字符串不表现为引用类型

本文关键字:引用类型 字符串 | 更新日期: 2023-09-27 18:34:42

今天我了解了值类型和引用类型。

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

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

字符串不表现为引用类型

区分变量对象很重要。

考虑代码:

String str = "";
FuntionString(str);

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

然后我们将该变量的值传递给 FuntionString 。 我们不传递对str变量的引用,我们只是传递数字 246。 它是该参考或编号的副本。

在该函数内部,它有一个完全不同的变量,恰好也称为 str . 标识符相同的事实不会改变它是一个恰好具有相同值的不同变量的事实。

当您更改 FuntionString 中的 str 变量时,您不会更改 Main str变量。 在 FuntionString 的主体完成后,Mainstr仍然像以前一样保留引用 246,并且 FuntionString 内部的 str 变量具有某个新引用的值,例如 3,值为 "updated value" 的字符串新字符串。 对变量的更改不会反映在 Main 中。

FunctionSB的情况下,该方法的实现实际上不会更改sb变量。 它不是更改变量,而是改变变量引用的对象。 在本例中,sb指向某个位置的对象,假设 39。 main 方法和此其他方法中的sb是两个不同的变量,具有同一引用的副本。 该方法不会更改sb变量,而是更改位置 39 处的 sb 对象。 两个sb对象仍然具有相同的值;它们保持不变,但它们都"指向"已更改的单个对象。 因此,可以从Main中观察到使用该方法执行的突变。

如果从FuntionString的定义中,您更改了两个变量都指向的字符串对象,而不是更改变量本身,则更改将从调用方"可观察到"。 当然,这是不可能的,因为字符串是不可变的。 该方法无法以调用方可以观察到的方式改变对象。

要意识到的是,当你编写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 字符串是不可变的,因此将始终如此。 无法修改字符串对象,只能创建一个新对象并重新分配它。 以稍微不同的方式重新声明这一点:是的,字符串是通过引用传递的,但是,由于字符串类型是不可变的,因此您仍然不能使用该引用以任何方式更改对象。

当您使用 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 ,因为您想修改引用本身。

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

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

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