it-swarm-eu.dev

Jaký je rozdíl mezi lambdas a delegáty v .NET Framework?

Dostal jsem se na tuto otázku hodně a myslel jsem, že bych si přál získat nějaké informace o tom, jak nejlépe popsat rozdíl.

66
ScottKoon

Jsou to vlastně dvě velmi odlišné věci. "Delegát" je vlastně název proměnné, která obsahuje odkaz na metodu nebo lambda, a lambda je metoda bez trvalého jména.

Lambdas je velmi podobný jiným metodám, kromě pár jemných rozdílů.

  1. Normální metoda je definovaná v “prohlášení” a svázaný k trvalému jménu, zatímco lambda je definována “za letu” v “výraz” a má žádné trvalé jméno.
  2. Některé lambdas lze použít s .NET výrazovými stromy, zatímco metody nemohou.

Delegát je definován takto:

delegate Int32 BinaryIntOp(Int32 x, Int32 y);

Proměnná typu BinaryIntOp může mít buď metodu nebo labmda, která jí byla přiřazena, pokud je podpis stejný: dva argumenty Int32 a návrat Int32.

Lambda může být definována takto:

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

Další věc, kterou je třeba poznamenat, je, že i když jsou obecné typy Func a Action často považovány za „typy lambda“, jsou stejně jako ostatní delegáti. Pěkná věc na nich je, že v podstatě definují jméno pro jakýkoli typ delegáta, který byste mohli potřebovat (až 4 parametry, i když si jistě můžete přidat další vlastní). Pokud tedy používáte širokou škálu typů delegátů, ale ne více než jednou, můžete se vyhnout přeplnění kódu pomocí deklarací delegátů pomocí funkce Func and Action.

Zde je ukázka toho, jak jsou Func a Action "nejen pro lambdy":

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

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

Další užitečná věc, kterou je třeba vědět, je to, že typy delegátů (nikoli metody samotné) se stejným podpisem, ale jiné názvy nebudou implicitně navzájem sesílány. To zahrnuje delegáty Func a Action. Pokud je však podpis totožný, můžete mezi ně explicitně odevzdat.

Jít extra míle .... V C # funkce jsou flexibilní, s použitím lambdas a delegátů. Ale C # nemá "prvotřídní funkce". Můžete použít název funkce přiřazený proměnné delegate v podstatě vytvořit objekt představující tuto funkci. Ale je to opravdu trik kompilátoru. Pokud spustíte příkaz zapsáním názvu funkce následovaného tečkou (tj. Pokuste se o přístup člena k samotné funkci), zjistíte, že zde nejsou žádní členové, na které by bylo možné odkazovat. Ani ty z Object. To zabraňuje programátorovi v tom, aby dělal užitečné (a potenciálně nebezpečné) věci, jako je přidávání rozšiřujících metod, které mohou být volány v jakékoli funkci. Nejlepší, co můžete udělat, je rozšířit samotnou třídu Delegate, což je jistě také užitečné, ale ne tak moc.

Update: Viz také Kargova odpověď ilustrující rozdíl mezi anonymními delegáty vs. metodami & lambdas.

Update 2: James Hart dělá důležitý, ačkoli velmi technický, poznamenat, že lambdas a delegates nejsou. NET entity (tj. CLR nemá představu o delegátovi nebo lambda), ale poněkud oni jsou framework a jazykové konstrukty.

79
Chris Ammerman

Tato otázka je trochu nejednoznačná, což vysvětluje velké rozdíly v odpovědích, které získáváte.

Vlastně jste se ptali, jaký je rozdíl mezi lambdas a delegáty v rámci .NET framework; to by mohlo být jedno z řady věcí. Ptáte se:

  • Jaký je rozdíl mezi výrazy lambda a anonymními delegáty v jazyce C # (nebo VB.NET)?

  • Jaký je rozdíl mezi objekty System.Linq.Expressions.LambdaExpression a System.Delegate objekty v .NET 3.5?

  • Nebo něco mezi těmito extrémy?

Zdá se, že někteří lidé se snaží odpovědět na otázku „jaký je rozdíl mezi výrazy C # Lambda a .NET System.Delegate?“, Což nedává smysl.

Rámec .NET sám o sobě nerozumí pojmům anonymních delegátů, výrazů lambda nebo uzávěrů - to jsou věci definované jazykovými specifikacemi. Přemýšlejte o tom, jak kompilátor C # překládá definici anonymní metody do metody na generované třídě s členskými proměnnými, které drží stav uzavření; k .NET, o delegátovi není nic anonymního; je to prostě anonymní, aby to programátor C # napsal. To platí i pro výraz lambda přiřazený typu delegáta.

