说明
试图创建一个以元组为键的字典。
但是没有调用GetHashCode和Equals函数,因此重复的键将添加到字典中。
这是我想用作字典关键字的Key
类:
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();
}
}
驱动程序代码:
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>
正常工作最简单的实现是什么?
如果您想使用自己的课程Key
:
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)
{
if (obj == null)
{
return false;
}
if (obj is Key other)
{
return _tuple.Item1 == other.Item1 && _tuple.Item2 == other.Item2;
}
return false;
}
public override int GetHashCode()
{
return _tuple.Item1.GetHashCode() ^ _tuple.Item2.GetHashCode();
}
public int Item1 => _tuple.Item1;
public int Item2 => _tuple.Item2;
}
...
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
}
另一种方法是实现自己的IEqualityComparer
(没有您的Key
类:]
public class TupleIntIntEqualityComparer : IEqualityComparer<Tuple<int, int>>
{
bool IEqualityComparer<Tuple<int, int>>.Equals(Tuple<int, int> x, Tuple<int, int> y)
{
if (x == null || y == null)
{
return false;
}
return x.Item1 == y.Item1 && x.Item2 == y.Item2;
}
int IEqualityComparer<Tuple<int, int>>.GetHashCode(Tuple<int, int> obj)
{
return obj.Item1.GetHashCode() ^ obj.Item2.GetHashCode();
}
}
...
public void Do()
{
var map = new Dictionary<Tuple<int, int>, int>(new TupleIntIntEqualityComparer());
map.Add(new Tuple<int, int>(1, 2), 3);
map.Add(new Tuple<int, int>(1, 2), 4); // will throw System.ArgumentException
}
另一种方法是使用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
}
如果您希望拥有自己的类来表示键,则可以简单地创建Tuple<int, int>
的子类并“免费”获得所需的行为
public class Key : Tuple<int, int>
{
public Key(int item1, int item2) : base(item1, item2)
{
}
}
问题是,当将一项添加到字典中时,将调用默认的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();
}
}
您可以尝试以下解决方案。
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();
}
}
这里只是发布@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();
}
}