私はこの質問をよく聞かれ、違いを最もよく説明する方法についていくつかの意見を求めたいと思いました。
私はこの質問をよく聞かれ、違いを最もよく説明する方法についていくつかの意見を求めたいと思いました。
回答:
それらは実際には2つの非常に異なるものです。「デリゲート」は、実際にはメソッドまたはラムダへの参照を保持する変数の名前であり、ラムダは永続的な名前のないメソッドです。
ラムダは、いくつかの微妙な違いを除いて、他の方法と非常によく似ています。
デリゲートは次のように定義されます。
delegate Int32 BinaryIntOp(Int32 x, Int32 y);
BinaryIntOp型の変数には、シグネチャが同じである限り、メソッドまたはlabmdaのいずれかを割り当てることができます。2つのInt32引数と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#には「ファーストクラスの関数」はありません。デリゲート変数に割り当てられた関数の名前を使用して、基本的にその関数を表すオブジェクトを作成できます。しかし、それは本当にコンパイラのトリックです。関数名の後にドットを記述してステートメントを開始すると(つまり、関数自体でメンバーアクセスを実行しようとすると)、参照するメンバーがないことがわかります。オブジェクトからのものでさえありません。これにより、プログラマーは、任意の関数で呼び出すことができる拡張メソッドを追加するなど、便利な(そしてもちろん潜在的に危険な)ことを行うことができなくなります。最善の方法は、Delegateクラス自体を拡張することです。これも確かに便利ですが、それほど多くはありません。
更新:匿名デリゲートとメソッドおよびラムダの違いを示すKargの回答も参照してください。
更新2:James Hartは、非常に技術的ではありますが、ラムダとデリゲートは.NETエンティティではなく(つまり、CLRにはデリゲートまたはラムダの概念がない)、フレームワークと言語の構成要素であることに注意してください。
質問は少しあいまいです。これは、得られる回答の大きな違いを説明しています。
実際に、.NETFrameworkのラムダとデリゲートの違いは何であるかを尋ねました。それは多くのことの1つかもしれません。あなたは尋ねていますか:
C#(またはVB.NET)言語のラムダ式と匿名デリゲートの違いは何ですか?
.NET 3.5のSystem.Linq.Expressions.LambdaExpressionオブジェクトとSystem.Delegateオブジェクトの違いは何ですか?
またはそれらの両極端の間またはその周辺のどこかに何か?
「C#Lambda式と.NET System.Delegateの違いは何ですか?」という質問に対する答えを提供しようとしているようですが、あまり意味がありません。
.NET Framework自体は、匿名デリゲート、ラムダ式、またはクロージャの概念を理解していません。これらはすべて、言語仕様によって定義されたものです。C#コンパイラが、匿名メソッドの定義を、クロージャ状態を保持するためのメンバー変数を持つ生成されたクラスのメソッドに変換する方法について考えてみてください。.NETにとって、デリゲートについて匿名のものは何もありません。それを書いているC#プログラマーには匿名です。これは、デリゲート型に割り当てられたラムダ式にも同様に当てはまります。
何.NET DOESが理解することは、デリゲートの考えである-メソッドのシグネチャを記述するタイプ、いずれかの特定のオブジェクトの特定のメソッドにバインドされた呼び出しを表すのインスタンス、またはに対して呼び出すことができる特定の種類の特定のメソッドにバインドされていない呼び出し上記のメソッドが上記の署名に準拠している、そのタイプのオブジェクト。このようなタイプはすべて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 string MyDelegate(int param1);
デリゲートはボディを実装しません。
ラムダは、デリゲートのシグネチャに一致する関数呼び出しです。上記のデリゲートには、次のいずれかを使用できます。
(int i) => i.ToString();
(int i) => "ignored i";
(int i) => "Step " + i.ToString() + " of 10";
Delegate
ただし、このタイプの名前は不適切です。型のオブジェクトを作成すると、Delegate
実際には、ラムダ、静的メソッド、クラスメソッドなどの関数を保持できる変数が作成されます。
デリゲートは関数ポインタのキューであり、デリゲートを呼び出すと複数のメソッドを呼び出すことができます。ラムダは本質的に匿名のメソッド宣言であり、使用されるコンテキストに応じて、コンパイラーによって異なる方法で解釈される可能性があります。
ラムダ式をメソッドとして指すデリゲートをデリゲートにキャストするか、特定のデリゲートタイプを期待するメソッドにパラメーターとして渡すと、コンパイラーがそれをキャストします。LINQステートメント内で使用すると、ラムダはコンパイラーによって単なるデリゲートではなく式ツリーに変換されます。
違いは、ラムダは別の式の内部でメソッドを定義するための簡潔な方法であるのに対し、デリゲートは実際のオブジェクトタイプであるということです。
質問が「ラムダと匿名デリゲートの違いは何ですか?」ということを意図していたことはかなり明らかです。ここでのすべての答えのうち、正解したのは1人だけです。主な違いは、ラムダを使用して式ツリーとデリゲートを作成できることです。
詳細については、MSDNを参照してください:http://msdn.microsoft.com/en-us/library/bb397687.aspx
ここでいくつかの基本。「デリゲート」は、実際にはメソッドまたはラムダへの参照を保持する変数の名前です。
これは匿名の方法です-
(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);
ラムダはデリゲートの簡略化されたバージョンです。匿名デリゲートのようなクロージャのプロパティのいくつかがありますが、暗黙の型指定を使用することもできます。このようなラムダ:
something.Sort((x, y) => return x.CompareTo(y));
デリゲートでできることよりもはるかに簡潔です。
something.Sort(sortMethod);
...
private int sortMethod(SomeType one, SomeType two)
{
one.CompareTo(two)
}
これは私が私の足の不自由なブログにしばらく置いた例です。ワーカースレッドからラベルを更新したいとします。デリゲート、アノンデリゲート、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#に関係していると思います。
(通常、いわゆるジェネリックデリゲートとは反対に、後で参照)デリゲートtypedef
は、関数ポインタ型の一種のc ++ 、たとえばc ++と見なす必要があります。
R (*thefunctionpointer) ( T ) ;
typedefthefunctionpointer
は、型のオブジェクトを受け取り、型のオブジェクトを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
ある式が。タイプではありませんが、デリゲート(ジェネリックかどうか)はタイプです。
道徳?最後に、関数ポインター型(またはラップされた+スマート+ジェネリック関数ポインター型)でない場合、デリゲート(またはジェネリックデリゲート)とは何ですか?他の何か!これとあれを参照してください。
まあ、本当に過度に単純化されたバージョンは、ラムダが無名関数の単なる省略形であるということです。デリゲートは、イベント、非同期呼び出し、複数のメソッドチェーンなど、匿名関数以外にも多くのことを実行できます。