Question Sorting lists and inheritance

edpfister

New member
Joined
Nov 2, 2012
Messages
3
Programming Experience
3-5
I am trying to add sorting support to a series of related classes. Here is a simplified and generic version which should show how things are laid out.

Class CItem
Class CSpecific1 inherits CItem
Class CSpecific2 inherits CItem
Class CSpecific3 inherits CSpecific1
... (several more like this)

Now CItem has some generic information that I would like to be able to sort on for any of the above classes (name and owner name are the most pressing). I have many lists and would like to do something like the following.

Dim lst as List(Of CSpecific2) = {get the list from somewhere}
lst.Sort(New CItemCompareOwner())
lst.Sort(New CItemCompareName())
...

I have tried adding the below compare class for the base class (CItem) which works on that specific class but does not work on other classes higher up the chain. I assume there is a way to have this work for all classes based off of CItem as I am only sorting on items in CItem. Doing this for each class would be ugly so I hope so. The specific error I get happens during runtime and is "Unable to case object of type 'Project.CItemCompareOwner' to type 'System.Collections.Generic.IComparer'1[Project.CSpecific1]'.

This is the compare class I am currently using. Also tried using "Implements iComparer" and using generic objects but that didn't work either. I can't remember if it was a similar exception though.
Class CItemCompareOwner
implements iComparer(Of CItem)
Public Function Compare(ByVal first as CItem, ByVal second as CItem) _
implemets System.Collections.Generic.IComparer(Of CItem).Compare
return first.Owner.Name.CompareTo(second.Owner.Name)
end function
end class

This is pretty new to me so any help would be appreciated.
 

IanRyder

Well-known member
Joined
Sep 9, 2012
Messages
1,130
Location
Healing, NE Lincs, UK
Programming Experience
10+
Hi,

The way to achieve this is to place the ICompare routine in the CItem class so your Sort routine inherits along with all the other properties that you need. Have a look here:-

Add two Buttons and a TextBox to a form and add the following code to demonstrate.

Code:
Public Class Form1
 
  Private Sub Button1_Click(sender As System.Object, e As System.EventArgs) Handles Button1.Click
    TextBox1.Clear()
 
    Dim lstNames As New List(Of CItem)
    Dim Name1 As New CItem
    Name1.Name = "Ian"
    Dim Name2 As New CItem
    Name2.Name = "Billy"
    Dim Name3 As New CItem
    Name3.Name = "Grahaem"
    Dim Name4 As New CItem
    Name4.Name = "John"
 
    lstNames.Add(Name1)
    lstNames.Add(Name2)
    lstNames.Add(Name3)
    lstNames.Add(Name4)
 
    For Each myName As CItem In lstNames
      TextBox1.Text += myName.Name & vbCrLf
    Next
    lstNames.Sort(New CItem.CItemCompareOwner)
    For Each myName As CItem In lstNames
      TextBox1.Text += myName.Name & vbCrLf
    Next
  End Sub
 
  Private Sub Button2_Click(sender As System.Object, e As System.EventArgs) Handles Button2.Click
    TextBox1.Clear()
 
    Dim lstCSNames As New List(Of CSpecific1)
    Dim csName1 As New CSpecific1
    csName1.Name = "Ian"
    Dim csName2 As New CSpecific1
    csName2.Name = "Billy"
    Dim csName3 As New CSpecific1
    csName3.Name = "Grahaem"
    Dim csName4 As New CSpecific1
    csName4.Name = "John"
 
    lstCSNames.Add(csName1)
    lstCSNames.Add(csName2)
    lstCSNames.Add(csName3)
    lstCSNames.Add(csName4)
 
    For Each myCSName As CSpecific1 In lstCSNames
      TextBox1.Text += myCSName.Name & vbCrLf
    Next
    lstCSNames.Sort(New CSpecific1.CItemCompareOwner)
    For Each mycsName As CItem In lstCSNames
      TextBox1.Text += mycsName.Name & vbCrLf
    Next
  End Sub
End Class
 
