活用接口——反例: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


原文地址:https://www.cnblogs.com/vwxyzh/p/782497.html