活用接口——反例:MultiKeyDictionary

    字典Dictionary<TKey, TValue>相信大家都用过,但是如果字典的键是一个TKey数组(或者IList<TKey>),怎么办?
    这就是今天要讨论的案例:MultiKeyDictionary<TKey, TValue>
    先来看看反例:
public   class  MultiKeyDictionary < TKey, TValue >  : IDictionary < TKey[], TValue >
{
    
private   static   readonly  List < Type >  COLUMNTYPELIST;
    
private   const   string  HASHCODECOLNAME  =   " HASHCODE " ;
    
private   const   string  KEYCOLMNFORMAT  =   " KEY{0} " ;
    
private   const   int  MAXPRIMARYKEYCOUNT  =   0x1f ;
    
private   int  _keyCount;
    
private  DataTable _dataTable;
    
private  TKey _defaultKeyValue;
    
private  Dictionary < string , TValue >  _hashTable;

    
private  MultiKeyDictionary()
    {
        
this ._keyCount  =   0 ;
        
this ._dataTable  =   null ;
        
this ._hashTable  =   null ;
        
this ._defaultKeyValue  =   default (TKey);
        
if  ( ! MultiKeyDictionary < TKey, TValue > .COLUMNTYPELIST.Contains( typeof (TKey)))
        {
            
throw   new  ArgumentException( " " typeof (TKey).Name));
        }
    }

    
public  MultiKeyDictionary( int  keyCount, TKey defaultKeyValue) :  this ()
    {
        
if  (keyCount  <=   0 )
        {
            
throw   new  ArgumentOutOfRangeException( " keyCount " );
        }
        
if  (defaultKeyValue  ==   null )
        {
            
throw   new  ArgumentNullException( " defaultKeyValue " );
        }
        
this ._keyCount  =  keyCount;
        
this ._dataTable  =   this .CreateDataTable();
        
this ._hashTable  =   new  Dictionary < string , TKey > ();
        
this ._defaultKeyValue  =  defaultKeyValue;
    }

    
public   void  Add(TKey[] keyList, TValue value)
    {
        
if  ((((keyList  ==   null ||  (keyList.Length  >   this ._keyCount))  ?   1  :  0 !=   0 )
        {
            
throw   new  ArgumentOutOfRangeException( " keyList " );
        }
        
if  ( this .ContainsKey(aoKeyList))
        {
            
throw   new  ArgumentException( " " );
        }
        DataRow dr 
=   this ._dataTable.NewRow();
        
for  ( int  i  =   0 ; i  <   this ._keyCount; i ++ )
        {
            TKey key 
=   default (TKey);
            TKey value 
=  key;
            
if  (i  <  keyList.Length)
            {
                value 
=  keyList[i];
            }
            
else
            {
                value 
=   this ._defaultKeyValue;
            }
            dr[i] 
=  value;
        }
        
string  hashCode  =  Guid.NewGuid().ToString();
        dr[
" HASHCODE " =  hashCode;
        
this ._dataTable.Rows.Add(dr);
        
this ._hashTable.Add(hashCode, value);
    }
    
// 此处省去600行代码
}
    第一次看到这个代码的时候,我也傻眼了,没想到这个给竟然能写成这样。里面用DataTable来放多个键值,并且,规定前面的每一个列是主键,到最后一 列,放一个Guid的ToString,然后再通过Dictionary<string, TValue>来查找对应的值。
    说说这么实现的不足之处吧(好像没什么必要。。。),第一用DataTable的话,效率将被极大的降低,第二,必须要预先告知数组的最大长度,如果,定多了影响性能,定少了,没法加数据。
    下面说说正确的实现方式吧。
    第一、MultiKeyDictionary<TKey, TValue>是Dictionary<TKey, TValue>的一种特例,相当于把Dictionary<TKey, TValue>中的TKey替换成TKey[],所以应该让MultiKeyDictionary<TKey, TValue>继承Dictionary<TKey[], TValue>。
    第二、剩下来的问题是,如何修改TKey[]的判等,如果Dictionary<TKey[], TValue>没有留下这个扩展,当然就没法直接利用继承了。看一下Dictionary<TKey, TValue>的构造函数,不难发现有一个public Dictionary(IEqualityComparer<TKey> comparer)的构造,IEqualityComparer<TKey>是个什么样的接口?它包含bool Equals(T x, T y);int GetHashCode(T obj);两个方法,也就是通过它,可以修改对象判等(包括GetHashCode)。
    第三、到这里,相信已经比较明确了,写一个类实现IEqualityComparer<TKey[]>接口,在构造 MultiKeyDictionary<TKey, TValue>时调用Dictionary<TKey[], TValue>的带参构造,将一个符合IEqualityComparer<TKey[]>接口的对象传入,就完成了一个这个 MultiKeyDictionary,并且,拥有所有Dictionary<TKey[], TValue>的功能,如果需要,还可以添加自己的成员。

    最后,有一个注意点,GetHashCode和Equals的相互关系:
    1、GetHashCode相等,对象未必Equals
    2、对象Equals,GetHashCode必定相等
    3、GetHashCode不相等,对象必定不Equals
    更具体的描述在 MSDN


你可能感兴趣的:(key)