Apa perbedaan antara lambda dan delegasi di .NET Framework?

88

Saya sering ditanyai pertanyaan ini dan saya pikir saya akan meminta beberapa masukan tentang cara terbaik menjelaskan perbedaannya.

ScottKoon
sumber
2
Yang dimaksud dengan "delegasi" adalah tipe delegasi, atau delegasi anonim? Mereka juga berbeda.
Chris Ammerman
kemungkinan duplikat kata kunci delegasi vs. notasi lambda
nawfal
1
mengapa orang membuat pertanyaannya begitu rumit? Jawab saja apa itu delegasi dan apa itu lambda. Berikan penjelasan sebanyak mungkin dan biarkan dia memilih apa saja yang cocok untuknya.
Imir Hoxha

Jawaban:

98

Mereka sebenarnya adalah dua hal yang sangat berbeda. "Delegasi" sebenarnya adalah nama untuk variabel yang menyimpan referensi ke metode atau lambda, dan lambda adalah metode tanpa nama permanen.

Lambda sangat mirip dengan metode lain, kecuali untuk beberapa perbedaan kecil.

  1. Metode normal didefinisikan dalam "pernyataan" dan terikat pada nama permanen, sedangkan lambda didefinisikan "dengan cepat" dalam "ekspresi" dan tidak memiliki nama permanen.
  2. Beberapa lambda dapat digunakan dengan pohon ekspresi .NET, sedangkan metode tidak bisa.

Seorang delegasi didefinisikan seperti ini:

delegate Int32 BinaryIntOp(Int32 x, Int32 y);

Variabel tipe BinaryIntOp dapat memiliki metode atau labmda yang ditetapkan, selama tanda tangannya sama: dua argumen Int32, dan kembalian Int32.

Lambda dapat didefinisikan seperti ini:

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

Hal lain yang perlu diperhatikan adalah meskipun tipe Func dan Action generik sering dianggap sebagai "tipe lambda", mereka sama seperti delegasi lainnya. Hal yang menyenangkan tentang mereka adalah bahwa mereka pada dasarnya menentukan nama untuk semua jenis delegasi yang mungkin Anda perlukan (hingga 4 parameter, meskipun Anda pasti dapat menambahkan lebih banyak dari Anda sendiri). Jadi, jika Anda menggunakan berbagai macam tipe delegasi, tetapi tidak lebih dari sekali, Anda dapat menghindari kekacauan kode Anda dengan deklarasi delegasi dengan menggunakan Func dan Action.

Berikut adalah ilustrasi tentang bagaimana Func dan Action "bukan hanya untuk lambda":

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

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

Hal lain yang berguna untuk diketahui adalah bahwa tipe delegasi (bukan metodenya sendiri) dengan tanda tangan yang sama tetapi nama yang berbeda tidak akan secara implisit dicor satu sama lain. Ini termasuk delegasi Func and Action. Namun jika tanda tangannya identik, Anda dapat secara eksplisit mentransmisikannya.

Bekerja ekstra .... Dalam fungsi C # fleksibel, dengan penggunaan lambda dan delegasi. Tapi C # tidak memiliki "fungsi kelas satu". Anda dapat menggunakan nama fungsi yang ditetapkan ke variabel delegasi untuk membuat objek yang mewakili fungsi tersebut. Tapi itu benar-benar trik kompiler. Jika Anda memulai pernyataan dengan menulis nama fungsi diikuti dengan titik (misalnya, coba lakukan akses anggota pada fungsi itu sendiri), Anda akan menemukan tidak ada anggota di sana yang dapat dirujuk. Bahkan yang dari Object. Hal ini mencegah pemrogram melakukan hal-hal yang berguna (dan berpotensi berbahaya tentunya) seperti menambahkan metode ekstensi yang dapat dipanggil pada fungsi apa pun. Hal terbaik yang dapat Anda lakukan adalah memperluas kelas Delegasi itu sendiri, yang tentunya juga berguna, tetapi tidak terlalu banyak.

