Microsoft Visual Studio 2010 – Generic IComparer

I have blogged at one time on an IComparer class that you could use to sort a property in an object within a collection http://www.jamesandchey.net/?p=601.

The downside was that the name of the object was hard-coded in the IComparer class. If I needed to sort different objects, I would have to create multiple IComparer classes. Well, that’s no good!

I was now on the hunt for one IComparer class that was generic in nature so I can sort multiple collections calling just the one IComparer class.

Here is a generic IComparer class written in visual basic. After the class, I demonstrate how to call the class.

Imports System.Collections.Generic
Imports System.Text
Imports System.Reflection

    Public Class GenericIComparer(Of T)
        Implements IComparer(Of T)

        Private m_memberName As String = String.Empty
        Private m_sortOrder As String = String.Empty
        Private m_methodParameters As New List(Of Object)()
        Private propertyInfo As PropertyInfo = Nothing
        Private methodInfo As MethodInfo = Nothing

        Public Property MemberName() As String
            Get
                Return m_memberName
            End Get
            Set(ByVal value As String)
                m_memberName = value
                GetReflected()
            End Set
        End Property

        Public Property SortOrder() As String
            Get
                Return m_sortOrder
            End Get
            Set(ByVal value As String)
                m_sortOrder = value
            End Set
        End Property

        Public ReadOnly Property MethodParameters() As List(Of Object)
            Get
                Return m_methodParameters
            End Get
        End Property

        Public Sub New()
        End Sub

        Public Sub New(ByVal memberName As String, ByVal sortOrder As String, ByVal methodParameters As List(Of Object))
            Me.m_memberName = memberName
            Me.m_sortOrder = sortOrder
            Me.m_methodParameters = methodParameters

            GetReflected()
        End Sub

        Private Sub GetReflected()
            Dim underlyingTypes As Type() = Me.[GetType]().GetGenericArguments()
            Dim thisUnderlyingtype As Type = underlyingTypes(0)

            Dim mi As MemberInfo() = thisUnderlyingtype.GetMember(m_memberName)
            If mi.Length > 0 Then
                If mi(0).MemberType = MemberTypes.[Property] Then
                    propertyInfo = thisUnderlyingtype.GetProperty(m_memberName)
                ElseIf mi(0).MemberType = MemberTypes.Method Then
                    Dim signatureTypes As Type() = New Type(-1) {}
                    If m_methodParameters IsNot Nothing AndAlso m_methodParameters.Count > 0 Then
                        signatureTypes = New Type(m_methodParameters.Count - 1) {}
                        For i As Integer = 0 To m_methodParameters.Count - 1
                            signatureTypes(i) = m_methodParameters(i).[GetType]()
                        Next
                        methodInfo = thisUnderlyingtype.GetMethod(m_memberName, signatureTypes)
                    Else
                        methodInfo = thisUnderlyingtype.GetMethod(m_memberName, signatureTypes)
                    End If
                Else
                    Throw New Exception("Member name: " & m_memberName & " is not a Public Property or " & "a Public Method in Type: " & thisUnderlyingtype.Name & ".")
                End If
            Else
                Throw New Exception("Member name: " & m_memberName & " not found.")
            End If
        End Sub

        Private Function GetComparable(ByVal obj As T) As IComparable
            If methodInfo IsNot Nothing Then
                Return DirectCast(methodInfo.Invoke(obj, m_methodParameters.ToArray()), IComparable)
            Else
                Return DirectCast(propertyInfo.GetValue(obj, Nothing), IComparable)
            End If

        End Function


        Public Function Compare(ByVal objOne As T, ByVal objTwo As T) As Integer Implements IComparer(Of T).Compare
            Dim iComparable1 As IComparable = GetComparable(objOne)
            Dim iComparable2 As IComparable = GetComparable(objTwo)

            If m_sortOrder IsNot Nothing AndAlso m_sortOrder.ToUpper().Equals("ASC") Then
                Return iComparable1.CompareTo(iComparable2)
            Else
                Return iComparable2.CompareTo(iComparable1)
            End If
        End Function

    End Class

Here is how to call the class:

'Sort the Collection by entered_date
Dim _IComparer As New GenericIComparer(Of _MyObject)
_IComparer.MemberName = "entered_date"
_IComparer.SortOrder = "asc"
_MyCollection.Sort(_IComparer)

1) I instantiate the GenericIComparer class.
2) I set the MemberName property to the object property (_MyObject) I want to sort on.
3) I set the SorOrder property to the direction I want to sort.
4) I then call the Sort method.

Note: _MyCollection happens to be defined as a List of (_MyObject). In other words, _MyCollection contains a collection of _MyObject’s. Each object has a property called ‘entered_date’. I simply want to sort the collection and put the objects in order (whether ascending or descending) by ‘entered_date’.

Leave a Reply