在C#中传递引用类型

9
class Test
{
    static void Func(StringBuilder myString)
    {
        myString.Append ("test");
        myString = null;
    }
    static void Main()
    {
        StringBuilder s1 = new StringBuilder();
        Func(s1);
        Console.WriteLine (s1);
    }
}

输出结果是“Test”,为什么不是null?

如果将s1按引用传递到Func(),那么为什么myString.Append("test")会改变它,但myString = null却没有呢?

提前感谢您的帮助。


这个话题已经被反复讨论过了。 - Dustin Kingen
http://stackoverflow.com/a/18649121/465495 - yazanpro
5个回答

12

你是通过值传递引用对象。

也就是说,对象的地址是按值传递的,但对象本身和对象的地址是相同的。因此,当调用方法时,虚拟机会复制引用;你只是在改变一个副本。

myString.Append 中,你使用复制的引用来访问对象(仍然是同一个对象),然后对它进行更改/调用。

你认为自己正在做的是这样的:

class Test
{
    static void Func(ref StringBuilder myString)
    {
        myString.Append("test");
        myString = null;
    }
    static void Main(string[] args)
    {
        StringBuilder s1 = new StringBuilder();
        Func(ref s1);
        Console.WriteLine(s1);
    }
}

更详细的解释:http://javadude.com/articles/passbyvalue.htm


11
如果将s1作为引用传递给Func(),那么为什么myString.Append("test")会改变它?
它不是按引用传递的,而是其地址值被传递到函数中。假设S1指向内存位置0x48,则该位置将传递给Func,其中Func参数myString将开始指向该位置。稍后,当您将文本附加到该位置时,它会被添加。但是稍后当您将null分配给myString时,它开始指向空,但原始位置保持不变。
您应该查看:由Jon Skeet介绍的C#参数传递 请考虑以下图表。在步骤1中,您创建了一个StringBuilder类型的新对象,并让S1引用该对象。

enter image description here

在第二步,将 S1 传递给 Func 后,现在 myString 也指向内存中的同一对象。

enter image description here

在使用Append方法向myString添加文本时,由于myString指向对象所占用的内存位置,因此会更新原始对象。在步骤3中,当你将null赋值给myString时,它失去了StringBuilder对象的地址,但这不会改变对象或指针S1

enter image description here


你的解释有点混乱,但因为链接而获得了赞。 - Dan Bechard
@Dan,刚刚添加了一些图表以澄清。 - Habib
@Habib,你用什么工具来制作这种图表?顺便点个赞。 - Soner Gönül
@SonerGönül,Microsoft Visio - Habib

2

在C#中,默认情况下,复杂类型的指针(引用)按值传递。您必须指定它按引用传递,例如:

static void Func(ref StringBuilder myString)
{
    myString.Append ("test");
    myString = null;
}

1
复杂类型是通过引用传递的,引用(指针)本身是按值传递的。 - Dan Bechard
1
@Dan - 答案已更新以澄清。谢谢。 :-) - Karl Anderson

0

在函数中添加ref以通过引用传递它。

static void Func(ref StringBuilder myString)
{
    myString.Append ("test");
    myString = null;
}

然后像这样调用:

Func(ref s1);

0
因为你将 StringBuilder s1 的引用传递给了 Func。在 Func 内部,对同一 StringBuilder 实例的引用被称为“myString”。你首先告诉它要做某事("myString.Append("Test")"),然后在 Func 内部将引用设置为 null。Append 的结果对 Main() 可见;在 Main 中,s1 不受设置为 null 的引用的影响。

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接