【问题标题】:Difference in behaviour at runtime between local variable and a class field局部变量和类字段在运行时的行为差异
【发布时间】:2021-09-03 20:01:18
【问题描述】:

我正在尝试编写一个递归方法来查找从 m 的左上角到右下角的所有可能方式的数量 n 网格(不向上移动)。

变量 key 应该保存 mn 的当前值,但如果我尝试使用类字段,这个变量表现得像是落后了一步之类的。

以下代码将以未处理的异常结束:

已添加具有相同密钥的项目。键:1,1

看起来很像,比如说,当你的初始 mn 分别是 3 和 2 时,(1, 2) 的函数将尝试添加它的 value 到带有“1,1”键的字典:

class Finder
{
    public Finder()
    {
        memo = new(new List<KeyValuePair<string, ulong>>
        { new KeyValuePair<string, ulong>("1,1", 1) });
    }

    private Dictionary<string, ulong> memo;
    private string key;
    private ulong value;

    public ulong FindPaths(int m, int n)
    {
        key = String.Format("{0},{1}", (m < n ? m : n), (m > n ? m : n));

        if (memo.ContainsKey(key))
        {
            return memo[key];
        }

        if (m == 0 || n == 0)
        {
            return 0;
        }

        value = FindPaths(m - 1, n) + FindPaths(m, n - 1);
        memo.Add(key, value);
        return value;
    }
}

当我将 Add 方法中预先计算的 key 更改为最初计算它的相同表达式时,问题就消失了:

memo.Add(String.Format("{0},{1}", (m < n ? m : n), (m > n ? m : n)), value);

现在,如果我们将类字段替换为局部变量,一切都会按预期进行:

class Finder
{
    public Finder()
    {
        memo = new(new List<KeyValuePair<string, ulong>>
        { new KeyValuePair<string, ulong>("1,1", 1) });
    }

    private Dictionary<string, ulong> memo;
    private ulong value;

    public ulong FindPaths(int m, int n)
    {
        string key;
        key = String.Format("{0},{1}", (m < n ? m : n), (m > n ? m : n));
        
        if (memo.ContainsKey(key))
        {
            return memo[key];
        }

        if (m == 0 || n == 0)
        {
            return 0;
        }

        value = FindPaths(m - 1, n) + FindPaths(m, n - 1);
        memo.Add(key, value);

        return value;
    }
}

这种行为差异是否是由于类字段位于堆上而局部变量位于堆栈上这一事实引起的?我认为在递归方法中使用类字段比每次方法调用自身时创建本地更实用。

请帮助我更深入地了解这个主题。

【问题讨论】:

  • 使用字段递归会更改 key 的值,从而破坏 memo.Add(key, value) 调用。请务必尽早使用 small 案例进行测试,以便在使用调试器单步执行时发现此问题。

标签: c# .net heap-memory stack-memory


【解决方案1】:

如果您的类中有一个成员变量,则该引用只存在一次。使用递归函数时,所有递归调用都在相同的内存基础上工作,因此在类对象中的相同内容上工作。

如果变量在你的函数中是本地的,那么在每个函数调用中它都是一个新的存储和新的变量内容。

当使用你的上层代码时,递归调用本身也会改变键值,然后在“添加”命令中,键内容与递归调用之前不同。

【讨论】:

    猜你喜欢
    • 2017-10-04
    • 1970-01-01
    • 1970-01-01
    • 2016-10-16
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多