13

What I want to do is combine lambda syntax with "params" to carry out an action on a series of object.

Let's say I want to make a bunch of controls invisible.

After a bit of fiddling I ended up with an extension method:

public static void On<T>(this Action<T> actionToCarryOut,params T[] listOfThings)
{
    foreach (var thing in listOfThings)
    {
        actionToCarryOut(thing);
    }
}

and then I can create an action:

Action<Control> makeInvisible = c => c.Visible = false;

and then call it:

makeInvisible.On(control1,control2, control3,control4);

This isn't very nice syntax though - it feels horribly clumsy.

I can create a method "Apply" in my base class:

protected void Apply<T>(Action<T> action, params T[] appliedTo)
{
    foreach (var item in appliedTo)
    {
        action(item);
    }
}

and then call it like this:

Apply<Control>(
    c => c.Visible = false,
    control1,
    control2,
    control3,);

But that means repeating the method in every base class I need it in, and I lose the advantage of type inference.

Is there a less clumsy way of doing this?

Edit: The nicest method I've seen so far is the fluent approach, which (with a couple of tweaks) would allow me to write:

Apply.Method((Control c) => c.Visible = false).To(
    control1, 
    control2, 
    control3, 
    control4};

This is 91 characters, compared to 107 for using a simple "foreach". Which leads me to believe that "foreach" might actually be the best approach after all!

Andrew Ducker
  • 5,308
  • 9
  • 37
  • 48
  • 2
    Given what you are doing, I don't see anything wrong with the first syntax. – mao47 Jul 16 '13 at 13:04
  • There's a nice Jon Skeet solution here: http://stackoverflow.com/questions/823532/apply-function-to-all-elements-of-collection-through-linq#answer-823563 – Michael Anderson Jul 16 '13 at 13:11
  • @Michael Anderson, even Chuck Norris was impressed by that answer – mao47 Jul 16 '13 at 13:15
  • whatever the answer it should be some improvement on `foreach (var control in new[] { control1, control2, control3, control4 }) control.Visible = false;` which works out the box without any extension. – Jodrell Jul 16 '13 at 13:33
  • This `Apply` or `On` could be named `ForEach`. The reason why you shouldn't do this, and why it's not included in LINQ, is at http://blogs.msdn.com/b/ericlippert/archive/2009/05/18/foreach-vs-foreach.aspx – Tim S. Jul 16 '13 at 14:01

4 Answers4

7

You could use an extension method like this instead:

static void ForEach<T>(this IEnumerable<T> items, Action<T> action)
{
    foreach (var item in items)
    {
        action(item);
    }
}

And then call it like:

new Control[] { control1, control2, control3 }.ForEach(makeInvisible);

Or simply

new Control[] { control1, control2, control3 }.ForEach(x => x.Visible = false);

If control1..n are all of the same type you can omit the base class:

new[] { control1, control2, control3 }.ForEach(x => x.Visible = false);
p.s.w.g
  • 146,324
  • 30
  • 291
  • 331
  • 4
    I honestly don't see how this syntax is any better. – Dustin Kingen Jul 16 '13 at 13:08
  • 4
    @Romoku It's just an alternative. Which syntax is 'better' is just a matter of style. – p.s.w.g Jul 16 '13 at 13:08
  • @Romoku, I prefer this syntax because it follows the pattern of how other Linq commands operate: you start with your data set and then you do actions do that data (`.Where`, `.GroupBy`, etc). – Brad Rem Jul 16 '13 at 13:15
  • semantically, I prefer `ForEach` but, isn't for each loop just a readable? – Jodrell Jul 16 '13 at 13:23
  • 1
    @Jodrell Actually, now that I think about it, I agree `ForEach` is a slightly more intuitive name than `Each`. Plus, using `ForEach` means on a `List` you wouldn't see two methods come up in intellisense. – p.s.w.g Jul 16 '13 at 13:28
  • Note that you do [loose some performance optimizations](http://blogs.msdn.com/b/mazhou/archive/2011/09/21/why-no-foreach-method-on-ienumerable-interfaces.aspx) when you do it this way. – Scott Chamberlain Jul 16 '13 at 13:55
4

Maybe you want to take a more OO approach by building a set of objects and then call a method on that set:

new[] { control1, control2, control3 }.ForEach(x => x.Visible = false);

The ForEach extension function is defined in the LinqKit toolset.

Jan
  • 15,802
  • 5
  • 35
  • 59
4

Why not just call it as a regular static method instead of an extension?

public static class Apply
{
    public static void To<T>(this Action<T> actionToCarryOut,params T[] listOfThings)
    {
        foreach (var thing in listOfThings)
        {
            actionToCarryOut(thing);
        }
    }
}

Then call it like this:

Apply.To<Control>(c => c.Visible = false,control1,control2, control3,control4);

EDIT

Here's a version that uses a Fluent syntax:

public class Apply<T>
{
    private Action<T> _action;

    public Apply(Action<T> action) { _action = action; }

    public static Apply<T> Method(Action<T> actionToCarryOut)
    {
        return new Apply<T>(actionToCarryOut);
    }

    public void To(params T[] listOfThings)
    {
        foreach (var thing in listOfThings)
        {
            _action(thing);
        }
    }

}

Usage:

Apply<Control>.Method(c => c.Visible = false).To(control1,control2, control3,control4);
D Stanley
  • 149,601
  • 11
  • 178
  • 240
2

Alternatively, you could just do,

control1.Visible = false;
control2.Visible = false;
control3.Visible = false;
control4.Visible = false;

This is less lines of code and will run faster. However, if you want an extension for a case less trivial than the one in your example, how about.

public static void ForEach<T>(this IEnumerable<T> source, Action<T> action)
{
    foreach(var t in source)
    {
        action(t);
    }
}

Then you could, rework your original slightly as

public static void Act<T>(Action<T> action, params T[] targets)
{
    targets.ForEach(action);
}

allowing,

Act(ctl => ctl.Visible = false, control1, control2, control3, control4); 

but, I would suggest

var controls = new[] { control1, control2, control3, control4 };

foreach (var control in controls)
{
    control.Visible = false;
}

is more readable than any of the extension methods, or if you put it on one line its very familiar.

foreach (var control in new[] { control1, control2, control3, control4 })
    control.Visible = false;

I think these final examples demonstrate why this seemingly useful extension is omitted from the framework.

Jodrell
  • 34,946
  • 5
  • 87
  • 124