56

多くの質問が Stack Overflow で回答されており、メンバーはラムダ式を使用してこれらの現実世界/時間の問題を解決する方法を指定しています。

ラムダ式の使用によるパフォーマンスへの影響を考慮していますか?

ラムダ vs 匿名デリゲート vs for/foreachループのパフォーマンスへの影響を調べた記事をいくつか見つけましたが、結果は異なります

  1. 匿名デリゲート vs ラムダ式 vs 関数呼び出しのパフォーマンス
  2. foreach と List.ForEach のパフォーマンス
  3. .NET/C# ループ パフォーマンス テスト (FOR、FOREACH、LINQ、およびラムダ) .
  4. DataTable.Select は LINQ より高速です

適切なソリューションを選択する際の評価基準は何ですか? ラムダを使用すると、コードがより簡潔になり、読みやすくなるという明白な理由を除いて。

4

9 に答える 9

35

ポイント 1 に焦点を当てますが、パフォーマンスの問題全体に 2 セントを与えることから始めます。差が大きくない限り、または使用量が多い場合を除いて、通常、追加されたときにユーザーにとって目に見える違いにならないマイクロ秒については気にしません。非集中的に呼び出されるメソッドを検討する場合にのみ気にしないことを強調します。パフォーマンスに関する特別な考慮事項があるのは、アプリケーション自体を設計する途中です。キャッシング、スレッドの使用、メソッドを呼び出す巧妙な方法 (複数の呼び出しを行うか、1 つだけ呼び出しを行うか)、接続をプールするかどうかなどに関心があります。生のパフォーマンスではなく、スケーラビリティに重点を置いています。1 人のユーザーの場合、1 ナノ秒単位でパフォーマンスが向上するかどうかは気にしません。

