C#ラムダ式:なぜそれらを使用する必要があるのですか?


309

Microsoft Lambda Expressionのドキュメントをすぐに読みました。

ただし、この種の例は、理解を深めるのに役立ちました。

delegate int del(int i);
del myDelegate = x => x * x;
int j = myDelegate(5); //j = 25

それでも、なぜそれがそのようなイノベーションなのか、私にはわかりません。「メソッド変数」が終了したときに死ぬのは単なるメソッドですよね?実際の方法の代わりにこれを使用する必要があるのはなぜですか?


3
このページにdelegate
アクセス

回答:


281

ラムダ式は匿名デリゲートのより単純な構文であり、匿名デリゲートを使用できるあらゆる場所で使用できます。ただし、その逆は当てはまりません。ラムダ式は、LINQ to SQLのような多くの魔法を可能にする式ツリーに変換できます。

以下は、匿名デリゲートを使用したLINQ to Objects式の例であり、次にラムダ式を使用して、それらがどれほど簡単に見えるかを示しています。

// anonymous delegate
var evens = Enumerable
                .Range(1, 100)
                .Where(delegate(int x) { return (x % 2) == 0; })
                .ToList();

// lambda expression
var evens = Enumerable
                .Range(1, 100)
                .Where(x => (x % 2) == 0)
                .ToList();

ラムダ式と匿名デリゲートには、個別の関数を作成するよりも利点があります。それらは、関数にパラメーター追加したり、使い捨てオブジェクトを作成したりせずにローカル状態を関数渡すことができるクロージャーを実装します。

式ツリーは、C#3.0の非常に強力な新機能で、APIは、実行可能なメソッドへの参照を取得するだけでなく、式の構造を調べることができます。APIはデリゲートパラメーターをパラメーターにする必要があるだけExpression<T>で、コンパイラーは匿名デリゲートの代わりにラムダから式ツリーを生成します。

void Example(Predicate<int> aDelegate);

次のように呼ばれます:

Example(x => x > 5);

になる:

void Example(Expression<Predicate<int>> expressionTree);

後者には、式を説明する抽象構文ツリーの表現が渡されますx > 5。LINQ to SQLは、この動作に依存して、C#式をサーバー側でのフィルタリング/順序付けなどに必要なSQL式に変換できます。


1
クロージャーがなければ、静的メソッドをコールバックとして使用できますが、それらのメソッドをいくつかのクラスで定義する必要があり、ほぼ確実に、そのようなメソッドのスコープを意図した使用法を超えて拡大します。
DK。

10
FWIW、匿名のデリゲートを持つクロージャーを持つことができるので、厳密にラムダは必要ありません。ラムダは匿名のデリゲートよりも非常に読みやすく、Linqを使用しないと目が出血します。
Benjol 2009年

138

匿名の関数と式は、完全なメソッドを作成するために必要な追加の作業の恩恵を受けない1回限りのメソッドに役立ちます。

この例を考えてみましょう:

 string person = people.Find(person => person.Contains("Joe"));

 public string FindPerson(string nameContains, List<string> persons)
 {
     foreach (string person in persons)
         if (person.Contains(nameContains))
             return person;
     return null;
 }

これらは機能的に同等です。


8
このラムダ式を処理するためにFind()メソッドはどのように定義されますか?
Patrick Desjardins、

3
Predicate <T>は、Findメソッドが期待しているものです。
Darren Kopp、

1
ラムダ式はPredicate <T>のコントラクトに一致するため、Find()メソッドはそれを受け入れます。
ジョセフデイグル

「string person = people.Find(persons => people.Contains( "Joe"));」という意味ですか?
Gern Blanston、2009

5
@FKCoder、いいえ、そうではありませんが、「string person = people.Find(p => p.Contains( "Joe"));」と言った方が明確だったかもしれません。
Benjol、2009年

84

別のコントロールを使用して、いくつかのコントロールのイベントのハンドラーを宣言したい状況で、それらが役立つことがわかりました。通常、これを行うには、コントロールの参照をクラスのフィールドに格納して、作成されたものとは異なるメソッドでそれらを使用できるようにする必要があります。

private ComboBox combo;
private Label label;

public CreateControls()
{
    combo = new ComboBox();
    label = new Label();
    //some initializing code
    combo.SelectedIndexChanged += new EventHandler(combo_SelectedIndexChanged);
}

void combo_SelectedIndexChanged(object sender, EventArgs e)
{
    label.Text = combo.SelectedValue;
}

ラムダ式のおかげで、次のように使用できます。

public CreateControls()
{
    ComboBox combo = new ComboBox();
    Label label = new Label();
    //some initializing code
    combo.SelectedIndexChanged += (s, e) => {label.Text = combo.SelectedValue;};
}

はるかに簡単です。


最初の例では、senderをキャストして値を取得しませんか?
Andrew