.NETDOESunderstand je představa delegáta - typ, který popisuje podpis metody, jejíž instance představují buď vázané volání na specifické metody na konkrétních objektech, nebo nevázané volání na konkrétní metodu na konkrétním objektu. typu, který může být vyvolán proti jakémukoliv objektu tohoto typu, kde uvedený způsob dodržuje uvedený podpis. Všechny tyto typy dědí ze systému System.Delegate.

.NET 3.5 také zavádí obor názvů System.Linq.Expressions, který obsahuje třídy pro popis kódových výrazů - a který tedy může také představovat vázané nebo nevázané volání metod na určité typy nebo objekty. Instance LambdaExpression pak mohou být zkompilovány do skutečných delegátů (přičemž dynamická metoda založená na struktuře výrazu je kódována a vrátí se ukazatel delegáta na ni).

V jazyce C # můžete vytvářet instance typů System.Expressions.Expression přiřazením výrazu lambda k proměnné uvedeného typu, která vytvoří odpovídající kód pro vytvoření výrazu za běhu.

Samozřejmě, pokud jste byli ptát se, jaký je rozdíl mezi výrazy lambda a anonymními metodami v C #, pak to všechno je dosti irelevantní a v tom případě je primárním rozdílem stručnost, která se opírá o anonymní delegáty když se nestaráte o parametry a neplánujete vracet hodnotu, a směrem k lambdas, když chcete typ inferencovaných parametrů a typů návratů.

A výrazy lambda podporují generování výrazu.

27
James Hart

Jeden rozdíl je v tom, že anonymní delegát může vynechat parametry, zatímco lambda musí odpovídat přesnému podpisu. Vzhledem k:

public delegate string TestDelegate(int i);

public void Test(TestDelegate d)
{}

můžete ji vyvolat následujícími čtyřmi způsoby (všimněte si, že druhý řádek má anonymního delegáta, který nemá žádné parametry):

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

Nelze předat výraz lambda, který nemá žádné parametry ani metodu, která nemá žádné parametry. Nejsou povoleny:

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

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

Delegáti jsou ekvivalentní funkčním ukazatelům/metodickým ukazatelům/zpětným voláním (vezměte si svůj výběr) a lambdas jsou do značné míry zjednodušené anonymní funkce. Přinejmenším to říkám lidem.

13
Dan Shield

S tím nemám tolik zkušeností, ale způsob, jakým bych to popsal, je, že delegát je obálka kolem jakékoli funkce, zatímco výraz lambda je sám anonymní funkcí.

3
chessguy

Delegát je vždy jen v podstatě ukazatel funkce. Lambda se může proměnit v delegáta, ale může se také proměnit ve strom výrazu LINQ. Například,

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

První řádek vytvoří delegáta, zatímco druhý vytvoří strom výrazu.

3
Curt Hagenlocher

Delegát je odkaz na metodu s konkrétním seznamem parametrů a typem návratu. Může nebo nemusí obsahovat objekt.

Výraz lambda je formou anonymní funkce.

2
Peter Ritchie

lambdas jsou jednoduše syntaktický cukr na delegáta. Kompilátor končí převedením lambdas na delegáty.

To jsou stejné, věřím:

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

Delegát je podpis funkce; něco jako 

delegate string MyDelegate(int param1);

Delegát neimplementuje tělo. 

Lambda je volání funkce, které odpovídá podpisu delegáta. Pro výše uvedeného delegáta můžete použít některý z;

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

Typ Delegate je však špatně pojmenován; Vytvoření objektu typu Delegate skutečně vytvoří proměnnou, která může obsahovat funkce - ať už jde o lambdas, statické metody nebo metody třídy.

2
Steve Cooper

Je zcela jasné, že otázka měla být "jaký je rozdíl mezi lambdas a anonymní delegáti?" Ze všech odpovědí zde jen jedna osoba měla pravdu - hlavní rozdíl je v tom, že lambdas lze použít k tvorbě výrazových stromů i delegátů.

Další informace naleznete na webu MSDN: http://msdn.Microsoft.com/en-us/library/bb397687.aspx

2
Philip Beber

Delegát je fronta ukazatelů funkcí, vyvolání delegáta může vyvolat více metod. Lambda je v podstatě anonymní deklarace metody, kterou může kompilátor interpretovat odlišně v závislosti na tom, jaký kontext se používá jako.

