IList< T>の.ForEach()はなぜですか? IEnumerable< T>ではありませんか? [複製]

StackOverflow https://stackoverflow.com/questions/800151

質問

  

可能な重複:
   ForEach拡張メソッドがない理由IEnumerableインターフェースで?

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

2行目から最終行の .ForEach()の前に挿入された .ToList()に注意してください。

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

役に立ちましたか?

解決

ForEach(Action) IEnumerable&lt; T&gt; が存在する前に存在していました。

他の拡張メソッドでは追加されていないため、C#デザイナーはそれが悪いデザインであると感じ、 foreach 構造を好むと推測できます。


編集:

独自の拡張メソッドを作成したい場合、 List&lt; T&gt; のメソッドをオーバーライドしませんが、 IEnumerable&lt; T&gtを実装する他のクラスでは機能します;

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

他のヒント

Eric Lippertによると、これはほぼ哲学的な理由で。投稿全体を読む必要がありますが、私が懸念している限り、ここに要点があります:

  

私は哲学的に反対です   そのような方法を2人に提供する   理由。

     

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

     

式の目的は   値を計算し、サイドを引き起こさない   効果。ステートメントの目的は   副作用を引き起こすため。呼び出しサイト   この事の非常に多くに見えるだろう   式のように(ただし、   確かに、メソッドは   戻り値が空の場合、式は   &#8220;ステートメントでのみ使用されます   式&#8221;コンテキスト。)

     

私と一緒に作るのは良くない   唯一のシーケンス演算子   それはその側でのみ有用です   効果。

     

2番目の理由はそうすることです   ゼロの新しい表現力を追加します   言語に。

IEnumerableの ForEach()は、次のように各ループで正常なためです:

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

ここでは推測しているだけですが、foreachをIEnumerableに設定すると、操作が副作用を引き起こすことになります。 「利用可能」なものはありません。拡張メソッドは副作用を引き起こし、foreachのような命令的メソッドをそこに置くと、私が推測するAPIを濁らせるでしょう。また、foreachは遅延コレクションを初期化します。

個人的には、副作用のない機能を副作用のある機能から分離するためだけに、自分自身を追加したいという誘惑を避けてきました。

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

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

ただし、これにこだわる必要はありません。パフォーマンスはアプリケーション設計の重要な要素ですが、アプリケーションがユーザビリティの問題を引き起こしている実際のパフォーマンスヒットを経験していない限り、時間は実際のビジネスプロジェクトの重要な要素であるため、保守性と再利用のためのコーディングに注力してください...

ForEachは具象クラス List&lt; T&gt;

で実装されます

推測ではありますが、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ループを使用するオプションがありません。

&quot;選択&quot;と呼ばれます。 IEnumerable&lt; T&gt; 啓発されました、ありがとうございます。

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

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

サミュエルが指摘したように、これを行うための独自の拡張メソッドを作成できます。

ライセンス: CC-BY-SA帰属
所属していません StackOverflow
scroll top