.NET Framework'te lambdalar ve temsilciler arasındaki fark nedir?


88

Bu soru bana çok soruldu ve aradaki farkı en iyi nasıl tanımlayacağım konusunda bazı görüşler isteyeceğimi düşündüm.


2
"Temsilciler" derken temsilci türlerini mi yoksa anonim temsilcileri mi kastediyorsunuz? Onlar da farklıdır.
Chris Ammerman


1
neden insanlar sorusunu bu kadar karmaşık hale getiriyor? Sadece delege nedir ve lambda nedir cevaplayın. Mümkün olduğunca fazla açıklama yapın ve kendisi için uygun olanı seçmesine izin verin.
Imir Hoxha

Yanıtlar:


98

Aslında çok farklı iki şey. "Temsilci" aslında bir yönteme veya bir lambda'ya başvuru tutan bir değişkenin adıdır ve bir lambda, kalıcı bir adı olmayan bir yöntemdir.

Lambdalar, birkaç ince farklılık dışında diğer yöntemlere çok benzer.

  1. Normal bir yöntem bir "ifadede" tanımlanır ve kalıcı bir isme bağlanır, oysa bir lambda "anında" bir "ifade" içinde tanımlanır ve kalıcı bir adı yoktur.
  2. Bazı lambdalar .NET ifade ağaçları ile kullanılabilirken yöntemler kullanılamaz.

Bir temsilci şu şekilde tanımlanır:

delegate Int32 BinaryIntOp(Int32 x, Int32 y);

İmza aynı olduğu sürece BinaryIntOp türündeki bir değişken, kendisine atanmış bir yönteme veya labmda'ya sahip olabilir: iki Int32 bağımsız değişkeni ve bir Int32 dönüşü.

Bir lambda şu şekilde tanımlanabilir:

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

Unutulmaması gereken bir diğer nokta da, jenerik Func ve Action türlerinin genellikle "lambda türleri" olarak kabul edilmelerine rağmen, tıpkı diğer temsilciler gibi olmalarıdır. Onlarla ilgili güzel olan şey, ihtiyaç duyabileceğiniz herhangi bir delege türü için temelde bir isim tanımlamalarıdır (4 parametreye kadar, yine de kendinizinkinden daha fazlasını ekleyebilirsiniz). Bu nedenle, çok çeşitli temsilci türleri kullanıyorsanız, ancak birden fazla kullanmıyorsanız, Func ve Action kullanarak kodunuzu temsilci bildirimleriyle karıştırmaktan kaçınabilirsiniz.

İşte Func ve Action'ın nasıl "sadece lambdas için değil" olduğuna dair bir örnek:

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

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

Bilinmesi gereken bir başka yararlı şey de, aynı imzaya sahip ancak farklı adlara sahip temsilci türlerinin (yöntemlerin kendileri değil) dolaylı olarak birbirine atılmayacağıdır. Bu, Func ve Action delegelerini içerir. Ancak imza aynıysa, açıkça aralarında geçiş yapabilirsiniz.

Fazladan bir yol kat etmek .... C # 'da işlevler, lambdas ve delegelerin kullanımıyla esnektir. Ancak C # "birinci sınıf işlevlere" sahip değildir. Temsilci değişkene atanan bir işlevin adını, esasen bu işlevi temsil eden bir nesne oluşturmak için kullanabilirsiniz. Ama bu gerçekten bir derleyici numarasıdır. Eğer işlev adını ve ardından bir nokta yazarak bir ifadeye başlarsanız (yani, işlevin kendisinde üye erişimi yapmaya çalışırsanız), başvurulacak üye olmadığını göreceksiniz. Object'teki olanlar bile. Bu, programcının herhangi bir işleve çağrılabilecek uzantı yöntemleri eklemek gibi yararlı (ve elbette potansiyel olarak tehlikeli) şeyler yapmasını engeller. Yapabileceğiniz en iyi şey Delegate sınıfının kendisini genişletmektir; bu kesinlikle yararlıdır, ancak o kadar da fazla değildir.

Güncelleme: Ayrıca , anonim temsilcilerle yöntemler ve lambdalar arasındaki farkı gösteren Karg'ın yanıtına da bakın .

Güncelleme 2: James Hart , önemli, ancak çok teknik, lambdas ve delegelerin .NET varlıkları olmadıklarına (yani CLR'nin delege veya lambda kavramına sahip olmadığına), bunun yerine çerçeve ve dil yapıları olduklarına dikkat çekiyor.


İyi açıklama. "Birinci sınıf işlevler" demek istediğinizi düşünmeme rağmen "birinci sınıf nesneler" değil. :)
ibz

1
Haklısın. Yazma sırasında cümleyi farklı yapılandırdım ("C # fonksiyonları aslında birinci sınıf nesneler değildir") ve bunu değiştirmeyi unuttum. Teşekkürler!
Chris Ammerman

