Click here to Skip to main content
65,938 articles
CodeProject is changing. Read more.
Articles
(untagged)

Passing parameters to predicates

0.00/5 (No votes)
1 Aug 2006 1  
Simple and thread-safe way to reuse predicates

Introduction

Inability to pass parameters to predicates often made me consider writing search functions for custom collections. One day I figured enough is enough. In this example I will demonstrate the workaround to passing parameters to predicates, and reasoning involved. There is no download - the code is tiny. I will use MSDN�s example code for List.TrueForAll as the starting point.

Intermediate step

What if we wanted to reuse the predicate to search not only for dinosaur names that end on �saurus�, but also for those that end on �tor�? Shouldn�t it be easily done?

I only kept the minimum required code from MSDN example. To approximate a more common situation, the code is moved into a class DinoClassification, which hypothetically classifies dinosaurs. The predicate and the variable that it uses are made non-static and also moved into that class.
using System;
using System.Collections.Generic;


public class DinoClassification
{
    private string m_Suffix;

    public void Classify()
    {
        List<string> dinosaurs = new List<string>(new string[] {
            "Compsognathus", "Amargasaurus", "Oviraptor", "Velociraptor",
            "Deinonychus", "Dilophosaurus", "Gallimimus", "Triceratops"});

        m_Suffix = "saurus";
        Console.WriteLine("\nFind(EndsWith {0}): {1}", m_Suffix, 
            dinosaurs.Find(EndsWith));
        m_Suffix = "tor";
        Console.WriteLine("\nFind(EndsWith {0}): {1}", m_Suffix, 
            dinosaurs.Find(EndsWith));
    }

    // Search predicate returns true if a string ends in correct suffix.

    private bool EndsWith(String s)
    {
        if ((s.Length >= m_Suffix.Length) &&
            (s.Substring(s.Length - m_Suffix.Length).ToLower() == 
                    m_Suffix.ToLower()))
        {
            return true;
        }
        else
        {
            return false;
        }
    }
}

public class Example
{
    public static void Main()
    {
        DinoClassification dcl = new DinoClassification();
        dcl.Classify();
    }
}

This works in a single-threaded environment. In multi-threaded application we could have m_Suffix written to from different threads, which would cause havoc. To make this code thread-safe one would need to have the search inside a critical section. This is out of question. Having iteration over a collection of arbitrary size in a critical section is not an option.

There�s another fact that I personally dislike about this implementation. DinoClassification is a dinosaur classification class. It has nothing to do with matching strings that make up a dinosaur name. Member variable m_Suffix and predicate EndsWith do not belong in DinoClassification class.

So, what I really want is:

  • Maintain class' thread safety with no effort on my part.
  • Move the predicate out of my class.
  • Keep it all as simple as possible.

Solution

Why not do just that? Wrap a variable and a predicate together and tuck away into a separate class.
using System;
using System.Collections.Generic;


public class EndsWith
{
    private string m_Suffix;

    // Initializes with suffix we want to match.

    public EndsWith(string Suffix)
    {
        m_Suffix = Suffix;
    }

    // Sets a different suffix to match.

    public string Suffix
    {
        get { return m_Suffix; }
        set { m_Suffix = value; }
    }
    
    // Gets the predicate.  Now it's possible to re-use this predicate with 

    // various suffixes.

    public Predicate<string> Match
    {
        get { return IsMatch; }
    }

    private bool IsMatch(string s)
    {
        if ((s.Length >= m_Suffix.Length) &&
            (s.Substring(s.Length - m_Suffix.Length).ToLower()
                                                 == m_Suffix.ToLower()))
        {
            return true;
        }
        else
        {
            return false;
        }
    }
}


public class DinoClassification
{
    public void Classify()
    {
        List<string> dinosaurs = new List<string>(new string[] {
            "Compsognathus", "Amargasaurus", "Oviraptor", "Velociraptor",
            "Deinonychus", "Dilophosaurus", "Gallimimus", "Triceratops"});

        Console.WriteLine("\nFind(EndsWith): {0}", 
            dinosaurs.Find(new EndsWith("saurus").Match));
        EndsWith predicate = new EndsWith("tor");
        Console.WriteLine("\nFind(EndsWith): {0}", 
            dinosaurs.Find(predicate.Match));
        predicate.Suffix = "hus";
        Console.WriteLine("\nFind(EndsWith): {0}", 
            dinosaurs.Find(predicate.Match));
    }
}



public class Example
{
    public static void Main()
    {
        DinoClassification dcl = new DinoClassification();
        dcl.Classify();
    }
}

EndsWith is a class that wraps the predicate and the variable. The variable can be set either through constructor parameter or Suffix property. Class has two properties. Property Suffix allows setting a new value for predicate to match. The Match property is of type predicate<string>. It returns the �parameterized� predicate used in Find(), Exists(), etc.

What was achieved in this example:

  • DinoClassification is thread-safe as long as there is no member variable of type EndsWith.
  • DinoClassification is not cluttered with code pieces that belong to utility layer.
  • Predicate parameters are passed to constructor and may later be modified through properties of the wrapper class.

License

This article has no explicit license attached to it but may contain usage terms in the article text or the download files themselves. If in doubt please contact the author via the discussion board below.

A list of licenses authors might use can be found here