1

在下面的代码中,当我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);
}
4

3 回答 3

2

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

ref调用 toArray.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
    }
于 2012-06-13T15:42:15.293 回答
2

你这里有两个问题。

首先,“返回前/返回后”问题意味着您看到了两个不同的执行帧——也就是说,在调试器中,堆栈跟踪将显示一堆PrintReverses 彼此重叠,因为每个都存在于自己的上下文中,并且它自己的状态,同时。它几乎(虽然不是真的)像这种方法的“实例”,你看到的是两个不同的。

其次,因为每个都有自己的状态,所以每个中的局部变量——包括关键的参数——也是重复的。它们最初指向同一个堆对象(您的初始 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 以指向其他位置,但原始变量仍然引用旧位置。

于 2012-06-13T15:48:44.583 回答
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 纠正我,希望这次我做对了

于 2012-06-13T15:37:40.053 回答