Click here to Skip to main content
15,886,137 members
Articles / Programming Languages / C#
Article

Passing parameters to predicates

Rate me:
Please Sign up or sign in to vote.
4.74/5 (36 votes)
1 Aug 20062 min read 111.1K   43   12
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.
C#
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.
C#
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


Written By
Software Developer (Senior)
Canada Canada
This member has not yet provided a Biography. Assume it's interesting and varied, and probably something to do with programming.

Comments and Discussions

 
GeneralMy vote of 4 Pin
Ricky Owen31-Jan-11 0:21
Ricky Owen31-Jan-11 0:21 
GeneralVery neat Pin
Peter Karaulshchykov9-Feb-10 5:40
Peter Karaulshchykov9-Feb-10 5:40 
GeneralNice Pin
mikeperetz14-Mar-09 9:01
mikeperetz14-Mar-09 9:01 
GeneralSimplier than this Pin
Normand1-Aug-08 5:34
Normand1-Aug-08 5:34 
GeneralRe: Simplier than this Pin
openshac21-Dec-09 4:38
openshac21-Dec-09 4:38 
GeneralDoh Pin
WesFromKC8-Aug-06 12:01
WesFromKC8-Aug-06 12:01 
JokeRe: Doh Pin
Andreas Saurwein29-May-08 7:46
Andreas Saurwein29-May-08 7:46 
GeneralSuggestions Pin
Steve Hansen31-Jul-06 22:42
Steve Hansen31-Jul-06 22:42 
The .net 2.0 string class has a EndsWith method, if you want to ignore case (like you do now you could use ToLower first)
I find this the perfect example of where to use the new anonymous delegates.

<br />
public class DinoClassification<br />
{<br />
    public void Classify()<br />
        {<br />
            List<string> dinosaurs = new List<string>(new string[] {<br />
            "Compsognathus", "Amargasaurus", "Oviraptor", "Velociraptor",<br />
            "Deinonychus", "Dilophosaurus", "Gallimimus", "Triceratops"});<br />
<br />
            Console.WriteLine("\nFind(EndsWith): {0}",<br />
                dinosaurs.Find(delegate(string dino)<br />
            {<br />
                return dino.EndsWith("saurus");<br />
            }));<br />
            Console.WriteLine("\nFind(EndsWith): {0}",<br />
                dinosaurs.Find(delegate(string dino)<br />
            {<br />
                return dino.EndsWith("tor");<br />
            }));<br />
            Console.WriteLine("\nFind(EndsWith): {0}",<br />
                dinosaurs.Find(delegate(string dino)<br />
            {<br />
                return dino.EndsWith("hus");<br />
            }));<br />
        }<br />
}

GeneralRe: Suggestions Pin
Alex Perepletov2-Aug-06 15:56
Alex Perepletov2-Aug-06 15:56 
GeneralRe: Suggestions Pin
Steve Hansen2-Aug-06 19:52
Steve Hansen2-Aug-06 19:52 
GeneralRe: Suggestions Pin
shivashankarp24-Dec-06 19:33
shivashankarp24-Dec-06 19:33 
GeneralRe: Suggestions Pin
Richard Deeming10-Aug-06 8:49
mveRichard Deeming10-Aug-06 8:49 

General General    News News    Suggestion Suggestion    Question Question    Bug Bug    Answer Answer    Joke Joke    Praise Praise    Rant Rant    Admin Admin   

Use Ctrl+Left/Right to switch messages, Ctrl+Up/Down to switch threads, Ctrl+Shift+Left/Right to switch pages.