支持 IEquatable 的多种比较类型

Support multiple comparison-types for IEquatable

我重载了现在看起来像这样的 Equals 方法:

 Public Overloads Function Equals(other As Part) As Boolean _
        Implements IEquatable(Of Part).Equals
        If other Is Nothing Then
            Return False
        End If

        Return (Me.PartId.Equals(other.PartId) And Me.PartName.Equals(other.PartName))
    End Function

如您所见,我正在根据 PartName 和 PartId 进行比较。并像下面这样使用它:

 Console.WriteLine(vbLf & "Contains(""1734""): {0}", parts.Contains(New Part() With { _
            .PartId = 1334, _
            .PartName = "chain rin8g" _
       }))

除此之外,我还希望能够单独比较 PartId 或 partName。所以我所做的是创建枚举:

Public Enum EqualsComparmission

    PartId
    PartName
    PartId_and_PartName

End Enum

所以我所做的是添加额外的方法:

  Public Overloads Function Equals(other As Part, compParameter As EqualsComparmission) As Boolean


        If other Is Nothing Then
            Return False
        End If

        Dim result As Boolean

        Select Case compParameter
            Case EqualsComparmission.PartId
                result = (Me.PartId.Equals(other.PartId))
            Case EqualsComparmission.PartName
                result = (Me.PartName.Equals(other.PartName))
            Case EqualsComparmission.PartId_and_PartName
                result = (Me.PartId.Equals(other.PartId) And Me.PartName.Equals(other.PartName))
            Case Else

        End Select

        Return result
    End Function

但在那之后我无法使用例如:

Console.WriteLine(vbLf & "Contains(""1734""): {0}",
parts.Contains(New Part() With { _
.PartId = 1334, _
.PartName = "chain rin8g" _
}), EqualsComparmission.PartId)

整个零件类:

Imports System.Collections.Generic
' Simple business object. A PartId is used to identify the type of part  
' but the part name can change.  
Public Class Part
    Implements IEquatable(Of Part)
    Public Property PartName() As String
        Get
            Return m_PartName
        End Get
        Set(value As String)
            m_PartName = Value
        End Set
    End Property
    Private m_PartName As String

    Public Property PartId() As Integer
        Get
            Return m_PartId
        End Get
        Set(value As Integer)
            m_PartId = Value
        End Set
    End Property
    Private m_PartId As Integer

    Public Overrides Function ToString() As String
        Return "ID: " & PartId & "   Name: " & PartName
    End Function
    Public Overrides Function Equals(obj As Object) As Boolean
        If obj Is Nothing Then
            Return False
        End If
        Dim objAsPart As Part = TryCast(obj, Part)
        If objAsPart Is Nothing Then
            Return False
        Else
            Return Equals(objAsPart)
        End If
    End Function
    Public Overrides Function GetHashCode() As Integer
        Return PartId
    End Function
    Public Overloads Function Equals(other As Part) As Boolean _
        Implements IEquatable(Of Part).Equals
        If other Is Nothing Then
            Return False
        End If

        Return (Me.PartId.Equals(other.PartId) And Me.PartName.Equals(other.PartName))
    End Function

    Public Overloads Function Equals(other As Part, compParameter As EqualsComparmission) As Boolean
        If other Is Nothing Then
            Return False
        End If

        Dim result As Boolean

        Select Case compParameter
            Case EqualsComparmission.PartId
                result = (Me.PartId.Equals(other.PartId))
            Case EqualsComparmission.PartName
                result = (Me.PartName.Equals(other.PartName))
            Case EqualsComparmission.PartId_and_PartName
                result = (Me.PartId.Equals(other.PartId) And Me.PartName.Equals(other.PartName))
            Case Else

        End Select
        Return result
    End Function


    ' Should also override == and != operators. 

End Class

编辑进一步讨论:

我想测试一些东西,因为如果 'Remove' 方法指的是 IEqutable 的 Equals 方法(根据 MSDN),我再次实现 Equtable 并修改了 Equals 方法以便为正确删除做好准备。不幸的是,当我像下面这样调用 remove 时,无论我定义什么,它总是会出现 EqualsComparmission.PartId 情况 - 这种情况:EqualsComparmission.PartId_and_PartName

它转到 Equal 方法的这一部分:

Case EqualsComparmission.PartId
                Return Me.PartId.Equals(other.PartId)

我是这样使用它的:

   Dim deleted As Boolean = parts.Remove(New Part(EqualsComparmission.PartId_and_PartName) With { _
             .PartId = 11, _
             .PartName = "al7a" _
        })

        Console.WriteLine("Found and deleted: true/false: : " & deleted )

全班:

Imports System.Collections.Generic

