In this post I am going to introduce you to the Action<T> generic delegate and how you can use it with the ForEach(…) method that is exposed on collection classes. In earlier posts on the Comparison<T> and Predicate<T> I showed how these new features could be put to good use to make your code more expressive. In this instance however I feel that the Action<T> delegate and the ForEach method are really only “sometimes code” – if you can refute that then please leave a comment – I’d love to hear about your use case.

For your reference, the Action<T> delegate is defined as:

public delegate void Action<T>(T obj);

We then pass an instance of this delegate into the ForEach(…) method of a generic collection class (like List<T>). What happens then is that each element in the collection is enumerated and passed as the argument to the delegate. So if you combined anonymous methods with this feature you would end up with the following code.

ForEach

To me, that code doesn’t appear to be any more readable than the good old fashioned foreach(…) construct that you find in the VB.NET and C# languages (among others). So maybe the scenario I’ve chosen is wrong? Well, it turns out that the ForEach(…) method might be useful for those smaller inline pieces of logic, like printing something out to the screen.

ForEachSmall

I guess I’m still not convinced, but I would love to hear the opinion of other people. Anyway – you can download the code for this sample over at Project Distributor.

8 Responses to “Action&lt;T&gt; and ForEach(…)”

  1. David Smith Says:

    Nice reference to the cookie monster’s eating habits!

    So what’s the difference between a foreach loop, and the List.ForEach(<T>)?

    I think it’s like the differnece between a while loop, and a for loop. They can do the same things, but they do it in a bit different ways.

    List.ForEach(<T>) Method:

    http://msdn2.microsoft.com/library/zecdkyw2.aspx

    Delegate Action<T>:

    http://msdn2.microsoft.com/library/018hxwa8(en-us,vs.80).aspx

    My conclusion (without having used it extensively): Yes, “sometimes” code.

    I get the feeling we’ll see better examples of when this implementation makes things much easier. Time will tell. =)

  2. Frans Bouma Says:

    Think in framework code. Say you have a collection of entities, and you want to validate them all and you have different validation routines (for example per context one validation routine). You then can write generic code which gets a validation method plugged in via an action delegate which is then used over a foreach.

    That’s less code than writing the foreach loop, specify the type etc.

  3. Luke Says:

    Frank beat me to the punch. Doh! :)

    I’m wondering if there’s also some sneaky way to squeeze in some polymorphism or inheritence into the mix as well. :)

  4. Mitch Denny Says:

    Hi David, Frans and Luke,

    Yes – the framework angle did occur to me which is where the “sometimes” came from. But can’t the same be achieved with a simple method call containing a foreach loop?

    I’m not really disputing what you say – its just food for thought. Sometimes code indeed.


  5. Hi Mitch,

    I’d argue that you’re confusing the usefulness of the Action<T> delegate with the usefulness of an anonymous instance of it.

    Wishing I hadn’t been stalling on hitting the download until the rush died off right now so I could try some of the code ideas rushing into my head.

    The thing is, generic lists are on one hand a “good thing” because they save you lots of “generated strongly typed lists” – on the other hand, they’re going to promote a lot of lazy, squiggly code.

    The instant you stick your classic foreach loop in you’ve added unstructured/anonymous behaviour to your generic list – and you know very well if it had been a class implementation of the list you probably would have at least added that foreach loop as a method on the class in the absence of a framework.

    The Action<T> delegate instantly gives you a very convenient visitor implementation on your “free” strongly typed list. Adding a little background work to it might leave you with something like this:

    // WARNING: Pseudocode

    DriverCommand command = new DriverCommand( new ImproveRatingForNoCrashesCommand(), new IssueCongratulationLetterCommand() );

    drivers.ForEach( command.Action );

    Hooking this up with a builder/factory and making good use of generics in your composite class structure gives you a huge set of possibilities.

    Definitely motivated to get my download cranking now :)

  6. Mike Says:

    The argument is;

    In your examples, you are just working with generic List objects.

    In the real world, you would never directly expose your collections to calling code. However, you may want to provide a user a way to iterate through a collection inside an object.

    Publishing a getting property is poor object orientation.

    Instead, provide a method which can be passed a delegate which will be executed on each of your internal collections methods.

    For example…

    class Driver

    {

    public void PrintOn(TextWriter writer)

    {

    writer.WriteLine(this);

    }

    }

    class Race

    {

    List<Driver> drivers;

    public void DriversDo(Action<Driver> block)

    {

    drivers.ForEach(block);

    }

    }

    static void Main(string[] args)

    {

    Race race = new Race();

    race.DriversDo(delegate(Driver driver) {

    driver.PrintOn(Console.Out); });

    }

    Poor example, but it basically allows you to enable calling code to execute over individual entries, without exposing the collection.

    Which could have been done in 1.1, only witha shitload more code.

  7. sonnyM Says:

    hmm, can anyone explain which foor each loop would be faster, traditional or via nwe generic method.
    cheers
    Sonny M

  8. Mitch Denny Says:

    Hi Sonny

    It has got more to do with what type of list you are trying to enumerate and how you determine the length. I would try to optimist in other areas before trying to optimise based on what loop construct to use.


Leave a Reply