Last active
August 29, 2015 14:02
-
-
Save netcore-jroger/7be2c182de16e758a292 to your computer and use it in GitHub Desktop.
参与老赵问答第二期:关于 struct 做为 Dictionary<TKey, TValue> 类型的键来使用的问题
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
/* | |
* 看到这个道题后想到了两点: | |
* 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