55

重複の可能性:
IEnumerableインターフェイスにForEach拡張メソッドがないのはなぜですか?

.ForEach()使用するのに適したイディオムであるLINQ-yコードを記述しているときに気づきました。たとえば、次の入力を受け取り、これらの出力を生成するコードを次に示します。

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

最後から2番目の行の.ToList()前に挿入されていることに注意してください。.ForEach()

.ForEach()の拡張メソッドとして使用できないのはなぜIEnumerable<T>ですか?このような例では、それは奇妙に思えます。

4

10 に答える 10

40

ForEach(Action)存在する前に存在していたからIEnumerable<T>です。

他の拡張メソッドでは追加されなかったため、C#の設計者はそれが悪い設計であると感じ、構成を好んだと推測できforeachます。


編集:

独自の拡張メソッドを作成できるようにしたい場合は、の拡張メソッドをオーバーライドしませんが、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);
  }
}
于 2009-04-28T22:54:04.483 に答える
40

Eric Lippertによると、これは主に哲学的な理由によるものです。投稿全体を読む必要がありますが、私に関する限り、ここに要点があります。

私は、2つの理由から、そのような方法を提供することに哲学的に反対しています。

最初の理由は、そうすることは、他のすべてのシーケンス演算子が基づいている関数型プログラミングの原則に違反することです。明らかに、このメソッドの呼び出しの唯一の目的は、副作用を引き起こすことです。

式の目的は、副作用を引き起こすことではなく、値を計算することです。ステートメントの目的は、副作用を引き起こすことです。このことの呼び出しサイトは、式に非常によく似ています(ただし、確かに、メソッドはvoidを返すため、式は「ステートメント式」のコンテキストでのみ使用できます)。

副作用にのみ役立つ唯一のシーケンス演算子を作成することは、私にはうまくいきません。

2番目の理由は、そうすることで言語に新しい表現力がゼロになることです。

于 2009-08-18T21:55:52.547 に答える
5

ForEach()IEnumerableでは、次のように各ループが正常であるためです。

for each T item in MyEnumerable
{
    // Action<T> goes here
}
于 2009-04-28T22:50:54.117 に答える
3

ForEachはIListにありませんが、リストにあります。例では具体的なリストを使用していました。

于 2009-04-28T22:56:14.183 に答える
3

ここで推測しているだけですが、foreachをIEnumerableに配置すると、その操作で副作用が発生します。「利用可能な」拡張メソッドはどれも副作用を引き起こしません。foreachのような命令型メソッドをそこに置くと、APIが泥だらけになると思います。また、foreachはレイジーコレクションを初期化します。

個人的には、副作用のない機能と副作用のある機能を区別するために、自分で追加したいという誘惑をかわしてきました。

于 2009-04-28T23:10:24.447 に答える
0

ForEachは具象クラスに実装されていますList<T>

于 2009-04-28T22:51:30.747 に答える
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ループを使用するオプションはありません。

于 2009-04-28T22:58:04.803 に答える
0

LINQはpull-modelに従い、その(拡張)メソッドはすべてIEnumerable<T>、を除いてを返す必要がありToList()ます。ToList()プルチェーンを終了するためにあります。

ForEach()プッシュモデルの世界からです。

Samuelが指摘しているように、これを行うための独自の拡張メソッドを作成することもできます。

于 2009-04-28T23:17:18.657 に答える
0

正直なところ、.ForEach(Action)がIEnumerableに含まれていない理由はわかりませんが、正しいか、間違っているか、無関心であるかはわかりません...

ただし、他のコメントで言及されているパフォーマンスの問題を強調したいと思います。コレクションをループする方法に基づいて、パフォーマンスが低下します。それは比較的マイナーですが、それにもかかわらず、それは確かに存在します。これは、関係を示すための信じられないほど高速でずさんなコードスニペットです...実行するのに1分ほどかかります。

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

しかし、これにあまり夢中にならないでください。パフォーマンスはアプリケーション設計の通貨ですが、ユーザビリティの問題を引き起こす実際のパフォーマンスヒットがアプリケーションで発生していない限り、時間は実際のビジネスプロジェクトの通貨であるため、保守性と再利用のためのコーディングに焦点を当ててください...

于 2009-11-19T18:59:10.753 に答える
-1

それは私が悟った上で「選択」と呼ばれてIEnumerable<T> います、ありがとう。

于 2009-04-28T22:52:12.430 に答える