如何在不装箱的情况下获取枚举的哈希码?

9
如果一个枚举类型存储在聚合类型中,可能希望将其包含在类型的哈希码中(假设使用典型的“multiply by primes”哈希函数)。如果只调用SomeEnum.GetHashCode(),似乎JIT会对实例进行装箱,即使在发布版本中也是如此。

对此进行分析显示,我的应用程序的大约10%的时间花费在各种GetHashCode函数中装箱枚举。

几种值类型实现了IEquatable或类似接口,这允许调用GetHashCode作为静态方法;从而避免了装箱。但是System.Enum不提供GetHashCode的静态重载。是否有一些计算应该使用的代码但又避免装箱的方法?


2
何必费心呢?枚举本身就是它自己的哈希码。只需将其强制转换为整数并结束即可。 - Raymond Chen
@Raymond:我本来以为这样可能会导致分布不均,但重新思考后我会尝试看看它是否可行。 - Billy ONeal
@Raymond:对于这个特定的测试用例确实有效。不过我会暂时保持开放状态…… - Billy ONeal
@RaymondChen 不一定。例如考虑 enum E : long { V1 = 1, V2 = 0x100000001L } - phoog
@Raymond:我原以为结果应该在整个 int 范围内均匀分布。但现在我的性能数据表明并非如此。 - Billy ONeal
显示剩余8条评论
1个回答

6
你可以将枚举类型转换为其底层类型(通常是int,除非枚举定义另有规定),并使用该类型重写的GetHashCode()方法。
enum TestEnum
{
    Test1,
    Test2
}

TestEnum t = TestEnum.Test1;
((int)t).GetHashCode(); // no boxing
t.GetHashCode(); // boxing

以下是此代码的IL:

IL_0000:  nop
IL_0001:  ldc.i4.0
IL_0002:  stloc.0
IL_0003:  ldloc.0
IL_0004:  stloc.1
IL_0005:  ldloca.s   V_1
IL_0007:  call       instance int32 [mscorlib]System.Int32::GetHashCode()
IL_000c:  pop
IL_000d:  ldloc.0
IL_000e:  box        ConsoleApplication1.Program/TestEnum
IL_0013:  callvirt   instance int32 [mscorlib]System.Object::GetHashCode()
IL_0018:  pop
IL_0019:  ret

编辑:为了完整起见,我应该指出int.GetHashCode()的内容只是return this;。正如Raymond Chen在上面的评论中指出的那样,将枚举强制转换为int就足以获取哈希码。


这是否确实避免了装箱?(例如,int.GetHashCode() 是否也会导致装箱?) - Billy ONeal
是的,这样可以避免装箱。从枚举到整数的强制转换避免了装箱(参考Jon Skeet的第一条评论:http://bytes.com/topic/c-sharp/answers/276556-enum-vs-constants-performance),在整数上调用GetHashCode也不会导致装箱,因为对于此结构体已重写GetHashCode(我甚至反编译了它的实现,它没有执行任何可能导致装箱操作的操作)。调用Enum版本的GetHashCode确实会导致装箱(正如您已经注意到的那样),因为它调用一个返回“object”的内部方法,然后在其上调用“GetHashCode()”。 - jam40jeff
顺便一提,在我发布之前,我错误地检查了shortGetHashCode()实现。对于int,实现只需return this;,所以(正如Raymond Chen已经评论过的)你可以将Enum值转换为int并将其用作哈希码。 - jam40jeff
“Enum” 也重写了 “GetHashCode”,但仍然会发生装箱。你会在调用站点而不是目标站点看到装箱。 - Billy ONeal
@BillyONeal,你说得对,将枚举调用GetHashCode()的装箱操作发生在调用点。然而,这似乎是因为System.Enum被实现为从ValueType继承的抽象类,而System.Int32是一个结构体。我会发布我的编译代码的IL以展示发生了什么。 - jam40jeff

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接