Můžete získat delegáta, který odkazuje na výraz lambda jako metodu tím, že ho převedete do delegáta, nebo pokud jej předáte jako parametr metodě, která očekává konkrétní typ delegáta, který kompilátor za vás přenese. Použijete-li ji uvnitř příkazu LINQ, bude překladač překládán do stromu výrazu namísto pouhého delegáta.

Rozdíl je ve skutečnosti, že lambda je způsob, jak definovat metodu uvnitř jiného výrazu, zatímco delegát je skutečný typ objektu.

1
justin.m.chase

Delegáti jsou opravdu jen strukturální typizace funkcí. Mohli byste udělat totéž s nominálním typováním a implementací anonymní třídy, která implementuje rozhraní nebo abstraktní třídu, ale která skončí jako hodně kódu, když je potřeba pouze jedna funkce.

Lambda pochází z myšlenky lambda kalkulu Alonzo kostela ve třicátých létech. Jedná se o anonymní způsob vytváření funkcí. Stávají se obzvláště užitečnými pro skládání funkcí

Zatímco někteří by mohli říci, že lambda je pro delegáty syntaktický cukr, řekl bych, že delegáti jsou mostem pro uvolnění lidí do lambdas v c #.

1
Steve g

Předpokládám, že se vaše otázka týká c # a ne. NET, kvůli nejednoznačnosti vaší otázky, protože .NET se nedostane osamoceně - to znamená bez c # - pochopení delegátů a výrazů lambda.

A (normální, v opozici k takzvaným generickým delegátům, cf pozdější) delegát by měl být považován za druh c ++ typedef typu ukazatele funkce, například v c ++:

R (*thefunctionpointer) ( T ) ;

typedef je typ thefunctionpointer, což je typ ukazatelů na funkci, která vezme objekt typu T a vrátí objekt typu R. Použili byste to takto:

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

kde thefunction by byla funkce s T a vrácením R.

V c # byste jít na

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

a používali byste to takto:

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

kde thefunction by byla funkce s T a vrácením R. To je pro delegáty, tzv. Normální delegáty.

Nyní máte také generické delegáty v c #, což jsou delegáti, kteří jsou generičtí tj. kteří jsou takřka "templated" tak, aby se tak dalo říci, a tím se používá výraz c ++. Jsou definovány takto:

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

A můžete je použít takto:

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

kde thefunction2 je funkce brát jako argument a vracet double.

Teď si představte, že místo thefunction2 bych chtěl použít "funkci", která není nikde definována, prohlášení a nikdy nebudu později používat. Pak nám c # umožňuje použít výraz této funkce. Výrazem mám na mysli výraz "matematický" (nebo funkční, držet se programů) jeho vyjádření, například: do double x I will přidružitdoublex*x. V matematice to píšete pomocí "lasto" latexového symbolu . V c # byl funkční zápis vypůjčen: =>. Například :

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

(double x) => x * x je výraz . Není to typ, zatímco delegáti (generičtí nebo ne) jsou.

Morálka? Na konci, co je delegát (resp. Generický delegát), ne-li typ ukazatele funkce (resp. Zabalený + inteligentní + obecný typ ukazatele funkce), huh? Něco jiného ! Viz toto a to .

Byl jsem příkladem, který jsem se na chvíli zdržel na mém chromém blogu. Řekněme, že chcete aktualizovat štítek z pracovního vlákna. Mám 4 příklady, jak aktualizovat tuto značku z 1 na 50 pomocí delegátů, delegátů anon a 2 typů lambdas.

 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

Některé základní zde. "Delegát" je vlastně název proměnné, která obsahuje odkaz na metodu nebo lambdu

Toto je anonymní metoda - (Řetězec testString) => {Console.WriteLine (testString); };

Protože anonymní metoda nemá žádné jméno, potřebujeme delegáta, ve kterém můžeme přiřadit obě tyto metody nebo výrazy. Pro Ex.

delegate void PrintTestString (řetězec testString); // deklarovat delegáta

PrintTestString print = (řetězec testString) => {Console.WriteLine (testString); }; vytisknout();


Stejně jako výraz lambda. Obvykle je potřebujeme delegovat

s => s.Age> someValue && s.Age <someValue // vrátí true/false

K použití tohoto výrazu můžeme použít delegáta func.

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

bool result = checkStudentAge (Objekt studenta);

0
Yogesh Prajapati

Lambdas jsou zjednodušené verze delegátů. Mají některé z vlastností uzavření jako anonymní delegáti, ale také vám umožní použít implicitní psaní. Taková lambda:

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

je mnohem stručnější než to, co můžete udělat s delegátem:

something.Sort(sortMethod);
...

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