Lambdas Using Funcs and Actions

// February 12th, 2008 // C#

Many of you are probably familiar with lambda expressions by now, as they have been extensively covered since the release of .Net 3.5. Most articles tend to focus on how to pass a lambda expression into a method, but how do you create a method that accepts a lambda expression? Enter the Func and Action types…

Funcs vs. Actions

Funcs and Actions are extremely similar; both give you first class support for passing an anonymous method into another method. The difference is in the return value: Funcs have one, Actions do not.

A Simple Action Consumer

There are several variants of the Action type, each taking a varying number of generic parameters. The simplest generic Action takes one generic parameters: the type of the object to be passed into the Action.

Suppose you had a list of articles and wanted to iterate through them and modify each of them in some way, but only the ones that were created before 2008. In this case you should probably use a foreach loop, but instead I’ll show you how to do it by encapsulating that foreach loop in another method that then calls your lambda:

   1: public void IterateArticles( Action<Article> process, List<Article> articles )
   2: {
   3:     foreach( Article article in articles )
   4:         if( article.Created.Year < 2008 )
   5:             process(article);
   6: }
   7:
   8: public void ProcessArticles()
   9: {
  10:     List<Article> articles = GetArticles();
  11:     IterateArticles( x=>{
  12:
  13:         x.Title = x.Title.Trim();
  14:         x.Category = x.Category.Trim();
  15:         x.LastModified = DateTime.Now;
  16:
  17:     }, articles );
  18: }

I’ll be the first to admit it isn’t the finest example of coding practices, but I believe it illustrates how you can use an Action to pass processing in as a parameter.

A Simple Func Consumer

What if you want to pass in your processing and retrieve a return value? Use the Func type. Let’s keep the same example as above, but this time we’ll place variable processing in the lambda to manipulate the article’s title differently based upon the year the article was published:

   1: public void IterateArticles( Func<Article,string> process, List<Article> articles )
   2: {
   3:     foreach( Article article in articles )
   4:         article.Title = process(article);
   5: }
   6:
   7: public void ProcessArticles()
   8: {
   9:     List<Article> articles = GetArticles();
  10:     IterateArticles( x=>{
  11:
  12:         if( x.Created.Year < 2008 )
  13:             return x.Title.ToLower();
  14:         else
  15:             return x.Title.ToUpper();
  16:
  17:     }, articles );
  18: }

In the above example you’ll see that we have changed Action to Func in the IterateArticles method signature and that we have added a string to the list of types in the Func definition. When using a Func, the last generic parameter is always the return type. You should also note that we can now return a value from our anonymous method.

A Two-Parameter Func

Finally let’s take a look at an example of a multi-parameter Func. Once again we’ll use the same example, except this time rather than passing the Article object to the anonymous method we’ll pass only the data it needs: the date created and the title:

   1: public void IterateArticles( Func<DateTime,string,string> process, List<Article> articles )
   2: {
   3:     foreach( Article article in articles )
   4:         article.Title = process( article.Created, article.Title );
   5: }
   6:
   7: public void ProcessArticles()
   8: {
   9:     List<Article> articles = GetArticles();
  10:     IterateArticles( (x,y)=>{
  11:
  12:         if( x.Year < 2008 )
  13:             return y.ToLower();
  14:         else
  15:             return y.ToUpper();
  16:
  17:     }, articles );
  18: }

Here we have specified three generic parameters for the Func; two to be passed in, one to return. In our lambda expression we have also had to wrap the “x,y” in parentheses to prevent the compiler from throwing an error because it thinks the comma is intended to separate parameters to the IterateArticles method rather than the Func.

A Zero-Parameter Func

Occasionally you may need to use a Func that will not require any parameters. Trying to pass this into the lambda consumer using only the “{…}” notation of anonymous methods will not work, you will need to use “()=>{…}” as seen below:

   1: public void IterateArticles( Func<string> process, List<Article> articles )
   2: {
   3:     foreach( Article article in articles )
   4:         article.Title = process();
   5: }
   6: public void ProcessArticles()
   7: {
   8:     List<Article> articles = GetArticles();
   9:     IterateArticles( ()=>{
  10:
  11:         return "Published on " + DateTime.Now.ToString();
  12:
  13:     }, articles );
  14: }

I hope this helps get you started creating your own lambda consuming methods. Keep in mind that Funcs and Actions are not limited to just one or two parameters as shown in this article; both can accept up to four input parameters (not including the Func’s extra return parameter). As you become more comfortable creating lambda expressions and lambda consumers I am certain you will find many places in our your code that can be simplified and/or made more flexible by using them.

Kick It on DotNetKicks.comShout It on DotNetShoutOuts.com

