INTRODUCTION
The IComparable interface and the IComparer interface are discussed in the same article for two
reasons:
- The IComparable interface and the IComparer interface are frequently used together.
- Although the IComparable interface and the IComparer interface are similar and have similar
names, the purposes that they serve are somewhat different.
If you have an array of types, such as
String or
Int32 that already support the IComparer interface, you can sort that array without providing any explicit
reference to the IComparer interface. In this case, the elements of the array are cast to
the default implementation of the IComparer interface (Comparer.Default) for you. However, if you want to provide sorting or comparison
capability for your custom objects, you must implement either or both of these
interfaces.
Note The concepts that are described in this article can be implemented by using Microsoft Visual C++ .NET 2002 or by using the .NET Framework 1.0. However, when you run the sample code, you may receive runtime exceptions, such as System.TypeLoadException. System.TypeLoadException is a known bug in Visual C++ .NET 2002.
back to the
topRequirements
The
following list outlines the recommended hardware, software, network
infrastructure, and service packs that you need:
- Visual C++ .NET 2003 or Visual C++ 2005
- The .NET Framework 1.1 or later
This
article assumes that you are familiar with the following topics:
C++ programming
concepts (an intermediate level of understanding)
back to the topUse the IComparable interface
You can use the IComparable interface to compare two objects of a
particular type. You must implement the IComparable interface if you want to provide any sorting capability for your
object.
To help you understand the IComparable interface, you can think of the IComparable interface as a way to do a default sort order for your objects. For
example, if you have an array of objects of your type, and you call the
Sort method on that array, the IComparable interface provides the comparison of objects during the sort.
When you implement the IComparable interface, you must implement the
CompareTo method as shown in the following code sample:
// Implement IComparable::CompareTo to provide the default sort order.
int IComparable::CompareTo(Object *obj)
{
car * c = __try_cast<car*>(obj);
return String::Compare(this->make, c->make);
}The comparison in the method is different depending on the data type of
the value that is being compared. The
String::Compare method is used in this example because the property that is
selected for the comparison is a string.
Note In the previous code sample,
car is the class that you want to provide the ordering
capability for.
back to the
topUse the IComparer interface
The IComparer interface provides additional comparison mechanisms. For example,
you may want to provide sorting of your class on several fields or on several properties. You may want to provide
a sort in ascending order or a sort in descending order, or both, on the same field.
To use the IComparer interface, follow these steps:
- Declare a class that implements the IComparer interface, and then implement the Compare method. See the following code sample:
__gc class car::sortYearAscendingHelper: public IComparer
{
private:
int IComparer::Compare(Object *a, Object *b)
{
car *c1 = __try_cast<car*>(a);
car *c2 = __try_cast<car*>(b);
if (c1->year > c2->year)
return 1;
if (c1->year < c2->year)
return -1;
else
return 0;
}
};Note The IComparer::Compare method requires a tertiary comparison. After the tertiary comparison, 1, 0, or -1 is returned
depending on whether one value is greater than, equal to, or less than the
other value. The sort order can be changed to an ascending sort order or to a descending sort order by switching the
logical operators in the IComparer::Compare method.
Note The class in the previous code sample is declared first
inside the car class that you want to provide the sorting capability for and
then defined outside the car class. - Declare a method that returns an instance of your IComparer object. See the following code sample:
// This method returns the IComparer object for the sort helper.
IComparer* car::sortYearAscending()
{
return __try_cast<IComparer*>(new sortYearAscendingHelper());
}Note The method in the previous code sample is first declared as static inside the car class and then defined outside the car class. The object is used as the second argument when you call the
overloaded Array::Sort method that accepts the IComparer object. See the following code sample:Array::Sort(carlist,car::sortYearAscending());
In the previous code sample, carlist is an array of car objects.
back to the topFollow the step-by-step example
The following example shows you how to use the IComparable interface and the IComparer interface. A class that is named
car is created. The
car object has the
make property and the
year property. A sort in ascending order for the
make property is enabled through the IComparable interface. A sort in descending order on the
make property is enabled through the IComparer interface. Both the sort in ascending order and the sort in descending order are provided for
the
year property by using the IComparer interface.
- Start Microsoft Visual Studio .NET 2003 or Microsoft Visual Studio 2005.
- On the File menu, point to
New, and then click Project.
The
New Project dialog box appears. - Under Project Types, click Visual
C++ Projects.
Note In Visual Studio 2005, click Visual C++ under Project Types. - Under Templates, click Console
Application (.NET).
Note In Visual Studio 2005, click Windows Forms Control Library under Templates. - In the Name box, type
CompareInterfaces, and then click
OK.
- In Solution Explorer, expand all the folders, and then
double-click the CompareInterfaces.cpp file in the
Source Files folder.
- Add the following code to the top of the Code window before
the _tmain function:
using namespace System::Collections;
- Add the following code after the using statements and before the _tmain function:
public __gc class car : public IComparable
{
private:
int year;
String * make;
public:
car(void){};
~car(void){};
public:
car(String *Make,int Year)
{
make = Make;
year = Year;
}
__property String* get_Make()
{
return make;
}
__property void set_Make(String* Make)
{
make = (Make);
}
__property int get_Year()
{
return year;
}
__property void set_Year(int Year)
{
year = Year;
}
public:
// Implement IComparable CompareTo to provide the default sort order.
int IComparable::CompareTo(Object *obj)
{
car * c = __try_cast<car*>(obj);
return String::Compare(this->make, c->make);
}
static IComparer* sortYearAscending();
static IComparer* sortYearDescending();
static IComparer* sortMakeDescending();
private:
__gc class sortYearAscendingHelper;
__gc class sortYearDescendingHelper;
__gc class sortMakeDescendingHelper;
};
// Nested classes start here.
// This nested class does the ascending sort on the year property.
__gc class car::sortYearAscendingHelper: public IComparer
{
private:
int IComparer::Compare(Object *a, Object *b)
{
car *c1 = __try_cast<car*>(a);
car *c2 = __try_cast<car*>(b);
if (c1->year > c2->year)
return 1;
if (c1->year < c2->year)
return -1;
else
return 0;
}
};
// This nested class does the descending sort on the year property.
__gc class car::sortYearDescendingHelper : public IComparer
{
int IComparer::Compare(Object *a, Object *b)
{
car *c1= __try_cast<car*>(a);
car *c2= __try_cast<car*>(b);
if (c1->year < c2->year)
return 1;
if (c1->year > c2->year)
return -1;
else
return 0;
}
};
// This nested class does the descending sort on the make property.
__gc class car::sortMakeDescendingHelper : public IComparer
{
int IComparer::Compare(Object *a, Object *b)
{
car *c1 = __try_cast<car*>(a);
car *c2 = __try_cast<car*>(b);
return String::Compare(c2->make,c1->make);
}
};
// Nested classes end here.
// This method returns the IComparer object for the sort helper.
IComparer* car::sortYearAscending()
{
return __try_cast<IComparer*>(new sortYearAscendingHelper());
}
// This method returns the IComparer object for the sort helper.
IComparer* car::sortYearDescending()
{
return __try_cast<IComparer*>(new sortYearDescendingHelper());
}
// This method returns the IComparer object for the sort helper.
IComparer* car::sortMakeDescending()
{
return __try_cast<IComparer*>(new sortMakeDescendingHelper());
}Note You must add the common language runtime support compiler option (/clr:oldSyntax) in Visual C++ 2005 to successfully compile the previous code sample.
To add the common language runtime support compiler option in Visual C++ 2005, follow these steps:
- Click Project, and then click <ProjectName> Properties.
Note <ProjectName> is a placeholder for the name of the project. - Expand Configuration Properties, and then click General.
- Click to select Common Language Runtime Support, Old Syntax (/clr:oldSyntax) in the Common Language Runtime support project setting in the right pane, click Apply, and then click OK.
For more information about the common language runtime support compiler option, visit the following Microsoft Web site: - Add the following helper function that displays the car object properties before the _tmain function and after the code that you added in the previous step:
void DisplayCars(car *carlist[])
{
for(int i=0;i<carlist->Length;i++)
{
car *c = __try_cast<car*>(carlist->get_Item(i));
Console::WriteLine(String::Concat(c->Make,S"\t\t", c->Year.ToString()));
}
} - Replace the code of the _tmain function with the following code:
car *carlist[];
carlist = new car *[6];
carlist[0]= new car(S"Ford",1992);
carlist[1]= new car(S"Fiat",1988);
carlist[2]= new car(S"Buick",1932);
carlist[3]= new car(S"Ford",1932);
carlist[4]= new car(S"Dodge",1999);
carlist[5]= new car(S"Honda",1977);
// Write out a header for the output.
Console::WriteLine(S"Array - Unsorted\n");
DisplayCars(carlist);
// Demo IComparable by sorting the array with the "default" sort order.
Array::Sort(carlist);
Console::WriteLine(S"\nArray - Sorted by Make (Ascending - IComparable)\n");
DisplayCars(carlist);
// Demo the ascending sort of the numeric value with IComparer.
Array::Sort(carlist,car::sortYearAscending());
Console::WriteLine(S"\nArray - Sorted by Year (Ascending - IComparer)\n");
DisplayCars(carlist);
// Demo the descending sort of the string value with IComparer.
Array::Sort(carlist,car::sortMakeDescending());
Console::WriteLine(S"\nArray - Sorted by Make (Descending - IComparer)\n");
DisplayCars(carlist);
// Demo the descending sort of the numeric value with IComparer.
Array::Sort(carlist,car::sortYearDescending());
Console::WriteLine(S"\nArray - Sorted by Year (Descending - IComparer)\n");
DisplayCars(carlist);
Console::ReadLine();
return 0;
- Press CTRL+SHIFT+S to save the
project.
- Press CTRL+SHIFT+B to build the
solution.
- Press CTRL+F5 to run the program.
The following output appears in the Console window:Array - Unsorted
Ford 1992
Fiat 1988
Buick 1932
Ford 1932
Dodge 1999
Honda 1977
Array - Sorted by Make (Ascending - IComparable)
Buick 1932
Dodge 1999
Fiat 1988
Ford 1932
Ford 1992
Honda 1977
Array - Sorted by Year (Ascending - IComparer)
Ford 1932
Buick 1932
Honda 1977
Fiat 1988
Ford 1992
Dodge 1999
Array - Sorted by Make (Descending - IComparer)
Honda 1977
Ford 1932
Ford 1992
Fiat 1988
Dodge 1999
Buick 1932
Array - Sorted by Year (Descending - IComparer)
Dodge 1999
Ford 1992
Fiat 1988
Honda 1977
Buick 1932
Ford 1932
back to the
top