Chain delegates sample

In my post about "best code ever" I mentioned a way to chain conditions into a single Predicate<T>.
 
Let’s say you need to match a collection of items to number of specified "templates", following the rule:
 if all specified fields of any given template match the fields of an item – include the item in the result set
 
You could express it statically with a complicated bunch of if-statements or you could use functional programming.
This small program in C# 2.0 demonstrates how to do that:

class Item

{

    public int? Id;

    public string Notes;

    public string Text;

    public int? Number;

 

    public Item() { }

    public Item(int? id, int? number, string notes, string text)

    {

        Id = id;

        Number = number;

        Notes = notes;

        Text = text;

    }

}

 

class Program

{

    static Predicate<T> AndChain<T>(IEnumerable<Predicate<T>> unchainedPredicates)

    {

        Predicate<T> predicates = delegate(T item) { return true; };

        foreach (Predicate<T> predicate in unchainedPredicates)

        {

            // local copy insures the current predicate instead of the last one will make it into the anonymous delegate

            Predicate<T> p = predicate;

            Predicate<T> chain = predicates;

            predicates = delegate(T item) { return p( item ) && chain( item ); };

        }

        return predicates;

    }

 

    static Predicate<T> OrChain<T>(IEnumerable<Predicate<T>> unchainedPredicates)

    {

        Predicate<T> predicates = delegate(T item) { return false; };

        foreach (Predicate<T> predicate in unchainedPredicates)

        {

            // local copy insures the current predicate instead of the last one will make it into the anonymous delegate

            Predicate<T> p = predicate;

            Predicate<T> chain = predicates;

            predicates = delegate(T item) { return p( item ) || chain( item ); };

        }

        return predicates;

    }

 

    static void Main(string[] args)

    {

        List<Item> items = new List<Item>(

        new Item[]

        {

            new Item(1, 2, "note1","text1"),

            new Item(2, 1, "note2","text2"),

            new Item(3, 2, "note3",""),

            new Item(4, 2, "note4","text4"),

        } );

 

        Item[] templates = new Item[]

        {

            new Item( 1, null, null, null ),

            new Item( null, null, "note2", null ),

            new Item( null, 2, null, "text" )

        };

 

        foreach (Item item in items.FindAll( BuildPredicate( templates ) ))

        {

            Console.WriteLine( "{0}: {1},{2},{3}", item.Id, item.Number, item.Notes, item.Text );

        }

    }

 

    private static Predicate<Item> BuildPredicate(IEnumerable<Item> templates)

    {

        List<Predicate<Item>> predicates = new List<Predicate<Item>>();

        foreach (Item template in templates)

        {

            Item local = template;

            Predicate<Item>[] templatePredicates = new Predicate<Item>[]

            {

                delegate(Item item)

                { return !local.Id.HasValue || item.Id == local.Id; },

                delegate(Item item)

                { return string.IsNullOrEmpty(local.Notes) || item.Notes == local.Notes; },

                delegate(Item item)

                { return string.IsNullOrEmpty(local.Text) || (!string.IsNullOrEmpty(item.Text) && item.Text.Contains(local.Text)); },

                delegate(Item item)

                { return !local.Number.HasValue || item.Number == local.Number; },

            };

            predicates.Add( AndChain( templatePredicates ) );

        }

        return OrChain( predicates );

    }

}

 

The results of this program are:
1: 2,note1,text1
2: 1,note2,text2
4: 2,note4,text4
 
While this may look incomprehensible to an untrained eye, this code is actually very simple. There’s bunch of collections being declared and evaluated here and there. It is simple in term of static structure. At runtime it could become quite complex and may be difficult to debug using .NET IDE, but chances are you won’t have to.
 
There’s one problem that you might have in real life applying this – not all fields would be nullable. The good news is the concept of template would have some means to specify if particular field has a value that needs matching. In my case I was dealing with a web-service "boundry" class generated from WSDL that has "Specified" for all value-type fields. 
 
Also, instead of Predicate<Item> itself one could use another class that wraps the predicate and separates the applicability flag (i.e. local.Number.HasValue in the sample code) from the actual item evaluation. This allows for even finer-grained rules and flexibility in their application. That’s the approach I used, but I didn’t put it here for clarity.
 
Hopefully this illustrates that once your code "becomes data" you can manipulate it and it gets much easier to express complex concepts and change them every which way.
Advertisements
Chain delegates sample

Leave a Reply

Fill in your details below or click an icon to log in:

WordPress.com Logo

You are commenting using your WordPress.com account. Log Out /  Change )

Google photo

You are commenting using your Google account. Log Out /  Change )

Twitter picture

You are commenting using your Twitter account. Log Out /  Change )

Facebook photo

You are commenting using your Facebook account. Log Out /  Change )

Connecting to %s