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 个),这应该没问题。但是您应该知道,对于较大的数据集,这可能非常低效。在这种情况下,您可能希望使用不同的方法来生成此数据(例如,哈希集、较大地图数据结构中的标志、混洗等)。
我在 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 个),这应该没问题。但是您应该知道,对于较大的数据集,这可能非常低效。在这种情况下,您可能希望使用不同的方法来生成此数据(例如,哈希集、较大地图数据结构中的标志、混洗等)。