web-dev-qa-db-ja.com

.NET Frameworkのラムダとデリゲートの違いは何ですか?

私はこの質問を頻繁に尋ねられ、違いを最もよく説明する方法についていくつかの意見を求めたいと思いました。

73
ScottKoon

それらは実際には2つの非常に異なるものです。 「デリゲート」は実際にはメソッドまたはラムダへの参照を保持する変数の名前であり、ラムダは永続的な名前のないメソッドです。

ラムダは、いくつかの微妙な違いを除いて、他のメソッドと非常によく似ています。

  1. 通常のメソッドは "statement" で定義され、永続的な名前に関連付けられていますが、ラムダは "expression" で「オンザフライ」で定義され、永続的ではありません名前。
  2. 一部のラムダは.NET式ツリーで使用できますが、メソッドでは使用できません。

デリゲートは次のように定義されます:

delegate Int32 BinaryIntOp(Int32 x, Int32 y);

BinaryIntOp型の変数には、シグネチャが同じである限り、メソッドまたはlabmdaのいずれかを割り当てることができます。2つのInt32引数と1つのInt32戻り値です。

ラムダは次のように定義できます。

BinaryIntOp sumOfSquares = (a, b) => a*a + b*b;

もう1つ注意すべき点は、汎用のFuncおよびAction型は「ラムダ型」と見なされることが多いものの、他のデリゲートとまったく同じであるということです。それらの良い点は、必要なデリゲートのタイプの名前を基本的に定義していることです(最大4つのパラメーターですが、独自のパラメーターをさらに追加できます)。したがって、多種多様なデリゲート型を使用している場合は、FuncとActionを使用することで、デリゲート宣言でコードが煩雑になるのを回避できます。

次に、FuncとActionが「ラムダだけではない」ことを示します。

Int32 DiffOfSquares(Int32 x, Int32 y)
{
  return x*x - y*y;
}

Func<Int32, Int32, Int32> funcPtr = DiffOfSquares;

知っておくと便利なもう1つのことは、同じシグネチャを持つが、名前が異なるデリゲート型(メソッド自体ではない)が暗黙的に相互にキャストされないことです。これには、FuncおよびActionデリゲートが含まれます。ただし、シグニチャーが同一の場合は、それらを明示的にキャストできます。

余計なことをします... C#では、ラムダとデリゲートを使用して、関数に柔軟性があります。ただし、C#には「ファーストクラスの関数」はありません。デリゲート変数に割り当てられた関数の名前を使用して、その関数を表すオブジェクトを基本的に作成できます。しかし、それは本当にコンパイラのトリックです。関数名の後にドットを続けてステートメントを記述した場合(つまり、関数自体にメンバーアクセスを実行しようとした場合)、参照するメンバーがないことがわかります。 Objectのものでもありません。これにより、プログラマーは、任意の関数で呼び出すことができる拡張メソッドを追加するなど、有用な(そしてもちろん潜在的に危険な)ことを実行できなくなります。できる最善の方法は、Delegateクラス自体を拡張することです。これも確かに便利ですが、それほど多くはありません。

更新:匿名デリゲートとメソッドおよびラムダの違いを示す Karg's answer も参照してください。

更新2: James Hart は非常に技術的ですが重要ですが、ラムダとデリゲートは.NETエンティティではないことに注意してください(つまり、CLRにはデリゲートまたはラムダの概念がありません)。言語構成。

86
Chris Ammerman

質問は少しあいまいです。これは、あなたが得ている答えの幅広い格差を説明しています。

あなたは実際に.NETフレームワークのラムダとデリゲートの違いは何かを尋ねました。それは多くのことの1つかもしれません。あなたは尋ねていますか?

  • C#(またはVB.NET)言語でのラムダ式と匿名デリゲートの違いは何ですか?

  • .NET 3.5のSystem.Linq.Expressions.LambdaExpressionオブジェクトとSystem.Delegateオブジェクトの違いは何ですか?

  • あるいは、これらの両極端の中間または周辺のどこかに何かありますか?

一部の人々は、「C#Lambda式と.NET System.Delegateの違いは何ですか?」という質問に対して答えを出そうとしているようですが、あまり意味がありません。