Normal bir yöntem bir "ifadede" tanımlanır Bir ifade, muhtemelen bir ifadeye dayalı olarak, zorunlu bir programın sırasındaki bir eylemdir. Yöntem tanımı farklı bir gramer yapısı değil mi? Yöntem tanımı, docs.microsoft.com/en-us/dotnet/csharp/tour-of-csharp/… '
Max Barraclough

32

Soru biraz belirsiz, bu da aldığınız yanıtlardaki geniş eşitsizliği açıklıyor.

Aslında .NET çerçevesinde lambdalar ve temsilciler arasındaki farkın ne olduğunu sordunuz; bu birkaç şeyden biri olabilir. Soruyor musun:

  • C # (veya VB.NET) dilinde lambda ifadeleri ile anonim temsilciler arasındaki fark nedir?

  • .NET 3.5'teki System.Linq.Expressions.LambdaExpression nesneleri ile System.Delegate nesneleri arasındaki fark nedir?

  • Veya bu aşırılıklar arasında veya çevresinde bir yerde bir şey?

Bazı insanlar size 'C # Lambda ifadeleri ile .NET System.Delegate arasındaki fark nedir?' Sorusunun cevabını vermeye çalışıyor gibi görünüyor ki bu pek bir anlam ifade etmiyor.

.NET çerçevesi kendi başına anonim temsilciler, lambda ifadeleri veya kapanış kavramlarını anlamaz - bunların hepsi dil belirtimlerinde tanımlanan şeylerdir. C # derleyicisinin anonim bir yöntemin tanımını, kapanma durumunu tutmak için üye değişkenlerle oluşturulan bir sınıf üzerinde bir yönteme nasıl çevirdiğini düşünün; .NET için, temsilci hakkında anonim hiçbir şey yoktur; sadece onu yazan C # programcısı için anonimdir. Bu, bir temsilci türüne atanan bir lambda ifadesi için de aynı derecede geçerlidir.

Ne NET YAPAR bir yöntem imzası tanımlayan bir tip ya karşı çağrılabilir belirli bir türde belirli bir yönteme özgü nesneleri veya ilişkisiz aramalarda özel yöntemler aramaları bağlı temsil örnekleri olan - anlayacakları bir temsilci fikirdir söz konusu yöntem söz konusu imzaya bağlı olduğunda bu türden herhangi bir nesne. Bu tür türlerin tümü System.Delegate'ten devralınır.

. LambdaExpression örnekleri daha sonra gerçek temsilciler olarak derlenebilir (burada ifadenin yapısına dayalı dinamik bir yöntem kodlanır ve buna bir temsilci işaretçisi döndürülür).

C # 'da, söz konusu türdeki bir değişkene bir lambda ifadesi atayarak System.Expressions.Expression türlerinin örneklerini üretebilirsiniz; bu, ifadeyi çalışma zamanında oluşturmak için uygun kodu üretir.

Tabii ki, eğer lambda ifadeleri ile C # 'da anonim yöntemler arasındaki farkın ne olduğunu soruyorsanız, sonuçta, tüm bunlar hemen hemen alakasızdır ve bu durumda birincil fark kısalıktır; t Parametreleri önemseyin ve bir değer döndürmeyi planlamayın ve çıkarılmış parametreler ve dönüş türleri yazmak istediğinizde lambdas'a doğru.

Lambda ifadeleri de ifade oluşturmayı destekler.


3
Harika bilgi! Reflektörü ateşlemek ve IL'ye bakmak için bana ilham verdin. Lambdaların sınıfların oluşturulmasına neden olduğunu bilmiyordum, ama şimdi düşündüğüm için mükemmel bir anlam ifade ediyor.
Chris Ammerman

21

Farklardan biri, anonim bir temsilcinin parametreleri atlayabilmesidir, ancak bir lambda'nın imzayla tam olarak eşleşmesi gerekir. Verilen:

public delegate string TestDelegate(int i);

public void Test(TestDelegate d)
{}

aşağıdaki dört şekilde çağırabilirsiniz (ikinci satırda herhangi bir parametresi olmayan anonim bir temsilci bulunduğunu unutmayın):

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

Parametresi olmayan bir lambda ifadesini veya parametresi olmayan bir yöntemi iletemezsiniz. Bunlara izin verilmez:

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

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

13

Temsilciler, işlev işaretçileri / yöntem işaretçileri / geri aramalara eşdeğerdir (seçiminizi yapın) ve lambdalar oldukça basitleştirilmiş anonim işlevlerdir. En azından insanlara bunu söylüyorum.


Kesinlikle! "Fark" yok. Doğası gereği farklı iki şeydir.
ibz

3