@Andrew:この単純な例では、問題のコンポーネントが1つしかなく、フィールドを直接使用するとキャストが保存されるため、送信者を使用する必要がなく、明確性が向上します。実際のシナリオでは、個人的には代わりに送信者を使用することも好みます。通常、可能な場合は複数のイベントに1つのイベントハンドラーを使用するため、実際の送信者を識別する必要があります。
Chris Tophski、2018年

35

ラムダのクリーンアップされたC#2.0の匿名デリゲート構文...たとえば

Strings.Find(s => s == "hello");

このようにC#2.0で行われました:

Strings.Find(delegate(String s) { return s == "hello"; });

機能的には、これらはまったく同じことを行います。構文ははるかに簡潔です。


3
それらはまったく同じではありません-@Neil Williamsが指摘するように、式ツリーを使用してラムダのASTを抽出できますが、匿名メソッドは同じように使用できません。
ljs

これはラムダの他の多くの利点の1つです。匿名メソッドよりもコードを理解するのに役立ちます。確かにラムダを作成するつもりはありませんが、これらはより頻繁に使用できるシナリオです。
グルジ

29

これはラムダ式を使用する1つの方法にすぎません。デリゲートを使用できる場所ならどこでもラムダ式を使用できます。これにより、次のようなことが可能になります。

List<string> strings = new List<string>();
strings.Add("Good");
strings.Add("Morning")
strings.Add("Starshine");
strings.Add("The");
strings.Add("Earth");
strings.Add("says");
strings.Add("hello");

strings.Find(s => s == "hello");

このコードは、「hello」という単語に一致するエントリをリストで検索します。これを行うもう1つの方法は、次のように、実際にデリゲートをFindメソッドに渡すことです。

List<string> strings = new List<string>();
strings.Add("Good");
strings.Add("Morning")
strings.Add("Starshine");
strings.Add("The");
strings.Add("Earth");
strings.Add("says");
strings.Add("hello");

private static bool FindHello(String s)
{
    return s == "hello";
}

strings.Find(FindHello);

編集

C#2.0では、これは匿名デリゲート構文を使用して実行できます。

  strings.Find(delegate(String s) { return s == "hello"; });

Lambdaはその構文を大幅にクリーンアップしました。


2
@ジョナサンホランド:匿名のデリゲート構文を編集して追加してくれてありがとう。それは例をうまく完了します。
スコットドーマン

匿名のデリゲートとは何ですか?//申し訳ありませんが、c#は初めてです
HackerMan、2014

1
@HackerMan、匿名デリゲートを「名前」のない関数と考えてください。関数を定義していますが、関数は入力と出力を持つことができますが、名前であるため、直接参照することはできません。上記のコードでは、メソッド(を受け取り、stringを返すbool)をFindメソッド自体のパラメーターとして定義しています。
スコットドーマン2014

22

マイクロソフトは、ラムダ式と呼ばれる匿名のデリゲートを作成する、よりクリーンで便利な方法を提供してくれました。ただし、このステートメントの式の部分にはあまり注意が払われていません。Microsoftは、ラムダ式に基づいて式ツリーを作成するためのクラスを含む名前空間全体System.Linq.Expressionsをリリースしました。式ツリーは、ロジックを表すオブジェクトで構成されています。たとえば、x = y + zは、.Netの式ツリーの一部である可能性がある式です。次の(簡単な)例を考えてみます。

using System;
using System.Linq;
using System.Linq.Expressions;


namespace ExpressionTreeThingy
{
    class Program
    {
        static void Main(string[] args)
        {
            Expression<Func<int, int>> expr = (x) => x + 1; //this is not a delegate, but an object
            var del = expr.Compile(); //compiles the object to a CLR delegate, at runtime
            Console.WriteLine(del(5)); //we are just invoking a delegate at this point
            Console.ReadKey();
        }
    }
}

この例は簡単です。また、「式を作成して実行時にコンパイルするのではなく、デリゲートを直接作成することもできたので、これは役に立たない」とお考えのことと思います。そして、あなたは正しいでしょう。しかし、これは式ツリーの基盤を提供します。Expressions名前空間ではいくつかの式を使用でき、独自の式を作成できます。これは、設計時またはコンパイル時にアルゴリズムがどうあるべきかを正確に知らない場合に役立つかもしれないことがわかると思います。これを使って関数電卓を書く例をどこかで見ました。ベイジアンシステムや遺伝的プログラミングにも使用できます。(AI)。私のキャリアの中で何度か、ユーザーが利用可能なデータを操作するために簡単な式(追加、サブトラクションなど)を入力できるExcelのような機能を作成する必要がありました。.Net 3.5より前のバージョンでは、C#の外部にあるスクリプト言語を使用するか、リフレクションでコード生成機能を使用して.Netコードをオンザフライで作成する必要がありました。次に、式ツリーを使用します。


12

