为何以int[]为键时Dictionary.ContainsKey返回False?
你的问题核心在于数组作为Dictionary键时的相等性判断逻辑——.NET中的数组是引用类型,默认情况下Dictionary会用引用相等来判断键是否相同,而不是比较数组的内容。
具体来说,你在循环里创建的int[] i = {x,y}和最后用来检查的int[] z = {0,0}是两个完全独立的对象,哪怕它们的元素完全一致,Dictionary也会认为这是两个不同的键,所以ContainsKey(z)会返回false。
最简单的解决方案:用ValueTuple代替数组
从C# 7.0开始,ValueTuple是处理这种简单坐标场景的最优选择,它是值类型,默认的相等性判断就是基于元素值的,不需要额外代码:
修改字典的声明:
static Dictionary<(int x, int y), Tile> map = new Dictionary<(int x, int y), Tile>();
然后在循环和检查时直接使用值元组:
static bool GenerateMap() { for (int y = 0; y < 3; y++) { for (int x = 0; x < 3; x++) { Tile tile; // 直接用值元组作为键 var key = (x, y); if(map.ContainsKey(key)) { tile = map[key]; Console.WriteLine("Contains!"); } else { tile = new Tile(); tile.Generate(); map.Add(key, tile); } } } // 用值元组检查 if (map.ContainsKey((0, 0))) return true; return false; }
这样修改后,(0,0)和循环中创建的(0,0)会被认为是相等的键,方法会正确返回true。
备选方案:自定义数组相等比较器
如果你必须使用数组作为键,可以给Dictionary传入一个自定义的IEqualityComparer<int[]>,让它根据数组内容判断相等:
首先实现比较器:
public class IntArrayEqualityComparer : IEqualityComparer<int[]> { public bool Equals(int[] a, int[] b) { // 处理null情况 if (a == null || b == null) return a == b; // 长度不同直接不相等 if (a.Length != b.Length) return false; // 逐一比较元素 for (int i = 0; i < a.Length; i++) { if (a[i] != b[i]) return false; } return true; } public int GetHashCode(int[] arr) { if (arr == null) return 0; // 基于数组内容生成哈希码 int hash = 17; foreach (int num in arr) { hash = hash * 31 + num.GetHashCode(); } return hash; } }
然后初始化字典时传入这个比较器:
static Dictionary<int[], Tile> map = new Dictionary<int[], Tile>(new IntArrayEqualityComparer());
这样原代码中的数组键就会根据内容判断相等,ContainsKey(z)也会返回true。
另一种备选:自定义坐标结构体
你还可以创建一个专门的Coordinate结构体来表示坐标,这样代码可读性更高,同时也能基于值判断相等:
public struct Coordinate { public int X { get; } public int Y { get; } public Coordinate(int x, int y) => (X, Y) = (x, y); // 重写相等性判断方法 public override bool Equals(object obj) { return obj is Coordinate coord && X == coord.X && Y == coord.Y; } public override int GetHashCode() => HashCode.Combine(X, Y); // 可以额外重载==和!=运算符,让代码更直观 public static bool operator ==(Coordinate left, Coordinate right) => left.Equals(right); public static bool operator !=(Coordinate left, Coordinate right) => !(left == right); }
然后字典声明为static Dictionary<Coordinate, Tile> map = new Dictionary<Coordinate, Tile>();,使用时创建new Coordinate(x, y)作为键即可。
内容的提问来源于stack exchange,提问作者Kat9_123

