为什么IList 上的.ForEach()而不是IEnumerable ? [重复]

时间:2022-09-10 23:47:53

Possible Duplicate:
Why is there not a ForEach extension method on the IEnumerable interface?

可能重复:为什么IEnumerable接口上没有ForEach扩展方法?

I've noticed when writing LINQ-y code that .ForEach() is a nice idiom to use. For example, here is a piece of code that takes the following inputs, and produces these outputs:

我注意到在编写LINQ-y代码时,.ForEach()是一个很好用的习惯用法。例如,这是一段代码,它接受以下输入,并产生以下输出:

{ "One" } => "One"
{ "One", "Two" } => "One, Two"
{ "One", "Two", "Three", "Four" } => "One, Two, Three and Four";

And the code:

和代码:

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

Note the interjected .ToList() before the .ForEach() on the second to last line.

请注意第二行到最后一行的.ForEach()之前插入的.ToList()。

Why is it that .ForEach() isn't available as an extension method on IEnumerable? With an example like this, it just seems weird.

为什么.ForEach()不能作为IEnumerable的扩展方法?有了这样的例子,它看起来很奇怪。

Thanks in advance,

提前致谢,

Dave

戴夫

10 个解决方案

#1


37  

Because ForEach(Action) existed before IEnumerable<T> existed.

因为在IEnumerable 存在之前存在ForEach(Action)。

Since it was not added with the other extension methods, one can assume that the C# designers felt it was a bad design and prefer the foreach construct.

由于它没有与其他扩展方法一起添加,因此可以假设C#设计者认为它是一个糟糕的设计并且更喜欢foreach结构。


Edit:

If you want you can create your own extension method, it won't override the one for a List<T> but it will work for any other class which implements IEnumerable<T>.

如果你想要你可以创建自己的扩展方法,它不会覆盖List 的方法,但它适用于任何其他实现IEnumerable 的类。

public static class IEnumerableExtensions
{
  public static void ForEach<T>(this IEnumerable<T> source, Action<T> action)
  {
    foreach (T item in source)
      action(item);
  }
}

#2


39  

According to Eric Lippert, this is mostly for philosophical reasons. You should read the whole post, but here's the gist as far as I'm concerned:

根据Eric Lippert的说法,这主要是出于哲学原因。你应该阅读整篇文章,但就我而言,这里是要点:

I am philosophically opposed to providing such a method, for two reasons.

出于两个原因,我在哲学上反对提供这样的方法。

The first reason is that doing so violates the functional programming principles that all the other sequence operators are based upon. Clearly the sole purpose of a call to this method is to cause side effects.

第一个原因是这样做违反了所有其他序列运算符所基于的函数式编程原则。显然,调用此方法的唯一目的是引起副作用。

The purpose of an expression is to compute a value, not to cause a side effect. The purpose of a statement is to cause a side effect. The call site of this thing would look an awful lot like an expression (though, admittedly, since the method is void-returning, the expression could only be used in a “statement expression” context.)

表达式的目的是计算一个值,而不是产生副作用。声明的目的是产生副作用。这个东西的调用站点看起来很像表达式(但是,诚然,因为该方法是void返回的,所以表达式只能用在“语句表达式”上下文中。)

It does not sit well with me to make the one and only sequence operator that is only useful for its side effects.

对我来说,制作唯一一个仅对其副作用有用的序列运算符并不合适。

The second reason is that doing so adds zero new representational power to the language.

第二个原因是,这样做会增加语言的新代表性能力。

#3


5  

Because ForEach() on an IEnumerable is just a normal for each loop like this:

因为IEnumerable上的ForEach()只是每个循环的正常,如下所示:

for each T item in MyEnumerable
{
    // Action<T> goes here
}

#4


3  

I am just guessing here , but putting foreach on IEnumerable would make operations on it to have side effects . None of the "available" extension methods cause side effects , putting an imperative method like foreach on there would muddy the api I guess . Also, foreach would initialize the lazy collection .

我只是在这里猜测,但将foreach放在IEnumerable上会使操作产生副作用。没有任何“可用的”扩展方法会产生副作用,像foreach这样的命令式方法会让我认为api很混乱。此外,foreach将初始化惰性集合。

Personally I've been fending off the temptation to just add my own , just to keep side effect free functions separate from ones with side effects.

就个人而言,我一直在抵制只是添加自己的诱惑,只是为了保持副作用免费功能与副作用分开。

#5


2  

ForEach isn't on IList it's on List. You were using the concrete List in your example.

ForEach不在IList上,它在List上。您在示例中使用了具体的List。

#6


2  

I honestly don't know for sure why the .ForEach(Action) isn't included on IEnumerable but, right, wrong or indifferent, that's the way it is...

老实说,我不确定为什么.ForEach(Action)不包含在IEnumerable中,但是,对,错,或无所谓,这就是它的方式......

I DID however want to highlight the performance issue mentioned in other comments. There is a performance hit based on how you loop over a collection. It is relatively minor but nevertheless, it certainly exists. Here is an incredibly fast and sloppy code snippet to show the relations... only takes a minute or so to run through.

我想要强调其他评论中提到的性能问题。根据您在集合中循环的方式,性能会受到影响。它虽然相对较小,但它确实存在。这是一个非常快速和草率的代码片段来显示关系...只需要一分钟左右的时间来完成。

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

Don't get hung up on this too much though. Performance is the currency of application design but unless your application is experiencing an actual performance hit that is causing usability problems, focus on coding for maintainability and reuse since time is the currency of real life business projects...

尽管如此,不要太过于沉溺于此。性能是应用程序设计的货币,但除非您的应用程序遇到导致可用性问题的实际性能损失,否则请关注可维护性和重用的编码,因为时间是现实生活中的商业项目的货币......

#7


0  

ForEach is implemented in the concrete class List<T>

ForEach在具体类List 中实现

#8


0  

Just a guess, but List can iterate over its items without creating an enumerator:

只是一个猜测,但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]);
    }
}

This can lead to better performance. With IEnumerable, you don't have the option to use an ordinary for-loop.

这可以带来更好的性能。使用IEnumerable,您无法使用普通的for循环。

#9


0  

It's called "Select" on IEnumerable<T> I am enlightened, thank you.

它在IEnumerable上被称为“选择” 我开悟了,谢谢。

#10


0  

LINQ follows the pull-model and all its (extension) methods should return IEnumerable<T>, except for ToList(). The ToList() is there to end the pull-chain.

LINQ遵循拉模型,其所有(扩展)方法都应该返回IEnumerable ,但ToList()除外。 ToList()用于结束拉链。

ForEach() is from the push-model world.

ForEach()来自推模型世界。

You can still write your own extension method to do this, as pointed out by Samuel.

正如塞缪尔所指出的,你仍然可以编写自己的扩展方法来做到这一点。

#1


37  

Because ForEach(Action) existed before IEnumerable<T> existed.

因为在IEnumerable 存在之前存在ForEach(Action)。

Since it was not added with the other extension methods, one can assume that the C# designers felt it was a bad design and prefer the foreach construct.

由于它没有与其他扩展方法一起添加,因此可以假设C#设计者认为它是一个糟糕的设计并且更喜欢foreach结构。


Edit:

If you want you can create your own extension method, it won't override the one for a List<T> but it will work for any other class which implements IEnumerable<T>.

如果你想要你可以创建自己的扩展方法,它不会覆盖List 的方法,但它适用于任何其他实现IEnumerable 的类。

public static class IEnumerableExtensions
{
  public static void ForEach<T>(this IEnumerable<T> source, Action<T> action)
  {
    foreach (T item in source)
      action(item);
  }
}

#2


39  

According to Eric Lippert, this is mostly for philosophical reasons. You should read the whole post, but here's the gist as far as I'm concerned:

根据Eric Lippert的说法,这主要是出于哲学原因。你应该阅读整篇文章,但就我而言,这里是要点:

I am philosophically opposed to providing such a method, for two reasons.

出于两个原因,我在哲学上反对提供这样的方法。

The first reason is that doing so violates the functional programming principles that all the other sequence operators are based upon. Clearly the sole purpose of a call to this method is to cause side effects.

第一个原因是这样做违反了所有其他序列运算符所基于的函数式编程原则。显然,调用此方法的唯一目的是引起副作用。