Pembaruan: Lihat juga jawaban Karg yang menggambarkan perbedaan antara delegasi anonim vs. metode & lambda.

Pembaruan 2: James Hart membuat catatan penting, meskipun sangat teknis, bahwa lambda dan delegasi bukanlah entitas .NET (yaitu, CLR tidak memiliki konsep delegasi atau lambda), tetapi mereka adalah kerangka kerja dan konstruksi bahasa.

Chris Ammerman
sumber
Penjelasan yang bagus. Meskipun saya pikir yang Anda maksud adalah "fungsi kelas satu", bukan "objek kelas satu". :)
ibz
1
Kamu benar. Saya memiliki kalimat yang terstruktur berbeda selama penulisan ("Fungsi C # sebenarnya bukan objek kelas satu") dan lupa mengubahnya. Terima kasih!
Chris Ammerman
Metode normal didefinisikan dalam "pernyataan" . Pernyataan adalah tindakan dalam urutan program imperatif, mungkin berdasarkan ekspresi. Bukankah definisi metode adalah struktur tata bahasa yang berbeda? Definisi metode tidak terdaftar di docs.microsoft.com/en-us/dotnet/csharp/tour-of-csharp/…
Max Barraclough
32

Pertanyaannya agak ambigu, yang menjelaskan perbedaan besar dalam jawaban yang Anda dapatkan.

Anda sebenarnya bertanya apa perbedaan antara lambda dan delegasi dalam kerangka .NET; itu mungkin salah satu dari beberapa hal. Apakah Anda bertanya:

  • Apa perbedaan antara ekspresi lambda dan delegasi anonim dalam bahasa C # (atau VB.NET)?

  • Apa perbedaan antara objek System.Linq.Expressions.LambdaExpression dan objek System.Delegate di .NET 3.5?

  • Atau sesuatu di antara atau di sekitar ekstrem itu?

Beberapa orang tampaknya mencoba memberi Anda jawaban atas pertanyaan 'apa perbedaan antara ekspresi C # Lambda dan .NET System.Delegate?', Yang tidak terlalu masuk akal.

Kerangka .NET itu sendiri tidak memahami konsep delegasi anonim, ekspresi lambda, atau penutupan - semua itu adalah hal yang ditentukan oleh spesifikasi bahasa. Pikirkan tentang bagaimana compiler C # menerjemahkan definisi metode anonim menjadi metode pada kelas yang dihasilkan dengan variabel anggota untuk menahan status tertutup; ke .NET, tidak ada yang anonim tentang delegasi; itu hanya anonim bagi programmer C # yang menulisnya. Itu sama benarnya dengan ekspresi lambda yang ditugaskan ke tipe delegasi.

Apa .NET TIDAK pahami adalah gagasan delegasi - tipe yang menjelaskan tanda tangan metode, contoh yang mewakili panggilan terikat ke metode tertentu pada objek tertentu, atau panggilan tak terikat ke metode tertentu pada tipe tertentu yang dapat dipanggil objek apa pun dari jenis itu, di mana metode tersebut mengikuti tanda tangan tersebut. Jenis seperti itu semuanya mewarisi dari System.Delegate.

.NET 3.5 juga memperkenalkan namespace System.Linq.Expressions, yang berisi kelas untuk menjelaskan ekspresi kode - dan karena itu juga dapat mewakili panggilan terikat atau tidak terikat ke metode pada jenis atau objek tertentu. Instans LambdaExpression kemudian dapat dikompilasi menjadi delegasi aktual (di mana metode dinamis berdasarkan struktur ekspresi dikodegenkan, dan penunjuk delegasi dikembalikan).

Dalam C # Anda dapat menghasilkan contoh tipe System.Expressions.Expression dengan menetapkan ekspresi lambda ke variabel tipe tersebut, yang akan menghasilkan kode yang sesuai untuk membangun ekspresi pada waktu proses.

