Si hay un fragmento de código que repites todo el tiempo de acuerdo con Do not Repeat Yourself deberías ponerlo en tu propia biblioteca y llamarlo. Con eso en mente, hay dos aspectos para obtener la respuesta correcta aquí. El primero es la claridad y la brevedad en el código que llama a la función de la biblioteca. El segundo son las implicaciones de rendimiento de foreach.
Primero pensemos en la claridad y brevedad en el código de llamada.
Usted puede hacer foreach en un número de maneras:
- bucle for
- bucle foreach
- Collection.ForEach
De todas las maneras de hacer una lista foreach. Porque cada uno con un lamba es el más claro y breve.
list.ForEach(i => Console.Write("{0}\t", i));
Así que en esta etapa puede ser similar a la List.ForEach es el camino a seguir. Sin embargo, ¿cuál es el rendimiento de esto? Es cierto que en este caso el tiempo para escribir en la consola regirá el rendimiento del código. Cuando sabemos algo sobre el rendimiento de una característica particular del lenguaje, al menos debemos considerarlo.
De acuerdo con Duston Campbell's performance measurements of foreach, la forma más rápida de iterar la lista con código optimizado es utilizando un bucle for sin una llamada a List.Count.
Sin embargo, for loop es una construcción prolija. También se lo ve como una forma muy iterativa de hacer las cosas que no coincide con la tendencia actual hacia expresiones idiomáticas funcionales.
Entonces, ¿podemos obtener brevedad, claridad y rendimiento? Podemos mediante un método de extensión. En un mundo ideal, creamos un método de extensión en la consola que toma una lista y la escribe con un delimitador. No podemos hacer esto porque Console es una clase estática y los métodos de extensión solo funcionan en instancias de clases. En su lugar tenemos que poner el método de extensión en la propia lista (según la sugerencia de David B):
public static void WriteLine(this List<int> theList)
{
foreach (int i in list)
{
Console.Write("{0}\t", t.ToString());
}
Console.WriteLine();
}
Este código se va a se utiliza en muchos lugares por lo que debemos llevar a cabo las siguientes mejoras:
- En lugar de usar foreach, debemos usar la forma más rápida de iterar la colección, que es un ciclo for con un conteo en caché.
- Actualmente, solo la Lista se puede pasar como argumento. Como función de biblioteca, podemos generalizarla mediante una pequeña cantidad de esfuerzo.
- Usar la Lista nos limita a solo Listas, El uso de IList le permite a este código trabajar con Arrays también.
- Dado que el método de extensión estará en un IList necesitamos cambiar el nombre para hacerlo más claro lo que estamos escribiendo a:
Así es como el código de la función se vería:
public static void WriteToConsole<T>(this IList<T> collection)
{
int count = collection.Count();
for(int i = 0; i < count; ++i)
{
Console.Write("{0}\t", collection[i].ToString(), delimiter);
}
Console.WriteLine();
}
Podemos mejorar esto aún más al permitir que el cliente pase el delimitador. entonces podríamos proporcionar una segunda función que escribe en la consola con el delimitador estándar de la siguiente manera:
public static void WriteToConsole<T>(this IList<T> collection)
{
WriteToConsole<T>(collection, "\t");
}
public static void WriteToConsole<T>(this IList<T> collection, string delimiter)
{
int count = collection.Count();
for(int i = 0; i < count; ++i)
{
Console.Write("{0}{1}", collection[i].ToString(), delimiter);
}
Console.WriteLine();
}
Así que ahora, teniendo en cuenta que queremos un breve, clara manera performant de las listas de escritura a la consola tenemos una. Aquí está todo el código fuente incluyendo una demostración del uso de la función de la biblioteca:
using System;
using System.Collections.Generic;
using System.Linq;
namespace ConsoleWritelineTest
{
public static class Extensions
{
public static void WriteToConsole<T>(this IList<T> collection)
{
WriteToConsole<T>(collection, "\t");
}
public static void WriteToConsole<T>(this IList<T> collection, string delimiter)
{
int count = collection.Count();
for(int i = 0; i < count; ++i)
{
Console.Write("{0}{1}", collection[i].ToString(), delimiter);
}
Console.WriteLine();
}
}
internal class Foo
{
override public string ToString()
{
return "FooClass";
}
}
internal class Program
{
static void Main(string[] args)
{
var myIntList = new List<int> {1, 2, 3, 4, 5};
var myDoubleList = new List<double> {1.1, 2.2, 3.3, 4.4};
var myDoubleArray = new Double[] {12.3, 12.4, 12.5, 12.6};
var myFooList = new List<Foo> {new Foo(), new Foo(), new Foo()};
// Using the standard delimiter /t
myIntList.WriteToConsole();
myDoubleList.WriteToConsole();
myDoubleArray.WriteToConsole();
myFooList.WriteToConsole();
// Using our own delimiter ~
myIntList.WriteToConsole("~");
Console.Read();
}
}
}
============================ ===========================
Puede pensar que este debería ser el final de la respuesta. Sin embargo, hay una nueva generalización que se puede hacer. No queda claro por la pregunta de Fatcat si él siempre está escribiendo en la consola. Quizás hay algo más que hacer en el foreach. En ese caso, la respuesta de Jason Bunting va a dar esa generalidad. He aquí su respuesta de nuevo:
list.ForEach(i => Console.Write("{0}\t", i));
Eso es menos que hagamos una configuración más a nuestros métodos de extensión y añadimos FastForEach de la siguiente manera:
public static void FastForEach<T>(this IList<T> collection, Action<T> actionToPerform)
{
int count = collection.Count();
for (int i = 0; i < count; ++i)
{
actionToPerform(collection[i]);
}
Console.WriteLine();
}
Esto nos permite ejecutar cualquier código arbitrario en contra de todos los elementos en la colección utilizando el método de iteración más rápido posible.
Podemos incluso cambiar la función WriteToConsole utilizar FastForEach
public static void WriteToConsole<T>(this IList<T> collection, string delimiter)
{
collection.FastForEach(item => Console.Write("{0}{1}", item.ToString(), delimiter));
}
Así que ahora todo el código fuente, incluyendo un ejemplo de uso de FastForEach es:
using System;
using System.Collections.Generic;
using System.Linq;
namespace ConsoleWritelineTest
{
public static class Extensions
{
public static void WriteToConsole<T>(this IList<T> collection)
{
WriteToConsole<T>(collection, "\t");
}
public static void WriteToConsole<T>(this IList<T> collection, string delimiter)
{
collection.FastForEach(item => Console.Write("{0}{1}", item.ToString(), delimiter));
}
public static void FastForEach<T>(this IList<T> collection, Action<T> actionToPerform)
{
int count = collection.Count();
for (int i = 0; i < count; ++i)
{
actionToPerform(collection[i]);
}
Console.WriteLine();
}
}
internal class Foo
{
override public string ToString()
{
return "FooClass";
}
}
internal class Program
{
static void Main(string[] args)
{
var myIntList = new List<int> {1, 2, 3, 4, 5};
var myDoubleList = new List<double> {1.1, 2.2, 3.3, 4.4};
var myDoubleArray = new Double[] {12.3, 12.4, 12.5, 12.6};
var myFooList = new List<Foo> {new Foo(), new Foo(), new Foo()};
// Using the standard delimiter /t
myIntList.WriteToConsole();
myDoubleList.WriteToConsole();
myDoubleArray.WriteToConsole();
myFooList.WriteToConsole();
// Using our own delimiter ~
myIntList.WriteToConsole("~");
// What if we want to write them to separate lines?
myIntList.FastForEach(item => Console.WriteLine(item.ToString()));
Console.Read();
}
}
}
Te ganas por unos 30 segundos. :) –