Public Class Part
    Implements IEqualityComparer(Of Part), IEquatable(Of Part)


    Public Property ComparisonType As EqualsComparmission

    Public Sub New(comparisonType As EqualsComparmission)
        Me.ComparisonType = comparisonType
    End Sub

    Public Sub New()
    End Sub

    Public Property PartName() As String
        Get
            Return m_PartName
        End Get
        Set(value As String)
            m_PartName = value
        End Set
    End Property
    Private m_PartName As String

    Public Property PartId() As Integer
        Get
            Return m_PartId
        End Get
        Set(value As Integer)
            m_PartId = value
        End Set
    End Property
    Private m_PartId As Integer

    Public Overrides Function ToString() As String
        Return "ID: " & PartId & "   Name: " & PartName
    End Function


    Public Function Equals1(x As Part, y As Part) As Boolean Implements System.Collections.Generic.IEqualityComparer(Of Part).Equals
        If x Is Nothing AndAlso y Is Nothing Then Return True
        If x Is Nothing OrElse y Is Nothing Then Return False

        Select Case ComparisonType
            Case EqualsComparmission.PartId
                Return x.PartId = y.PartId
            Case EqualsComparmission.PartName
                Return String.Equals(x.PartName, y.PartName)
            Case EqualsComparmission.PartId_and_PartName
                Return x.PartId = y.PartId AndAlso String.Equals(x.PartName, y.PartName)
            Case Else
                Throw New NotSupportedException("Unknown comparison type for parts: " & ComparisonType.ToString())
        End Select
    End Function

    Public Function GetHashCode1(obj As Part) As Integer Implements System.Collections.Generic.IEqualityComparer(Of Part).GetHashCode
        Select Case ComparisonType
            Case EqualsComparmission.PartId
                Return obj.PartId
            Case EqualsComparmission.PartName
                Return If(obj.PartName Is Nothing, 0, obj.PartName.GetHashCode())
            Case EqualsComparmission.PartId_and_PartName
                Dim hash = 17

                hash = hash * 23 + obj.PartId
                hash = hash * 23 + If(obj.PartName Is Nothing, 0, obj.PartName.GetHashCode())
                Return hash
            Case Else
                Throw New NotSupportedException("Unknown comparison type for parts: " & ComparisonType.ToString())
        End Select
    End Function

    Public Overloads Function Equals(other As Part) As Boolean Implements IEquatable(Of Part).Equals
        If other Is Nothing Then
            Return False
        End If

        Select Case ComparisonType
            Case EqualsComparmission.PartId
                Return Me.PartId.Equals(other.PartId)
            Case EqualsComparmission.PartName
                Return Me.PartName.Equals(other.PartName)
            Case EqualsComparmission.PartId_and_PartName
                Return (Me.PartId.Equals(other.PartId) And Me.PartName.Equals(other.PartName))
            Case Else
                Throw New NotSupportedException("Unknown comparison type for parts: " & ComparisonType.ToString())
        End Select

        Return (Me.PartId.Equals(other.PartId) And Me.PartName.Equals(other.PartName))
    End Function
End Class

我会创建一个自定义的 IEqualityComparer(Of Part),它将枚举作为 属性 并作为构造函数参数。然后您可以将它用于大多数 LINQ 方法,例如 Contains.

我的想法是:

Public Class PartComparer
    Implements IEqualityComparer(Of Part)

    Public Sub New(comparisonType As EqualsComparison)
        Me.ComparisonType = comparisonType
    End Sub

    Public Property ComparisonType As EqualsComparison = EqualsComparison.PartId

    Public Function Equals1(x As Part, y As Part) As Boolean Implements System.Collections.Generic.IEqualityComparer(Of Part).Equals
        If x Is Nothing AndAlso y Is Nothing Then Return True
        If x Is Nothing OrElse y Is Nothing Then Return False

        Select Case ComparisonType
            Case EqualsComparison.PartId
                Return x.PartId = y.PartId
            Case EqualsComparison.PartName
                Return String.Equals(x.PartName, y.PartName)
            Case EqualsComparison.PartId_and_PartName
                Return x.PartId = y.PartId AndAlso String.Equals(x.PartName, y.PartName)
            Case Else
                Throw New NotSupportedException("Unknown comparison type for parts: " & ComparisonType.ToString())
        End Select
    End Function

    Public Function GetHashCode1(obj As Part) As Integer Implements System.Collections.Generic.IEqualityComparer(Of Part).GetHashCode
        Select Case ComparisonType
            Case EqualsComparison.PartId
                Return obj.PartId
            Case EqualsComparison.PartName
                Return If(obj.PartName Is Nothing, 0, obj.PartName.GetHashCode())
            Case EqualsComparison.PartId_and_PartName
                Dim hash = 17

                hash = hash * 23 + obj.PartId
                hash = hash * 23 + If(obj.PartName Is Nothing, 0, obj.PartName.GetHashCode())
                Return hash
            Case Else
                Throw New NotSupportedException("Unknown comparison type for parts: " & ComparisonType.ToString())
        End Select
    End Function
End Class

您可以这样使用它(与 Enumerable.Contains 一起使用,但也可以与其他 LINQ 方法一起使用):

Dim contains As Boolean = parts.Contains(New Part() With { _
        .PartId = 1334, _
        .PartName = "chain rin8g" _
   }, New PartComparer(EqualsComparison.PartId_and_PartName))

根据您的评论,您希望通过提供给定的 PartEqualsComparison 来删除 List(Of Part) 的一个(或多个)部分。你可以写一个像这样的扩展:

Module Extensions
    <Extension()>
    Public Sub Remove(partList As List(Of Part), partToDelete As Part, comparisonType As EqualsComparison, Optional onlyOne As Boolean = True)
        Dim comparer = New PartComparer(comparisonType)
        For i As Int32 = partList.Count - 1 To 0 Step -1
            If comparer.Equals(partList(i), partToDelete) Then
                partList.RemoveAt(i)
                If onlyOne Then Return
            End If
        Next
    End Sub
End Module

现在可以了:

Dim partToDelete = New Part With {.PartId = 4711}
parts.Remove(partToDelete, EqualsComparison.PartId)