tags:

views:

545

answers:

6

Suppose I have some code that looks like this:

foreach(type x in list y)
{
   //dostuff1(x)
}

foreach(type x in list y)
{
   //dostuff2(x)
}

foreach(type x in list y)
{
   //dostuff3(x)
}

foreach(type x in list y)
{
   //dostuff4(x)
}

foreach(type x in list y)
{
   //dostuff5(x)
}

I cannot combine things into one big for loop like this:

foreach (type x in list y)
{
    //dostuff1(x)
    //dostuff2(x)
    //dostuff3(x)
    //dostuff4(x)
    //dostuff5(x)
}

Doing so would change the order. Any commentary on the best ways to make the code simpler in C#?

I imagine I could solve this problem by creating a function like this, though I'd rather leave it the way it is than force future readers of my code to understand yield:

void func(type x)
{
    dostuff1(x)
    yield 0;
    dostuff2(x)
    yield 0;
    dostuff3(x)
    yield 0;
    dostuff4(x)
    yield 0;
    dostuff5(x)
    yield break;
}

for (int i = 0; i<5; ++i)
{
   foreach (type x in list y)
   {
       //Call func(x) using yield semantics, which I'm not going to look up right now
   }
}
+26  A: 

Another alternative:

List<Action<Foo>> actions = new List<Action<Foo>> { 
    doStuff1, doStuff2, doStuff3, doStuff4, doStuff5
};

foreach (Action<Foo> action in actions)
{
    foreach (Foo x in list)
    {
        action(x);
    }
}

Just checked, and that works. For instance:

using System;
using System.Collections.Generic;

public class Test
{
    static void Main(string[] args)
    {
        var actions = new List<Action<string>> {
            First, Second
        };

        foreach (var action in actions)
        {
            foreach (string arg in args)
            {
                action(arg);
            }
        }
    }

    static void First(string x)
    {
        Console.WriteLine("First: " + x);
    }

    static void Second(string x)
    {
        Console.WriteLine("Second: " + x);
    }
}

Results of running Test.exe a b c

First: a
First: b
First: c
Second: a
Second: b
Second: c
Jon Skeet
+1 - Delegates are so much nicer in C# than in VB.NET :(
Patrick McDonald
+1  A: 

How about:

interface IDoStuff
{
     void DoStuff(x);
}

List<IDoStuff> listOfActions = ...
foreach (IDoStuff iDoStuff in listOfActions)
{
    foreach (type x in list y)
    {
         iDoStuff(x);
    }
}

[edit] And yes, you should rather go for the generic solution as J.Skeet said (although you can use a generic interface instead of a delegate as well).

Groo
Why use an interface when delegates are so much more convenient? :)
Jon Skeet
You're right.. But I sometimes prefer them when I need to associate some state with the action, just to encapsulate each action+data in a separate class (not that you cannot also pass another a public delegate from a different class).. Or do you think delegates should be used in those cases as well?
Groo
There are a couple benefits to delegates here: 1) You can use lambda expressions and anonymous methods (which give closure support - associating state!)2) You can have multiple delegate instances from the same class - much less clutter than having one class per interface implementation.
Jon Skeet
A: 

If you must retain the sequential nature, there's not much you can do. You could do some extension method shortcuts, but IMHO this makes for less readable code. Also, you may run into problems depending on your method signatures.

You could refactor to move the iteration to separate functions.

// Method 1
DoStuff1ToList(y);
DoStuff2ToList(y);
DoStuff3ToList(y);
DoStuff4ToList(y);
DoStuff5ToList(y);

// Do Stuff 1
foreach (var x in y)
{ 
    // do actual stuff
}
Matt Brunell
+5  A: 

If you have a fairly constant list of actions, you could just avoid the foreach loops, but still do the actions explicitly (haven't tested the code):

list.ForEach(action1);
list.ForEach(action2);
list.ForEach(action3);
list.ForEach(action4);
Grzenio
A: 

i think this is the same as testing.ForEach(action) so just use that if your going down this kind of route.

private static void DoDifferentStuffToThings()
    {
        List<string> testing = new List<string>() { "hello", "world" };

        Action<string> action1 = (a) =>
        {
            Console.WriteLine("Action 1 {0}", a);
        };

        Action<string> action2 = (a) =>
        {
            Console.WriteLine("Action 2 {0}", a);
        };

        DoStuffToThings<string>(testing, action1);
        DoStuffToThings<string>(testing, action2);
    }

    private static void DoStuffToThings<T>(IEnumerable<T> list, Action<T> dothing)
        where T : class
    {
        foreach (var item in list)
        {
            dothing(item);
        }
    }
Hath
+2  A: 

Jon Skeet's answer is excellent (I just voted it up). Here's an idea to take it one step further:

If you do this a lot, you could make an extension method called "DoActionsInOrder" (or maybe you can come up with a better name) that does this. Here's the idea:

public static void DoActionsInOrder<T>(this IEnumerable<T> stream, params Action<T> actionList)
{
     foreach(var action in actionList)
     {
          foreach(var item in stream)
          {
               action(item);
          }
     }
}

Then, you could call it like this:

myList.DoActionsInOrder(doStuff1, doStuff2, doStuff3, doStuff4, doStuff5);
Charlie Flowers
damn synchronicity! I just finished up the exact same extension method. The only difference I can see is that you use var instead of T :) +1, then!
flq
:) Thanks. It is amazing how this site has us clamoring to answer other peoples' questions as fast as we can! I could get addicted!
Charlie Flowers