Public Class CItem
  Public Property Name As String
 
  Public Class CItemCompareOwner : Implements IComparer(Of CItem)
    Public Function Compare(ByVal FirstObject As CItem, ByVal SecondObject As CItem) As Integer Implements IComparer(Of CItem).Compare
      If FirstObject.Name < SecondObject.Name Then
        Return -1
      Else
        If FirstObject.Name > SecondObject.Name Then
          Return 1
        Else
          Return 0
        End If
      End If
    End Function
  End Class
End Class
 
Public Class CSpecific1
  Inherits CItem
End Class
 
Public Class CSpecific2
  Inherits CItem
End Class
 
Public Class CSpecific3
  Inherits CSpecific1
End Class
Hope that helps.

Cheers,

Ian
 

JohnH

VB.NET Forum Moderator
Staff member
Joined
Dec 17, 2005
Messages
15,336
Location
Norway
Programming Experience
10+
IanRyder said:
The way to achieve this is to place the ICompare routine in the CItem class so your Sort routine inherits along with all the other properties that you need.
For default comparison the class should instead implement IComparable(Of T), example:
Public Class CItem
    Implements IComparable(Of CItem)

    Public Function CompareTo(other As CItem) As Integer Implements System.IComparable(Of CItem).CompareTo
        Return Me.Name.CompareTo(other.Name)
    End Function

    Public Property Name As String

 End Class

When sorting you then don't specify custom comparer:
lstCSNames.Sort()
 

IanRyder

Well-known member
Joined
Sep 9, 2012
Messages
1,130
Location
Healing, NE Lincs, UK
Programming Experience
10+
Thanks JohnH,

I have not used the IComparable(Of T) interface before so just had a quick look at the MSDN info for this. Would I be right in thinking that you can only sort in ascending order using this interface?

Cheers,

Ian
 

JohnH

VB.NET Forum Moderator
Staff member
Joined
Dec 17, 2005
Messages
15,336
Location
Norway
Programming Experience
10+
Would I be right in thinking that you can only sort in ascending order using this interface?
No, descending is just opposite comparison, instead of me-CompareTo-other you do a other-CompareTo-me.
 

edpfister

New member
Joined
Nov 2, 2012
Messages
3
Programming Experience
3-5
I tried adding the compare inside the class and still received the same error. I then tried your exact code and on pressing the 2nd button I also received that error. Did this work for you? If so could this be a difference in VS versions? I am using 2008 (9.0.21022.8).
 

JohnH

VB.NET Forum Moderator
Staff member
Joined
Dec 17, 2005
Messages
15,336
Location
Norway
Programming Experience
10+
I tried adding the compare inside the class and still received the same error. I then tried your exact code and on pressing the 2nd button I also received that error. Did this work for you? If so could this be a difference in VS versions? I am using 2008 (9.0.21022.8).
You must be talking about IanRyders post. Yes, it works in VB 2010, where type variance was added to .Net 4, see Covariance and Contravariance (C# and Visual Basic). You should have no trouble using the default IComparable I posted in Vb 2008.

If you want to use a custom base type comparer to sort a more specific type list in VB 2008 you can cast the list to the base type before sorting it:
        Dim basecast = lstCSNames.Cast(Of CItem).ToList
        basecast.Sort(New CItemCompareOwner)

lstCSNames refers to the List(Of CSpecific1) that was used in IanRyders post by the way.
 

edpfister

New member
Joined
Nov 2, 2012
Messages
3
Programming Experience
3-5
Ok, that fixed it. Thanks a lot, I really appreciate the help.
 

JohnH

VB.NET Forum Moderator
Staff member
Joined
Dec 17, 2005
Messages
15,336
Location
Norway
Programming Experience
10+
me said:
If you want to use a custom base type comparer to sort a more specific type list in VB 2008 you can cast the list to the base type before sorting it:
Another option is to make the comparer generic:
    Class CItemCompareOwner(Of T As CItem)
        Implements IComparer(Of T)
        Public Function Compare(ByVal first As T, ByVal second As T) As Integer Implements IComparer(Of T).Compare
            Return first.Name.CompareTo(second.Name)
        End Function
    End Class

You would then specify the specific type to use when creating the comparer:
lstCSNames.Sort(New CItemCompareOwner(Of CSpecific1))
 
Top Bottom