C# 无限 While 循环使用包含嵌套字典的 ContainsValue

C# Infinite While Loop using ContainsValue with nested Dictionary

我在 PHP 中有这个脚本,我使用

while( in_array(array('x' => $x, 'y' => $y), $worldMap) ){ ... }

检查我的世界地图是否已经在那些 XY 位置有空间。 如果为真,我随机化 X 或 Y,然后 WHILE 循环再次使用新值检查等等,如果为假,我用最后生成的 XY 填充 worldMap 数组。

现在,我正尝试用 C# 重写该代码,但遇到了无限循环。

这是我当前的代码:

public int nbRooms = 10;

private Dictionary<int, Dictionary<string, int>> worldMap = new Dictionary<int, Dictionary<string, int>>();
private Dictionary<string, int> roomXY = new Dictionary<string, int>();

private string[] arrayXY = {"X","Y"};
private int[] arrayNbr = {-1,1};
private int X = 0;
private int Y = 0;

    for(int i = 0; i <= nbRooms; i++)
    {
        while(worldMap.ContainsValue(roomXY))
        {
            string XorY = arrayXY[Random.Range(0, 2)];

            switch(XorY)
            {
            case "X": X += arrayNbr[Random.Range(0, 2)];
                break;
            case "Y": Y += arrayNbr[Random.Range(0, 2)];
                break;
            }

            roomXY.Clear();
            roomXY.Add("X", X);
            roomXY.Add("Y", Y);
        }

        worldMap.Add(i, roomXY);
    }

ContainsValue 使用默认相等比较器 EqualityComparer.Default for TValue,字典中值的类型。 roomXY 是一​​个(引用)字典对象,引用不会通过更改此对象的 X 和 Y 坐标而改变,因此您 运行 进入无限循环。

这里的基本问题是,默认情况下,两个引用类型对象之间的比较只是比较引用本身。当您更改 roomXY 对象的内容时,您不会更改引用本身(即实际对象保持不变),因此一旦您将对象添加到 worldMap 字典一次,它下次循环检查时总是在那里。

很好地说明了为什么在移植代码时移植 意图 很重要,但不一定是确切的实现,因为语言处理事物的方式不同。

事实上,根据您发布的代码,在这种情况下您可能不想在任何地方使用字典 class。它可以使用字典对象来工作,但您并没有真正利用这些数据结构的类字典性质。似乎您在这里更多地使用字典,因为从语义上讲,它们的操作似乎与您在 PHP 中使用的数据结构类似,但实际上 C# 提供了可能更合适的其他语言功能。

例如,您可以这样编写代码:

struct Room
{
    public readonly int X;
    public readonly int Y;

    public Room(int x, int y) { X = x; Y = y; }
}

public int nbRooms = 10;

private Room[] worldMap = new Room[nbRooms];

private string[] arrayXY = {"X","Y"};
private int[] arrayNbr = {-1,1};
private int X = 0;
private int Y = 0;
private Room roomXY = new Room(X, Y);

    for(int i = 0; i <= nbRooms; i++)
    {
        while(Array.IndexOf(worldMap, roomXY) >= 0)
        {
            string XorY = arrayXY[Random.Range(0, 2)];

            switch(XorY)
            {
            case "X": X += arrayNbr[Random.Range(0, 2)];
                break;
            case "Y": Y += arrayNbr[Random.Range(0, 2)];
                break;
            }

            roomXY = new Room(X, Y);    
        }

        worldMap[i] = roomXY;
    }

由于 C# 默认为值类型(即 struct)实现相等比较的方式,这会将 roomXY 值的实际内容与 [=12] 中的值进行比较=].

注意:您的原始实现和上面的实现都在 worldMap 数据结构中使用线性搜索。对于这里的少量房间(10 个),这应该没问题。但是您应该知道,对于较大的数据集,这可能非常低效。在这种情况下,您可能希望使用不同的方法来生成此数据(例如,哈希集、较大地图数据结构中的标志、混洗等)。