The purpose of an expression is to compute a value, not to cause a side effect. The purpose of a statement is to cause a side effect. The call site of this thing would look an awful lot like an expression (though, admittedly, since the method is void-returning, the expression could only be used in a “statement expression” context.)

表达式的目的是计算一个值,而不是产生副作用。声明的目的是产生副作用。这个东西的调用站点看起来很像表达式(但是,诚然,因为该方法是void返回的,所以表达式只能用在“语句表达式”上下文中。)

It does not sit well with me to make the one and only sequence operator that is only useful for its side effects.

对我来说,制作唯一一个仅对其副作用有用的序列运算符并不合适。

The second reason is that doing so adds zero new representational power to the language.

第二个原因是,这样做会增加语言的新代表性能力。

#3


5  

Because ForEach() on an IEnumerable is just a normal for each loop like this:

因为IEnumerable上的ForEach()只是每个循环的正常,如下所示:

for each T item in MyEnumerable
{
    // Action<T> goes here
}

#4


3  

I am just guessing here , but putting foreach on IEnumerable would make operations on it to have side effects . None of the "available" extension methods cause side effects , putting an imperative method like foreach on there would muddy the api I guess . Also, foreach would initialize the lazy collection .

我只是在这里猜测,但将foreach放在IEnumerable上会使操作产生副作用。没有任何“可用的”扩展方法会产生副作用,像foreach这样的命令式方法会让我认为api很混乱。此外,foreach将初始化惰性集合。

Personally I've been fending off the temptation to just add my own , just to keep side effect free functions separate from ones with side effects.

就个人而言,我一直在抵制只是添加自己的诱惑,只是为了保持副作用免费功能与副作用分开。

#5


2  

ForEach isn't on IList it's on List. You were using the concrete List in your example.

ForEach不在IList上,它在List上。您在示例中使用了具体的List。

#6


2  

I honestly don't know for sure why the .ForEach(Action) isn't included on IEnumerable but, right, wrong or indifferent, that's the way it is...

老实说,我不确定为什么.ForEach(Action)不包含在IEnumerable中,但是,对,错,或无所谓,这就是它的方式......

I DID however want to highlight the performance issue mentioned in other comments. There is a performance hit based on how you loop over a collection. It is relatively minor but nevertheless, it certainly exists. Here is an incredibly fast and sloppy code snippet to show the relations... only takes a minute or so to run through.

我想要强调其他评论中提到的性能问题。根据您在集合中循环的方式,性能会受到影响。它虽然相对较小,但它确实存在。这是一个非常快速和草率的代码片段来显示关系...只需要一分钟左右的时间来完成。

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

Don't get hung up on this too much though. Performance is the currency of application design but unless your application is experiencing an actual performance hit that is causing usability problems, focus on coding for maintainability and reuse since time is the currency of real life business projects...

尽管如此,不要太过于沉溺于此。性能是应用程序设计的货币,但除非您的应用程序遇到导致可用性问题的实际性能损失,否则请关注可维护性和重用的编码,因为时间是现实生活中的商业项目的货币......

#7


0  

ForEach is implemented in the concrete class List<T>

ForEach在具体类List 中实现

#8


0  

Just a guess, but List can iterate over its items without creating an enumerator:

只是一个猜测,但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]);
    }
}

This can lead to better performance. With IEnumerable, you don't have the option to use an ordinary for-loop.

这可以带来更好的性能。使用IEnumerable,您无法使用普通的for循环。

#9


0  

It's called "Select" on IEnumerable<T> I am enlightened, thank you.

它在IEnumerable上被称为“选择” 我开悟了,谢谢。

#10


0  

LINQ follows the pull-model and all its (extension) methods should return IEnumerable<T>, except for ToList(). The ToList() is there to end the pull-chain.

LINQ遵循拉模型,其所有(扩展)方法都应该返回IEnumerable ,但ToList()除外。 ToList()用于结束拉链。

ForEach() is from the push-model world.

ForEach()来自推模型世界。

You can still write your own extension method to do this, as pointed out by Samuel.

正如塞缪尔所指出的,你仍然可以编写自己的扩展方法来做到这一点。