10 Responses to “Lambdas Using Funcs and Actions”

  1. Troy Goode says:

    Updated to include the zero-parameter Func scenario after I had a bit of trouble figuring it out myself a few minutes ago. :-)

  2. tgmdbm says:

    Nice atricle. didn’t know about the zero-parameter Func syntax.

    I know this isn’t about LINQ but i don’t know why it doesn’t have Each(Action). I wrote my own and haven’t used [i]foreach[/i] since.

    articles
    .Where( a => a.Created.Year < 2008 )
    .Each( process );

  3. Troy Goode says:

    LINQ does actually provide that mechanism as an extension method to the List type, but it is named ForEach. To use your example you would have to do:

    articles
    .Where( a=> a.Created.Year < 2008 )
    .ToList()
    .ForEach( process );

    Why does it have to be converted to a List before the ForEach method can be called? Because before then the LINQ statement exists as an expression tree and has not actually been evaluated.

  4. Troy Goode says:

    Actually, a correction to my previous statement. The above code is correct, but the expression tree comment only pertains to Linq-To-Sql queries.

    Because we were working with an already existing List the Where(…) method actually returns a type of IEnumerable. Unfortunately the ForEach extension method is implemented on List and not IEnumerable (a grave oversight in my opinion) and thus you have to first convert IEnumerable to List first.

  5. tgmdbm says:

    I know that List has a ForEach method. that’s not the point. [i]Also it’s not an extension method.[/i]

    Why does it have to be converted to a List before the [b]Each[/b] method can be called? [b]It doesn’t![/b] Because the result of a LINQ query is always enumerable.

  6. Troy Goode says:

    Thanks for clarifying that the ForEach method is intrinsic to the List class and not an extension method. I wasn’t familiar with the method before 3.5 so I incorrectly assumed it had been added as an extension method (when in fact it had been there all along). I should have noticed that it didn’t have the extension method icon in Intellisense, oh well.

    You are also correct that results returned from LINQ are always enumerable. Linq-To-Objects returns results of IEnumerable while Linq-To-Sql returns results of IQueryable. The difference? IQueryable maintains the expression tree and, to the best of my knowledge, does not execute your SQL statement until a call is made to a concrete instance of its contained results (which a foreach iteration would do).

    Example:
    var x = from u in context.Users select u;
    x = x.Where( u=> u.UserName != “” );
    x.ToList().ForEach( u=> blah(u) );

    The above code would execute the following SQL statement (assuming only the UserName column exists in table Users):

    SELECT UserName FROM Users
    WHERE UserName <> ”

    Now take the example of iterating the elements between the first statement and second statement:

    Example:
    var x = from u in context.Users select u;
    foreach( User u in x ) blah(u);
    x = x.Where( u=> u.UserName != ” );

    In this case the SQL statement that would be generated is just:

    SELECT UserName FROM Users

    After that statement you have begun your iteration (via a foreach, or your custom extension method) which has kicked off the SQL statement to give you your data to iterate, after which it is all LINQ-To-Objects.

    Is this a problem? Not necessarily. But for beginning LINQ users I think it would be non-obvious. Calling .ToList() makes your conversion from IQueryable and its expression tree to IEnumerable and its concrete data explicit.

    That being said:

    (a) I believe it should be possible to create a ForEach implementation for IQueryable that delays execution of the passed delegate by placing it into the expression tree — but I don’t know enough to say for certain.

    (b) I feel it is rather silly that only List got the ForEach method. Dictionary didn’t get it, even though LINQ provides us with a .ToDictionary method as well. It still seems to me that ForEach should be implemented as an extension method on IEnumerable (but not necessarily IQueryable).

  7. Troy Goode says:

    As soon as I posted that I realized that you cannot implement an extension method on IEnumerable without it also being implemented on IQueryable because IQueryable implements IEnumerable.

  8. BEM says:

    Like tgmbm, I created my own ForEach extension method that works on plain IEnumerable objects. It’s quite simple and extremely useful.

    public static void myForEach(this IEnumerable items, Action action)
    {
    Debug.Assert(action != null)
    foreach (T item in items)
    action(item);
    }

  9. tgmdbm says:

    Exactly right, Troy, as soon as you start to iterate over the IQueriable it effectively becomes an IEnumerable. The Each method will make that happen and that’s just something that the programmer needs to e aware of. A nice corollary would be to overload Each for IQueriable and change the comment to reflect that.

    I actually have 2 overloads of Each, one which takes Action and the other which takes Action where the int is the zero based index of the current iteration, quite useful in certain situations.

    members
    .OrderBy( m => m.Points )
    .Each( (m, i) => m.Rank = i + 1 )

  10. Troy Goode says:

    That overload of your Each method that passes the index to the count is brilliant. I think I may steal it. =)

    I’m going to try investigating whether it is possible to create ForEach/Each extension method for the IQueryable interface that works properly with the expression tree when I have some free time. Once question that brings up that I am unsure of is what happens when you have two extension methods with the same name that apply to an object? Will it correctly use the extension method applied to the closest type match?