不调用以元组为键的字典 GetHashCode
Dictionary with Tuple as Key GetHashCode is not called
描述
正在尝试创建一个以元组为键的字典。
但是 GetHashCode 和 Equals 函数没有被调用,因此重复的键将被添加到字典中。
这是我想用作字典键的 Key
class:
class Key : IEqualityComparer<Tuple<int, int>>
{
private Tuple<int, int> _tuple;
public Key(int a, int b)
{
_tuple = new Tuple<int, int>(a, b);
}
public bool Equals(Tuple<int, int> x, Tuple<int, int> y)
{
return (x.Item1 == y.Item1 && x.Item2 == y.Item2);
}
public int GetHashCode(Tuple<int, int> obj)
{
return obj.Item1.GetHashCode() ^ obj.Item2.GetHashCode();
}
}
Driver代码:
public static void Main() {
var map = new Dictionary<Key, int>();
map.Add(new Key(1, 2), 3);
map.Add(new Key(1, 2), 4); // <==== Should not add!
}
问题
如何解决这个问题?
Dictionary<Tuple<int, int>, int>
正常工作的最简单实施是什么?
您可以尝试以下解决方案。
public class Key : IEquatable<Key>
{
private Tuple<int, int> _tuple;
public Key(int a, int b)
{
_tuple = new Tuple<int, int>(a, b);
}
public bool Equals(Key other)
{
return (this.GetHashCode() == other.GetHashCode());
}
public override int GetHashCode()
{
return _tuple.GetHashCode();
}
}
另一种方法是使用 ValueTuple
作为键,默认情况下会根据它的值进行比较。
public static void Main()
{
var map = new Dictionary<(int, int), int>();
map.Add((1, 2), 3);
map.Add((1, 2), 4); // Throw an exception
}
如果您想拥有自己的 class 来表示一个键,您可以简单地创建 Tuple<int, int>
的子 class 并获得所需的行为 "for free"
public class Key : Tuple<int, int>
{
public Key(int item1, int item2) : base(item1, item2)
{
}
}
如果你想使用自己的 class Key
:
public class Key
{
public Key(int item1, int item2)
{
Tuple = new Tuple<int, int>(item1, item2);
}
public override bool Equals(object obj)
{
if (obj == null)
{
return false;
}
if (obj is Key other)
{
return Tuple.Equals(other.Tuple);
}
return false;
}
public override int GetHashCode()
{
return Tuple.GetHashCode();
}
public Tuple<int, int> Tuple { get; private set; }
}
public void Do()
{
var map = new Dictionary<Key, int>();
map.Add(new Key(1, 2), 3);
map.Add(new Key(1, 2), 4); // will throw System.ArgumentException
}
另一种方法就是使用 Tuple
class:
public void Do()
{
var map = new Dictionary<Tuple<int, int>, int>();
map.Add(new Tuple<int, int>(1, 2), 3);
map.Add(new Tuple<int, int>(1, 2), 4); // will throw System.ArgumentException
}
问题是,在向字典添加项目时,会调用默认的 Equals
和 GetHashCode
方法,它们使用引用比较来确定是否相等。
如果要覆盖此行为,则需要使用 override
关键字,并覆盖方法:
class Key : IEquatable<Key>
{
private readonly Tuple<int, int> tuple;
public Key(int a, int b)
{
tuple = new Tuple<int, int>(a, b);
}
public bool Equals(Key other)
{
return other != null &&
tuple.Item1 == other.tuple.Item1 &&
tuple.Item2 == other.tuple.Item2;
}
public override bool Equals(object obj)
{
return Equals(obj as Key);
}
public override int GetHashCode()
{
return tuple.Item1.GetHashCode() ^ tuple.Item2.GetHashCode();
}
}
只是在此处发布@Dmitri 答案的简化版本作为参考。
最简单的方法(无需安装额外的包)并且不实现任何接口,就是重写 Equals
和 GetHashCode
方法,如下所示:
public class Key
{
private readonly Tuple<int, int> _tuple;
public Key(int item1, int item2)
{
_tuple = new Tuple<int, int>(item1, item2);
}
public override bool Equals(object obj)
{
var other = obj as Key;
return _tuple.Item1 == other?._tuple.Item1 && _tuple.Item2 == other?._tuple.Item2;
}
public override int GetHashCode()
{
return _tuple.Item1.GetHashCode() ^ _tuple.Item2.GetHashCode();
}
}
描述
正在尝试创建一个以元组为键的字典。
但是 GetHashCode 和 Equals 函数没有被调用,因此重复的键将被添加到字典中。
这是我想用作字典键的 Key
class:
class Key : IEqualityComparer<Tuple<int, int>>
{
private Tuple<int, int> _tuple;
public Key(int a, int b)
{
_tuple = new Tuple<int, int>(a, b);
}
public bool Equals(Tuple<int, int> x, Tuple<int, int> y)
{
return (x.Item1 == y.Item1 && x.Item2 == y.Item2);
}
public int GetHashCode(Tuple<int, int> obj)
{
return obj.Item1.GetHashCode() ^ obj.Item2.GetHashCode();
}
}
Driver代码:
public static void Main() {
var map = new Dictionary<Key, int>();
map.Add(new Key(1, 2), 3);
map.Add(new Key(1, 2), 4); // <==== Should not add!
}
问题
如何解决这个问题?
Dictionary<Tuple<int, int>, int>
正常工作的最简单实施是什么?
您可以尝试以下解决方案。
public class Key : IEquatable<Key>
{
private Tuple<int, int> _tuple;
public Key(int a, int b)
{
_tuple = new Tuple<int, int>(a, b);
}
public bool Equals(Key other)
{
return (this.GetHashCode() == other.GetHashCode());
}
public override int GetHashCode()
{
return _tuple.GetHashCode();
}
}
另一种方法是使用 ValueTuple
作为键,默认情况下会根据它的值进行比较。
public static void Main()
{
var map = new Dictionary<(int, int), int>();
map.Add((1, 2), 3);
map.Add((1, 2), 4); // Throw an exception
}
如果您想拥有自己的 class 来表示一个键,您可以简单地创建 Tuple<int, int>
的子 class 并获得所需的行为 "for free"
public class Key : Tuple<int, int>
{
public Key(int item1, int item2) : base(item1, item2)
{
}
}
如果你想使用自己的 class Key
:
public class Key
{
public Key(int item1, int item2)
{
Tuple = new Tuple<int, int>(item1, item2);
}
public override bool Equals(object obj)
{
if (obj == null)
{
return false;
}
if (obj is Key other)
{
return Tuple.Equals(other.Tuple);
}
return false;
}
public override int GetHashCode()
{
return Tuple.GetHashCode();
}
public Tuple<int, int> Tuple { get; private set; }
}
public void Do()
{
var map = new Dictionary<Key, int>();
map.Add(new Key(1, 2), 3);
map.Add(new Key(1, 2), 4); // will throw System.ArgumentException
}
另一种方法就是使用 Tuple
class:
public void Do()
{
var map = new Dictionary<Tuple<int, int>, int>();
map.Add(new Tuple<int, int>(1, 2), 3);
map.Add(new Tuple<int, int>(1, 2), 4); // will throw System.ArgumentException
}
问题是,在向字典添加项目时,会调用默认的 Equals
和 GetHashCode
方法,它们使用引用比较来确定是否相等。
如果要覆盖此行为,则需要使用 override
关键字,并覆盖方法:
class Key : IEquatable<Key>
{
private readonly Tuple<int, int> tuple;
public Key(int a, int b)
{
tuple = new Tuple<int, int>(a, b);
}
public bool Equals(Key other)
{
return other != null &&
tuple.Item1 == other.tuple.Item1 &&
tuple.Item2 == other.tuple.Item2;
}
public override bool Equals(object obj)
{
return Equals(obj as Key);
}
public override int GetHashCode()
{
return tuple.Item1.GetHashCode() ^ tuple.Item2.GetHashCode();
}
}
只是在此处发布@Dmitri 答案的简化版本作为参考。
最简单的方法(无需安装额外的包)并且不实现任何接口,就是重写 Equals
和 GetHashCode
方法,如下所示:
public class Key
{
private readonly Tuple<int, int> _tuple;
public Key(int item1, int item2)
{
_tuple = new Tuple<int, int>(item1, item2);
}
public override bool Equals(object obj)
{
var other = obj as Key;
return _tuple.Item1 == other?._tuple.Item1 && _tuple.Item2 == other?._tuple.Item2;
}
public override int GetHashCode()
{
return _tuple.Item1.GetHashCode() ^ _tuple.Item2.GetHashCode();
}
}