是否有类似于元组的 .NET 内置结构(或推荐的构建方法)在哈希代码方面是顺序不变的?



是否有类似于元组的.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);

是否有类似于元组的 .NET 内置结构(或推荐的构建方法(,它在相等性和哈希码方面是顺序不变的?

不。如果有的话,它会被打破。

经常过度简化"密钥的哈希代码不得更改",这是错误的。实际上,"密钥在用作密钥时不得更改",不更改的哈希代码应该反映这一点。如果某些内容发生了变化,从而改变了它与其他项目的比较方式,那么哈希代码必须更改以反映这一点。当它用作密钥时,这不能发生,但这意味着密钥不得更改。否则,如果您创建了一个对象,对其进行了更改,然后将其用作键,则它将无法正常工作。

好的,所以改变你的问题

是否有类似于元组的 .NET 内置结构(或推荐的构建方法(在相等性和哈希码方面是顺序不变的 [当组件项不变时]?

是的,元组就是一个例子。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.Equals(…)被内联,记住EqualityComparer<T>.Default不再有意义(。

最新更新