Is this code for string interning described?

I am creating a key for a dictionary, which is a two-line structure. When I test this method in a console application, it works, but I'm not sure that the only reason it works is because the lines are interned and therefore have the same references.

Foo foo1 = new Foo(); Foo foo2 = new Foo(); foo1.Key1 = "abc"; foo2.Key1 = "abc"; foo1.Key2 = "def"; foo2.Key2 = "def"; Dictionary<Foo, string> bar = new Dictionary<Foo, string>(); bar.Add(foo1, "found"); if(bar.ContainsKey(foo2)) System.Console.WriteLine("This works."); else System.Console.WriteLine("Does not work"); 

The structure is simple:

 public struct Foo { public string Key1; public string Key2; } 

Are there any cases that could lead to a failure or can I rely on this as a unique key?

+4
source share
5 answers

According to Microsoft documentation, you should always override GetHashCode () if you intend to use your own data structures as keys in HashTables, otherwise you may not be safe.

"The objects used as the key in the Hashtable object must also override the GetHashCode method, because these objects must generate their own hash code."

http://msdn.microsoft.com/en-us/library/system.object.gethashcode.aspx

+4
source

According to my rubim answer comment above, here is a link to how I think the structure itself should be implemented. Pay attention to immutable fields that can only be initialized through the constructor.

 public struct Foo { private readonly string key1; private readonly string key2; public string Key1 { get { return this.key1; } } public string Key2 { get { return this.key2; } } public Foo(string key1, string key2) { this.key1 = key1; this.key2 = key2; } public static bool operator ==(Foo foo1, Foo foo2) { return foo1.Equals(foo2); } public static bool operator !=(Foo foo1, Foo foo2) { return !(foo1 == foo2); } public override bool Equals(object obj) { if (!(obj is Foo)) { return false; } Foo foo = (Foo)obj; bool key1Equal = ((this.key1 == null) && (foo.Key1 == null)) || ((this.key1 != null) && this.key1.Equals(foo.Key1)); bool key2Equal = ((this.key2 == null) && (foo.Key2 == null)) || ((this.key2 != null) && this.key2.Equals(foo.Key2)); return key1Equal && key2Equal; } public override int GetHashCode() { unchecked { int hash = 17; hash = (23 * hash) + (this.key1 == null ? 0 : this.key1.GetHashCode()); return (31 * hash) + (this.key2 == null ? 0 : this.key2.GetHashCode()); } } public override string ToString() { return (this.key1 == null ? string.Empty : this.key1.ToString() + ",") + (this.key2 == null ? string.Empty : this.key2.ToString()); } } 

Then the method of their use will be as follows:

  Foo foo1 = new Foo("abc", "def"); Foo foo2 = new Foo("abc", "def"); Dictionary<Foo, string> bar = new Dictionary<Foo, string>(); bar.Add(foo1, "found"); if (bar.ContainsKey(foo2)) { Console.WriteLine("This works."); } else { Console.WriteLine("Does not work"); } 
+4
source

http://msdn.microsoft.com/en-us/library/dd183755.aspx

Any structure that you define already has a default implementation of the equality value that it inherits from System .. ::. ValueType overrides the Object .. :: method. Equals (Object). This implementation uses reflection to examine all public and non-public fields and properties in a type. Although this implementation gives the correct results, it is relatively slow compared to the custom implementation that you write specifically for this type.

So, if the properties are equal, they are equal. You might want to override Equals.

+3
source

According to MSDN,

The default implementation of the GetHashCode method does not guarantee unique return values ​​for different objects. [...]

The GetHashCode method can be overridden by a derived type. Value types must override this method to provide a hash function suitable for that type and provide useful distribution in the hash table.

(my attention.)

Thus, your code is not guaranteed to work, and if it works, it will not guarantee continued operation in the next version of the .NET framework.

For what it's worth, the current Monos ValueType.GetHashCode implementation calls the GetHashCode member types, so the code will work correctly regardless of string interning.

[sorry previous answer, it was just wrong.]

+1
source

Your code should work without any problems in .NET, and I would dare to say that it will remain the same even if the version of .NET changes. The fact is that the dictionary uses two things to access the value using a specific key: Equals () and GetHashCode () as follows

  • get all the keys corresponding to the required key value with the value GetHashCode () (the value of GetHashCode should be equal for equal objects and should be different for different objects)
  • filter the list created in the previous step using Equals ().

The standard Equals () values ​​for value types use reflection to access fields and compare them (as mentioned earlier). Bearing this in mind, the default value of GetHashCode () should correspond to how the default implementation of Equals () works, if a == b, a.GetHashCode () == b.GetHashCode () - in any case, there would be no meaning in providing a default if it does not even meet the minimum minimum. What MSDN says GetHashCode () does not provide unique values, and that is understandable. Keep in mind that such a simple implementation:

 public int GetHashCode() { return 0; } 

still valid (dictionaries, hashtables, etc. will work), even if the values ​​are not unique. Performance, of course, is another matter, the use of such an implementation will lead to a full scan of each collection and testing of each element for equality.

0
source

Source: https://habr.com/ru/post/1299552/


All Articles