Tentu saja, jika memang begitu bertanya apa perbedaan antara ekspresi lambda dan metode anonim di C #, setelah semua, maka semua ini cukup banyak irelevant, dan dalam hal bahwa perbedaan utama adalah singkatnya, yang bersandar ke arah delegasi anonim ketika Anda don' t peduli tentang parameter dan tidak berencana untuk mengembalikan nilai, dan menuju lambda saat Anda ingin mengetikkan parameter yang dirujuk dan jenis kembalian.

Dan ekspresi lambda mendukung pembuatan ekspresi.

James Hart
sumber
3
Info bagus! Anda menginspirasi saya untuk menyalakan reflektor dan melihat IL. Saya tidak tahu lambda menghasilkan kelas yang dihasilkan, tetapi sangat masuk akal sekarang setelah saya memikirkannya.
Chris Ammerman
21

Satu perbedaan adalah delegasi anonim dapat menghilangkan parameter sementara lambda harus cocok dengan tanda tangan yang tepat. Diberikan:

public delegate string TestDelegate(int i);

public void Test(TestDelegate d)
{}

Anda dapat memanggilnya dengan empat cara berikut (perhatikan bahwa baris kedua memiliki delegasi anonim yang tidak memiliki parameter apa pun):

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;
}

Anda tidak dapat meneruskan ekspresi lambda yang tidak memiliki parameter atau metode yang tidak memiliki parameter. Ini tidak diperbolehkan:

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

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

Delegasi setara dengan function pointers / method pointers / callbacks (pilihlah), dan lambda adalah fungsi anonim yang cukup disederhanakan. Setidaknya itulah yang saya katakan kepada orang-orang.

Dan Shield
sumber
Persis! Tidak ada perbedaan". Mereka adalah dua hal yang berbeda secara inheren.
ibz
3

Saya tidak memiliki banyak pengalaman dengan ini, tetapi cara saya menggambarkannya adalah bahwa delegasi adalah pembungkus di sekitar fungsi apa pun, sedangkan ekspresi lambda itu sendiri merupakan fungsi anonim.

catur
sumber
3

Delegasi pada dasarnya selalu merupakan penunjuk fungsi. Lambda bisa berubah menjadi delegasi, tetapi juga bisa berubah menjadi pohon ekspresi LINQ. Misalnya,

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

Baris pertama menghasilkan delegasi, sedangkan baris kedua menghasilkan pohon ekspresi.

Curt Hagenlocher
sumber
2
Itu benar, tetapi perbedaan di antara keduanya adalah bahwa keduanya adalah konsep yang sama sekali berbeda . Ini seperti membandingkan apel dan jeruk. Lihat jawaban Dan Shield.
ibz
2

lambda hanyalah gula sintaksis pada delegasi. Kompilator akhirnya mengubah lambda menjadi delegasi.

Ini sama, saya percaya:

Delegate delegate = x => "hi!";
Delegate delegate = delegate(object x) { return "hi";};
Gilligan
sumber
2
tidak satu pun dari contoh ini yang dapat dikompilasi. Bahkan jika Anda mengubah nama instance Delegatedari 'delegate', yang merupakan kata kunci.
Steve Cooper
2

Delegasi adalah tanda tangan fungsi; sesuatu seperti

delegate string MyDelegate(int param1);

Delegasi tidak mengimplementasikan badan.

Lambda adalah panggilan fungsi yang cocok dengan tanda tangan delegasi. Untuk delegasi di atas, Anda dapat menggunakan salah satu dari;

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

The DelegateTipe parah bernama, meskipun; membuat objek bertipe Delegatesebenarnya membuat variabel yang dapat menampung fungsi - baik itu lambda, metode statis, atau metode kelas.

