about IComparer and Comparer

T

Tony

Hello!

I'm reading in a book and this can't be correct it says.
"Objects passed to Comparer.Compare() are checked to see if they support
IComparable.
If they do, then that implementation is used"

Assume I have a collection of Item(s) in an ArrayList
This class called Item support the IComparable interface which contains the
CompareTo method
so this method is impemented in class Item.

Now I can sort in the way that this CompareTo is implemented.

Now assume I want to be able to sort the collection of item(s) in another
way then this CompareTo is implementet
so I create an helper class which implement the IComparer interface which
contains method Compare.

So not to my question when object of type Item is passed to the Compare
method that implementation
is used of course not the implementation that exist for the IComparable.

//Tony
 
M

Marc Gravell

ArrayList.Sort has several overloads; the parameterless one uses
Comparer.Default, which (as you discussed) will use the objects
IComparable support (if any). If you want a custom sort, there is an
overload (2, actually) that accepts your custom IComparer, in which
case Comparer.Default is *not* used.

Note that in .NET 2.0, it would be better to consider List<T>,
IComparer<T>, IComparable<T> and Comparer<T>.Default.

Marc
 
J

Jon Skeet [C# MVP]

I'm reading in a book and this can't be correct it says.
"Objects passed to Comparer.Compare() are checked to see if they support
IComparable.
If they do, then that implementation is used"

One thing to note is that most of the time you should now be using the
generic Comparer said:
Assume I have a collection of Item(s) in an ArrayList
This class called Item support the IComparable interface which contains the
CompareTo method
so this method is impemented in class Item.

Now I can sort in the way that this CompareTo is implemented.

Now assume I want to be able to sort the collection of item(s) in another
way then this CompareTo is implementet
so I create an helper class which implement the IComparer interface which
contains method Compare.

So far so good - but at that point you don't use the Comparer class,
you just pass an instance of your IComparer implementation into
whatever sorting method you're using.
So not to my question when object of type Item is passed to the Compare
method that implementation
is used of course not the implementation that exist for the IComparable.

The quote you gave specified part of the implementation of
Comparer.Compare. If you're not calling Comparer.Compare, then the
quote is irrelevant. Why would you call Comparer.Compare if you want
to use a custom IComparer implementation?

Perhaps it would help if you'd provide a short but complete example,
and ask what the behaviour is and why for a specific method call.

Jon
 
T

Tony

Here I have not Complete program enough to be able to answer my question.
Here again I pass an object to this Comparer.Compare() although there is an
Default in between.
Below when I pass object to this Comparer.Default.Compare I don't use the
implementation from
IComparable that the text above is claiming I use whatever relevent code I
want.?

So what the text is claiming must be completely nonsens.!

public class Item : IComparable
{
private int steelGrade;
private int heatNumber;
private string mspName;
private string dateTime;
private int optTime;
private ArrayList listOpt;
private ArrayList listDateTime;

public ArrayList ListDateTime
{
set { listDateTime = value; }
get { return listDateTime; }
}

public ArrayList ListOpt
{
set {listOpt = value; }
get { return listOpt; }
}

public int OptTime
{
set { optTime = value; }
get { return optTime; }
}

public string DateTime
{
set { dateTime = value; }
get { return dateTime; }
}

public int Steelgrade
{
set { steelGrade = value; }
get { return steelGrade; }
}

public int HeatNumber
{
set { heatNumber = value; }
get { return heatNumber; }
}

public string MspName
{
set { mspName = value; }
get { return mspName; }
}

public int CompareTo(object right)
{
if (right is Item)
{
Item item = right as Item;
if (this.HeatNumber != item.HeatNumber)
return this.HeatNumber.CompareTo(item.HeatNumber);
else
return this.MspName.CompareTo(item.MspName);
}
else
throw new ArgumentException("Object to compare is not a Item
object");

}
}


public class ItemComparer : IComparer
{
public static IComparer Default = new ItemComparer();

public int Compare(object left, object right)
{
if (left is Item && right is Item)
{
if (((Item)left).Steelgrade != ((Item)right).Steelgrade)
return Comparer.Default.Compare(
((Item)left).Steelgrade, ((Item)right).Steelgrade);
else
return Comparer.Default.Compare(
((Item)left).MspName, ((Item)right).MspName);
}
else
throw new ArgumentException("One of the object is not an
Item");
}
}

//Tony
 
M

Marc Gravell

That is *not* a complete program; it has no "Main", so does nothing
(not even compile...).

Re your question - the only things you pass to
Comparer.Default.Compare(...) are two ints (the two Steelgrade
properties); I fully expect that Comparer will give the correct answer
for comparing two ints, but there is no reason to expect it to call
your IComparable implementation - it isn't comparing Item, it is
comparing ints - it will use the Int32.Compare method.

Marc
 
J

Jon Skeet [C# MVP]

Here I have not Complete program enough to be able to answer my question.
Here again I pass an object to this Comparer.Compare() although there is an
Default in between.

Well, your code is really making things confusing by having an
ItemComparer.Default as well as using Comparer.Default.
Below when I pass object to this Comparer.Default.Compare I don't use the
implementation from
IComparable that the text above is claiming I use whatever relevent code I
want.?

The call to Comparer.Default.Compare with the two different Steelgrade
values will use the fact that int implements IComparable. That's all
that the text is claiming. The rest of your code isn't a call to
Comparer.Compare, it's an implementation of IComparer.Compare. There's
a huge difference.

Mind you, your code would be significantly simpler if you just called
Compare directly, preferrably avoiding all those casts:

public int Compare(object left, object right)
{
// TODO: optimise for left == right, and consider cases
// where one side or other is null
Item leftItem = left as Item;
Item rightItem = right as Item;
if (leftItem == null || rightItem == null)
{
throw new ArgumentException("One of the objects is not an
Item");
}
if (leftItem.Steelgrade != rightItem.Steelgrade)
{
return leftItem.Steelgrade.CompareTo(rightItem.Steelgrade);
}
// TODO: Consider what kind of string comparison you want (culture/
ordinal/case sensitive)
return leftItem.MspName.CompareTo(rightItem.MspName);
}
So what the text is claiming must be completely nonsens.!

No it's not. Comparer.Compare will use the IComparable.CompareTo
implementation. But other implementations of IComparer are free to do
what they want.

It seems to me that you're not distinguishing between the interface
(IComparer) and *one* implementation (Comparer).

Jon
 
M

Marc Gravell

Here's a Main that demonstrates it working just fine... you'll have to
forgive some C# 3 initializer syntax (easy enough to see what it does,
though):

class Program
{
static void Main()
{
ArrayList list = new ArrayList();
list.Add(new Item { HeatNumber = 1, Steelgrade = 4, MspName =
"def" });
list.Add(new Item { HeatNumber = 3, Steelgrade = 4, MspName =
"ghi" });
list.Add(new Item { HeatNumber = 1, Steelgrade = 2, MspName =
"abc" });
list.Add(new Item { HeatNumber = 2, Steelgrade = 1, MspName =
"jkl" });
Console.WriteLine("Using custom sort (HeatNumber, MspName)");
list.Sort();
foreach (Item item in list)
{
Console.WriteLine("{0}: hn: {1}, sg: {2}", item.MspName,
item.HeatNumber, item.Steelgrade);
}
Console.WriteLine("Using custom sort (Steelgrade, MspName)");
list.Sort(ItemComparer.Default);
foreach (Item item in list)
{
Console.WriteLine("{0}: hn: {1}, sg: {2}", item.MspName,
item.HeatNumber, item.Steelgrade);
}
}
}
 
M

Marc Gravell

(sorry, copy/paste - first is the default sort using the item's
IComparable implementation)
 
T

Tony

Hello!

You say that your code would be significantly simpler if you just called
Compare directly, preferrably avoiding all those casts:
You you mean as the code prove instead CompareTo(...) I assume

//Tony
 
J

Jon Skeet [C# MVP]

You say that your code would be significantly simpler if you just called
Compare directly, preferrably avoiding all those casts:
You you mean as the code prove instead CompareTo(...) I assume

Sorry, yes, I meant CompareTo.

Jon
 

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