KnowDotNet

Comparing Objects in VB.NET

using IComparer

by William Ryan

Any VB6 programmer that's come over to VB.NET has no doubt fallen in love with it.  So many things that were brutally painful to do in VB6 are a piece of cake in .NET.  Obviously entire books have been written on this subject, but I'd like to discuss one of them....Interfaces.

If you aren't familiar with them,
Interfaces are a code contract that your classes must fulfill if you choose to implement them.  If you come from a C/C++ background (you know doubt have mastered this concept), you probably remember breaking up your classes into a header and a body, thereby seperating your class description from its implementation. Interfaces in .NET aren't exactly the same, however they ultimately get you to a point where you can decouple a description from its implementation.

Let's say that you have two objects called Person with three properties,
FirstName, LastName and DOB...

Option Strict On
Option Explicit On

Public Class Person

Private _FirstName As String
Private _LastName As String
Private _DOB As Date

Public Property FirstName() As String
   Get
      Return _FirstName
   End Get
   Set (ByVal Value as String)
     _FirstName = Value
   End Set
End Property

Public Property LastName() As String
   Get
      Return _LastName
   End Get
   Set (ByVal Value as String)
     _LastName = Value
   End Set
End Property

Public Property DOB() As Date
   Get
      Return _DOB
   End Get
   Set (ByVal Value as Date)
     _DOB = Value
   End Set
End Property

End Class


Ok, so somewhere in my code, I create to
Person objects, Bill Jones and Joe Bills.  

Dim Person1 as New Person()
Person1.FirstName = "Bill"
Person1.LastName = "Jones"
Person1.DOB = "01/01/1980"

Dim Person2 as New Person()
Person2.FirstName = "Joe"
Person2.LastName = "Bills"
Person2.DOB = "01/01/1990"


Now, let's say we wanted to compare these two people.  They aren't integers, they are complex objects (well, as complex as a three property class can be) so how do you compare them?  Well, that's up to you.  One method could be see who makes more money or who has more education but then we decide that would be kind of shallow.  So we decide to compare them based on Age.  One of the few things that I really dislike about VB.NET is lack of operator overloading, but even if we had it here, it wouldn't do us much good.  So, how would we compare these?  Well, we could do the old procedural way.... if Person2.DOB > Person1.DOB Then whatever.  However, if we had to do this all over the place, we'd be writing a lot of code over and over and contrary to what many believe, writing a lot of code isn't the mark of a skilled programmer.  Here's where interfaces come in and IComparer in particular.  So, the first thing we need to do is modify out class definition:

Public Class Person should be changed to Public Class Person : Implements IComparer

Immediately we'll have a function shelled out for the only method which IComparer mandates, Compare.  Now, the return type on this is an Integer, but to make things clear, let's create an Enumeration Stature, which has three members, FirstGreaterThanSecond, SecondGreaterThanFirst and Equal

Public Enum Stature As Integer
     FirstGreaterThanSecond
     SecondGreaterThanFirst
     Equal
End Enum


Let's implement our function now:

Public Function Compare(ByVal o1 As Object, ByVal o2 As Object) Implements IComparer.Compare
       Dim p1 As Person
       Dim p2 As Person
      
       p1 = CType(o1, Person)
       p2 = CType(o2, Person)

      If p1.DOB > p2.DOB Then
          Return Stature.FirstGreaterThanSecond
      Else If
          p1.DOB < p2.DOB Then<BR>            Return Stature.SecondGreaterThanFirst
           End If
      Else
          Return Stature.Equal
      End If
End Function


That's about all there is to it to get it to work, but this example is obviously a bit silly because Persons are seldomly rated on Age alone.  But you may have a more complex object that you need to be able to Sort for instance, and sort on multiple fields.  In such a case, the ability to compare on different fields becomes increasingly important and as you can see, it wouldn't take much more code to come up with a much more sophisticated Compare algorithm.  Another thing I'd recommend is the use of an Enumeration. Enum have two really great things going for them: 1) They are value types (and value types are good) 2) They show up in intellisense which makes your code much friendlier to users of your code.  I did leave one important detail out of my code, but I did it intentionally so I can make a point.  Notice that if you use the standard compare function, you are passing in two parameters of type Object.  So, someone could have passed in an ArrayList and  a Foo object and this code would have blown up.  As such, YOU are the one in charge of the implementation and are responsible for EVERYTHING in it.  All the Interface does is define a contract and make sure that you do something to fulfill it.  It does nothing to ensure that your implementation is valid.  I could have changed the Person type to Foo and pass in Two Foo objects and it would work.  This is very important because you can pass in virtually anything and aren't even forced to compare objects of the type of the class you are implementing.  So it gives you a lot of power, but with it, you have the power to shoot off your foot.