公司系统中有一接口访问量大,内部计算逻辑较为复杂。在优化时打算把Request中的参数做为Key,Response做为Value放到进程内缓存中,以降低服务器压力,提高接口响应速度。因为Response中一些数据时效性要求较高,所以缓存设置一个较短的过期时间(比如10s)。
但这里牵涉到一个问题,如何有效的判断两次请求的参数是相等的。C#中自定义类型会从Object类继承Equals和GetHashCode两个方法,可以根据实际需求来重写这两个方法实现对象相等性比较。
默认实现根据对象在内存中的地址,即引用是否相同来判断对象是否相等。应该说是identity而非equality。与Python中的is、== 操作符类似。这种默认实现通常不能满足需求,自定义实现Equals思路如下:
根据上述思路,实现自定义类型的Equals方法:
反编译之后,可以看到ValueType中Equals方法的实现,即值类型的默认实现:
上面可以看到,ValueType中Equals实现思路如下:
重写Equals方法应满足以下几点:
默认实现根据对象在内存中的地址,即引用来计算哈希码。换言之, ReferenceEquals方法返回true的两个对象的哈希码也相同。
默认实现通过反射基于字段的值来计算哈希码。换言之,两个值类型实例的所有字段值都相等,那么它们的哈希码也相等。
重写Equals方法后,通常也需要重写GetHashCode方法,反之亦然。因为在哈希结构(如字典)中,存取数据时需要用到键的哈希码。如下图是Github上Dictionary根据key获取value的一段源码,代码中先比较了hashCode是否相等,然后再调用Enquals方法对key做相等性判断:
重写GetHashCode方法应注意以下事项:
通常,对于可变引用对象,应重写GetHashCode方法,除非能保证以下两点:
如果要重写可变对象的GetHashCode方法,尽可能在文档中指出:如果对象要用作哈希结构的key,尽可能不要修改该对象,否则,在读取数据时可能会引发KeyNotFoundException。
⚠️ 不同的.NET版本、不同的平台(32位、64位系统)对于GetHashCode的默认实现可能会有差异。因此,若使用默认的GetHashCode方法,须注意以下两点:
下面是微软官方文档中对于GetHashCode的一段总结,人太懒水平又差,就不翻译了,抄录在这里备以后查询:
A hash function must have the following properties:
For example, the implementation of the GetHashCode() method provided by the String class returns identical hash codes for identical string values. Therefore, two String objects return the same hash code if they represent the same string value. Also, the method uses all the characters in the string to generate reasonably randomly distributed output, even when the input is clustered in certain ranges (for example, many users might have strings that contain only the lower 128 ASCII characters, even though a string can contain any of the 65,535 Unicode characters).
Providing a good hash function on a class can significantly affect the performance of adding those objects to a hash table. In a hash table with keys that provide a good implementation of a hash function, searching for an element takes constant time (for example, an O(1) operation).
In a hash table with a poor implementation of a hash function, the performance of a search depends on the number of items in the hash table (for example, an O(n
) operation, where n
is the number of items in the hash table).
A malicious user can input data that increases the number of collisions, which can significantly degrade the performance of applications that depend on hash tables, under the following conditions:
Derived classes that override GetHashCode() must also override Equals(Object) to guarantee that two objects considered equal have the same hash code; otherwise, the Hashtable type might not work correctly.