简体   繁体   中英

C# Struct dictionary key not working with array member

I'm trying to create a struct that takes a variable number of parameters during construction, with the intent of using this object as a dictionary key (the Tuple type is not supported in my version of .Net):

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

However, when I use this struct as a key for a dictionary, the ContainsKey method is returning false.

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

What's going on? Is there an issue with using a mutable object (like an array) in the struct?

The default equality and hash code implementations of a custom struct will be based on the default equality and hash code methods of their members, in your case an array. The array uses referenced based identity, not value based identity. If you want to different arrays with the same values to be equal you'll need to override Equals and GetHashCode to be dependent on the values of the array.

In general, the types in .NET try to define Equals such that if x and y are private fields of a class, the value of x.Equals(y) cannot change unless the class writes to those fields. If x and y are mutable reference types, that implies that the only way x.Equals(y) can be true is if x and y identify the same object. If x.Equals(y) were to return true when x and y identify different objects whose state happened to be the same, and code elsewhere with a reference to one of those objects were to modify its state, that outside code could change the value of x.Equals(y) without having access to either x or y .

I think it could be reasonably argued that .NET suffers from its lack of "immutable array" types; if such types existed then they, being immutable, could guarantee that if two instances ever contained the same items, they would do so forevermore. Since .NET doesn't have any such types, however, it is necessary to live with that limitation.

The best thing to do would probably be to have your struct's constructor construct an array which was one element longer than the passed-in array, and contained a copy of of original array's contents along with a hash of the values therein. This array would then never be exposed to outside code, and could thus be guaranteed never to be modified. Your equals method could check whether the thing to which your type was being compared was another struct of the same type and, if so, check whether the arrays are the same length, whether the stored hash values match, and if so whether all the other items match. Your GetHashCode value should then return the hash value which you stored in the extra array slot. If you do this, you should also implement IEquatable<yourOwnType> .

Note that while it would be possible to use a field rather than an array slot to hold the hash value, using an array slot would be somewhat more efficient and would avoid the possibility that improper multi-threading code might create a struct instance whose hash value didn't agree with the array's contents.

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM