web-dev-qa-db-ja.com

ラムダ式を使用しない場合

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

私たちはそれを使いすぎており、ラムダ式を使用することによるパフォーマンスへの影響を考慮していますか?

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

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

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

51
Binoj Antony

ポイント1に焦点を当てますが、パフォーマンスの問題全体に2セントを与えることから始めます。差が大きいか、使用量が集中しない限り、通常、追加してもユーザーに目に見える差が出ないほどのマイクロ秒は気にしません。私は、集中的でない呼び出されたメソッドを考慮するときだけ気にしないことを強調します。パフォーマンスに関する特別な考慮事項があるのは、アプリケーション自体の設計方法です。キャッシング、スレッドの使用、メソッドを呼び出す巧妙な方法(複数の呼び出しを行うか、1つの呼び出しのみを試みるか)、接続をプールするかどうかなどに関心があります。実際、通常は'生のパフォーマンスではなく、スケーラビリティに焦点を合わせます。単一のユーザーでナノ秒の小さなスライスでそれが実行されるかどうかは気にしませんが、影響に気付かずに大量の同時ユーザーでシステムをロードする機能があることを大いに気にしています。

とはいえ、ここでポイント1についての私の意見を述べます。匿名のメソッドが大好きです。彼らは私に大きな柔軟性とコードの優雅さを与えます。匿名メソッドのもう1つの優れた機能は、コンテナメソッドからローカル変数を直接使用できることです(もちろん、ILの観点からではなく、C#の観点から)。彼らは私にしばしばコードの負荷をspareしみません。匿名メソッドはいつ使用しますか?一度だけ、必要なコードは他の場所では必要ありません。 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には両者の間に違いはありません。Lambda式を使用する方がはるかに読みやすいからです。繰り返しますが、これらはすべてC#コンパイラのトリックであるため(JITコンパイラのトリックではありません)、パフォーマンスへの影響はありません。匿名メソッドを過度に使用しているとは思わなかったように、匿名メソッドを表すためにLambda式を過度に使用しているとは感じません。もちろん、同じロジックが繰り返しコードに適用されます。ラムダを実行せず、通常のデリゲートを使用します。 outやref引数の受け渡しなど、匿名メソッドまたはプレーンデリゲートに戻る他の制限があります。

Lambda式に関するその他の良い点は、まったく同じ構文で匿名メソッドを表す必要がないことです。ラムダ式は、あなたが推測した式を表すこともできます。次の例をご覧ください。

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に変換できます。これは、Linq 2 SQLが行うことです。たとえば、Where、Selectなどの各句に対してエンジンで処理を実行する代わりに実行します。無名メソッドを作成する場合でも式を送信する場合でも、呼び出し構文は同じです。

32
Rui Craveiro

私の答えは一般的ではありません。

ラムダは3つの理由から常に99%がより良い選択だと思います。

第1に、開発者がスマートであると想定することにはまったく何の問題もありません。他の答えには、あなた以外のすべての開発者が愚かであるという前提があります。そうではありません。

第二に、Lamdas(など)は最新の構文です。明日は、現在よりも一般的になります。プロジェクトのコードは、現在の規則と新しい規則から流れる必要があります。

第三に、「昔ながらの方法」でコードを書くのは簡単に思えるかもしれませんが、コンパイラーにとっては簡単ではありません。これは重要です。従来のアプローチでは、コンパイラが改訂されるため、改善の余地はほとんどありません。コンパイラーを使用してそれらを拡張するLambdas(など)は、コンパイラーが時間とともにそれらをより適切に処理するので、メリットがあります。

総括する:

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

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

//ジェリー

25

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

16
shoosh

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

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

(後者の方が高速になると予想される場合)

同様に、インメモリオブジェクト以外の何かotherについて話している場合、ラムダは型チェックを維持する上で最も強力なツールの1つです(むしろ常に文字列を解析するよりも)。

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

.NET 4.0には、ループ、コンマなどのようなものに対して 追加のExpressionノード があることに注意してください。言語はそれらをサポートしていませんが、ランタイムはサポートしています。これは完全を期すためだけに言及しています:Expressionが行うような手動のforeach構築を使用すべきだとは確かに言っていません!

14
Marc Gravell

パフォーマンスの違いは通常非常に小さいと思います(そしてループの場合、2番目の記事の結果を見ると、明らかにJon Skeetには同様の記事があります here ) )パフォーマンスが絶対的であり、ナンバーワンの非機能要件であるソフトウェアを記述している場合を除き、パフォーマンス上の理由だけでソリューションを選択することはほとんどありません。微小最適化を行います。

何を選択するのですか?状況だけでなく、人にも依存すると思います。ほんの一例として、通常のforeachループよりもList.Foreachを好む人がいます。私は個人的に後者を好みます。通常は読みやすいのですが、これに反対するのは誰ですか?

6
Razzie

経験則:

  1. 自然で読みやすいコードを作成してください。
  2. コードの重複を避けます(ラムダ式には少し余分な注意が必要な場合があります)。
  3. 問題がある場合にのみ最適化し、その問題が実際に何であるかをバックアップするためのデータのみを使用して最適化します。
4
Dustin Campbell

ラムダはその引数を別の関数に直接渡すだけです。関数アプリケーションのラムダを作成しないでください。

例:

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

より良いです:

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

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

4
MichaelGG

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

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

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

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

  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を除き、残りはstrong理由使用しないラムダではありません。

こちらもご覧ください threadFunc/Actionデリゲート。ラムダ式と一緒に使用されることが多いためです。

2
nawfal

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

2