Steve Cooper
sumber
Saat Anda membuat variabel dengan tipe MyDelegate, itu sebenarnya bukan tipe runtime. Jenis runtime adalah Delegasi. Ada trik kompilator yang terlibat dalam bagaimana delegasi, lambda, dan pohon ekspresi mengkompilasi, yang menurut saya menyebabkan kode menyiratkan hal-hal yang tidak benar.
Chris Ammerman
2

Delegasi adalah referensi ke metode dengan daftar parameter tertentu dan tipe kembalian. Ini mungkin atau mungkin tidak termasuk objek.

Ekspresi lambda adalah bentuk fungsi anonim.

Peter Ritchie
sumber
2

Delegasi adalah Antrian penunjuk fungsi, memanggil delegasi dapat memanggil beberapa metode. Lambda pada dasarnya adalah deklarasi metode anonim yang dapat diinterpretasikan oleh kompilator secara berbeda, tergantung pada konteks apa ia digunakan.

Anda bisa mendapatkan delegasi yang menunjuk ke ekspresi lambda sebagai metode dengan mentransmisikannya ke dalam delegasi, atau jika meneruskannya sebagai parameter ke metode yang mengharapkan tipe delegasi tertentu kompilator akan mentransmisikannya untuk Anda. Menggunakannya di dalam pernyataan LINQ, lambda akan diterjemahkan oleh kompilator menjadi pohon ekspresi, bukan hanya sebuah delegasi.

Perbedaan sebenarnya adalah lambda adalah cara singkat untuk mendefinisikan metode di dalam ekspresi lain, sedangkan delegasi adalah tipe objek yang sebenarnya.

justin.m.chase
sumber
2

Cukup jelas bahwa pertanyaannya adalah "apa perbedaan antara lambda dan delegasi anonim ?" Dari semua jawaban di sini, hanya satu orang yang benar - perbedaan utamanya adalah lambda dapat digunakan untuk membuat pohon ekspresi serta delegasi.

Anda dapat membaca lebih lanjut di MSDN: http://msdn.microsoft.com/en-us/library/bb397687.aspx

Philip Beber
sumber
1

Delegasi sebenarnya hanyalah pengetikan struktural untuk fungsi. Anda dapat melakukan hal yang sama dengan pengetikan nominal dan mengimplementasikan kelas anonim yang mengimplementasikan antarmuka atau kelas abstrak, tetapi itu akan menjadi banyak kode ketika hanya satu fungsi yang dibutuhkan.

Lambda berasal dari ide kalkulus lambda dari Gereja Alonzo pada tahun 1930-an. Ini adalah cara anonim untuk membuat fungsi. Mereka menjadi sangat berguna untuk menyusun fungsi

Jadi sementara beberapa orang mungkin mengatakan lambda adalah gula sintaksis untuk delegasi, saya akan mengatakan delegasi adalah jembatan untuk memudahkan orang menjadi lambda di c #.

Steve g
sumber
1

Beberapa dasar di sini. "Delegasi" sebenarnya adalah nama untuk variabel yang memiliki referensi ke metode atau lambda

Ini adalah metode anonim -

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

Karena metode anonim tidak memiliki nama, kami memerlukan delegasi tempat kami dapat menetapkan kedua metode atau ekspresi ini. Untuk Ex.

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

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

Sama dengan ekspresi lambda. Biasanya kami membutuhkan delegasi untuk menggunakannya

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

Kita dapat menggunakan sebuah func delegate untuk menggunakan ekspresi ini.

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

bool result = checkStudentAge ( Student Object);
Yogesh Prajapati
sumber
0

Lambdas adalah versi delegasi yang disederhanakan. Mereka memiliki beberapa properti closure seperti delegasi anonim, tetapi juga memungkinkan Anda menggunakan pengetikan tersirat. Lambda seperti ini:

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

jauh lebih ringkas daripada yang dapat Anda lakukan dengan seorang delegasi:

something.Sort(sortMethod);
...

