是否有一个类似于Tuple的.NET内置结构(或建议的方法来构建一个),它是关于哈希码的顺序不变的?

时间:2017-08-08 12:03:10

标签: c# .net

是否存在类似于Tuple的.NET内置结构(或建议的方法来构建一个),它是关于等式和哈希码的顺序不变的?

下面的代码具有预期的ishashequal = false,我正在寻找的结构将返回true。

var dict = new Dictionary<Tuple<char, char>, int>();
var x = new Tuple<char, char>('a', 'b');           
var y = new Tuple<char, char>('b', 'a');
dict.Add(x, 1);
bool isequal = dict.ContainsKey(y);

1 个答案:

答案 0 :(得分:-1)

  

是否存在类似于Tuple的.NET内置结构(或建议的方法来构建一个),它是关于等式和哈希码的顺序不变的?

没有。如果有的话就会被打破。

通常会给出“密钥的哈希码不能改变”的过度简化,这是错误的。实际上,“用作密钥时密钥必须不会改变”,哈希代码不会改变应该反映出来。如果某些内容发生变化,会改变其与其他项目的比较方式,那么哈希码必须才能反映出来。当它被用作密钥时,不得发生这种情况,但这意味着密钥不得更改。否则,如果您创建了一个对象,并将其更改为然后将其用作键,则无效。

好的,所以改变你的问题

  

是否存在类似于Tuple的.NET内置结构(或建议的构建方法),关于相等和哈希码的顺序不变[当组件项不变时]?

是的, Tuple 就是一个例子。与 ValueTuple 和由相同部分组成的匿名对象一样。

  

下面的代码具有预期的ishashequal = false,我正在寻找的结构将返回true

这又是一个不同的东西。元组是给定数量元素的有限序列。作为一个序列意味着秩序是显着的。我们不希望元组(a, b)(b, a)哈希相同,因为(a, b)是与(b, a)不同的元组,所以不能认为它是平等的(理想情况下)但不是严格的要求)不会有相同的哈希码。

确实Tuple<int, string>根本不能在不同的订单中使用相同的元素。

Tuple表示元组,但您描述的是使用有限集。有限集{a, b}与有限集{b, a}相同,因为顺序不重要。

你需要做两件事之一。

创建一个结构来表示一组有限的两个元素

public sealed class FiniteSet2<T> : IEquatable<FiniteSet2<T>>
{
    public T First { get; }
    public T Second { get; }
    public FiniteSet2(T first, T second)
    {
        First = first;
        Second = second;
    }

    public bool Equals(FiniteSet2<T> other)
    {
        if ((object)other != null)
        {
            return false;
        }

        // Test for same order.
        if (EqualityComparer<T>.Default.Equals(First, other.First))
        {
            return EqualityComparer<T>.Default.Equals(Second, other.Second);
        }

        // Test for different order.
        return EqualityComparer<T>.Default.Equals(First, other.Second)
            && EqualityComparer<T>.Default.Equals(Second, other.First)
    }

    public override bool Equals(object obj) => Equals(obj as FiniteSet2<T>);

    // Deliberately matches elements in different order.
    public override int GetHashCode() => First.GetHashCode() ^ Second.GetHashCode();
}

或者,如果您确实需要使用元组,请定义适当的比较器:

public sealed class CompareAsSetEqualityComparer<T> : IEqualityComparer<Tuple<T, T>>
{
    public bool Equals(Tuple<T, T> x, Tuple<T, T> y)
    {
        if ((object)x == y)
        {
            return true;
        }

        if ((object)x == null | (object)y == null)
        {
            return false;
        }

        if (EqualityComparer<T>.Default.Equals(x.Item1, y.Item1))
        {
            return EqualityComparer<T>.Default.Equals(x.Item2, y.Item2);
        }

        return EqualityComparer<T>.Default.Equals(x.Item1, y.Item2)
            && EqualityComparer<T>.Default.Equals(x.Item2, y.Item1);
    }

    public int GetHashCode(Tuple<T, T> obj) =>
        obj == null ? 0 : obj.Item1.GetHashCode() ^ obj.Item2.GetHashCode();
}

当然,如果元素是引用类型,它们本身就可以变异,这仍然会改变其他不可变的集合或元组。

(旁白:最近抖动的改善意味着,当序列EqualityComparer<T>.Default被内联时,记忆EqualityComparer<T>.Default.Equals(…)不再有意义。)