.NETフレームワーク自体は、匿名のデリゲート、ラムダ式、またはクロージャの概念を理解していません。これらはすべて言語仕様で定義されているものです。 C#コンパイラーが、匿名メソッドの定義を、クロージャー状態を保持するためのメンバー変数を持つ生成されたクラスのメソッドに変換する方法について考えてください。 .NETにとって、デリゲートについては何も匿名ではありません。それは、それを書いているC#プログラマにとっては匿名です。これは、デリゲート型に割り当てられたラムダ式についても同様です。

どの.NET [〜#〜]が行う[〜#〜]デリゲートの概念を理解する-メソッドシグネチャを記述する型であり、そのインスタンスは特定のオブジェクトの特定のメソッドへのバインドされた呼び出しのいずれかを表す、または特定のタイプの任意のオブジェクトに対して呼び出すことができる特定のタイプの特定のメソッドへのバインドされていない呼び出し。このような型はすべてSystem.Delegateを継承します。

.NET 3.5ではSystem.Linq.Expressions名前空間も導入されています。これには、コード式を記述するためのクラスが含まれているため、特定の型またはオブジェクトのメソッドへのバインドされた呼び出しまたはバインドされていない呼び出しを表すことができます。次に、LambdaExpressionインスタンスを実際のデリゲートにコンパイルできます(これにより、式の構造に基づく動的メソッドがコード生成され、それへのデリゲートポインターが返されます)。

C#では、ラムダ式を上記の型の変数に割り当てることにより、System.Expressions.Expression型のインスタンスを生成できます。これにより、実行時に式を構築するための適切なコードが生成されます。

もちろん、C#でラムダ式と無名メソッドの違いを尋ねるwereとすると、結局のところ、これはほとんど無関係であり、その場合の主な違いは簡潔さです。パラメータを気にせず、値を返す予定がない場合は匿名のデリゲートに向け、型推論されたパラメータと戻り型が必要な場合はラムダに向けます。

また、ラムダ式は式の生成をサポートしています。

29
James Hart

1つの違いは、ラムダが正確な署名と一致する必要があるのに対し、匿名デリゲートはパラメーターを省略できることです。与えられた:

public delegate string TestDelegate(int i);

public void Test(TestDelegate d)
{}

次の4つの方法で呼び出すことができます(2行目には、パラメーターを持たない匿名デリゲートがあることに注意してください)。

Test(delegate(int i) { return String.Empty; });
Test(delegate { return String.Empty; });
Test(i => String.Empty);
Test(D);

private string D(int i)
{
    return String.Empty;
}

パラメータを持たないラムダ式や、パラメータを持たないメソッドを渡すことはできません。これらは許可されていません:

Test(() => String.Empty); //Not allowed, lambda must match signature
Test(D2); //Not allowed, method must match signature

private string D2()
{
    return String.Empty;
}
19
Karg

デリゲートは関数ポインター/メソッドポインター/コールバック(選択してください)と同等であり、ラムダはかなり単純化された無名関数です。少なくともそれは私が人々に言うことです。

13
Dan Shield

これについてはあまり経験はありませんが、デリゲートは関数のラッパーであるのに対し、ラムダ式自体は匿名関数であると説明します。

3
chessguy

デリゲートは、常に基本的には単なる関数ポインターです。ラムダはデリゲートに変換できますが、LINQ式ツリーに変換することもできます。例えば、

Func<int, int> f = x => x + 1;
Expression<Func<int, int>> exprTree = x => x + 1;

1行目はデリゲートを生成し、2行目は式ツリーを生成します。

3

デリゲートは、特定のパラメーターリストと戻り値の型を持つメソッドへの参照です。オブジェクトが含まれる場合と含まれない場合があります。

ラムダ式は、無名関数の形式です。

2
Peter Ritchie

ラムダはデリゲートの単純な構文糖です。コンパイラーはラムダをデリゲートに変換します。

これらは同じだと思います:

Delegate delegate = x => "hi!";
Delegate delegate = delegate(object x) { return "hi";};
2
Gilligan

デリゲートは関数のシグネチャです。何かのようなもの

delegate string MyDelegate(int param1);

デリゲートは本体を実装していません。

ラムダは、デリゲートの署名と一致する関数呼び出しです。上記のデリゲートでは、次のいずれかを使用できます。

(int i) => i.ToString();
(int i) => "ignored i";
(int i) => "Step " + i.ToString() + " of 10";

ただし、Delegateタイプの名前は不適切です。タイプDelegateのオブジェクトを作成すると、実際には、ラムダ、静的メソッド、クラスメソッドなどの関数を保持できる変数が作成されます。

2
Steve Cooper

質問が「ラムダとanonymousデリゲートの違いは何ですか?」ここでのすべての答えのうち、1人だけが正しく理解しました。主な違いは、ラムダを使用して式ツリーとデリゲートを作成できることです。

詳細はMSDNで読むことができます: http://msdn.Microsoft.com/en-us/library/bb397687.aspx

2
Philip Beber

デリゲートは関数ポインタのキューであり、デリゲートを呼び出すと複数のメソッドが呼び出される場合があります。ラムダは本質的には匿名のメソッド宣言であり、それがどのコンテキストで使用されているかによって、コンパイラーによって異なる解釈がされる可能性があります。

ラムダ式をメソッドとしてポイントするデリゲートをデリゲートにキャストするか、特定のデリゲートタイプを予期するメソッドにパラメーターとして渡すと、コンパイラーがキャストします。 LINQステートメント内でラムダを使用すると、ラムダはコンパイラーによって単なるデリゲートではなく式ツリーに変換されます。

違いは、ラムダは別の式の内部でメソッドを定義するための簡潔な方法であり、デリゲートは実際のオブジェクト型であることです。

1
justin.m.chase

デリゲートは、実際には関数の構造型付けにすぎません。名義の型付けと、インターフェイスまたは抽象クラスを実装する匿名クラスの実装でも同じことができますが、関数が1つだけ必要な場合は、多くのコードになります。

ラムダは、1930年代のアロンツォ教会のラムダ計算のアイデアに由来しています。関数を作成する匿名の方法です。それらは関数の作成に特に役立ちます

したがって、ラムダはデリゲートの構文糖質であると言う人もいるでしょうが、デリゲートはc#で人々をラムダに緩和するための架け橋だと思います。

1
Steve g

私はあなたの質問が.NETではなくc#に関係していると思います。これは、質問の曖昧さのためです。

normal、いわゆるgenericデリゲート、cf後で)デリゲートは、たとえばc ++では、関数ポインタ型の一種のc ++ typedefと見なす必要があります。

R (*thefunctionpointer) ( T ) ;

typedefは型thefunctionpointerです。これは、型Tのオブジェクトを受け取り、型Rのオブジェクトを返す関数へのポインターの型です。次のように使用します。

thefunctionpointer = &thefunction ;
R r = (*thefunctionpointer) ( t ) ; // where t is of type T

ここで、thefunctionTを取り、Rを返す関数です。

C#ではあなたは行くでしょう

delegate R thedelegate( T t ) ; // and yes, here the identifier t is needed

そして、あなたはこのようにそれを使うでしょう:

thedelegate thedel = thefunction ;
R r = thedel ( t ) ; // where t is of type T

ここで、thefunctionTを取り、Rを返す関数です。これは、通常のデリゲートと呼ばれるデリゲート用です。

これで、ジェネリックなデリゲートであるc#のジェネリックデリゲートieがあり、いわば「テンプレート化」され、それによってc ++式が使用されます。 。彼らはこのように定義されています:

public delegate TResult Func<in T, out TResult>(T arg);

そして、あなたはこのようにそれらを使うことができます:

Func<double, double> thefunctor = thefunction2; // call it a functor because it is
                                                // really as a functor that you should
                                                // "see" it
double y = thefunctor(2.0);

ここで、thefunction2は、引数として取り、doubleを返す関数です。

ここで、thefunction2の代わりに、現時点ではまだ定義されていない「関数」をステートメントで使用し、後で使用しないことを想像してください。次に、c#を使用すると、この関数のを使用できます。表現とは、「数学」(またはプログラムに固執するための機能的)な表現を意味します。たとえば、double xassociatedoublex*x。数学では、これを "\ mapsto"ラテックス記号 を使用して記述します。 C#では、関数表記が借用されています:=>。例えば ​​:

