VB.NET列表中属性相同对象哈希值不同导致去重失败如何修复
问题成因
.NET 中引用类型默认的相等比较规则为引用相等,即判断两个变量是否指向堆内存中的同一个实例,而非判断实例的业务属性值是否相等。
你每次点击按钮都会通过New DocInfo(...)创建全新的对象实例,哪怕两个实例的title和fullPath属性完全一致,也属于不同的内存对象:
- 默认的
IList(Of T).Contains方法会基于引用相等判断,因此会认为新对象不存在于现有列表中 - 默认的
GetHashCode方法基于对象内存地址生成,所以你会看到属性相同的实例哈希值完全不同
解决方案
有两种常用实现方案,按需选择即可:
方案1:重写DocInfo类的相等比较逻辑
直接在DocInfo类中重写Equals和GetHashCode方法,将相等判断规则改为基于业务属性匹配,修改后原有AddUnique方法无需改动即可正常工作:
Class DocInfo Public Property title As String Public Property fullPath As String Sub New(title As String, fullPath As String) Me.title = title Me.fullPath = fullPath End Sub ' 重写相等判断逻辑,按title和fullPath判断两个实例是否等价 Public Overrides Function Equals(obj As Object) As Boolean If obj Is Nothing OrElse Me.GetType() <> obj.GetType() Then Return False End If Dim other As DocInfo = CType(obj, DocInfo) Return Me.title = other.title AndAlso Me.fullPath = other.fullPath End Function ' 重写哈希值生成规则,用参与相等判断的属性计算哈希 Public Overrides Function GetHashCode() As Integer ' .NET Core 2.1+ / .NET 5+ 支持HashCode.Combine Return HashCode.Combine(title, fullPath) ' 低版本.NET可替换为以下写法 ' Dim hashTitle = If(title?.GetHashCode(), 0) ' Dim hashPath = If(fullPath?.GetHashCode(), 0) ' Return hashTitle Xor (hashPath << 1) End Function End Class
方案2:修改扩展方法支持自定义比较规则
如果不方便修改DocInfo类,可以改造AddUnique扩展方法,支持传入业务比较规则,无需改动原有实体类:
优化后的扩展方法(性能优于原版,避免每次全列表遍历比较)
<Extension()> Public Sub AddUniqueBy(Of T, TKey)(ByVal self As IList(Of T), ByVal items As IEnumerable(Of T), ByVal keySelector As Func(Of T, TKey)) ' 用HashSet存储已有键,查询时间复杂度为O(1),元素量大时性能远高于原版的O(n)查询 Dim existingKeys As New HashSet(Of TKey)(self.Select(keySelector)) For Each item In items Dim key = keySelector(item) If Not existingKeys.Contains(key) Then self.Add(item) existingKeys.Add(key) End If Next End Sub
调用示例
docsInfo.AddUniqueBy(candidateItems, Function(d) New With {d.title, d.fullPath})
内容的提问来源于stack exchange,提问作者Justin8051
相关产品推荐
相关产品推荐

