Skip to content

Instantly share code, notes, and snippets.

@netcore-jroger
Last active August 29, 2015 14:02
Show Gist options
  • Star 0 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save netcore-jroger/7be2c182de16e758a292 to your computer and use it in GitHub Desktop.
Save netcore-jroger/7be2c182de16e758a292 to your computer and use it in GitHub Desktop.
参与老赵问答第二期:关于 struct 做为 Dictionary<TKey, TValue> 类型的键来使用的问题
/*
* 看到这个道题后想到了两点:
* 1.对 Dictionary<TKey, TValue> 的实现是否有所了解
* 2.对值类型是否有所了解,或者说对值类型和引用类型的区别是否有所了解
*
*
* 1.关于 Dictionary<TKey, TValue> 的实现。先看一下当实例化一个 Dictionary<TValue, TValue> 后添加键值对的方法 Add(TKey key, TValue value)
* Add 方法又直接调用了 Insert(TKey key, TValue value, bool add) 私有方法,我从里面摘抄了感觉重要的地方。
*/
int hashCode = comparer.GetHashCode(key) & 0x7FFFFFFF;
...
for (int i = buckets[targetBucket]; i >= 0; i = entries[i].next) {
if (entries[i].hashCode == hashCode && comparer.Equals(entries[i].key, key)) {
if (add) {
ThrowHelper.ThrowArgumentException(ExceptionResource.Argument_AddingDuplicate);
}
entries[i].value = value;
version++;
return;
}
}
...
entries[index].hashCode = hashCode;
entries[index].next = buckets[targetBucket];
entries[index].key = key;
entries[index].value = value;
buckets[targetBucket] = index;
/*
* 这三段的大概意思是取 TKey 实例的 hashCode 值,再对比是否已经存在这个键,如果存在则抛出异常。最后如果不存在这个键,则添加这个键值对。
* 需要注意了,这里会调用 TKey 实例的 GetHashCode() 方法和 Equals(object obj) 方法。但是奇怪的是我发现在 .NET 里面声明的 struct 如果没有 override 它的 GetHashCode() 方法的话,
* 那么同一类型的这些对象的 GetHashCode() 的值全是一样的。而我们常用的 class 却没有这个问题。所以,当需要使用 struct 做为字典的 key 的时候,必须 override GetHashCode() 方法
* 和 Equals(object obj) 方法,而看了 comparer 的实现,我觉得也得实现 IEquatable<T> 接口。在实践中也发现当使用 struct 作为字典的 key 的时候也调用了 IEquatable<T> 接口的 Equals(T other) 方法。
*/
/*
* 2. 单概念上说,我也知道值类型分配到线程堆栈上,引用类型分配到 CLR 的托管堆上面。在程序里面,操作值类型是直接改变其值或拷贝一份儿;而引用类型则是对其引用指针进行操作。
* 但是实际上我不知道为什么相同的 struct 类型实例,在默认情况下总是会得到相同的 HashCode。
* 希望老赵给指点指点。
* 不知道这次答的是不是你想考的知识点。
*/
/* 示例 */
public struct Size : IKey, IEquatable<Size>
{
public Size(int hashCode) : this()
{
this.HashCode = hashCode;
}
public override int GetHashCode()
{
var hashCode = base.GetHashCode();
return hashCode ^ HashCode;
}
public override bool Equals(object obj)
{
return base.Equals( obj );
}
public bool Equals(Size other) { return other.Equals( (object)this ); }
public int HashCode { get; private set; }
}
public interface IKey
{
int HashCode { get; }
}
/* 调用 */
var s1 = new Size(1);
var s2 = new Size(2);
Dictionary<Size, int> dic = new Dictionary<Size, int>();
dic.Add( s1, 1 );
dic.Add( s2, 2 );
Console.WriteLine( dic[s1] );
/*
不过这有一个非常不好的地方就是要通过 struct 的构造函数传递一个值提供给 GetHashCode() 方法使用。万一传错了,有可能不是同一个对象也添加不进去了。
因为 struct 不能声明无参构造函数。所以目前还没有想到别的办法。
*/
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment