C# – 如何覆盖 List<T> 的 GetHashCode 以计算 ETag,T 是一条记录

C# – How to override GetHashCode for List<T> to calculate ETag with T being a record

对于 NetCore Web API GET 方法,我需要计算 returned List<T> 的 ETag。 T 是 record 形式的 DTO,仅包含原始类型。

我想计算列表的哈希值。我正在搜索有关如何实施 GetHashCode() 的信息,但找不到任何信息。 object.GetHashCode() 的文档 不说明有关列表或集合的任何信息。根据代码的结果,我观察到在每个 运行 上,相同的列表数据会创建不同的哈希码。我的结论是 GetHashCode() 使用引用类型项的指针值。

GetHashCode() of record 计算每个成员值的哈希码。因此,我通过遍历列表项创建了列表哈希码:

List<GetGroupsDTO> dtoList = commandResult.Value;
int hash = 17;
foreach(GetGroupsDTO dto in dtoList)
{
   hash = hash * 23 + dto.GetHashCode();
}
string eTagPayload = hash.ToString().SurroundWithDoubleQuotes();

当然,我不想对每个 List<T> 都这样做。我想覆盖 GetHashCode(),但我正在努力解决它。我不知道如何为通用列表覆盖它。我可以派生一个新的 class DTOList,我可以在其中覆盖 GetHashCode()。但这会导致其他地方更加复杂。由于 EFCore Set 查询的结果填充了列表,因此我需要一个自定义转换器,然后是一个自定义序列化程序到 return Web API.

中的列表

因此我想知道我是否应该为 List 创建一个扩展方法,或者只是一个将 List 作为参数的函数。还有其他方法可以计算 ETag 吗?如何有效地计算 DTO 对象列表的 ETag?

一点扩展方法和 HashCode 可以帮助解决这个问题:

internal static class EnumerableExtensions {
    public static int GetCombinedHashCode<T>(this IEnumerable<T> source) => 
        source.Aggregate(typeof(T).GetHashCode(), (hash, t) => HashCode.Combine(hash, t));
}

typeof(T).GetHashCode 为哈希播种是相当随意的,但确保不同类型的空集合不会都“看起来相等”,因为它们通常不会比较相等。这是否重要或什至是可取的将取决于您的情况。

当然,只有在 T 具有有意义的 GetHashCode 实现的情况下,此结果才可用,但一般情况下散列都是如此。为了更加安心,可以添加 where T : IEquatable<T> 约束,尽管这不是涉及散列的方法的标准方法。添加对散列使用自定义 IEqualityComparer<T> 的功能留作练习。