C# 在GetHashCode函数中创建对象是个坏主意吗?

C# 在GetHashCode函数中创建对象是个坏主意吗?,c#,performance,gethashcode,visitor,C#,Performance,Gethashcode,Visitor,我有一个基类MyBase和十几个派生类。我在代码中严重依赖访问者模式。因此基类是访问者的抽象宿主,每个派生类都是一个具体宿主。我使用的是一个独立的比较器,它实现了IEqualityComparer接口。此接口中有两种方法:Boolean Equals(MyBase a,MyBase b)和Int32 GetHashCode(MyBase obj)。在每种方法中,我都使用访问者将MyBase的实例解析为其中一种派生类型的实例。这就是我避免与演员打交道的方式。因此,访问者是每次调用Equals和Ge

我有一个基类
MyBase
和十几个派生类。我在代码中严重依赖访问者模式。因此基类是访问者的抽象宿主,每个派生类都是一个具体宿主。我使用的是一个独立的比较器,它实现了
IEqualityComparer
接口。此接口中有两种方法:
Boolean Equals(MyBase a,MyBase b)
Int32 GetHashCode(MyBase obj)
。在每种方法中,我都使用访问者将
MyBase
的实例解析为其中一种派生类型的实例。这就是我避免与演员打交道的方式。因此,访问者是每次调用
Equals
GetHashCode
时都需要创建的对象。我读过很多关于尽可能便宜地使用
GetHashCode
的书,所以问题是:


考虑到性能,在
GetHashCode
Equals
方法中创建对象(访问者)是一个坏主意吗?

这个问题假定创建一个新对象是昂贵的。事实并非如此。创建新对象需要将堆指针向上移动对象的大小,将这些字节归零,然后调用构造函数。只要构造器不做任何昂贵的事情,并且有足够的内存进行分配(如果没有,GC需要运行,这并不便宜),这将非常快


与创建新对象相关的“成本”是在需要收集新对象时产生的。创建更多的对象意味着您需要更频繁地收集垃圾,因此在将来的某个不确定的时间点上会减慢代码的速度。话虽如此,除非你正在创建大量的对象,或者非常大的对象,并且这些对象的寿命不长,否则这根本不应该是一个问题

这个问题假定创建一个新对象是昂贵的。事实并非如此。创建新对象需要将堆指针向上移动对象的大小,将这些字节归零,然后调用构造函数。只要构造器不做任何昂贵的事情,并且有足够的内存进行分配(如果没有,GC需要运行,这并不便宜),这将非常快


与创建新对象相关的“成本”是在需要收集新对象时产生的。创建更多的对象意味着您需要更频繁地收集垃圾,因此在将来的某个不确定的时间点上会减慢代码的速度。话虽如此,除非你正在创建大量的对象,或者非常大的对象,并且这些对象的寿命不长,否则这根本不应该是一个问题

应该以这样一种方式编写
GetHashCode()
的实现,即第一次调用之后的每个调用都会很快。如果计算一个对象的哈希值需要构造一个新的对象,这是一个很好的迹象,表明该对象可能应该在计算一次哈希值后缓存该哈希值,因此可以快速响应未来对哈希代码的请求。请注意,可能不值得使用标志来指示是否已计算哈希代码;相反,确定零不是有效的哈希代码值,并具有一个
HashCode
字段,该字段初始化为零,但在计算完后会被哈希代码覆盖。如果哈希代码的计算方法将产生零,则替换其他任意值。零和另一个值应该只在大约1/4000000000的时间内出现,因此将该另一个值的概率加倍到1/20000000000应该没什么大不了的


如果您正在缓存散列代码,那么如果它们的计算有点昂贵,那应该没什么大不了的,因为它只会执行一次。即使缓存了哈希代码,也应该尽量使其速度合理,但不要太担心。

GetHashCode()
的实现应该以这样一种方式编写,即第一次调用之后的每次调用都会很快。如果计算一个对象的哈希值需要构造一个新的对象,这是一个很好的迹象,表明该对象可能应该在计算一次哈希值后缓存该哈希值,因此可以快速响应未来对哈希代码的请求。请注意,可能不值得使用标志来指示是否已计算哈希代码;相反,确定零不是有效的哈希代码值,并具有一个
HashCode
字段,该字段初始化为零,但在计算完后会被哈希代码覆盖。如果哈希代码的计算方法将产生零,则替换其他任意值。零和另一个值应该只在大约1/4000000000的时间内出现,因此将该另一个值的概率加倍到1/20000000000应该没什么大不了的


如果您正在缓存散列代码,那么如果它们的计算有点昂贵,那应该没什么大不了的,因为它只会执行一次。即使一个哈希代码被缓存,人们仍然应该尝试使它相当快,但不要太担心它。

我认为
GetHashCode
的指南规定它永远不应该抛出。+1@Romoku对象的创建如何与“不应抛出”相关?@AlexeiLevenkov内存不足异常。@Romoku我不认为“不应抛出”意味着“必须处理所有异常情况”,而是“不应在正常代码流中抛出”。否则,您也会排除任何函数调用,因为它们至少会导致JIT失败或其他异常。“GetHashCode方法的实现不能抛出异常。”我认为
GetHashCode
的指导原则规定它永远不应该抛出异常。+1@Romoku对象的创建与“永远不应该抛出”有什么关系?@AlexeiLevenkov内存不足异常。@Romoku我不认为“永远不应该抛出”