May 5, 2009

Basics: Adding Lambda Support To Your Classes

I talked recently about Linq and Extension methods and showed how you could refactor For Each loops into simple linq statements.  When using Linq one of the things you will often come across are Lambda functions (anonymous methods declared inline).

If you’re a curious type, then you might ask yourself “How can I make it so my classes can use Lambda functions?”.  If that’s you then read on :-)

In this scenario I’m going to try and do a few things.  I want to create a simple class that has a collection of strings in it and I want to have a method in that class to print those strings.  But here’s the catch, I don’t want to actually code up that printing method itself in my class.  It doesn’t really belong there and instead  I want someone else to handle the printing – I just want to send them the strings in the class and let them worry about it.

Now I could do it using Dependency Injection techniques, but that’s a different discussion.  Here and now, I want to have someone supply me a function and I’ll just call it.

Test First

Let’s start by creating a test for it first (forgive me for not putting any asserts in the code).  Here’s what it looks like

[TestMethod]
public void TestMethod()
{
StringHolder stringHolder = new StringHolder();
stringHolder.Strings.Add("string 1");
stringHolder.Strings.Add("string number 2");
stringHolder.PrintUsing(Console.WriteLine);
stringHolder.PrintUsing(p => Console.WriteLine(p.ToUpper()));
}

Nothing too complex.  Add some strings and call the method named PrintUsing.  But have a look at those two PrintUsing statements.  What do you see?


The first one says we want to do the print using Console.Writeline.  That’s easy enough to read, but what about the second one?  The second one has one of those funny => operators – it’s a lambda function.  A method defined in line.


What this method is saying is that for each string to be printed, pass it in as a parameter named “p” to a method that has no name (it’s declared inline so needs no name).  That method will call Console.WriteLine, passing to it the string once it has been converted to Uppercase.  Pretty cool.


The StringHolder Class and Action<T>


So let’s implement the class

public class StringHolder
{
public StringHolder()
{
Strings = new List<string>();
}

public List<string> Strings { get; private set; }

public void PrintUsing(Action<string> action)
{
foreach (var part in Strings)
{
action(part);
}
}
}

Here we see something interesting.  Have a look at the parameter for the PrintUsing method – it is looking for an Action<string>.  An Action<T> is a .NET type for a lambda function that returns a void.  The <T> type is the parameter to pass to the method.  If we used Action<string,int> we would be wanting two parameters; a string and an integer.


What we also see is that inside the method we simply call the action passed in as if it were a normal method, including the passing of the parameters.  Quite elegant and simple.


Functions vs Actions


So how about we now expand things a little and add a few extra lines to the end of our test as so

var averageLength = stringHolder.CustomFunction(l => l.Sum(p => p.Length)/l.Count);
Console.WriteLine("Average length: " + averageLength);

We now want to allow people to supply a custom function so that they can apply if to our entire list.  So instead of giving them one string at a time, we’re going to send them the whole string list and they can manipulate it as they choose.


In this case we’re going to work out the average length of the strings in the list.  To do it we add up the length of all the strings using Linq’s Sum method, and then divide it by the number of strings in the list.


Now what do we have to add to our class in order to support this?  As it turns out, we have to add just 3 lines:

public T CustomFunction<T>(Func<List<string>,T> function)
{
return function(Strings);
}

Notice the Func<List<string>,T> type on the parameter. It’s just like the Action<T> we looked at earlier, but with a slight difference. We now have two types being listed.  The parameter types and the return type.  The return type is always the last type in the type list.


Now that it’s declared as a parameter we can call function() just like any other method and then do something with it’s return value.  In our case, we’ll just return it.  I should also point out that we’re using generics here – the type <T> after the CustomFunction method name says that T is a generic type.  In other words the function we pass in can actually return any type it likes, and we’ll pass that back to the calling code.


Default Actions and Overrides


How can any of this be useful? Well, let’s consider our string class and think about showing a total of the number of strings in the class.  I might want a default behaviour, but also allow people to override that behaviour by supplying a different method to use into one of my classes, and I want to be able to do it at run time.


Here’s how it might work.  Let’s start with a test as follows:

[TestMethod]
public void CallAnAction()
{
StringHolder stringHolder = new StringHolder();
stringHolder.MyAction();
}

