如果有一段代码根据 Don't Repeat Yourself 一直重复,你应该把它放在你自己的库中并调用它。考虑到这一点,在这里获得正确答案有两个方面。首先是调用库函数的代码简洁明了。第二个是 foreach 的性能影响。
首先让我们考虑一下调用代码的清晰性和简洁性。
您可以通过多种方式执行 foreach:
- for 循环
- foreach 循环
- Collection.ForEach
在所有方法中,使用lamba 进行foreach List.ForEach 是最清晰和最简洁的。
list.ForEach(i => Console.Write("{0}\t", i));
所以在这个阶段它可能看起来像 List.ForEach 是要走的路。然而这有什么表现呢?确实,在这种情况下,写入控制台的时间将决定代码的性能。当我们对特定语言特性的性能有所了解时,我们当然至少应该考虑它。
根据Duston Campbell 对 foreach 的性能测量,在优化代码下迭代列表的最快方法是使用 for 循环而不调用 List.Count。
然而,for 循环是一个冗长的结构。它也被视为一种非常迭代的做事方式,与当前的功能习语趋势不符。
那么我们能否获得简洁、清晰和性能?我们可以通过使用扩展方法。在理想情况下,我们会在 Console 上创建一个扩展方法,它接受一个列表并用分隔符写入它。我们不能这样做,因为 Console 是一个静态类,而扩展方法仅适用于类的实例。相反,我们需要将扩展方法放在列表本身(根据 David B 的建议):
public static void WriteLine(this List<int> theList)
{
foreach (int i in list)
{
Console.Write("{0}\t", t.ToString());
}
Console.WriteLine();
}
这段代码将在很多地方使用,所以我们应该进行以下改进:
- 我们应该使用迭代集合的最快方法,而不是使用 foreach,这是一个带有缓存计数的 for 循环。
- 目前只有 List 可以作为参数传递。作为一个库函数,我们可以通过少量的努力来概括它。
- 使用 List 将我们限制为仅使用列表,使用 IList 也允许此代码与数组一起使用。
- 由于扩展方法将在 IList 上,我们需要更改名称以更清楚地显示我们正在写入的内容:
以下是函数代码的外观:
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();
}
我们可以通过允许客户端传入分隔符来进一步改进这一点。然后我们可以提供第二个函数,使用标准分隔符写入控制台,如下所示:
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();
}
所以现在,考虑到我们想要一种简短、清晰的将列表写入控制台的高性能方式,我们有一个。这是完整的源代码,包括使用库函数的演示:
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();
}
}
}
==================================================== =====
您可能认为这应该是答案的结尾。但是,可以进行进一步的概括。从 fatcat 的问题中不清楚他是否总是在向控制台写信。也许在 foreach 中还有其他事情要做。在那种情况下,Jason Bunting 的回答将给出一般性。这是他的回答:
list.ForEach(i => Console.Write("{0}\t", i));
除非我们对扩展方法进行进一步改进并添加 FastForEach,如下所示:
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();
}
这允许我们使用尽可能快的迭代方法对集合中的每个元素执行任意代码。
我们甚至可以将 WriteToConsole 函数更改为使用 FastForEach
public static void WriteToConsole<T>(this IList<T> collection, string delimiter)
{
collection.FastForEach(item => Console.Write("{0}{1}", item.ToString(), delimiter));
}
所以现在整个源代码,包括 FastForEach 的一个示例用法是:
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();
}
}
}