【问题标题】:C# Infinite While Loop using ContainsValue with nested DictionaryC# 无限 While 循环使用 ContainsValue 和嵌套字典
【发布时间】:2023-04-07 04:52:01
【问题描述】:

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

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

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

现在,我正在尝试用 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);
    }

【问题讨论】:

    标签: c# dictionary while-loop contains infinite


    【解决方案1】:

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

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

    事实上,根据您发布的代码,在这种情况下您似乎不想在任何地方使用字典类。可以使用字典对象使其工作,但您并没有真正利用这些数据结构的类字典性质。您似乎在这里更多地使用字典,因为从语义上讲,它们的操作方式与您在 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 值的实际内容与 worldMap 中找到的值进行比较。

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

    【讨论】:

      【解决方案2】:

      ContainsValue 使用默认相等比较器EqualityComparer.Default 表示TValue,即字典中的值类型。 roomXY 是一​​个(引用)字典对象,引用不会因为改变这个对象的 X 和 Y 坐标而改变,所以你会陷入无限的 while 循环。

      【讨论】:

        猜你喜欢
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 2015-01-13
        • 1970-01-01
        相关资源
        最近更新 更多