I simply call the MyAction method on the stringHolder object and it does something.  In this example we’ll get it to write the number of strings out to the console as follows (I know, incredibly complex stuff)

public void MyAction()
{
Console.WriteLine("Number of strings: " + Strings.Count);
}

But I can’t override this behaviour.  I could potentially sublcass it and do an overload on the method (assuming I made it virtual), but that’s not really what I want to do.  As a different approach I can refactor my class slightly and make the MyAction method a property as shown:

public class StringHolder
{
public StringHolder()
{
Strings = new List<string>();
MyAction = () => Console.WriteLine("Number of strings: " + Strings.Count);
}

public Action MyAction { get; set; }

We’ve now made MyAction an Action property and we supply the default method signature in the constructor.  The () at the start of the lambda declaration indicates that there are no parameters for the method.  The good thing is that our test remains unchanged and everything works.


Now we can take this a step further and from our user code supply an alternate behaviour, overriding that defined by the StringHolder class as shown here:

[TestMethod]
public void CallAnAction()
{
StringHolder stringHolder = new StringHolder();
stringHolder.MyAction = () => Console.WriteLine("Alternate " + stringHolder.Strings.Count);
stringHolder.MyAction();
}

We set the property value to be the new lambda function we’ve defined and we’re all set.  This time when we call the MyAction() method the program will use the newly supplied lambda method we’ve supplied and show the alternate output.


Conclusion


What we’ve done is overridden the behaviour of our StringHolder class at runtime avoiding the hassle of subclassing, dependency injection or any other compile time techniques we might use.  It’s an easy and simple way to provide hooks into your classes where you want to have a standard behaviour that can be overridden.

5 comments:

  1. Thanks for useful post.

    I have a question about your last example.
    From point view of OCP, it is true that class will be open for extension and dynamically at runtime but is not closed for modification.

    Is not possible that user of class can change the behavior of MyAction() in a way completely different than my intention? is not that may lead to unknown result?

    ReplyDelete
  2. Great article!

    I have a question. If I want to get a default action with a generic parameter like this:

    [TestMethod]
    public void CallAnAction()
    {
    IList<int> list1 = new List<int>();
    list1.Add(1);
    list1.Add(2);
    IList<string> list2 = new List<string>();
    list2.Add("string 1");
    list2.Add("string 2");

    StaticClass.MyAction<int>(list1);
    StaticClass.MyAction<string>(list2);
    }

    The original implementation without default actions is the following:

    private void MyAction<T>(IList<T> list)
    {
    foreach (var item in list)
    {
    Console.WriteLine(item.ToString());
    }
    }

    How can I define a default action in that case? I've already tried this:

    public Action<IList<T>> MyDefaultAction<T> { get; set; }

    But, seems it doesn't acceptable by compiler.

    ReplyDelete
  3. @waheed - Exposing an Action<T> property is very similar to having an event handler. The consumer gets to decide what the action does but the implementation gets to decide when it is called, with which parameters and what to do with the result (for Func<T> or Predicate<T>). The technique is similar to the Command and Strategy patterns.

    @Oleg - In C# you cannot have a standalone generic property (which is a shame because it would be really handy some days). To achieve a similar result you could make your static class itself generic but be aware that two versions with different generic types are technically separate classes (and will hence have different state). i.e.

    public static class StaticClass<T>
    {
    public Action<IList<T>> MyDefaultAction { get; set; }

    public static void MyAction(IList<T> list)
    {
    MyDefaultAction(list);
    }
    }

    Usage:
    StaticClass<int>.MyDefaultAction = l => Console.WriteLine( "Length: " + l.Count );
    StaticClass<string>.MyDefaultAction = l => Console.WriteLine( "First Item: " + l.First() );
    StaticClass<int>.MyAction(list1);
    StaticCass<string>.MyAction(list2);

    Note that the int and string versions of StaticClass have their own MyDefaultAction.

    ReplyDelete
  4. Who doesn't know this, useless post.

    ReplyDelete
  5. @anonymous Thanks for your, um, not so useful and constructive comment. Maybe you missed the word "Basics" in the title indicating that this was an introductory post?

    ReplyDelete