Compare Just About Any Property of Any Object
Archimedes once claimed that given a lever long enough, a fulcrum strong enough, and a place to stand, he could move the world. (Archimedes also reportedly ran through the streets of Athens naked shouting Eureka when he discovered displacement, but that's another story.) Advanced techniques such as interfaces, reflection, and generics may not enable you to move the world, but they are sufficient tools for doing a lot of work, which is what Archimedes meant.
Implementing IComparer
Listing 1: An Enumeration with Specific Values
Public Enum SortDirection
Descending = -1
Ascending = 1
Enum
By multiplying the return value of a comparison by 1 or -1, you change the direction of the sort.
Listing 2: The PropertyComparer
Public Class PropertyComparer(Of T)
Implements IComparer(Of T)
Private FPropertyName As String = ""
Private FDirection As SortDirection
Public Sub New(ByVal propertyName As String)
FPropertyName = propertyName
FDirection = SortDirection.Ascending
End Sub
Public Sub New(ByVal propertyName As String, _
ByVal Direction As SortDirection)
FPropertyName = propertyName
FDirection = Direction
End Sub
' Try to sort based on type using CompareTo method
' Multiple by FDirection to alternate sort direction
Public Function Compare(ByVal x As T, ByVal y As T) _
As Integer Implements System.Collections.Generic. _
IComparer(Of T).Compare
Dim propertyX As PropertyInfo = _
x.GetType().GetProperty(FPropertyName)
Dim propertyY As PropertyInfo = _
y.GetType().GetProperty(FPropertyName)
Dim px As Object = propertyX.GetValue(x, Nothing)
Dim py As Object = propertyY.GetValue(y, Nothing)
If (TypeOf px Is Integer) Then
Return Compare(Of Integer)(CType(px, Integer), _
CType(py, Integer)) * FDirection
End If
If (TypeOf px Is Decimal) Then
Return Compare(Of Decimal)(CType(px, Decimal), _
CType(py, Decimal)) * FDirection
End If
If (TypeOf px Is DateTime) Then
Return Compare(Of DateTime)(CType(px, DateTime), _
CType(py, DateTime)) * FDirection
End If
If (TypeOf px Is Double) Then
Return Compare(Of Double)(CType(px, Double), _
CType(py, Double)) * FDirection
End If
If (TypeOf px Is String) Then
Return Compare(Of String)(CType(px, String), _
CType(py, String)) * FDirection
End If
If (TypeOf px Is Decimal) Then
Return Compare(Of Decimal)(CType(px, Decimal), _
CType(py, Decimal)) * FDirection
End If
Dim methodX As MethodInfo = _
propertyX.GetType().GetMethod("CompareTo")
If (methodX Is Nothing = False) Then
Return CType(methodX.Invoke(px, New Object() {py}), _
Integer) * FDirection
Else
Return 0
End If
End Function
Private Function Compare(Of K As IComparable)(ByVal x As K, _
ByVal y As K) As Integer
Return x.CompareTo(y)
End Function
End Class
Defining Something to Sort
You can sort just about anything. To make the demonstration reflect something you may be familiar with, Listing 3 contains an implementation of a simple Customer class:
Listing 3: A Simple Customer Class
Public Class Customer
Private FCustomerNumber As Integer
Private FName As String
Public Sub New(ByVal customerNumber As Integer, _
ByVal name As String)
FCustomerNumber = customerNumber
FName = name
End Sub
Public ReadOnly Property CustomerNumber() As Integer
Get
Return FCustomerNumber
End Get
End Property
Public Property Name() As String
Get
Return FName
End Get
Set(ByVal value As String)
FName = value
End Set
End Property
End Class
You can sort a list of Customer objects by the Name or CustomerNumber.
Invoking the Sort Behavior
Listing 4: Code to Demonstrate the PropertyComparer
Imports System
Imports System.Collections.Generic
Imports System.Text
Imports System.Reflection
Module Module1
Sub Main()
Dim list As List(Of Customer) = New List(Of Customer)
list.Add(New Customer("Paul"))
list.Add(New Customer("Noah"))
list.Add(New Customer("Alex"))
list.Add(New Customer("Jim"))
list.Sort(New PropertyComparer(Of Customer)("Name"))
Dim o As Customer
For Each o In list
Console.WriteLine(o.Name)
Next
Console.ReadLine()
End Sub
End Module
No comments:
Post a Comment