Go to series index

Magic without delegates

Action type delegates are one of the little wonders brought to .NET from functional programming. They can be defined as a method that has only one parameter (in its simplest overload) and returns no value. They’re usually used to store references to methods or to pass a method as a parameter without having to explicitly declare a delegate. Just define the parameter with the same signature you expect to receive and the magic starts to work.

An important detail we can see when observing the Action signature is that type T is contravariant, so we can use this type in any other derived type.

Learn more about covariance and contravariance in Generics here.

Let’s see some of this magic. Assuming we have a method that accepts a parameter of type Action, we can call the method and pass it (or rather inject) the desired behavior, that is, pass it a method that meets the signature as a parameter:

void test()
{
     string msg = "This is the value...";
     doSomethingWithStringValue(enqueueMessage, msg);
     doSomethingWithStringValue(saveToDatabase, msg);
     doSomethingWithStringValue(writeMessageToConsole, msg);
}

private void doSomethingWithStringValue(Action<string> actionToDo, string value)
{
     //do several things with this value
     validateMessage(value);
     compressMessage(value);
     //when finishing...
     actionToDo(value);
}

private void enqueueMessage(string value)
{
     //do something & enqueue this value
     Queue<string> messages = new Queue<string>();
     messages.Enqueue(value);
}

private void saveToDatabase(string value)
{
     //do something & save to db this value
     addLineToUserLog(value);
}

private void writeMessageToConsole(string value)
{
     //do something & output this value
     Console.WriteLine(value);
}

On one hand, we have three methods that do different things but have the same signature (they all expect a string type parameter). And on the other hand, we have a method that has a parameter of type Action. That is, this parameter accepts as a value any method that has the same signature we’ve declared. This way, we can invoke it several times and in each one we can tell it to use a different method to do something different. Very similar to JavaScript’s asynchronous functions or the Promise pattern.

Pretty cool, right? It’s the same as using delegates but, uhm… wait! Yes, without using them :smile:

Actions everywhere

Well, more and more framework classes are making use of this type of delegate and its sibling Func, which is basically the same but returns a value. Without going too far, LINQ extension methods (Select, Where, OrderBy) use Func and almost all of TPL is based on using Action, from the For and ForEach loops of the static Parallel class, to the explicit creation of tasks through the Task class.

For example, when we want to execute a task asynchronously, we can use the StartNew method of the Task.Factory class. This method has an overload that accepts a parameter of type Action or Func, and best of all, it can be created inline, that is, at the same moment the call is made. Let’s see some examples:

Starting with a simple method:

private void doSomething()
{
    //Pause for 0 to 10 seconds (random)
    Random r = new Random(Guid.NewGuid().GetHashCode());
    Thread.Sleep(r.Next(10000));
}

Since it’s a method that neither receives parameters nor returns anything, we can use its simplest overload:

Task.Factory.StartNew(doSomething);

Another option, if the method had an int parameter to specify the number of seconds (instead of being random) could be this:

private void doSomething(int seconds)
{
    int mseconds = seconds * 1000
    Thread.Sleep(mseconds);
}

Task.Factory.StartNew(() => doSomething(5));

Here we see something more interesting. Something we’ve probably observed many times and used before: A lambda expression. This expression is also something taken from functional programming, and can be read as: “goes to”. On the left side of the expression, input parameters or variables are specified (if they exist, in this case they don’t), and on the right side the expression itself. The previous case is so simple that it has no parameters and we only use the right side of the expression to send the value 5 to the method.

When using a lambda expression, the instructions contained in that expression can span multiple lines, so we can also do something like this:

Task.Factory.StartNew(() =>
{
    int x = 5;
    doSomething(x);
    Console.WriteLine("finished!");
});

Or directly this:

Task.Factory.StartNew(() =>
{
    int x = 5;
    int mseconds = seconds * 1000
    Thread.Sleep(mseconds);
    Console.WriteLine("finished!");
});

In this case, we can even omit the doSomething method and use the code inline directly in the StartNew call. However, a piece of advice: It’s not advisable to abuse inline expressions, so if we have more than 5 or 6 lines, it might be more convenient to refactor this code to not make it too complex and respect good design principles.

Now with parameters

So far when making the call we’ve always used an Action type delegate without parameters, hence the empty parentheses on the left side of the lambda expression. However, we’ll find many cases where we need to pass parameters. Without going too far, the Parallel.For method has an Action type parameter that needs to be passed an int value, which is logical since within a loop it’s very necessary to know the iteration value at all times:

Parallel.For(1, 40, (i) =>
{
    serie.Add(i.Fibonacci());
});

Notice that it’s not necessary to define the data type of variable i because the compiler itself is capable of inferring it, but obviously we can also declare the type before the variable name, as always (int i).

We can pass as many parameters as the Action needs. The same method has another overload that accepts a ParallelLoopState object to be able to cancel the loop:

Parallel.For(1, 40, (i, loopState) =>
{
    serie.Add(i.Fibonacci());
    if (i > 35) loopState.Break();
});

And of course we can create our own actions with as many parameters as necessary. Although like before, if we need to pass more than 3 or 4 parameters to an Action, maybe we should ask ourselves if we’re doing things right:

private void saveToDatabase(string value, bool useDetails)
{
    addLineToUserLog(value);
    if (useDetails) addLineToUserLogDetails();
}

void test()
{
    //Define an action that points to the saveToDatabase method
    Action<string, bool> myAction = (v, s) =>
    {
        saveToDatabase(v, s);
    };

    string value = "This is the value...";
    bool usedetails = true;
    myAction(value, usedetails); //Here the action is called and the method it points to
}

Wrapping up

Action type delegates are very useful for simplifying work with delegates (now that I think about it, it’s been quite a while since I’ve used them, not even for declaring events). They allow us to specify the actions to perform and can have up to 16 parameters - too many in my opinion - and like void methods, they don’t return any value. If we want the same thing but being able to return a result, we should use its sibling Func<T, TResult> which is exactly the same, but in all its overloads (and it has as many as Action) the last argument represents the return value.

Go to series index

Parallel Series 08

Author

Lluis Franco

Publish Date

07 - 01 - 2013