Bonus: The Baker's Dozen Potpourri: Sorting Custom Lists with Anonymous Methods
I'm still a DataSet guyalthough I admit that the capabilities of the .NET generics List class are powerful, especially when combined with the new anonymous methods in C# 2.0. This Baker's Dozen Potpourri will present some code snippets for sorting and filtering custom list collections.
For example, I have a list of records with
LocationID,
Customer ID, and
Amount Due fields, and I want to filter on Locations 1 and 2, where the
Amount Due is greater than 10000. I also want to sort the results on
Amount Due descending, within
Location. If I use ADO.NET, I can accomplish this with a DataView, as follows:
DataView dv = new DataView(dt);
dv.RowFilter =
"LocationID in (1,2) AND AmountDue > 10000";
dv.Sort = "LocationID, AmountDue DESC";
In the "DataSets vs. custom collections" debate, proponents of DataSets argue that you'd have to write more complicated code to achieve the same functionality with custom collections. (I certainly made that argument prior to Visual Studio 2005.)
However, Visual Studio 2005 provides two new capabilities that I can combine to produce a good counterpart to the ADO.NET snippet above. First, the new List class contains methods to sort and filter (using the
Sort and
FindAll methods). I'll write a custom method for sorting/filtering, and specify the name of the method as a delegate parameter for the
Sort/FindAll method.
Second, C# 2.0 allows developers to use anonymous methods to place logic in place of a delegate. Instead of writing a separate custom method, developers can include code inline where the delegate would otherwise appear. I'll show you some code samples to demonstrate. Instead of a DataSet, I'll take an example of a custom list called
CustomerRec, with properties for
LocationID and
AmountDue.
The code inserts an anonymous method inside the list's
FindAll method to create a filtered list of customers where
LocationID equals 1. Then the code sorts the filtered list on
Amount Due descending.
Note that the delegate parameter for the
Sort method receives two parameters, one for each object instance as part of a sort comparison. The anonymous method code will execute for each item in the list. For each execution, the code compares the two incoming values and uses the .NET
CompareTo method to return the greater of the two values. If the example called for a sort in ascending sequence, the code would compare the first parameter to the second, but since the example calls for a descending sort, the code reverses the use of the parameters.
// anonymous method to filter on Location = 1
List<CustomerRec> oFilteredCustomers =
oCustomerRecs.FindAll(
(delegate(CustomerRec oRec) {
return (oRec.LocationID == 1 );})
);
// anonymous method to sort on amount due DESC
// by reversing the incoming parameters
oFilteredCustomers.Sort(
delegate(CustomerRec oRec1, CustomerRec oRec2)
{ return oRec2.AmountDue.CompareTo
(oRec1.AmountDue); });
Developers can include more complex in-line code, such as combinations of
OR and
AND. The next code sample duplicates the logic from the ADO.NET sample that filters the data on either Location 1 or 2, and
Amount Due greater than 10000.
// anonymous method to filter on
// either Location 1 or 2, AND amount due GT 10000
List<CustomerRec> oFilteredCustomers =
oCustomerRecs.FindAll((delegate(CustomerRec oRec) {
return (
(oRec.LocationID == 1 || oRec.LocationID == 2)
&& oRec.AmountDue > 10000);
}));
Finally, the last code sample shows an anonymous method to sort the filtered list on amount descending within location. The delegate receives two parameters for each incoming comparison: if the locations are equal, the code compares the amount due of the second parameter against the first. If the locations are not equal, the code compares the location ID of the first parameter against the second.
// Now sort on amount due DESC, within Location
// To do so, check the two incoming locations 1st
// If they are equal, reverse the order of two
// incoming parameters, and compare the amount due
// [just like above]
// If they AREN'T equal, compare the two locations
oFilteredCustomers.Sort(
delegate(CustomerRec oRec1, CustomerRec oRec2)
{ return oRec1.LocationID == oRec2.LocationID ?
oRec2.AmountDue.CompareTo(oRec1.AmountDue):
oRec1.LocationID.CompareTo(oRec2.LocationID);
});
So in the end, while the developer must write a little more code, it is now possible using the new List class to implement advanced sorting and filtering functions. In addition, the ability to either implement anonymous methods opens the door to write custom filtering beyond ADO.NET syntax (ADO.NET does not support hooks for custom filtering methods).
You can find the entire source code for this article on
my Web site. For additional information, check out my
blog.
Closing Thoughts
Have you ever submitted something (an article, a paper, some code, etc.) and thought of some good ideas AFTER the fact? Well, I'm the king of thinking of things afterwards. Fortunately, that's the type of thing that makes blogs valuable. Check my blog (www.TheBakersDozen.net) for follow-up tips and notes on Baker's Dozen articles
and maybe a few additional treats!