.NET Framework のラムダとデリゲートの違いは何ですか?
質問
この質問をよく受けるので、違いを最もよく説明する方法について意見を求めたいと思いました。
解決
実際には、これら 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 つのパラメーター。ただし、独自のパラメーターをさらに追加することもできます)。したがって、さまざまなデリゲート型を 1 回だけ使用している場合は、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 クラス自体を拡張することです。これも確かに便利ですが、それほどではありません。
アップデート:こちらもご覧ください カーグの答え 匿名の参加者と匿名の参加者の違いを示しています。メソッドとラムダ。
更新 2: ジェームス・ハート 非常に技術的ではありますが、ラムダとデリゲートは .NET エンティティではないことに注意してください (つまり、.NET エンティティではありません)。CLR にはデリゲートやラムダの概念はありませんが、それらはフレームワークと言語の構成要素です。
他のヒント
この質問は少しあいまいであり、それが得られる答えに大きな差があることを説明しています。
あなたは実際に、.NET Framework のラムダとデリゲートの違いは何かと尋ねました。それはいくつかあることのうちの 1 つかもしれません。あなたは次のように尋ねていますか?
C# (または VB.NET) 言語のラムダ式と匿名デリゲートの違いは何ですか?
.NET 3.5 の System.Linq.Expressions.LambdaExpression オブジェクトと System.Delegate オブジェクトの違いは何ですか?
それともそれらの両極端の中間にあるもの、あるいはその周辺にあるものでしょうか?
「C# の Lambda 式と .NET System.Delegate の違いは何ですか?」という質問に答えようとしている人もいるようですが、これはあまり意味がありません。
.NET Framework 自体は、匿名デリゲート、ラムダ式、クロージャの概念を理解していません。これらはすべて言語仕様によって定義されています。C# コンパイラが匿名メソッドの定義を、クロージャ状態を保持するメンバー変数を持つ生成されたクラスのメソッドにどのように変換するかを考えてみましょう。.NET にとって、デリゲートについては匿名性は何もありません。それを作成する C# プログラマーにとっては匿名であるだけです。これは、デリゲート型に割り当てられたラムダ式にも同様に当てはまります。
.NETとは あります デリゲートとは、メソッド シグネチャを記述する型の概念です。そのインスタンスは、特定のオブジェクトの特定のメソッドへのバインドされた呼び出し、またはそのオブジェクトに対して呼び出すことができる特定の型の特定のメソッドへのアンバインドされた呼び出しを表します。タイプであり、前記メソッドは前記署名に従う。このような型はすべて System.Delegate から継承します。
.NET 3.5 では、System.Linq.Expressions 名前空間も導入されています。この名前空間には、コード式を記述するためのクラスが含まれており、したがって、特定の型またはオブジェクトのメソッドへのバインドまたはアンバインド呼び出しを表すこともできます。その後、LambdaExpression インスタンスを実際のデリゲートにコンパイルできます (これにより、式の構造に基づいた動的メソッドがコード生成され、それへのデリゲート ポインターが返されます)。
C# では、ラムダ式をその型の変数に割り当てることで、System.Expressions.Expression 型のインスタンスを生成できます。これにより、実行時に式を構築するための適切なコードが生成されます。
もちろん、もしあなたが だった C# のラムダ式と匿名メソッドの違いは何なのかと尋ねると、結局のところ、これはほとんど無関係です。その場合、主な違いは簡潔さです。パラメーターを気にせず、実行する場合は、匿名デリゲートに傾きます。値を返す予定はなく、型推論されたパラメーターと戻り値の型が必要な場合はラムダを使用します。
また、ラムダ式は式の生成をサポートします。
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;
}
デリゲートは関数ポインター/メソッド ポインター/コールバック (お好みで選んでください) に相当し、ラムダはかなり単純化された匿名関数です。少なくとも私は人々にそう伝えています。
私にはこれに関する豊富な経験はありませんが、これを説明すると、デリゲートは関数のラッパーであるのに対し、ラムダ式自体は匿名関数であると言えます。
デリゲートは常に基本的には関数ポインターです。ラムダはデリゲートに変わることができますが、LINQ 式ツリーに変わることもできます。例えば、
Func<int, int> f = x => x + 1;
Expression<Func<int, int>> exprTree = x => x + 1;
最初の行はデリゲートを生成し、2 行目は式ツリーを生成します。
ラムダは単にデリゲートの糖衣構文です。コンパイラは最終的にラムダをデリゲートに変換します。
これらは同じだと私は思います:
Delegate delegate = x => "hi!";
Delegate delegate = delegate(object x) { return "hi";};
デリゲートは関数のシグネチャです。何かのようなもの
delegate string MyDelegate(int param1);
デリゲートはボディを実装しません。
ラムダは、デリゲートの署名と一致する関数呼び出しです。上記のデリゲートには、次のいずれかを使用できます。
(int i) => i.ToString();
(int i) => "ignored i";
(int i) => "Step " + i.ToString() + " of 10";
の Delegate
ただし、型の名前は不適切です。型のオブジェクトを作成する Delegate
実際には、ラムダ、静的メソッド、クラス メソッドなどの関数を保持できる変数を作成します。
デリゲートは、特定のパラメーター リストと戻り値の型を持つメソッドへの参照です。オブジェクトが含まれる場合と含まれない場合があります。
ラムダ式は匿名関数の形式です。
質問が「ラムダとラムダの違いは何ですか」ということを意図していたことは明らかです。 匿名 デリゲート?」 ここでのすべての答えのうち、正しく答えたのは 1 人だけでした。主な違いは、ラムダを使用してデリゲートだけでなく式ツリーを作成できることです。
MSDN で詳細を読むことができます。 http://msdn.microsoft.com/en-us/library/bb397687.aspx
デリゲートは実際には関数の単なる構造型付けです。名目上の型付けと、インターフェイスまたは抽象クラスを実装する匿名クラスの実装でも同じことを行うことができますが、必要な関数が 1 つだけの場合、大量のコードが必要になります。
ラムダは、1930 年代のアロンゾ教会のラムダ計算のアイデアに由来しています。これは関数を作成する匿名の方法です。これらは関数を構成する場合に特に役立ちます
したがって、ラムダはデリゲートの糖衣構文であると言う人もいるかもしれませんが、私はデリゲートは人々を C# のラムダに簡単に理解させるための架け橋であると言います。
デリゲートは関数ポインターのキューであり、デリゲートを呼び出すと複数のメソッドを呼び出すことができます。ラムダは本質的に匿名メソッド宣言であり、どのようなコンテキストとして使用されるかに応じて、コンパイラーによって異なる解釈がされる可能性があります。
ラムダ式をメソッドとして指すデリゲートを取得するには、ラムダ式をデリゲートにキャストするか、特定のデリゲート型を予期するメソッドにパラメータとして渡す場合、コンパイラがそれをキャストします。これを LINQ ステートメント内で使用すると、ラムダはコンパイラによって単なるデリゲートではなく式ツリーに変換されます。
本当の違いは、ラムダは別の式の内部でメソッドを定義する簡潔な方法であるのに対し、デリゲートは実際のオブジェクト型であることです。
ラムダはデリゲートの簡略化されたバージョンです。それらはいくつかの特性を持っています。 閉鎖 匿名デリゲートと同様ですが、暗黙の型指定も使用できます。次のようなラムダ:
something.Sort((x, y) => return x.CompareTo(y));
デリゲートでできることよりもはるかに簡潔です。
something.Sort(sortMethod);
...
private int sortMethod(SomeType one, SomeType two)
{
one.CompareTo(two)
}
これは私が私の下手なブログにしばらく載せた例です。ワーカー スレッドからラベルを更新したいとします。デリゲート、anon デリゲート、および 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);
}
}
.NET は単独では、つまり C# なしではデリゲートとラムダ式を理解できないため、質問の曖昧さのため、あなたの質問は .NET ではなく C# に関するものだと思います。
あ(普通, 、いわゆるものに対抗して ジェネリック 代表者、 参照 後ほど) デリゲートは C++ の一種と見なされるべきです typedef
関数ポインタ型の場合、たとえば c++ では次のようになります。
R (*thefunctionpointer) ( T ) ;
typedef は型です thefunctionpointer
これは、次の型のオブジェクトを取る関数へのポインタの型です。 T
型のオブジェクトを返します R
. 。次のように使用します。
thefunctionpointer = &thefunction ;
R r = (*thefunctionpointer) ( t ) ; // where t is of type T
どこ thefunction
を取る関数になります T
そして返す 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
どこ thefunction
を取る関数になります T
そして返す R
. 。これはデリゲート、いわゆる通常のデリゲート用です。
これで、C# にも汎用デリゲートがあります。これは汎用のデリゲートです。 つまり これはいわば「テンプレート化」されており、それによって 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 x
私はします 仲間 の double
x*x
. 。数学ではこれを次のように書きます。 「\mapsto」ラテックス シンボル. 。C# では関数表記が借用されました。 =>
. 。例えば :
Func<double, double> thefunctor = ( (double x) => x * x ); // outer brackets are not
// mandatory
(double x) => x * x
です 表現. 。これは型ではありませんが、デリゲート (ジェネリックかどうかに関係なく) は型ではありません。
道徳?最後に、デリゲートとは何ですか (それぞれ汎用デリゲート)、関数ポインター型でない場合(それぞれラップ+スマート+ジェネリック関数ポインタ型)、あれ?何か別のもの!見る これ そして それ.
ここでいくつかの基本的なことを説明します。「デリゲート」は実際には、メソッドまたはラムダへの参照を保持する変数の名前です。
これは匿名メソッドです -
(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);
そうですね、本当に単純化しすぎたバージョンでは、ラムダは匿名関数の単なる短縮形であるということです。デリゲートは単なる匿名関数以上のことを行うことができます。イベント、非同期呼び出し、複数のメソッド チェーンなどです。