2009-03-20 18 views
6

Supongamos que tengo un código que se parece a esto:C# Código Simplificación de consulta: La secuencial Foreach Loops

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) 
} 

No puedo combinar las cosas en un gran bucle de esta manera:

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

Si lo hace, cambiaría el orden ¿Algún comentario sobre las mejores formas de simplificar el código en C#?

me imagino que podía resolver este problema mediante la creación de una función como esta, aunque yo prefiero dejarlo como está de forzar a los futuros lectores de mi código para entender 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 
    } 
} 

Respuesta

30

Otra alternativa:

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); 
    } 
} 

Acaba de comprobar, y eso funciona. Por ejemplo:

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); 
    } 
} 

resultados de ejecutar Test.exe a b c

First: a 
First: b 
First: c 
Second: a 
Second: b 
Second: c 
+0

+1 - Los delegados son mucho más agradable en C# que en VB.NET :( –

2

¿Qué tal:

interface IDoStuff 
{ 
    void DoStuff(x); 
} 

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

[editar] Y sí, usted debe ir en lugar de la solución genérica como J. Skeet dijo (aunque también puedes usar una interfaz genérica en lugar de un delegado).

+0

¿Por qué usar una interfaz cuando los delegados son mucho más convenientes? :) –

+0

Tienes razón ...Pero a veces los prefiero cuando necesito asociar algún estado con la acción, solo para encapsular cada acción + datos en una clase separada (no es que no puedas pasar a otro un delegado público de una clase diferente). ¿O crees que los delegados debe ser utilizado en esos casos también? – Groo

+0

Aquí hay un par de ventajas para los delegados: 1) Puede usar expresiones lambda y métodos anónimos (que brindan soporte de cierre: asociando estado) 2) Puede tener varias instancias delegadas de la misma clase, mucho menos desorden que tener una clase por implementación de interfaz. –

0

Si debe conservar la naturaleza secuencial, no hay mucho que pueda hacer. Podría hacer algunos métodos abreviados de métodos de extensión, pero en mi humilde opinión esto hace que el código sea menos legible. Además, puede tener problemas dependiendo de las firmas de su método.

Podría refactorizar para mover la iteración a funciones separadas.

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

// Do Stuff 1 
foreach (var x in y) 
{ 
    // do actual stuff 
} 
5

Si usted tiene una lista bastante constante de acciones, sólo podría evitar los foreach, pero todavía lo hacen las acciones de manera explícita (no han probado el código):

list.ForEach(action1); 
list.ForEach(action2); 
list.ForEach(action3); 
list.ForEach(action4); 
0

Creo que este es lo mismo que testing.ForEach (acción) así que solo use eso si va por este tipo de ruta.

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); 
     } 
    } 
5

La respuesta de Jon Skeet es excelente (la acabo de votar). Aquí hay una idea para dar un paso más:

Si haces esto mucho, podrías hacer un método de extensión llamado "DoActionsInOrder" (o tal vez puedas encontrar un nombre mejor) que lo haga. Aquí está la 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); 
      } 
    } 
} 

A continuación, se le puede llamar así:

myList.DoActionsInOrder(doStuff1, doStuff2, doStuff3, doStuff4, doStuff5); 
+0

maldita sincronicidad! Acabo de terminar exactamente el mismo método de extensión. ¡La única diferencia que puedo ver es que usas var en lugar de T :) +1, entonces! – flq

+0

:) Gracias. ¡Es sorprendente cómo este sitio nos tiene clamando para responder las preguntas de otras personas tan rápido como podamos! ¡Podría volverme adicto! –