当前位置:  开发笔记 > 编程语言 > 正文

Object.GetHashCode()的默认实现

如何解决《Object.GetHashCode()的默认实现》经验,为你挑选了3个好方法。

默认实现如何GetHashCode()工作?它是否有效且足够好地处理结构,类,数组等?

我试图决定在什么情况下我应该自己打包,在什么情况下我可以安全地依赖默认实现来做好.如果可能的话,我不想重新发明轮子.



1> David Brown..:
namespace System {
    public class Object {
        [MethodImpl(MethodImplOptions.InternalCall)]
        internal static extern int InternalGetHashCode(object obj);

        public virtual int GetHashCode() {
            return InternalGetHashCode(this);
        }
    }
}

InternalGetHashCode映射到CLR中的ObjectNative :: GetHashCode函数,如下所示:

FCIMPL1(INT32, ObjectNative::GetHashCode, Object* obj) {  
    CONTRACTL  
    {  
        THROWS;  
        DISABLED(GC_NOTRIGGER);  
        INJECT_FAULT(FCThrow(kOutOfMemoryException););  
        MODE_COOPERATIVE;  
        SO_TOLERANT;  
    }  
    CONTRACTL_END;  

    VALIDATEOBJECTREF(obj);  

    DWORD idx = 0;  

    if (obj == 0)  
        return 0;  

    OBJECTREF objRef(obj);  

    HELPER_METHOD_FRAME_BEGIN_RET_1(objRef);        // Set up a frame  

    idx = GetHashCodeEx(OBJECTREFToObject(objRef));  

    HELPER_METHOD_FRAME_END();  

    return idx;  
}  
FCIMPLEND

GetHashCodeEx的完整实现相当大,因此更容易链接到C++源代码.


为什么文档声称实现对散列不是特别有用?如果一个对象等于它自己而没有别的,任何哈希代码方法总会为给定的对象实例返回相同的值,并且通常会为不同的实例返回不同的值,问题是什么?
@ It'sNotALie.然后感谢[Archive.org](https://web.archive.org/web/20100826100454/http://www.koders.com/cpp/fid187B41425FAF5DEC8C7921012C309DC6534FE487.aspx)获取副本;-)
该文档引用必须来自非常早期的版本.在当前的MSDN文章中不再这样写,可能是因为它是错误的.
他们改变了措辞,是的,但它仍然说基本相同:"因此,不得将此方法的默认实现用作散列目的的唯一对象标识符."
@ ta.speot.is:如果您想要确定是否已将*特定实例*添加到字典中,则引用相等性是完美的.对于字符串,正如您所注意到的,一个*通常*更感兴趣的是是否已经添加了包含*相同字符序列*的字符串.这就是为什么`string`会覆盖`GetHashCode`.另一方面,假设您想要计算各种控件处理`Paint`事件的次数.您可以使用`Dictionary `(每个`int []`存储将只包含一个项目).
@ ta.speot.is:当你从正在观看的其中一个控件中获取一个Paint事件时,可以使用`MyControlCounts [Sender] [0] ++;`(或者使用`TryGetValue`的一些变体).即使控件恰好定义了某种形式的值相等,也不会是您感兴趣的内容.您需要使用引用相等性以及默认(基于引用的)哈希代码.

2> Marc Gravell..:

对于一个类,默认值基本上是引用相等,这通常很好.如果编写一个结构,更常见的是覆盖相等(尤其是避免装箱),但是你编写一个结构是非常罕见的!

当重写等式,你应该始终有一个匹配的Equals()GetHashCode()(即两个值,如果Equals()返回true,他们必须返回相同的哈希码,但反过来不是必需的) -这是常见的也提供==/ !=运营商,并经常到也实施IEquatable.

为了生成哈希码,通常使用因式和,因为这可以避免配对值上的冲突 - 例如,对于基本的2字段哈希:

unchecked // disable overflow, for the unlikely possibility that you
{         // are compiling with overflow-checking enabled
    int hash = 27;
    hash = (13 * hash) + field1.GetHashCode();
    hash = (13 * hash) + field2.GetHashCode();
    return hash;
}

这样做的好处是:

{1,2}的哈希值与{2,1}的哈希值不同

{1,1}的哈希值与{2,2}的哈希值不同

等 - 如果只使用未加权的总和或xor(^)等,这可能很常见.


@sinelaw是的,应该执行`unchecked`.幸运的是,`unchecked`是C#中的默认值,但最好将其显式化; 编辑

3> Guffa..:

ObjectGetHashCode方法的文档说"不得将此方法的默认实现用作散列目的的唯一对象标识符." ValueType的那个说"如果你调用派生类型的GetHashCode方法,返回值可能不适合用作哈希表中的键." .

基本数据类型,例如byte,short,int,long,char以及string实现良好的GetHashCode方法.其他一些类和结构(Point例如,实现GetHashCode可能适合或不适合您的特定需求的方法).你只需要试一试,看看它是否足够好.

每个类或结构的文档可以告诉您它是否覆盖默认实现.如果它没有覆盖它,你应该使用自己的实现.对于您自己创建需要使用该GetHashCode方法的任何类或结构,您应该创建自己的实现,使用适当的成员来计算哈希代码.


@ user502144这有什么问题?它是一个完美的唯一标识符,易于计算,对平等没有误报......
我不同意你应该*例行地*添加你自己的实现.简单地说,绝大多数类(特别是)永远不会被测试为相等 - 或者它们在哪里,内置的引用相等是好的.在(已经很少见)编写结构的时候,它会更常见,更真实.
推荐阅读
夏晶阳--艺术
这个屌丝很懒,什么也没留下!
DevBox开发工具箱 | 专业的在线开发工具网站    京公网安备 11010802040832号  |  京ICP备19059560号-6
Copyright © 1998 - 2020 DevBox.CN. All Rights Reserved devBox.cn 开发工具箱 版权所有