Bununla ilgili pek fazla deneyimim yok, ancak bunu tarif etme şeklim, bir temsilcinin herhangi bir işlevin etrafını sarması, oysa lambda ifadesinin kendisi anonim bir işlev olmasıdır.


3

Temsilci her zaman temelde bir işlev göstericisidir. Bir lambda bir temsilciye dönüşebilir, ancak aynı zamanda bir LINQ ifade ağacına da dönüşebilir. Örneğin,

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

İlk satır bir temsilci üretirken, ikincisi bir ifade ağacı üretir.


2
Bu doğru, ancak aralarındaki fark , tamamen farklı iki kavram olmasıdır . Bu, elma ve portakalları karşılaştırmak gibidir. Dan Shield'ın cevabını görün.
ibz

2

lambdalar bir delege üzerindeki basit sözdizimsel şekerdir. Derleyici lambdaları temsilcilere dönüştürerek sona erer.

Bunların aynı olduğuna inanıyorum:

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

2
bu örneklerin hiçbiri derlenmez. DelegateBir anahtar kelime olan 'temsilci'den' örneğinin adını değiştirseniz bile .
Steve Cooper

2

Temsilci, bir işlev imzasıdır; gibi bir şey

delegate string MyDelegate(int param1);

Temsilci bir organ uygulamaz.

Lambda, temsilcinin imzasıyla eşleşen bir işlev çağrısıdır. Yukarıdaki temsilci için aşağıdakilerden herhangi birini kullanabilirsiniz;

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

DelegateTip kötü olsa adlandırılır; türünde bir nesne oluşturmak Delegateaslında fonksiyonları tutabilen bir değişken yaratır - bunlar lambdalar, statik yöntemler veya sınıf yöntemleri.


MyDelegate türünde bir değişken oluşturduğunuzda, bu aslında çalışma zamanı türü değildir. Çalışma zamanı türü Delegate'dir. Temsilcilerin, lambdaların ve ifade ağaçlarının nasıl derlendiğiyle ilgili derleyici püf noktaları var, bence kodun doğru olmayan şeyleri ima etmesine neden oluyor.
Chris Ammerman

2

Temsilci, belirli bir parametre listesine ve dönüş türüne sahip bir yönteme referanstır. Bir nesne içerebilir veya içermeyebilir.

Lambda ifadesi, anonim işlevin bir biçimidir.


2

Temsilci, işlev işaretçileri Kuyruğudur, bir temsilciyi çağırmak birden çok yöntemi çağırabilir. Bir lambda, esasen, hangi bağlamda kullanıldığına bağlı olarak derleyici tarafından farklı şekilde yorumlanabilen anonim bir yöntem bildirimidir.

Bir temsilciye çevirerek lambda ifadesine bir yöntem olarak işaret eden bir temsilci elde edebilirsiniz veya bunu, belirli bir temsilci türünü bekleyen bir yönteme bir parametre olarak geçiriyorsanız, derleyicinin sizin için çevirmesini sağlayabilirsiniz. Bunu bir LINQ ifadesinin içinde kullanarak, lambda, derleyici tarafından yalnızca bir temsilci yerine bir ifade ağacına çevrilir.

Aradaki fark gerçekte, bir lambda'nın başka bir ifadenin içindeki bir yöntemi tanımlamanın kısa bir yolu olması, bir temsilcinin ise gerçek bir nesne türü olmasıdır.


2

Sorunun "lambdalar ile isimsiz delegeler arasındaki fark nedir?" Olması gerektiği çok açık. Buradaki tüm cevaplardan sadece bir kişi doğru anladı - temel fark, lambdaların delegelerin yanı sıra ifade ağaçları oluşturmak için de kullanılabilmesidir.

MSDN hakkında daha fazlasını okuyabilirsiniz: http://msdn.microsoft.com/en-us/library/bb397687.aspx


1

Temsilciler gerçekten sadece işlevler için yapısal tiplemedir. Aynı şeyi nominal yazarak ve bir arabirim veya soyut sınıf uygulayan anonim bir sınıfı uygulayarak da yapabilirsiniz, ancak bu, yalnızca bir işlev gerektiğinde çok fazla kod olur.

Lambda, 1930'larda Alonzo Kilisesi'nin lambda hesabı fikrinden geliyor. Fonksiyon oluşturmanın anonim bir yoludur. Özellikle işlevleri oluşturmak için yararlı hale gelirler

Bu nedenle, bazıları lambda'nın delegeler için sözdizimsel şeker olduğunu söylese de, delegelerin insanları c # 'da lambdalara dönüştürmek için bir köprü olduğunu söyleyebilirim.


1

Burada biraz temel. "Temsilci" aslında bir yönteme veya lambda'ya başvuru tutan bir değişkenin adıdır

