这就是今天要讨论的案例: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>来查找对应的值。{
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的话,效率将被极大的降低,第二,必须要预先告知数组的最大长度,如果,定多了影响性能,定少了,没法加数据。
下面说说正确的实现方式吧。
第一、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