[Play here] What should I pay special attention to when I use a custom value type as the dictionary key?

Source: Internet
Author: User

The day before yesterday, I followed Zhao's Public Account: Zhao renxi. I pushed a quick Q & A yesterday. I will share my Q & A experience with you. I hope this will be helpful to fellow cainiao.

In fact, this problem has been explained in his blog. I guess, haha. However, the problems mentioned by Daniel are all at a high level, which requires a little effort to understand the level. This process is the process of pulling us away from Daniel, in order to constantly strengthen their proximity to them.

I. general dictionary usage (proficient and skipped)

In my usual coding, the most frequently used code snippet [1]:

1 public class Example 2 {3 public static void Main () 4 {5 Dictionary <string, string> openWith = new Dictionary <string, string> (); 6 7 openWith. add ("txt", "notepad.exe"); 8 openWith. add ("bmp", "paint.exe"); 9 openWith. add ("rtf", "wordpad.exe"); 10 11 if (! OpenWith. containsKey ("ht") 12 {13 openWith. add ("ht", "hypertrm.exe"); 14} 15 16 foreach (KeyValuePair <string, string> kvp in openWith) 17 {18 Console. writeLine ("Key = {0}, Value = {1}", 19 kvp. key, kvp. value); 20} 21 22 Dictionary <string, string>. valueCollection valueColl = openWith. values; 23 foreach (string s in valueColl) 24 {25 Console. writeLine ("Value = {0}", s); 26} 27 28 Dictionary <string, string>. keyCollection keyColl = openWith. keys; 29 foreach (string s in keyColl) 30 {31 Console. writeLine ("Key = {0}", s); 32} 33 Console. readKey (); 34}Common usage of Dictionary

Where the Dictionary <key, value> key is usually of the int or string type. Now we need to customize the value type and use it as the Dictionary key. What should we do?

Ii. Customize the value type and consider the problems encountered when using the dictionary key

The reference type is defined throughout the day, and the value type is actually rarely written. In fact, it is less familiar with the advantages of the value type! Code segment [2]:

 1   private struct MyKey 2   { 3       private readonly int _a; 4       private readonly int _b; 5       public MyKey(int a, int b) 6       { 7            _a = a; 8            _b = b; 9        }10    }

This is OK. You can add some attributes and methods as needed. For some simple requirements, it is more efficient to define a struct.

The value type in the code snippet [2] can be used as the dictionary key. But is that enough? Everything is not perfect. Have you considered the [packing] that comes with the value type? For example, the code segment [3]:

1  public static void Main()2   {3       Dictionary<MyKey, string> testDic = new Dictionary<MyKey, string>();4       MyKey key12 = new MyKey(1, 2);5       testDic.Add(key12, "1&2");6       Console.ReadKey();7   }

When we analyze the problem, we need to copy the guy (. net Reflector) to understand the principle and find out how it is implemented in the. NET Framework )! This is an important tool for our progress.

Partial implementation of the Insert method called by the Add method in Dictionary, such as the code segment [4]:

 1     int num = this.comparer.GetHashCode(key) & 0x7fffffff; 2     int index = num % this.buckets.Length; 3     int num3 = 0; 4     for (int i = this.buckets[index]; i >= 0; i = this.entries[i].next) 5     { 6          if ((this.entries[i].hashCode == num) && this.comparer.Equals(this.entries[i].key, key)) 7          { 8              if (add) 9             {10                  ThrowHelper.ThrowArgumentException(ExceptionResource.Argument_AddingDuplicate);11             }12             this.entries[i].value = value;13             this.version++;14             return;15          }16          num3++;17      }

Note: The GetHashCode and Equals methods of the object are used. We know that all types are ultimately inherited from System. object. If the GetHashCode and Equals methods are not overwritten in the custom type and all classes of Its Inheritance level (the inheritance level of the custom value type is MyKey => System. valueType => System. object), then the corresponding method of the base class Object will be called, which will inevitably lead to the [packing] operation.

3. If the problem is found, solve the problem!

In Dictionary, the code segment [4] shows the two methods called through this. comparer. What is this. comparer? Continue mining. It is an IEqualityComparer <T> type object maintained in the Dictionary class. Code segment [5]:

 1 public Dictionary(int capacity, IEqualityComparer<TKey> comparer) 2 { 3     if (capacity < 0) 4     { 5         ThrowHelper.ThrowArgumentOutOfRangeException(ExceptionArgument.capacity); 6     } 7     if (capacity > 0) 8     { 9         this.Initialize(capacity);10     }11     this.comparer = comparer ?? EqualityComparer<TKey>.Default;12 }

If the comparator is not provided in the parameter when a Dictionary is created, the defaultEqualityComparer<T>.DefaultThe object assigns a value to this. comparer. Its constructor is the code segment [6]:

1 [SecuritySafeCritical] 2 private static EqualityComparer <T> CreateComparer () 3 {4 RuntimeType c = (RuntimeType) typeof (T); 5 if (c = typeof (byte )) 6 {7 return (EqualityComparer <T>) new ByteEqualityComparer (); 8} 9 if (typeof (IEquatable <T> ). isAssignableFrom (c) 10 {11 return (EqualityComparer <T>) RuntimeTypeHandle. createInstanceForAnotherGenericParameter (RuntimeType) typeof (GenericEqualityComparer <int>), c); 12} 13 if (c. isGenericType & (c. getGenericTypeDefinition () = typeof (Nullable <>) 14 {15 RuntimeType type2 = (RuntimeType) c. getGenericArguments () [0]; 16 if (typeof (IEquatable <> ). makeGenericType (new Type [] {type2 }). isAssignableFrom (type2) 17 {18 return (EqualityComparer <T>) RuntimeTypeHandle. createInstanceForAnotherGenericParameter (RuntimeType) typeof (NullableEqualityComparer <int>), type2); 19} 20} 21 if (c. isEnum & (Enum. getUnderlyingType (c) = typeof (int) 22 {23 return (EqualityComparer <T>) RuntimeTypeHandle. createInstanceForAnotherGenericParameter (RuntimeType) typeof (EnumEqualityComparer <int>), c); 24} 25 return new ObjectEqualityComparer <T> (); 26}CreateComparer Method

It can be seen that different comparator will be used according to different situations. The most appropriate nature is implementation.IEquatable<T>The branch of the interface is:

1     if (typeof(IEquatable<T>).IsAssignableFrom(c))2     {3   return (EqualityComparer<T>) RuntimeTypeHandle.CreateInstanceForAnotherGenericParameter((RuntimeType) typeof(GenericEqualityComparer<int>), c);4     }

To meet this if condition, our custom value type MyKey should implement the IEquatable <MyKey> interface (implement the Equals method ), in addition, the GetHashCode method can be rewritten (the Object's GetHashCode method will also cause packing) to completely avoid packing.

Therefore, my final implementation is the code segment [7]:

 1     public struct MyKey : IEquatable<MyKey> 2     { 3         private readonly int _a; 4         private readonly int _b; 5  6         public MyKey(int a, int b) 7         { 8             _a = a; 9             _b = b;10         }11         public override int GetHashCode()12         {13             return (this._a ^ this._b);14         }15 16         public bool Equals(MyKey that)17         {18             return (this._a == that._a) && (this._b == that._b);19         }20     }

In this case, we analyzed a simple Q & A, which is quite enjoyable!

 

Finally, if there is something wrong with this article, you are welcome to correct me! Thank you!

If you think this article is helpful to you, you should give it a compliment and use it as an encouragement.

 

Related Article

Contact Us

The content source of this page is from Internet, which doesn't represent Alibaba Cloud's opinion; products and services mentioned on that page don't have any relationship with Alibaba Cloud. If the content of the page makes you feel confusing, please write us an email, we will handle the problem within 5 days after receiving your email.

If you find any instances of plagiarism from the community, please send an email to: info-contact@alibabacloud.com and provide relevant evidence. A staff member will contact you within 5 working days.

A Free Trial That Lets You Build Big!

Start building with 50+ products and up to 12 months usage for Elastic Compute Service

  • Sales Support

    1 on 1 presale consultation

  • After-Sales Support

    24/7 Technical Support 6 Free Tickets per Quarter Faster Response

  • Alibaba Cloud offers highly flexible support services tailored to meet your exact needs.