Amirthalingam covers the subject in detail, but I thought I'd share my reference implementation (and put it up somewhere that I can find it when I forget how to do this!). Purple elipses ( ... ) indicate where the finished code continues.
We'll implement a simple C# console application to start with to act as the framework for our reference implementation.
using System;Next, let's define a "complex" class against a List
using System.Collections.Generic;
using System.Text;
namespace List_Find_Predicate_Test
{
class Program
{
static void Main(string[] args)
{
}
}
}
...You'll notice I've spiced things up with a custom enumeration used within the Criteria class. Next, let's add some code to the Main method to build us a list of criteria objects:
class Program
{
private enum Gender
{
Male,
Female
}
private class Criteria
{
public string Field;
public Type DataType;
public Criteria(string theField, Type theDataType)
{
this.Field = theField;
this.DataType = theDataType;
}
}
...
}
...
Simple enough so far. The complexity happens in providing a mechanism to let the List... ...
static void Main(string[] args)
{
List<Criteria> critList = new List<Criteria>();
critList.Add(new Criteria("Name", typeof(System.String)));
critList.Add(new Criteria("Age", typeof(System.Int16)));
critList.Add(new Criteria("Gender", typeof(Gender)));
Console.WriteLine("List has {0} entries...", critList.Count);
// TODO: Search the list
...
}
Let's add a quick search of the List, and get the type assigned to the "Name" criteria:
...OK - that was easy enough - just your common-or-garden-variety top-down programming so far. But now we get to the meat of the issue - the implementation of the FindCriteriaByName method - and this is where Predicates are used.
static void Main(string[] args)
{
...
// Search the list
Criteria crit = FindCriteriaByName(critList, "Name");
Console.WriteLine("Name criteria has type {0}", crit.DataType.Name);
// TODO: Search without having to use a helper method
...
}
// TODO: Implement FindCriteriaByName
...
The key to Predicates is that they reference a class - in this case a class that is dedicated to filtering a List
You'll notice that the critical FilterByField method accepts a Criteria object as its parameter - when we call this class through a Predicate, this is the method that will be used to perform the filtering. So we can now finally implement the filter method:... private class CriteriaFieldFilter
class Program
{
...
{
private string criteriaField;
public CriteriaFieldFilter(string fieldName)
{
criteriaField = fieldName;
}
public bool FilterByField(Criteria crit)
{
return (crit.Field == criteriaField);
}
}
...
}
...
...So our filtermethod starts by instantiating an instance of our filter class tailored with the field name passed in the theName parameter. The Predicate is then created against a pointer to the FilterByField method of that instance, and in turn that Predicate is used with the List
class Program
{
...
// Implement FindCriteriaByName
private static Criteria FindCriteriaByName(List<Criteria> theList, string theName)
{
CriteriaFieldFilter searchFilter = new CriteriaFieldFilter(theName);
Predicate<Criteria> searchPredicate = new Predicate<Criteria>(searchFilter.FilterByField);
Criteria crit = theList.Find(searchPredicate);
return crit;
}
...
}
...
But is there a more compact way rather than having to implement the helper method at all? Of course there is! We just compact the code into an in-line function, thus:
...Rather harder on the eye, but more compact. I'd imagine that something could be done with Anonymous classes/methods to simplify this further, but that's a topic of research for another time.
static void Main(string[] args)
{
...
// Search without having to use a helper method
crit = critList.Find(new Predicate<Criteria>((new CriteriaFieldFilter("Gender")).FilterByField));
Console.WriteLine("Gender criteria has type {0}", crit.DataType.Name);
...
}
...
Phew! A complex topic for just performing a Find in a List