C# 为什么 .ForEach() 在 IList<T> 而不是在 IEnumerable<T>?

声明:本页面是StackOverFlow热门问题的中英对照翻译,遵循CC BY-SA 4.0协议,如果您需要使用它,必须同样遵循CC BY-SA许可,注明原文地址和作者信息,同时你必须将它归于原作者(不是我):StackOverFlow 原文地址: http://stackoverflow.com/questions/800151/
Warning: these are provided under cc-by-sa 4.0 license. You are free to use/share it, But you must attribute it to the original authors (not me): StackOverFlow

提示:将鼠标放在中文语句上可以显示对应的英文。显示中英文
时间:2020-08-05 01:41:27  来源:igfitidea点击:

Why is .ForEach() on IList<T> and not on IEnumerable<T>?

c#listlinqc#-3.0ienumerable

提问by Olema

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.

注意倒数第二行.ToList()之前插入的.ForEach()

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

为什么它.ForEach()不能作为扩展方法使用IEnumerable<T>?像这样的例子,看起来很奇怪。

采纳答案by Samuel

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

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

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 foreachconstruct.

由于它没有与其他扩展方法一起添加,因此可以假设 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>.

如果您希望可以创建自己的扩展方法,它不会覆盖 a 的扩展方法,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);
  }
}

回答by Joel Coehoorn

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

因为ForEach()在 IEnumerable 上对于这样的每个循环来说只是一个正常的:

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

回答by Chad Grant

ForEach is implemented in the concrete class List<T>

ForEach 在具体类中实现 List<T>

回答by JP Alioto

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

就叫“Select”,IEnumerable<T>我开悟了,谢谢。

回答by Joshua Belden

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

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

回答by Rauhotz

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 循环。

回答by Surya

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.

就我个人而言,我一直在抵制只添加我自己的 ,只是为了将无副作用的功能与有副作用的功能分开。

回答by tofi9

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 遵循 pull-model 并且它的所有(扩展)方法都应该返回IEnumerable<T>,除了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.

正如 Samuel 所指出的,您仍然可以编写自己的扩展方法来执行此操作。

回答by Justin R.

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

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.

我在哲学上反对提供这样一种方法,原因有二。

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

表达式的目的是计算一个值,而不是产生副作用。声明的目的是引起副作用。这个东西的调用点看起来非常像一个表达式(虽然,无可否认,因为该方法是返回空值的,所以该表达式只能在“语句表达式”上下文中使用。)

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

第二个原因是这样做为语言增加了零新的表征能力。

回答by Aaron

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...

不过不要太在意这个。性能是应用程序设计的货币,但除非您的应用程序遇到导致可用性问题的实际性能下降,否则请专注于可维护性和重用性的编码,因为时间是现实生活中的商业项目的货币……