C# 使用对象哈希代码作为内存缓存键有意义吗?

C# 使用对象哈希代码作为内存缓存键有意义吗?,c#,caching,hash,memorycache,C#,Caching,Hash,Memorycache,我试图在MemoryCache对象中缓存昂贵函数的结果 MemoryCache需要一个字符串键,因此我想知道执行以下操作是否有效: string key = Char.ConvertFromUtf32(myObject.GetHashCode()); if (!_resourceDescriptionCache.Contains(key)) { _resourceDescriptionCache[key] = ExpensiveFunction(myObject); } return (

我试图在MemoryCache对象中缓存昂贵函数的结果

MemoryCache需要一个字符串键,因此我想知道执行以下操作是否有效:

string key = Char.ConvertFromUtf32(myObject.GetHashCode());
if (!_resourceDescriptionCache.Contains(key))
{
    _resourceDescriptionCache[key] = ExpensiveFunction(myObject);
}
return (string)_resourceDescriptionCache[key];
使用一个UTF32字符作为一个潜在的大缓存的键感觉很奇怪。

这取决于具体情况

在许多情况下,使用GetHashCode()可能会导致错误行为:

哈希代码用于在基于哈希表的集合中高效插入和查找。哈希代码不是永久值。因此:

  • 不要序列化哈希代码值或将其存储在数据库中
  • 不要使用哈希代码作为键从键控集合检索对象
  • 不要跨应用程序域或进程发送哈希代码。在某些情况下,可以基于每个进程或每个应用程序域计算哈希代码

如果内存缓存发生(或将来可能发生)在与调用它的代码不同的进程或应用程序域中,则第三个条件失败

使用一个UTF32字符作为潜在大型缓存的密钥感觉很奇怪

如果缓存的内容足够多,则32位哈希上的冲突率可能会因为缓存的错误而高得令人不安


在缓存数以千万计的内容时,我使用了一个名为(由Google创建,开源)的64位哈希,并取得了很好的成功。您也可以使用Guid,尽管维护键的内存是Guid的两倍,而64位哈希是Guid的两倍。

内存缓存由普通C#字典支持。它实际上没有什么不同,只是它提供了过期时间

碰撞的几率为2^32,这是一个整数的大小。即使您确实设法发生了碰撞,字典也有相应的安全措施(通过在碰撞中使用Equals)


编辑:仅当为字典提供未更改的键(例如:dictionary())时,才会处理键冲突。在这种情况下,由于MemoryCache使用字符串,因此没有冲突检测。

哈希代码可能会发生冲突<代码>返回0是
GetHashCode
的有效实现。多个密钥将共享一个缓存插槽,这不是您想要的。。。您将混淆对象

如果您的代码与
不兼容,则返回0作为
GetHashCode
的实现,您的代码被破坏


选择一个更好的缓存键。

潜在的大缓存
=字典中潜在的高冲突率冲突可以使用更昂贵的
Equals
方法解决。当使用哈希代码作为字典的键时,哈希冲突会导致不正确的行为,而不仅仅是更昂贵的计算。这是一个巨大的区别。在提供的代码示例中,它使用了object.GetHashCode(),这可能是非常独特的。总的来说,如果有几百万个键,字典仍然可以很好地处理它;毕竟大约有42亿把钥匙。生日问题不适用,除非实现的自定义哈希生成器只生成有限的密钥集。@Dan:只有100000个对象,至少有68%的几率发生一次哈希冲突。对于1000000个条目,概率非常接近100%。这是一个很好的观点。我违反了规则#2-将哈希代码用作集合的键。现在我被困在不知道如何产生一个便宜好的缓存密钥。例如,序列化对象的成本太高。ToHashCode()当前是如何实现的?假设它在一个属性或属性组合中起作用,该属性或属性组合应该是唯一的(足够)。您可以通过连接这些属性的字符串表示来构建字符串键。不幸的是,这个类实际上处理对象类型,因此
GetHashCode()
以对象实现它的任何方式实现。如果类型将其
GetHashCode()
方法搞糟,并且存在冲突,则所有冲突将共享相同的
ExpensiveFunction(object)
(类似于序列化程序)值,即使它们会产生不同的结果。通过实际使用
key=myObject.GetType()+Char.ConvertFromUtf32(myObject.GetHashCode()),我尽了最大努力来隔离这种效果
@Alain:您正在有效地实现每个对象类型的GetHashCode作为哈希键。如果你真的在处理任何对象,那可能是你能做的最好的了。如果能够让所有对象至少实现一个公共接口,那么还可以让它们提供更好的散列键。您仍然会遇到我在回答中提到的与使用GetHashCode()相关的问题(同时检查链接,还有一些我认为不太可能应用的问题,因此我没有复制这些问题)。如何:
key=myObject.GetType()+Char.ConvertFromUtf32(myObject.GetHashCode())
,这样正确实现
GetHashCode()
的类型就不会受到故意无法实现它的类型的影响?对于某些上下文,
ExpensiveFunction()
是一个序列化程序,它在对象上进行反射,以在某种“浏览器”窗口中预览其内容。对我来说,每个对象都有一个正确的“预览”并不重要,更重要的是,每次对象出现在对象浏览器中时,我不要浪费太多资源重新序列化每个对象。(在前面的评论中,我误解了你。)不过,你仍然认为
myObject.GetHashCode()
是一个唯一的键。如果这实际上是100%的情况下,那么这将工作<代码>每个对象都有一个正确的“预览”对我来说不太重要。
-那么,你的方法就行了。即使是有损缓存密钥也可以。我不完全理解序列化注释,但也许您可以散列序列化表示的前32个字节,并将其用作键。@usr:如果使用的序列化格式具有任何类型的公共头,