Func<double, double> thefunctor = ( (double x) => x * x ); // outer brackets are not
                                                           // mandatory

(double x) => x * xexpression です。これはタイプではありませんが、デリゲート(ジェネリックかどうか)はタイプです。

道徳?最後に、デリゲート(またはジェネリックデリゲート)とは何ですか?何か! this および that を参照してください。

ここに私が私の足りないブログにしばらく載せた例があります。ワーカースレッドからラベルを更新したいとします。デリゲート、アノンデリゲート、および2種類のラムダを使用して、そのラベルを1から50に更新する方法の例を4つ持っています。

 private void button2_Click(object sender, EventArgs e) 
     { 
         BackgroundWorker worker = new BackgroundWorker(); 
         worker.DoWork += new DoWorkEventHandler(worker_DoWork); 
         worker.RunWorkerAsync(); 
     } 

     private delegate void UpdateProgDelegate(int count); 
     private void UpdateText(int count) 
     { 
         if (this.lblTest.InvokeRequired) 
         { 
             UpdateProgDelegate updateCallBack = new UpdateProgDelegate(UpdateText); 
             this.Invoke(updateCallBack, new object[] { count }); 
         } 
         else 
         { 
             lblTest.Text = count.ToString(); 
         } 
     } 

     void worker_DoWork(object sender, DoWorkEventArgs e) 
     {   
         /* Old Skool delegate usage.  See above for delegate and method definitions */ 
         for (int i = 0; i < 50; i++) 
         { 
             UpdateText(i); 
             Thread.Sleep(50); 
         } 

         // Anonymous Method 
         for (int i = 0; i < 50; i++) 
         { 
             lblTest.Invoke((MethodInvoker)(delegate() 
             { 
                 lblTest.Text = i.ToString(); 
             })); 
             Thread.Sleep(50); 
         } 

         /* Lambda using the new Func delegate. This lets us take in an int and 
          * return a string.  The last parameter is the return type. so 
          * So Func<int, string, double> would take in an int and a string 
          * and return a double.  count is our int parameter.*/ 
         Func<int, string> UpdateProgress = (count) => lblTest.Text = count.ToString(); 
         for (int i = 0; i < 50; i++) 
         { 
             lblTest.Invoke(UpdateProgress, i); 
             Thread.Sleep(50); 
         } 

         /* Finally we have a totally inline Lambda using the Action delegate 
          * Action is more or less the same as Func but it returns void. We could 
          * use it with parameters if we wanted to like this: 
          * Action<string> UpdateProgress = (count) => lblT…*/ 
         for (int i = 0; i < 50; i++) 
         { 
             lblTest.Invoke((Action)(() => lblTest.Text = i.ToString())); 
             Thread.Sleep(50); 
         } 
     }
0
Echostorm

ここでいくつかの基本。 「デリゲート」は、実際にはメソッドまたはラムダへの参照を保持する変数の名前です

これは匿名メソッドです-

(string testString) => { Console.WriteLine(testString); };

無名メソッドには名前がないため、これらのメソッドまたは式の両方を割り当てることができるデリゲートが必要です。例:.

delegate void PrintTestString(string testString); // declare a delegate

PrintTestString print = (string testString) => { Console.WriteLine(testString); }; 
print();

ラムダ式と同じです。通常、それらを使用するにはデリゲートが必要です

s => s.Age > someValue && s.Age < someValue    // will return true/false

Funcデリゲートを使用して、この式を使用できます。

Func< Student,bool> checkStudentAge = s => s.Age > someValue && s.Age < someValue ;

bool result = checkStudentAge ( Student Object);
0

ラムダはデリゲートの簡略化されたバージョンです。それらには、匿名デリゲートのような closure のプロパティの一部がありますが、暗黙のタイピングを使用することもできます。このようなラムダ:

something.Sort((x, y) => return x.CompareTo(y));

デリゲートでできることよりもはるかに簡潔です:

something.Sort(sortMethod);
...

private int sortMethod(SomeType one, SomeType two)
{
    one.CompareTo(two)
}
0
Michael Meadows