The .Net IComparer Interface - Part 3


Part one described the sample application user experience.

Part two explained the main form engineering of the C# solution.

Part three here will describe the C# solution IComparer sort machinery.

Part four will explain the C# solution data display form, and wrap up with a brief description of the differences between the C# and example applications.

Download the sample C# / software at the GitHub repository.


As Part 2 explained, the IComparer expects to sort 1-D arrays. This might seem a little restrictive, but it actually offers great flexibility. The 1-D array can hold elements of any structure or data type. Once we have a clean element type definition, we only need to customize the IComparer object to handle the element types of that 1-D array. This sample application, and Windows applications generally, can use this approach.

In the application, the IComparer object in ColumnSorter sorts arrays of strings based on one or more columns. To make things easier, a ColumnSorter object places the separate list of sort column indices, with ASC / DESC flags, in an object property. In CSharp_Demo_App.cs, line 203 declares and initializes
as a new ColumnSorter object. That declaration has a zero-based, comma-delimited character (string) list of column indices - in other words, "0" maps to the first column, "2" maps to the third column, etc. The sort order for the columns defaults to ascending. In the list, a prefixed hyphen
in front of a column number character flags descending sort for that column. Note that column 0 - the first column - can also use the hyphen flag character. This works because the "0" (zero) character is a text string. In the list, place the column indices themselves in the record sort order the business rules and / or the user wants to see. At line 203 of CSharp_Demo_App.cs in the sample application, ColumnSorter sorts by column two ascending, then by column one descending. When the ColumnSorter class creates / initializes a new ColumnSorter object, the class constructor code of lines 13 to 22 place the column index array in class property
as a string array.

Back at CSharp_Demo_App.cs, line 205 calls the Array class static method Sort, with
as arguments. The Array class Sort static method
accepts column_comparer as an argument because one of the overloads will accept an IComparer interface. The ColumnSorter class inherits from the IComparer interface, so it will work here.

In C# / / etc., the IComparer interface provides a way to compare two objects, returning the result of that comparison. IComparer offers huge flexibility, because we must only place the comparison objects in an array, and provide some code that defines exactly how to compare those objects for sorting. Here, the Array.Sort method uses the IComparer.Compare method to make the comparisons for the sorting. The IComparer.Compare method takes two parameters of type Object and returns an integer based on its comparison machinery. Other parameter types will work for the Compare method, but the Object type offers great flexibility. At lines 34 and 35

1. Cast the input parameters
as string arrays
the Compare method casts the Object parameters as string array variables. The line 41 for-loop

2. Loop through the sort columns
in the IComparer 
handles each individual column index item in the sort_Columns array. This loop looks for a leading sign in the sort_Columns array item, maps that sign, if it exists, to variable sortSign, and if necessary, then trims the sign substring to remove the sign character. The col variable has the actual column index value - this will tell the comparison machinery which array column it will compare in the method parameters. The comparison machinery needs to know the data types of the individual object parameter values it will compare, so it does a little type testing on these values at lines 65 to 69.

The if-block of lines 71 to 118

3. Compare numeric values
in IComparer
handles numeric values. We must handle numeric value comparisons separately from string value comparisons because numeric values, as strings, sort very differently from numeric values as numeric values. The line 78 if-block handles descending sorting. In the if-blocks of lines 88 to 96, if the second numeric value exceeds the first numeric value, the block returns 1; if the first numeric value exceeds the second numeric value, it returns -1. Note that the value comparisons at lines 88 to 96 avoid if-then-else. 
If the IComparer compares two equal values of any type, it returns 0, and this case will fall through to the end of the method. All comparison blocks handle equal values this way, to make them more compact. The else block of lines 98 to 117 handles ascending sorting, with appropriate changes.

The if-block of lines 119 to 166

4. Compare date values
in IComparer
handles datetime comparisons in a similar way. The if-block of lines 168 to 219

5. Compare other data
types in IComparer
handles comparisons of all other types. At line 224, the method returns 0 as a default value

6. Return 0 (zero) for defined
equivalent values
of any type
to cover comparisons of two equal values.

To generalize this approach for all parameter structures / types, start with ColumnSorter.cs and in the parameter objects, figure out how to drill down into the object components that drive the sorting business rules. This will extract the data components on which the sort comparison will operate. Define the actual sort criteria as shown in lines 88 to 96, extending out to lines 78 to 117 to cover ascending and descending sorts. To cover more input parameter types, add more blocks similar to lines 78 to 117. Then, pass the objects into the IComparer as shown at line 24, encapsulating everything as shown in ColumnSorter.cs itself.

Part 3 here showed how the ColumnSorter class works.the overall sample C# application engineering works. Part 4 will describe how form textForm1 shows the assembled, sorted text.