特定の場所で1回だけ使用されるメソッドを、それらが使用される場所から遠く離れた場所で定義する必要がないようにします。良い使い方は、並べ替えなどの一般的なアルゴリズムのコンパレータとして使用できます。これにより、カスタムソート関数を定義して、どこから何をソートしているかを確認するのではなく、ソートを呼び出すことができます。

そしてそれは本当にイノベーションではありません。LISPには約30年以上ラムダ関数がありました。


6

また、メソッドに作用する汎用コードを記述する際にラムダ式の使用を見つけることができます。

例:メソッド呼び出しにかかる時間を計算するための汎用関数。(つまりActionここに)

public static long Measure(Action action)
{
    Stopwatch sw = new Stopwatch();
    sw.Start();
    action();
    sw.Stop();
    return sw.ElapsedMilliseconds;
}

そして、次のようにラムダ式を使用して上記のメソッドを呼び出すことができます、

var timeTaken = Measure(() => yourMethod(param));

式を使用すると、メソッドから戻り値を取得し、同様にパラメータを出力できます

var timeTaken = Measure(() => returnValue = yourMethod(param, out outParam));

5

ラムダ式は、匿名メソッドを表す簡潔な方法です。匿名メソッドとラムダ式の両方でメソッドの実装をインラインで定義できますが、匿名メソッドでは、メソッドのパラメーターの型と戻り値の型を明示的に定義する必要があります。ラムダ式は、C#3.0の型推論機能を使用して、コンパイラーがコンテキストに基づいて変数の型を推論できるようにします。タイピングの手間が省けて非常に便利です。


5

ラムダ式は、デリゲートインスタンスの代わりに記述された匿名メソッドのようなものです。

delegate int MyDelagate (int i);
MyDelagate delSquareFunction = x => x * x;

ラムダ式を考えます x => x * x;

入力パラメーター値はx(=>の左側)

関数ロジックはx * x(=>の右側)

ラムダ式のコードは、式ではなくステートメントブロックにすることができます。

x => {return x * x;};

注:Funcは事前定義された汎用デリゲートです。

    Console.WriteLine(MyMethod(x => "Hi " + x));

    public static string MyMethod(Func<string, string> strategy)
    {
        return strategy("Lijo").ToString();
    }

参考文献

  1. デリゲートとインターフェイスをどのように交換して使用できますか?

4

多くの場合、機能は1か所でしか使用しないため、メソッドを作成するとクラスが雑然とするだけです。


3

これは、小さな操作を実行し、使用される場所の近くに配置する方法です(変数をその使用ポイントの近くで宣言するのとは異なります)。これにより、コードが読みやすくなります。式を匿名化することで、関数が他の場所で使用され、「拡張」されるように変更された場合、誰かがクライアントコードを解読することがさらに困難になります。

同様に、なぜforeachを使用する必要があるのですか?単純なforループを使用するか、IEnumerableを直接使用するだけで、すべてをforeachで実行できます。回答:必要ありませんが、コードが読みやすくなります。


0

革新は型の安全性と透明性にあります。ラムダ式のタイプは宣言しませんが、それらは推定され、コード検索、静的分析、リファクタリングツール、およびランタイムリフレクションで使用できます。

たとえば、SQLを使用する前に、SQLインジェクション攻撃を受ける可能性がありました。これは、ハッカーが通常は数値が予想される場所に文字列を渡したためです。これで、保護されているLINQラムダ式を使用します。

純粋なデリゲートでLINQ APIを構築することはできません。これは、式ツリーを評価する前に結合する必要があるためです。

2016年、ほとんどの一般的な言語はラムダ式をサポートしており、C#は主流の命令型言語の中でこの進化の先駆者の1人でした。


0

これはおそらくラムダ式を使用する理由についての最良の説明です-> https://youtu.be/j9nj5dTo54Q

要約すると、コードの可読性を向上させ、コードを複製するのではなく再利用してエラーの可能性を減らし、舞台裏で行われている最適化を活用することです。


0

ラムダ式と無名関数の最大の利点は、ライブラリ/フレームワークのクライアント(プログラマー)が、指定されたライブラリ/フレームワーク(LINQ、ASP.NET Core、および他の多くの)通常の方法ではできない方法で。ただし、その強さは、単一のアプリケーションプログラマーにとっては明らかではありませんが、後でライブラリコードの動作を構成する他のユーザーやライブラリを使用するユーザーが使用するライブラリを作成するものにとっては明らかです。したがって、ラムダ式を効果的に使用するコンテキストは、ライブラリ/フレームワークの使用/作成です。

また、1回限りの使用コードについて説明しているため、コードが複雑になるクラスのメンバーである必要はありません。クラスオブジェクトの操作を構成するたびに、フォーカスが明確でないクラスを宣言する必要があると想像してください。

弊社のサイトを使用することにより、あなたは弊社のクッキーポリシーおよびプライバシーポリシーを読み、理解したものとみなされます。
Licensed under cc by-sa 3.0 with attribution required.