そうは言っても、ポイント1についての私の意見は次のとおりです。私は無名メソッドが大好きです。それらは私に大きな柔軟性とコードの優雅さを与えてくれます。匿名メソッドのもう 1 つの優れた機能は、コンテナー メソッドからローカル変数を直接使用できることです (もちろん、IL の観点からではなく、C# の観点から)。多くの場合、大量のコードを省いてくれます。匿名メソッドはいつ使用しますか? 私が必要としているコードが、他の場所では必要とされていない場合があります。2 つの異なる場所で使用する場合、再利用手法としてのコピー アンド ペーストは好きではないので、普通のデリゲートを使用します。したがって、shoosh が答えたように、コードの重複は良くありません。理論的には、匿名は IL のものではなく C# のトリックであるため、パフォーマンスの違いはありません。

匿名メソッドについて私が考えていることのほとんどは、ラムダ式に当てはまります。後者は、匿名メソッドを表すコンパクトな構文として使用できるからです。次の方法を想定しましょう。

public static void DoSomethingMethod(string[] names, Func<string, bool> myExpression)
{
    Console.WriteLine("Lambda used to represent an anonymous method");
    foreach (var item in names)
    {
        if (myExpression(item))
            Console.WriteLine("Found {0}", item);
    }
}

文字列の配列を受け取り、それらのそれぞれに対して、渡されたメソッドを呼び出します。そのメソッドが true を返す場合、"Found..." と表示されます。このメソッドは、次の方法で呼び出すことができます。

string[] names = {"Alice", "Bob", "Charles"};
DoSomethingMethod(names, delegate(string p) { return p == "Alice"; });

ただし、次の方法で呼び出すこともできます。

DoSomethingMethod(names, p => p == "Alice");

ラムダ式を使用する方がはるかに読みやすいため、両者の間に IL の違いはありません。繰り返しになりますが、これらはすべて C# コンパイラのトリック (JIT コンパイラのトリックではない) であるため、パフォーマンスへの影響はありません。匿名メソッドを使いすぎているとは思わなかったのと同様に、匿名メソッドを表すために Lambda 式を使いすぎているとは思いません。もちろん、繰り返されるコードにも同じロジックが適用されます。ラムダを実行せず、通常のデリゲートを使用してください。out または ref 引数の受け渡しなど、匿名メソッドまたはプレーン デリゲートに戻る他の制限があります。

Lambda 式のもう 1 つの優れた点は、まったく同じ構文で匿名メソッドを表す必要がないことです。ラムダ式は、ご想像のとおり、式を表すこともできます。次の例を見てください。

public static void DoSomethingExpression(string[] names, System.Linq.Expressions.Expression<Func<string, bool>> myExpression)
{
    Console.WriteLine("Lambda used to represent an expression");
    BinaryExpression bExpr = myExpression.Body as BinaryExpression;
    if (bExpr == null)
        return;
    Console.WriteLine("It is a binary expression");
    Console.WriteLine("The node type is {0}", bExpr.NodeType.ToString());
    Console.WriteLine("The left side is {0}", bExpr.Left.NodeType.ToString());
    Console.WriteLine("The right side is {0}", bExpr.Right.NodeType.ToString());
    if (bExpr.Right.NodeType == ExpressionType.Constant)
    {
        ConstantExpression right = (ConstantExpression)bExpr.Right;
        Console.WriteLine("The value of the right side is {0}", right.Value.ToString());
    }
 }

少し異なる署名に注意してください。2 番目のパラメーターは、デリゲートではなく式を受け取ります。このメソッドを呼び出す方法は次のとおりです。

DoSomethingExpression(names, p => p == "Alice");

これは、ラムダを使用して匿名メソッドを作成するときに行った呼び出しとまったく同じです。ここでの違いは、匿名メソッドを作成するのではなく、式ツリーを作成することです。これらの式ツリーのおかげで、ラムダ式を SQL に変換できます。これは、たとえば、Where、Select などの各句に対してエンジンで何かを実行する代わりに、Linq 2 SQL が行うことです。匿名メソッドを作成する場合でも、式を送信する場合でも、呼び出し構文は同じです。

于 2009-03-23T12:27:06.490 に答える
27

私の答えは人気がありません。

私は 3 つの理由から Lambda が 99% 常により良い選択であると信じています。

まず、開発者が頭が良いと仮定することはまったく問題ありません。他の答えには、あなた以外のすべての開発者が愚かであるという根本的な前提があります。そうではありません。

第二に、Lamdas (他) は最新の構文であり、明日は今日よりも一般的になるでしょう。プロジェクトのコードは、現在および新たに出現している慣習に沿っている必要があります。

第三に、「昔ながらの方法」でコードを書く方が簡単に思えるかもしれませんが、コンパイラにとってはそう簡単ではありません。これは重要です。コンパイラが改訂されているため、従来のアプローチを改善する機会はほとんどありません。それらを拡張するためにコンパイラーに依存するラムダ (その他) は、コンパイラーが時間の経過とともにそれらをより適切に処理するため、利益を得ることができます。

総括する:

  1. 開発者はそれを処理できます
  2. みんなやってる
  3. 将来性がある

繰り返しますが、これが一般的な答えではないことはわかっています。そして、「シンプル イズ ベスト」が私のモットーでもあると信じてください。メンテナンスは、どのソースにとっても重要な側面です。わかった。しかし、私たちはいくつかの決まり文句の経験則で現実を覆い隠していると思います。

// ジェリー

于 2011-12-20T22:27:36.427 に答える
19

コードの複製。
同じ匿名関数を複数回書いていることに気付いたとしても、それは 1 つであってはなりません。

于 2009-03-23T10:56:05.477 に答える
14

デリゲートの使用について話している場合、ラムダ メソッドと匿名メソッドに違いはないはずです。構文が異なるだけで同じです。また、(デリゲートとして使用される) 名前付きメソッドも、ランタイムの観点からは同一です。違いは、デリゲートの使用とインライン コードの使用の違いです。つまり、

list.ForEach(s=>s.Foo());
// vs.
foreach(var s in list) { s.Foo(); }

(後者の方が速いと思います)

同様に、メモリ内オブジェクト以外のことを話している場合、ラムダは (文字列を常に解析するのではなく) 型チェックを維持するという点で最も強力なツールの 1 つです。

確かに、単純なforeachwith コードが LINQ バージョンよりも高速になる場合があります。これは、実行する呼び出しが少なくなり、呼び出しにかかる時間が小さいが測定可能な時間になるためです。ただし、多くの場合、コードは単にボトルネックではなく、単純なコード (特にグループ化など) は数ナノ秒よりもはるかに価値があります。

また、.NET 4.0 では、ループやカンマなどのためのノードが追加Expressionされていることにも注意してください。言語はそれらをサポートしていませんが、ランタイムはサポートしています。Expressionこれは完全を期すためだけに言及してforeachいます。

于 2009-03-23T11:13:09.913 に答える
6

通常、パフォーマンスの違いは非常に小さいので (ループの場合は、2 番目の記事の結果を見れば明らかです (ところで、Jon Skeet が同様の記事をここに掲載しています))。パフォーマンスが絶対最大の非機能要件であり、実際にマイクロ最適化を行う必要があるソフトウェアを作成している場合を除き、パフォーマンス上の理由だけのソリューションです。

いつ何を選ぶ?状況にもよると思いますが、人にもよると思います。例として、通常の foreach ループよりも List.Foreach を優先する人もいます。個人的には後者の方が読みやすいので好みですが、これに反対する人がいるでしょうか?

于 2009-03-23T11:14:21.937 に答える
4

ラムダがその引数を別の関数に直接渡すときはいつでも。関数適用用のラムダを作成しないでください。

例:

var coll = new ObservableCollection<int>();
myInts.ForEach(x => coll.Add(x))

次のように優れています:

var coll = new ObservableCollection<int>();
myInts.ForEach(coll.Add)

主な例外は、C# の型推論がなんらかの理由で失敗する場合です (多くの場合、これが当てはまります)。

于 2009-03-23T17:57:04.670 に答える
4

経験則:

  1. 自然で読みやすいコードを書いてください。
  2. コードの重複を避けてください (ラムダ式には少し余分な注意が必要になる場合があります)。
  3. 問題がある場合にのみ最適化し、その問題が実際に何であるかをバックアップするデータのみを使用して最適化します。
于 2009-03-23T11:28:48.643 に答える
2

ラムダ式はクールです。古いdelegate構文よりも、匿名関数または式ツリーに変換できる、宣言からパラメーターの型が推測される、よりクリーンで簡潔であるなど、いくつかの利点があります。匿名関数が必要です。以前のスタイルが持っていた大きな利点の 1 つは、使用しない場合はパラメーター宣言を完全に省略できることです。お気に入り

Action<int> a = delegate { }; //takes one argument, but no argument specified

これは、何もしない空のデリゲートを宣言する必要がある場合に便利ですが、ラムダを使用しない十分な理由にはなりません。

ラムダを使用すると、迅速な匿名メソッドを記述できます。これにより、匿名メソッドが無意味になる場所、つまり名前付きメソッドがより意味のある場所では、ラムダが無意味になります。名前付きメソッドよりも、匿名メソッドは不利になる可能性があります(ラムダ式自体ではありませんが、最近ではラムダが匿名メソッドを広く表しているため、関連性があります):

  1. ロジックの重複につながる傾向があるため(多くの場合、再利用は困難です)

  2. 次のように、書き込む必要がない場合:

    //this is unnecessary 
    Func<string, int> f = x => int.Parse(x);
    
    //this is enough
    Func<string, int> f = int.Parse;
    
  3. 匿名のイテレータ ブロックを書き込むことは不可能であるためです。

    Func<IEnumerable<int>> f = () => { yield return 0; }; //impossible
    
  4. 再帰ラムダには、次のような奇抜な行がもう 1 行必要になるためです。

    Func<int, int> f = null;
    f = x => (x <= 1) ? 1 : x * f(x - 1);
    
  5. まあ、反射はちょっと面倒なので、それは議論の余地がありますね。

ポイント 3 を除けば、残りはラムダを使用しない強い理由ではありません。

ラムダ式と一緒に使用されることが多いため、デリゲートの不利な点については、このスレッドも参照してください。Func/Action

于 2013-12-20T12:51:18.337 に答える
2

再帰が必要な場合は、ラムダを使用しないでください。そうしないと、気が散ってしまいます

于 2009-03-23T11:35:46.253 に答える