我试图创建一个在构造过程中采用可变数量参数的结构,目的是将此对象用作字典键(我的版本中不支持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!?
发生了什么?在结构中使用可变对象(如数组)是否存在问题?
答案 0 :(得分:5)
自定义struct
的默认相等和哈希代码实现将基于其成员的默认相等和哈希代码方法,在您的情况下是数组。该数组使用基于引用的标识,而不是基于值的标识。如果您希望具有相同值的不同数组相等,则需要覆盖Equals
和GetHashCode
以依赖于数组的值。
答案 1 :(得分:0)
一般情况下,.NET中的类型尝试定义Equals
,如果x
和y
是类的私有字段,则x.Equals(y)
的值不能更改除非该类写入这些字段。如果x
和y
是可变引用类型,则意味着x.Equals(y)
和x
标识同一对象的唯一方法是y
。当x.Equals(y)
和x
识别出状态恰好相同的不同对象时,y
返回true,而其他一些对象的代码则修改其状态,外部代码可能会更改x.Equals(y)
的值而无法访问x
或y
。
我认为可以合理地认为.NET缺乏“不可变数组”类型;如果存在这样的类型,那么它们是不可变的,可以保证如果两个实例包含相同的项目,它们将永远这样做。但是,由于.NET没有任何此类类型,因此必须遵守该限制。
最好的做法可能是让你的struct的构造函数构造一个数组,该数组比传入的数组长一个元素,并包含原始数组内容的副本以及其中值的散列。这个数组永远不会暴露给外部代码,因此可以保证永远不会被修改。你的equals
方法可以检查你的类型被比较的东西是否是同一类型的另一个结构,如果是,检查数组是否长度相同,存储的哈希值是否匹配,如果是,是否所有其他项目都匹配。然后,您的GetHashCode
值应返回存储在额外数组槽中的哈希值。如果您这样做,您还应该实施IEquatable<yourOwnType>
。
请注意,尽管可以使用字段而不是数组槽来保存哈希值,但使用数组槽会更有效,并且可以避免不正确的多线程代码创建结构实例的可能性其哈希值与数组的内容不一致。