递归,返回后带有char数组的C#

时间:2012-06-13 15:32:42

标签: c# arrays recursion char return

在下面的代码中,当我在调试器中单步执行代码时检查Chars变量时,char数组的大小在最后一次迭代中的返回行之前为0,但是在返回行之后1并继续增长回原来的大小。

为什么会这样?感谢您提前提供任何帮助。

static void Main(string[] args)
{
    string str = "Hello";
    PrintReverse(str.ToArray()); // prints "olleH"
    Console.Read();
}

static void PrintReverse(char[] Chars)
{
    Console.Write(Chars[Chars.Length - 1]);
    Array.Resize(ref Chars, Chars.Length - 1);
    if (Chars.Length == 0) return;
    PrintReverse(Chars);
}

3 个答案:

答案 0 :(得分:2)

尝试将ref添加到参数声明中,现在应该按预期的方式工作。

如果没有ref,对Array.Resize的调用只能修改本地数组引用,而不能修改从Main传入的引用。

    static void Main(string[] args)
    {
        string str = "Hello";
        var array = str.ToArray();
        PrintReverse(ref array);
        Console.Read();
        Debug.Assert(array.Length == 0);
    }
    static void PrintReverse(ref char[] Chars)
    {
        Console.Write(Chars[Chars.Length - 1]);
        Array.Resize(ref Chars, Chars.Length - 1);
        if (Chars.Length == 0) return;
        PrintReverse(ref Chars);
    }

修改

我错误地认为ref引起了浅层克隆,这就是证据:

    static void Main(string[] args)
    {
        var array = new[] { new object() };
        TestRef(ref array, array);
    }

    static void TestRef(ref object[] arrayByRef, object[] arrayByValue)
    {
        Debug.Assert(ReferenceEquals(arrayByRef, arrayByValue)); //no difference whether passed by ref or value, if there was a shallow clone happening, this would fail
        Array.Resize(ref arrayByRef, 2);
        Debug.Assert(!ReferenceEquals(arrayByRef, arrayByValue)); //only now do they differ
    }

答案 1 :(得分:2)

这里有两个问题。

首先,'返回前/后'问题意味着您正在看到两个不同的执行帧 - 也就是说,在调试器中,堆栈跟踪将显示一堆PrintReverse在彼此之上,因为每个在它自己的背景下,同时具有自己的状态。它几乎(虽然不是真的)像该方法的“实例”,你会看到两个不同的。

其次,因为每个都有自己的状态,所以每个中的局部变量 - 包括关键的参数 - 也是重复的。它们最初指向相同的堆对象(您的初始Char数组),但它们都是不同的变量。

现在,看看这段代码:

char[] test1 = new char[] { '1', '2', '3' }, test2 = test1;
Array.Resize(ref test2, 2);
MessageBox.Show(new string(test1) + " - " + new string(test2)); // result: 123 - 12

如果你运行它,你会看到虽然变量最初引用同一个对象,但Array.Resize 创建一个新对象并更改传入的变量的引用以指向新的一个。第一个变量中的引用仍然指向旧的(不可变的)对象。

这就是你的情况,只有Chars参数。在每种方法中,您使用Array.Resize()重新分配Chars指向其他位置,但原始变量仍然引用旧位置。

答案 2 :(得分:1)

考虑执行链。你是递归调用一个日渐阵列,直到你得到0的方法,然后返回给调用者(同样的方法),这样你所看到的增长回你穿越回调用堆栈原来的大小。

没有额外的逻辑是发生作为其结果,当递归调用是在该方法中的最后一个电话,但你能看到调试结束每个呼叫,这反过来有一个阵列大小1比呼叫更大之前它

但是,如果要通过引用传递数组,则数组大小将保持为0,因为它在调用堆栈中出现,因为每次连续调用Array.Resize都会创建一个新数组并更新所有引用到新数组而不是只有该调用的本地引用。 (如果你没有通过引用传递,它只更新引用的副本,并且不会更新它之前的调用中的那些副本。)

这是因为Array.Resize创建一个新数组并更新引用以指向新数组而不是旧数组,并且不通过引用传递,您将发送引用的副本到原始数组而不是对数组的实际引用,因此对Array.Resize的调用不会更新旧引用。

static void PrintReverse(ref char[] Chars)
{
    Console.Write(Chars[Chars.Length - 1]);
    Array.Resize(ref Chars, Chars.Length - 1);
    if (Chars.Length == 0) return;
    PrintReverse(ref Chars);
}

感谢Groo纠正我,希望这次我能做对