If you’re like me, then you like to experiment with new and different ways to do the usual programming tasks that you do. Perhaps its just a reaction to the boredom of always doing things the same way. Sometimes its an attempt to make the easy but time consuming tasks faster. Often is in a search for better looking, more abstract code. I like to think of it as a learning opportunity. The other day I was playing with sorting a list of objects. There are build in ways to do this in the List(Of T)
class, so I though I would experiment with the different overloads of the List(Of T).Sort()
method. While what is provided by the .NET Framework is adequate to make clean code, I played around anyways because you never know when you will run into something that doesn’t give you a clean solution out of the box. So just in case, its good to add more tools to your mental utility belt, at least that’s what I tell myself so it doesn’t feel like I’m wasting time.
For sorting an object based on an arbitrary property, the Sort()
is not helpful as you cannot specify how the comparison is to be made. So that takes us to the first overload, Sort(IComparer(Of T))
. This method is probably too cumbersome for standard adhoc and one time sorts that you may have to do. It requires that you pass it an instance of a class that implements IComparer(Of T)
, and the Compare
method of that class will define your comparisons. One of my favorite features in .NET is generic types, as it increases code reuse substantially. So, why not make a generic class that takes a lambda function in the constructor to use as the comparison?
Public Class GenericCompare(Of T) Implements System.Collections.Generic.IComparer(Of T) Dim Func As Func(Of T, T, Integer) Public Sub New(ByVal Func As Func(Of T, T, Integer)) Me.Func = Func End Sub Public Function Compare(ByVal x As T, ByVal y As T) As Integer Implements System.Collections.Generic.IComparer(Of T).Compare Return Func(x, y) End Function End Class
Assuming a general object to sort like this one:
Public Class SomeObject Public Property Name As String Public Property Value1 As String Public Property Value2 As String End Class
That will make our sort look like this:
'Sort with custom compare Items.Sort(New GenericCompare(Of SomeObject)(Function(x, y) If x.Name > y.Name Then Return 1 ElseIf x.Name < y.Name Then Return -1 Else Return 0 End If End Function))
The above uses a long implementation for the comparison, which could be used for customizing the sort order or sorting on multiple fields if needed. If you just want a basic string comparison of one property, you can shorten the above to this:
'Or, sort with default string compare, one liner Items.Sort(New GenericCompare(Of SomeObject)(Function(x, y) String.Compare(x.Name, y.Name)))
Going crazy with this concept, you could even introduce an extension method to add another overload to Sort()
.
Public Module ExtensionMethods <System.Runtime.CompilerServices.Extension()> Public Sub Sort(Of T)(ByVal List As List(Of T), ByVal Func As Func(Of T, T, Integer)) List.Sort(New GenericCompare(Of T)(Func)) End Sub End Module
Then your sort becomes this:
'Or with the extension method Items.Sort(Function(x As SomeObject, y As SomeObject) String.Compare(x.Name, y.Name))
I’m not sure why exactly, but if you notice in this usage, I had to specify the type for the x, y
arguments. Unlike the last example, the compiler isn’t able to infer the type of the arguments.
Now, if you are looking at the different overloads for the Sort
method, you may have notice that there is another overload that does basically what we just created. List(Of T).Sort(Comparison(Of T)
takes a delegate to a function. So in this usage you would just have to create a function in your code that follows the Comparison(Of T)
delegate. As we know however, a lambda expression can be used anywhere a delegate is expected, so we can shorten this up into a one-liner as well.
Items.Sort(New Comparison(Of SomeObject)(Function(x As SomeObject, y As SomeObject) String.Compare(x.Name, y.Name)))
Although, the New Comparison(Of SomeObject)(...)
is actually not necessary, and the use of this override can be shortened to the equivalent of the extension method example above.
Items.Sort(Function(x As SomeObject, y As SomeObject) String.Compare(x.Name, y.Name))
OK, so I played around a lot with the List(Of T).Sort
methods and concluded that sorting a list of objects on an arbitrary property is pretty easy, and that .NET provides an overload that accepts a delegate or lambda for the comparison. So did I waste my time inventing that first method? Possibly, but I did prove to myself that you can create a generic class that can be made to accept lambdas that define its behavior. I can safely say I learned something and added another technique to the old toolbox, so in my onion a great use of time.