C#结构字典键无法与数组成员一起使用

本文关键字:组成员 数组 一起 结构 字典 | 更新日期: 2023-09-27 18:24:10

我正试图创建一个结构,该结构在构造过程中使用可变数量的参数,目的是将此对象用作字典键(在我的.Net版本中不支持Tuple类型):

struct TupleKey
{
    int[] args;
    public TupleKey(params int[] args) { this.args = args; }
}

但是,当我使用这个struct作为字典的关键字时,ContainsKey方法返回false。

var d = new Dictionary<TupleKey, int>();
d.Add(new TupleKey(1, 1), 1);
Console.WriteLine(d.ContainsKey(new TupleKey(1,1)));  // false!?

怎么回事?在结构中使用可变对象(如数组)是否存在问题?

C#结构字典键无法与数组成员一起使用

自定义struct的默认相等和哈希代码实现将基于其成员(在您的情况下是数组)的默认相等方法和哈希代码方法。数组使用基于引用的标识,而不是基于值的标识。如果希望具有相同值的不同数组相等,则需要重写EqualsGetHashCode,使其取决于数组的值。

通常,.NET中的类型会尝试定义Equals,这样,如果xy是类的私有字段,则除非该类写入这些字段,否则x.Equals(y)的值不会更改。如果xy是可变的引用类型,这意味着x.Equals(y)为真的唯一方法是xy标识相同的对象。如果当xy识别出状态恰好相同的不同对象时,x.Equals(y)返回true,并且引用其中一个对象的其他代码将修改其状态,则外部代码可以在不访问xy的情况下更改x.Equals(y)的值。

我认为可以合理地说,.NET缺少"不可变数组"类型;如果存在这样的类型,那么它们是不可变的,可以保证如果两个实例包含相同的项,它们将永远这样做。然而,由于.NET没有任何这样的类型,因此有必要接受这种限制。

最好的做法可能是让结构的构造函数构造一个数组,该数组比传入的数组长一个元素,并包含原始数组内容的副本以及其中值的哈希。这样,这个数组就永远不会暴露在外部代码中,因此可以保证永远不会被修改。equals方法可以检查与您的类型进行比较的对象是否是同一类型的另一个结构,如果是,则检查数组的长度是否相同,存储的哈希值是否匹配,如果匹配,则检查所有其他项是否匹配。然后,您的GetHashCode值应该返回存储在额外数组插槽中的哈希值。如果您这样做,您还应该实现IEquatable<yourOwnType>

请注意,虽然可以使用字段而不是数组槽来保存哈希值,但使用数组槽会更高效,并可以避免不适当的多线程代码可能会创建哈希值与数组内容不一致的结构实例。