private int sortMethod(SomeType one, SomeType two)
{
    one.CompareTo(two)
}
Michael Meadows
sumber
Maksud Anda lambda seperti metode anonim yang disederhanakan (bukan delegasi). Seperti metode (anonim atau tidak), mereka dapat ditugaskan ke variabel delegasi.
Lucas
0

Inilah contoh yang saya pasang di blog saya yang lumpuh. Katakanlah Anda ingin memperbarui label dari utas pekerja. Saya punya 4 contoh cara memperbarui label itu dari 1 menjadi 50 menggunakan delegasi, delegasi langsung dan 2 jenis lambda.

 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); 
         } 
     }
Echostorm
sumber
0

Saya berasumsi bahwa pertanyaan Anda menyangkut c # dan bukan. NET, karena ambiguitas pertanyaan Anda, karena. NET tidak sendirian - yaitu, tanpa c # - pemahaman delegasi dan ekspresi lambda.

A ( normal , berlawanan dengan apa yang disebut delegasi generik , cf later) delegasi harus dilihat sebagai jenis c ++ typedefdari tipe penunjuk fungsi, misalnya di c ++:

R (*thefunctionpointer) ( T ) ;

typedef adalah tipe thefunctionpointeryang merupakan tipe pointer ke fungsi yang mengambil objek bertipe Tdan mengembalikan objek bertipe R. Anda akan menggunakannya seperti ini:

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

di mana thefunctionakan menjadi fungsi yang mengambil Tdan mengembalikan R.

Dalam c # Anda akan pergi

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

dan Anda akan menggunakannya seperti ini:

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

di mana thefunctionakan menjadi fungsi yang mengambil Tdan mengembalikan R. Ini untuk delegasi, yang disebut delegasi biasa.

Sekarang, Anda juga memiliki delegasi generik di c #, yang merupakan delegasi yang generik, misalnya yang "menggunakan template", dengan demikian menggunakan ekspresi c ++. Mereka didefinisikan seperti ini:

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

Dan Anda dapat menggunakannya seperti ini:

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);

di mana thefunction2fungsi mengambil sebagai argumen dan mengembalikan a double.

Sekarang bayangkan bahwa alih-alih thefunction2saya ingin menggunakan "fungsi" yang tidak didefinisikan di mana pun untuk saat ini, dengan pernyataan, dan yang tidak akan pernah saya gunakan nanti. Kemudian c # mengijinkan kita untuk menggunakan ekspresi dari fungsi ini. Dengan ekspresi Maksudku "matematika" (atau fungsional, untuk tetap program) ekspresi itu, misalnya: ke double xsaya akan mengasosiasikan dengan double x*x. Dalam matematika Anda menulis ini menggunakan simbol lateks "\ mapsto" . Dalam c # notasi fungsional telah dipinjam: =>. Misalnya :

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

(double x) => x * xadalah ekspresi . Ini bukan tipe, sedangkan delegasi (generik atau tidak) adalah.

Moralitas? Pada akhirnya, apa itu delegate (resp. Generic delegate), jika bukan tipe penunjuk fungsi (resp. Dibungkus + jenis penunjuk fungsi pintar + generik), ya? Sesuatu yang lain! Lihat ini dan itu .

ujsgeyrr1f0d0d0r0h1h0j0j_juj
sumber
-1

Nah, versi yang sangat disederhanakan adalah lambda hanyalah singkatan dari fungsi anonim. Delegasi dapat melakukan lebih dari sekadar fungsi anonim: hal-hal seperti acara, panggilan asinkron, dan beberapa rantai metode.

Joel Coehoorn
sumber
1
lambda dapat digunakan sebagai penangan acara; tombol.Click + = (pengirim, eventArgs) => {MessageBox.Show ("Klik"); } dan memanggil System.Threading.Thread (() => Console.Write ("Dieksekusi pada utas")) secara asinkron. Start ();
Steve Cooper