Array.Sort with multiple IComparer's

A

AMercer

I am able to use Array.Sort with an IComparer (generic or otherwise). I make
a class that implements IComparer and do the sorting in that class with an
overload like:

Array.Sort(Of SomeClass)(MyArray, Start, Length, Me).

So far, so good. What I need to do is have multiple IComparers so I can
sort the array in different ways under program control. If I try to do this
in my sorting class, I can't use an Implements clause (only one comparer
allowed), but it seems that I can get around that with the following
arrangement that is stripped down to its simplest form:

Public Class SomeClass
Public Function Compare1(ByVal x As Integer, ByVal y As Integer) As Integer
Return x - y
End Function
Public Function Compare2(ByVal x As Integer, ByVal y As Integer) As Integer
Return y - x
End Function
Public Sub SomeSub()
Dim a() As Integer = {6, 2, 4, 3, 5, 1}
Array.Sort(Of Integer)(a, AddressOf Compare1)
Array.Sort(Of Integer)(a, AddressOf Compare2)
'Array.Sort(Of Integer)(a, 0, 6, AddressOf Compare1)
'AddressOf' expression cannot be converted to
'System.Collections.Generic.IComparer(Of Integer)' because
'System.Collections.Generic.IComparer(Of Integer)' is not a delegate type.
End Sub
End Class

The class contains two comparers without an Implements statement. The first
two sort statements compile ok and run ok. The third (commented out) sort
statement diagnoses with the 'not a delegate type' error.

This looks like a bug in the VB compiler to me. How can one overload work
and the other fail? Regardless, does anyone know how to do this kind of
thing? I don't want to make a separate class for each comparer because all
the comparers need to get at come class variables. That means that in the
SomeClass example above, both comparers need visibility to SomeClass
properties. I guess I could make one class per comparer and have each
inherit SomeClass, but this seems convoluted. What is more natural is to
have multiple Compare functions in one class. Any ideas?
 
A

AMercer

Thanks for the reply. I appreciate what you have, especially about multiple
keys to break ties, but it doesn't meet my needs. My problem is sorting a
very large array, and I need maximum performance from my IComparers. I can't
afford a switch-case construct in the comparer. I have three comparers, and
they are all implemented efficiently. What I need is a way to have my
software decide which one to use. I can do this by packaging each one in a
class, but that seems contrived, and I was just looking for an alternative.
I'm thinking that a VB limitation is getting in my way, but I'm not sure.
 
A

Armin Zingler

AMercer said:
Thanks for the reply. I appreciate what you have, especially about
multiple keys to break ties, but it doesn't meet my needs. My
problem is sorting a very large array, and I need maximum performance
from my IComparers. I can't afford a switch-case construct in the
comparer. I have three comparers, and they are all implemented
efficiently. What I need is a way to have my software decide which
one to use. I can do this by packaging each one in a class, but that
seems contrived, and I was just looking for an alternative. I'm
thinking that a VB limitation is getting in my way, but I'm not sure.


Where's the problem if you use multiple comparers? Somewhere you must decide
which one to use anyway. If you don't decide it before calling the Sort
method, you have to decide it with each comparison.

One possible solution:

Class MyComparer
Implements IComparer(Of Integer)

Public Comparison As Comparison(Of Integer)

Public Shared ReadOnly AscendingComparison _
As New Comparison(Of Integer)(Function(x, y) x - y)
Public Shared ReadOnly DescendingComparison _
As New Comparison(Of Integer)(Function(x, y) y - x)

Public Function Compare(ByVal x As Integer, ByVal y As Integer) _
As Integer _
Implements System.Collections.Generic.IComparer(Of Integer).Compare

Return Comparison(x, y)

End Function
End Class

You can create one instance of MyComparer only and assign either the
AscendingComparison or the DescendingComparison field or any other
Comparison object to the public Comparison field before calling the sort
method. So you have one comparer object only and no select case inside the
Compare function. Though, there is one indirection due to the additional
call in the Compare function.

However, I think the quickest way is using multiple comparer objects.


Armin
 
A

Armin Zingler

Reading your initial question again, you can make the class itself implement
IComparer and use different delegates, each pointing to one of the Compare
functions inside the same class. These functions do have access to all
class' members. The latter is what you wanted if I got it right.


Armin
 
A

AMercer

Thanks for the reply. What you wrote makes sense. But I still get a
compiler diagnostic re overload resolution failure when I add sub Test to
your MyComparer class:

Public Sub Test
Dim a() As Integer = {4, 3, 2, 5, 1}
Comparison = AscendingComparison
Array.Sort(a, Comparison) ' ok using Comparison
Array.Sort(a, 0, 5) ' ok using default comparer
Array.Sort(a, 0, 5, Comparison) ' overload resolution failure diagnostic
End Sub

Either I'm missing something or there is a VB compiler bug.
 
A

Armin Zingler

AMercer said:
Thanks for the reply. What you wrote makes sense. But I still get a
compiler diagnostic re overload resolution failure when I add sub
Test to your MyComparer class:

Public Sub Test
Dim a() As Integer = {4, 3, 2, 5, 1}
Comparison = AscendingComparison
Array.Sort(a, Comparison) ' ok using Comparison
Array.Sort(a, 0, 5) ' ok using default comparer
Array.Sort(a, 0, 5, Comparison) ' overload resolution failure
diagnostic End Sub

Either I'm missing something or there is a VB compiler bug.

If the class implements IComparer now:

Array.Sort(a, 0, 5, Me)


Armin
 

Ask a Question

Want to reply to this thread or ask your own question?

You'll need to choose a username for the site, which only take a couple of moments. After that, you can post your question and our members will help you out.

Ask a Question

Top