不调用以元组为键的字典GetHashCode

问题描述 投票:1回答:5

说明


试图创建一个以元组为键的字典。

但是没有调用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>正常工作最简单的实现是什么?

c# dictionary .net-core hashtable
5个回答
2
投票

如果您想使用自己的课程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
    }

3
投票

另一种方法是使用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)
    {
    }
}

2
投票

问题是,当将一项添加到字典中时,将调用默认的EqualsGetHashCode方法,它们使用引用比较来确定相等性。

如果要覆盖此行为,则需要使用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();
    }
}

1
投票

您可以尝试以下解决方案。

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();
    }
}

0
投票

这里只是发布@Dmitri答案的简化版本作为参考。

最简单的方法(不安装额外的软件包)并且不实现任何接口,只是如下覆盖EqualsGetHashCode方法:

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();
    }

}
© www.soinside.com 2019 - 2024. All rights reserved.