Bu anonim bir yöntemdir -

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

Anonim yöntemin herhangi bir adı olmadığı için, bu iki yöntemi veya ifadeyi atayabileceğimiz bir temsilciye ihtiyacımız var. Örn.

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

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

Lambda ifadesiyle aynı. Genellikle bunları kullanmak için temsilciye ihtiyacımız var

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

Bu ifadeyi kullanmak için bir func delegesi kullanabiliriz.

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

bool result = checkStudentAge ( Student Object);

0

Lambdalar, temsilcilerin basitleştirilmiş sürümleridir. İsimsiz temsilciler gibi bir kapatma işleminin bazı özelliklerine sahiptirler , ancak aynı zamanda zımni yazmayı kullanmanıza da izin verirler. Bunun gibi bir lambda:

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

bir temsilciyle yapabileceklerinizden çok daha özlüdür:

something.Sort(sortMethod);
...

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

Lambdaların basitleştirilmiş anonim yöntemler (temsilci değil) gibi olduğunu söylüyorsunuz. Yöntemler gibi (anonim veya değil), bir temsilci değişkenine atanabilir.
Lucas

0

Bu, zayıf bloguma bir süre koyduğum bir örnek. Çalışan iş parçacığından bir etiketi güncellemek istediğinizi varsayalım. Delegeler, anon delegeler ve 2 tür lambda kullanarak bu etiketi 1'den 50'ye nasıl güncelleyeceğime dair 4 örneğim var.

 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

Sorunuzun belirsizliği nedeniyle sorunun .NET ile değil c # ile ilgili olduğunu varsayıyorum, çünkü .NET tek başına - yani, c # olmadan - temsilcilerin ve lambda ifadelerinin anlaşılması.

A ( normal , genel delegelerin aksine , daha sonra cf ) delege, örneğin c ++ ' typedefda bir işlev işaretçisi türünün bir tür c ++ olarak görülmelidir :

R (*thefunctionpointer) ( T ) ;

typedef, türden thefunctionpointerbir nesneyi alan ve türden Tbir nesne döndüren bir işleve yönelik işaretçilerin türü olan türdür R. Bunu şu şekilde kullanırsın:

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

nerede thefunctionbir Talıp döndüren bir fonksiyon olurduR .

C # için gideceksin

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

ve bunu şu şekilde kullanırsınız:

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

nerede thefunctionbir Talıp döndüren bir fonksiyon olurduR . Bu, normal delegeler olarak adlandırılan delegeler içindir.

Şimdi, c # 'da jenerik delegeler olan, yani deyim yerindeyse c ++ ifadesi kullanan "şablonlu" delegeleriniz var . Şöyle tanımlanırlar:

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

Ve bunları şu şekilde kullanabilirsiniz:

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

thefunction2argüman olarak alıp bir döndüren bir fonksiyon nerededouble ?

Şimdi, bunun yerine, thefunction2şimdilik hiçbir yerde bir ifadeyle tanımlanmamış ve daha sonra asla kullanmayacağım bir "işlev" kullanmak istediğimi hayal edin . Daha sonra c # , bu fonksiyonun ifadesini kullanmamıza izin verir . İfade ile bunun "matematiksel" (veya programlara bağlı kalmak için işlevsel) ifadesini kastediyorum, örneğindouble x ben edecektir ilişkilendirmekdouble x*x . Matematikte bunu "\ mapsto" lateks sembolünü kullanarak yazarsınız . C # fonksiyonel gösterimi ödünç alınmış: =>. Örneğin :

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

(double x) => x * xbir ifadedir . Bu bir tür değildir, oysa temsilciler (genel veya değil) öyledir.

Ahlak mı? Sonunda, bir işlev işaretçi türü değilse (sırasıyla sarılmış + akıllı + genel işlev işaretçisi türü), bir temsilci (veya genel temsilci) nedir? Başka bir şey ! Bunu gör ve bu .


-1

Aslında aşırı basitleştirilmiş versiyon, bir lambda'nın anonim bir fonksiyonun kısaltması olmasıdır. Bir temsilci, anonim işlevlerden çok daha fazlasını yapabilir: olaylar gibi şeyler, zaman uyumsuz çağrılar ve birden çok yöntem zinciri.


1
lambdalar olay işleyicileri olarak kullanılabilir; button.Click + = (gönderen, eventArgs) => {MessageBox.Show ("Tıkla"); } ve asenkron olarak yeni System.Threading.Thread (() => Console.Write ("Bir iş parçacığında yürütüldü")) çağrıldı. Start ();
Steve Cooper
Sitemizi kullandığınızda şunları okuyup anladığınızı kabul etmiş olursunuz: Çerez Politikası ve Gizlilik Politikası.
Licensed under cc by-sa 3.0 with attribution required.