可能重复:
Why is there not a ForEach extension method on the IEnumerable interface?
我注意到在编写LINQ-y代码时,.ForEach()是一个很好用的习惯用法。例如,这是一段代码,它接受以下输入,并产生这些输出:
{ "One" } => "One"
{ "One", "Two" } => "One, Two"
{ "One", "Two", "Three", "Four" } => "One, Two, Three and Four";
代码:
private string InsertCommasAttempt(IEnumerable<string> words)
{
List<string> wordList = words.ToList();
StringBuilder sb = new StringBuilder();
var wordsAndSeparators = wordList.Select((string word, int pos) =>
{
if (pos == 0) return new { Word = word, Leading = string.Empty };
if (pos == wordList.Count - 1) return new { Word = word, Leading = " and " };
return new { Word = word, Leading = ", " };
});
wordsAndSeparators.ToList().ForEach(v => sb.Append(v.Leading).Append(v.Word));
return sb.ToString();
}
注意第二行到最后一行的.ForEach()之前插入的.ToList()。
为什么.ForEach()不能用作IEnumerable的扩展方法?有了这样的例子,它看起来很奇怪。
提前致谢,
戴夫
答案 0 :(得分:39)
根据Eric Lippert的说法,这是mostly for philosophical reasons。你应该阅读整篇文章,但就我而言,这里是要点:
我在哲学上反对 提供这样一种方法,两个 的原因。
第一个原因就是这样做 违反了函数式编程 所有其他顺序的原则 运营商是基于。显然是 唯一目的是调用此方法 是造成副作用。
表达的目的是 计算一个值,而不是引起一个方面 影响。声明的目的是 引起副作用。通话网站 这件事情看起来很糟糕 像一个表达(虽然, 诚然,因为方法是 无效返回,表达式可以 仅用于“声明” 表达“上下文。”
与我合作并不合适 唯一的序列运算符 这只对它的一方有用 的效果。
第二个原因就是这样做 增加零代表性能力 语言。
答案 1 :(得分:38)
因为IEnumerable<T>
存在foreach
之前存在。
由于没有添加其他扩展方法,可以假设C#设计者认为这是一个糟糕的设计并且更喜欢List<T>
构造。
如果你想要你可以创建自己的扩展方法,它不会覆盖IEnumerable<T>
的方法,但它适用于任何其他实现public static class IEnumerableExtensions
{
public static void ForEach<T>(this IEnumerable<T> source, Action<T> action)
{
foreach (T item in source)
action(item);
}
}
的类。
{{1}}
答案 2 :(得分:5)
因为IEnumerable上的ForEach()
只是每个循环的正常,所以:
for each T item in MyEnumerable
{
// Action<T> goes here
}
答案 3 :(得分:3)
我只是在这里猜测,但是将foreach放在IEnumerable上会使操作产生副作用。没有任何“可用的”扩展方法会产生副作用,像foreach这样的必要方法会让我认为api很混乱。此外,foreach会初始化懒惰的集合。
就个人而言,我一直在试图添加自己的诱惑,只是为了保持副作用免费功能与副作用分开。
答案 4 :(得分:2)
ForEach不在IList上,它在List上。您在示例中使用了具体的List。
答案 5 :(得分:2)
老实说,我不确定为什么.ForEach(Action)不包含在IEnumerable中,但是,对,错或无关紧要,就是这样......
但我想强调其他评论中提到的性能问题。根据您在集合中循环的方式,性能会受到影响。它相对较小,但它确实存在。这是一个非常快速和草率的代码片段来显示关系...只需要一分钟左右的时间来完成。
class Program
{
static void Main(string[] args)
{
Console.WriteLine("Start Loop timing test: loading collection...");
List<int> l = new List<int>();
for (long i = 0; i < 60000000; i++)
{
l.Add(Convert.ToInt32(i));
}
Console.WriteLine("Collection loaded with {0} elements: start timings",l.Count());
Console.WriteLine("\n<===============================================>\n");
Console.WriteLine("foreach loop test starting...");
DateTime start = DateTime.Now;
//l.ForEach(x => l[x].ToString());
foreach (int x in l)
l[x].ToString();
Console.WriteLine("foreach Loop Time for {0} elements = {1}", l.Count(), DateTime.Now - start);
Console.WriteLine("\n<===============================================>\n");
Console.WriteLine("List.ForEach(x => x.action) loop test starting...");
start = DateTime.Now;
l.ForEach(x => l[x].ToString());
Console.WriteLine("List.ForEach(x => x.action) Loop Time for {0} elements = {1}", l.Count(), DateTime.Now - start);
Console.WriteLine("\n<===============================================>\n");
Console.WriteLine("for loop test starting...");
start = DateTime.Now;
int count = l.Count();
for (int i = 0; i < count; i++)
{
l[i].ToString();
}
Console.WriteLine("for Loop Time for {0} elements = {1}", l.Count(), DateTime.Now - start);
Console.WriteLine("\n<===============================================>\n");
Console.WriteLine("\n\nPress Enter to continue...");
Console.ReadLine();
}
尽管如此,不要因为太多而挂断电话。性能是应用程序设计的替代品,但除非您的应用程序遇到导致可用性问题的实际性能损失,否则请关注可维护性和重用的编码,因为时间是现实生活中的商业项目的货币......
答案 6 :(得分:0)
ForEach在具体类List<T>
答案 7 :(得分:0)
在上称为“选择”
我很开明,谢谢你。IEnumerable<T>
答案 8 :(得分:0)
只是猜测,但List可以在不创建枚举器的情况下迭代其项目:
public void ForEach(Action<T> action)
{
if (action == null)
{
ThrowHelper.ThrowArgumentNullException(ExceptionArgument.match);
}
for (int i = 0; i < this._size; i++)
{
action(this._items[i]);
}
}
这可以带来更好的性能。使用IEnumerable,您无法使用普通的for循环。
答案 9 :(得分:0)
LINQ遵循拉模型,其所有(扩展)方法都应返回IEnumerable<T>
,ToList()
除外。 ToList()
可以结束拉链。
ForEach()
来自推模型世界。
如Samuel所指出的那样,您仍然可以编写自己的扩展方法来执行此操作。