Często zadawano mi to pytanie i pomyślałem, że poproszę o wskazówki, jak najlepiej opisać różnicę.
Często zadawano mi to pytanie i pomyślałem, że poproszę o wskazówki, jak najlepiej opisać różnicę.
Odpowiedzi:
W rzeczywistości są to dwie bardzo różne rzeczy. „Delegat” to w rzeczywistości nazwa zmiennej, która zawiera odniesienie do metody lub wyrażenia lambda, a lambda to metoda bez stałej nazwy.
Lambdy są bardzo podobne do innych metod, z wyjątkiem kilku subtelnych różnic.
Delegat jest definiowany w następujący sposób:
delegate Int32 BinaryIntOp(Int32 x, Int32 y);
Zmienna typu BinaryIntOp może mieć przypisaną metodę lub labmda, o ile podpis jest taki sam: dwa argumenty Int32 i zwrot Int32.
Lambda można zdefiniować w ten sposób:
BinaryIntOp sumOfSquares = (a, b) => a*a + b*b;
Inną rzeczą, na którą należy zwrócić uwagę, jest to, że chociaż ogólne typy Func i Action są często uważane za „typy lambda”, są one takie same jak inne delegaty. Zaletą jest to, że zasadniczo definiują nazwę dla dowolnego typu delegata, którego możesz potrzebować (do 4 parametrów, chociaż z pewnością możesz dodać więcej własnych). Jeśli więc używasz szerokiej gamy typów delegatów, ale nie więcej niż raz, możesz uniknąć zaśmiecania kodu deklaracjami delegatów przy użyciu funkcji Func i Action.
Oto ilustracja tego, jak Func i Action są „nie tylko dla lambd”:
Int32 DiffOfSquares(Int32 x, Int32 y)
{
return x*x - y*y;
}
Func<Int32, Int32, Int32> funcPtr = DiffOfSquares;
Inną przydatną rzeczą jest wiedzieć, że typy delegatów (a nie same metody) z tą samą sygnaturą, ale różnymi nazwami nie będą do siebie rzutowane niejawnie. Obejmuje to delegatów Func i Action. Jeśli jednak podpis jest identyczny, możesz jawnie rzutować między nimi.
Idziemy o krok dalej ... W C # funkcje są elastyczne, z wykorzystaniem lambd i delegatów. Ale C # nie ma „funkcji pierwszej klasy”. Możesz użyć nazwy funkcji przypisanej do zmiennej delegata, aby zasadniczo utworzyć obiekt reprezentujący tę funkcję. Ale to naprawdę sztuczka kompilatora. Jeśli rozpoczniesz instrukcję od wpisania nazwy funkcji, po której następuje kropka (tj. Spróbujesz uzyskać dostęp do elementu członkowskiego samej funkcji), zobaczysz, że nie ma tam żadnych elementów członkowskich, do których można by się odwołać. Nawet te z Object. Zapobiega to wykonywaniu przez programistę pożytecznych (i potencjalnie niebezpiecznych) rzeczy, takich jak dodawanie metod rozszerzających, które można wywołać w dowolnej funkcji. Najlepsze, co możesz zrobić, to rozszerzyć samą klasę Delegate, która z pewnością jest również przydatna, ale nie aż tak bardzo.
Aktualizacja: Zobacz także odpowiedź Karga ilustrującą różnicę między anonimowymi delegatami a metodami i lambdami.
Aktualizacja 2: James Hart zwraca uwagę, że lambdy i delegaty nie są jednostkami .NET (tj. CLR nie ma pojęcia delegata ani lambdy), ale raczej są konstrukcjami ramowymi i językowymi.
Pytanie jest trochę niejednoznaczne, co wyjaśnia dużą rozbieżność w otrzymywanych odpowiedziach.
Właściwie zapytałeś, jaka jest różnica między lambdami a delegatami w środowisku .NET; to może być jedna z wielu rzeczy. Czy ty pytasz:
Jaka jest różnica między wyrażeniami lambda a anonimowymi delegatami w języku C # (lub VB.NET)?
Jaka jest różnica między obiektami System.Linq.Expressions.LambdaExpression a obiektami System.Delegate w .NET 3.5?
A może coś pomiędzy tymi skrajnościami lub wokół nich?
Wydaje się, że niektórzy ludzie próbują udzielić odpowiedzi na pytanie „jaka jest różnica między wyrażeniami Lambda w języku C # a .NET System.Delegate?”, Co nie ma większego sensu.
Platforma .NET sama w sobie nie rozumie koncepcji anonimowych delegatów, wyrażeń lambda ani domknięć - to wszystko jest zdefiniowane przez specyfikacje języka. Pomyśl o tym, jak kompilator C # tłumaczy definicję metody anonimowej na metodę w wygenerowanej klasie ze zmiennymi składowymi do przechowywania stanu zamknięcia; w .NET nie ma nic anonimowego w delegacie; jest po prostu anonimowy dla programisty C # piszącego go. To samo dotyczy wyrażenia lambda przypisanego do typu delegata.
Co .NET robi zrozumieć jest pomysł delegata - typ, który opisuje podpis metody, które reprezentują zarówno przypadki związane wywołań metod szczegółowych dotyczących poszczególnych obiektów lub rozmów niezwiązanych z konkretnym sposobem na określonego rodzaju, które mogą być stosowane w odniesieniu każdy obiekt tego typu, jeśli wspomniana metoda jest zgodna z wspomnianym podpisem. Wszystkie takie typy dziedziczą po System.Delegate.
NET 3.5 wprowadza również przestrzeń nazw System.Linq.Expressions, która zawiera klasy do opisywania wyrażeń kodu - i która w związku z tym może również reprezentować powiązane lub niezwiązane wywołania metod na określonych typach lub obiektach. Wystąpienia LambdaExpression można następnie skompilować do rzeczywistych delegatów (przy czym metoda dynamiczna oparta na strukturze wyrażenia jest kodowana i zwracany jest wskaźnik delegata).
W języku C # można tworzyć wystąpienia typów System.Expressions.Expression, przypisując wyrażenie lambda do zmiennej tego typu, co spowoduje wygenerowanie odpowiedniego kodu do skonstruowania wyrażenia w czasie wykonywania.
Oczywiście, jeśli zostały pytaniem, jaka jest różnica między wyrażeniami lambda i anonimowe metody w C #, po wszystkim, to wszystko to jest dość dużo irelevant, aw tym przypadku różnica jest zwięzłość, który skłania się ku anonimowych delegatów kiedy don” Nie przejmuj się parametrami i nie planuj zwracania wartości, a także lambd, gdy chcesz wpisać parametry wywnioskowane i typy zwracane.
Wyrażenia lambda obsługują generowanie wyrażeń.
Jedyną różnicą jest to, że anonimowy delegat może pomijać parametry, podczas gdy lambda musi pasować do dokładnego podpisu. Dany:
public delegate string TestDelegate(int i);
public void Test(TestDelegate d)
{}
możesz to wywołać na cztery sposoby (zwróć uwagę, że druga linia ma anonimowego delegata, który nie ma żadnych parametrów):
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;
}
Nie można przekazać wyrażenia lambda, które nie ma parametrów ani metody, która nie ma parametrów. Nie są dozwolone:
Test(() => String.Empty); //Not allowed, lambda must match signature
Test(D2); //Not allowed, method must match signature
private string D2()
{
return String.Empty;
}
Delegaci są odpowiednikami wskaźników funkcji / wskaźników metod / wywołań zwrotnych (wybierz), a lambdy są w zasadzie uproszczonymi funkcjami anonimowymi. Tak przynajmniej mówię ludziom.
Delegat jest zawsze po prostu wskaźnikiem funkcji. Lambda może przekształcić się w delegata, ale może również przekształcić się w drzewo wyrażenia LINQ. Na przykład,
Func<int, int> f = x => x + 1;
Expression<Func<int, int>> exprTree = x => x + 1;
Pierwsza linia tworzy delegata, a druga tworzy drzewo wyrażeń.
lambdy są po prostu cukrem syntaktycznym dla delegata. Kompilator kończy konwersję wyrażeń lambda na delegatów.
Sądzę, że są takie same:
Delegate delegate = x => "hi!";
Delegate delegate = delegate(object x) { return "hi";};
Delegate
z „delegat”, które jest słowem kluczowym.
Delegat to podpis funkcji; coś jak
delegate string MyDelegate(int param1);
Delegat nie implementuje treści.
Lambda to wywołanie funkcji, które pasuje do podpisu delegata. W przypadku powyższego delegata możesz użyć dowolnego z;
(int i) => i.ToString();
(int i) => "ignored i";
(int i) => "Step " + i.ToString() + " of 10";
Jednak Delegate
typ jest źle nazwany; tworzenie obiektu typu w Delegate
rzeczywistości tworzy zmienną, która może przechowywać funkcje - czy to lambdy, metody statyczne, czy metody klas.
Delegat to odwołanie do metody z określoną listą parametrów i zwracanym typem. Może zawierać obiekt lub nie.
Wyrażenie lambda jest formą funkcji anonimowej.
Delegat to kolejka wskaźników funkcji, wywołanie delegata może wywołać wiele metod. Lambda jest zasadniczo anonimową deklaracją metody, która może być różnie interpretowana przez kompilator, w zależności od kontekstu, w jakim jest używana.
Możesz pobrać delegata, który wskazuje na wyrażenie lambda jako metodę, rzutując je na delegata lub przekazując go jako parametr do metody, która oczekuje określonego typu delegata, kompilator rzuci go za Ciebie. Używając go wewnątrz instrukcji LINQ, lambda zostanie przetłumaczona przez kompilator na drzewo wyrażeń, a nie po prostu delegata.
Różnica polega na tym, że lambda to zwięzły sposób definiowania metody wewnątrz innego wyrażenia, podczas gdy delegat jest rzeczywistym typem obiektu.
Jest całkiem jasne, że pytanie miało być „Jaka jest różnica między lambdas i anonimowych delegatami?" Spośród wszystkich odpowiedzi tylko jedna osoba zrozumiała to dobrze - główna różnica polega na tym, że lambdy mogą być używane zarówno do tworzenia drzew wyrażeń, jak i delegatów.
Możesz przeczytać więcej na MSDN: http://msdn.microsoft.com/en-us/library/bb397687.aspx
Delegaci są tak naprawdę tylko strukturalnym typowaniem funkcji. Możesz zrobić to samo przy nominalnym wpisywaniu i implementowaniu anonimowej klasy, która implementuje interfejs lub klasę abstrakcyjną, ale kończy się to dużą ilością kodu, gdy potrzebna jest tylko jedna funkcja.
Lambda wywodzi się z idei rachunku lambda Alonzo Churcha w latach trzydziestych XX wieku. Jest to anonimowy sposób tworzenia funkcji. Stają się szczególnie przydatne do tworzenia funkcji
Więc chociaż niektórzy mogą powiedzieć, że lambda jest cukrem syntaktycznym dla delegatów, powiedziałbym, że delegaci są pomostem ułatwiającym ludziom przejście na lambdy w języku c #.
Tutaj trochę podstawowych. „Delegat” to w rzeczywistości nazwa zmiennej, która zawiera odniesienie do metody lub wyrażenia lambda
To jest metoda anonimowa -
(string testString) => { Console.WriteLine(testString); };
Ponieważ metoda anonimowa nie ma żadnej nazwy, potrzebujemy delegata, w którym możemy przypisać obie te metody lub wyrażenie. Dla Ex.
delegate void PrintTestString(string testString); // declare a delegate
PrintTestString print = (string testString) => { Console.WriteLine(testString); };
print();
To samo z wyrażeniem lambda. Zwykle do ich używania potrzebujemy pełnomocnika
s => s.Age > someValue && s.Age < someValue // will return true/false
Możemy użyć delegata func, aby użyć tego wyrażenia.
Func< Student,bool> checkStudentAge = s => s.Age > someValue && s.Age < someValue ;
bool result = checkStudentAge ( Student Object);
Lambdy to uproszczone wersje delegatów. Mają niektóre właściwości zamknięcia, takie jak anonimowi delegaci, ale pozwalają również na użycie niejawnego wpisywania. Taka lambda:
something.Sort((x, y) => return x.CompareTo(y));
jest o wiele bardziej zwięzłe niż to, co możesz zrobić z delegatem:
something.Sort(sortMethod);
...
private int sortMethod(SomeType one, SomeType two)
{
one.CompareTo(two)
}
Oto przykład, który chwilę zamieściłem na moim kiepskim blogu. Powiedzmy, że chcesz zaktualizować etykietę z wątku roboczego. Mam 4 przykłady, jak zaktualizować tę etykietę od 1 do 50 za pomocą delegatów, delegatów anon i 2 typów lambd.
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);
}
}
Zakładam, że Twoje pytanie dotyczy języka C # a nie .NET, ze względu na niejednoznaczność pytania, ponieważ .NET nie radzi sobie samodzielnie - czyli bez C # - rozumienia delegatów i wyrażeń lambda.
Delegat ( normalny , w przeciwieństwie do tzw. Delegatów generycznych , patrz później) delegat powinien być postrzegany jako rodzaj c ++ typedef
typu wskaźnika funkcji, na przykład w języku c ++:
R (*thefunctionpointer) ( T ) ;
typedef to typ thefunctionpointer
będący typem wskaźników do funkcji pobierającej obiekt typu T
i zwracającej obiekt typu R
. Używałbyś tego w ten sposób:
thefunctionpointer = &thefunction ;
R r = (*thefunctionpointer) ( t ) ; // where t is of type T
gdzie thefunction
byłaby funkcja przyjmująca a T
i zwracająca R
.
W c # poszedłbyś na
delegate R thedelegate( T t ) ; // and yes, here the identifier t is needed
i użyłbyś tego w ten sposób:
thedelegate thedel = thefunction ;
R r = thedel ( t ) ; // where t is of type T
gdzie thefunction
byłaby funkcja przyjmująca a T
i zwracająca R
. Dotyczy to delegatów, tzw. Zwykłych delegatów.
Teraz masz również delegatów ogólnych w języku C #, którzy są delegatami, które są ogólne, tj. Są „szablonowe”, że tak powiem, przy użyciu wyrażenia c ++. Są zdefiniowane w następujący sposób:
public delegate TResult Func<in T, out TResult>(T arg);
Możesz ich używać w ten sposób:
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);
gdzie thefunction2
jest funkcją przyjmującą jako argument i zwracającą double
.
Teraz wyobraź sobie, że zamiast tego thefunction2
chciałbym użyć „funkcji”, która na razie nie jest nigdzie zdefiniowana, za pomocą instrukcji i której nigdy nie będę używać później. Następnie c # pozwala nam użyć wyrażenia tej funkcji. Przez ekspresję mam na myśli „matematyczny” (lub funkcjonalny, aby trzymać się programów) ekspresję nim, na przykład: Do double x
będę kojarzyćdouble
x*x
. W matematyce piszesz to za pomocą lateksowego symbolu „\ mapsto” . W języku C # notacja funkcjonalna została zapożyczona: =>
. Na przykład :
Func<double, double> thefunctor = ( (double x) => x * x ); // outer brackets are not
// mandatory
(double x) => x * x
jest wyrażeniem . To nie jest typ, podczas gdy delegaci (ogólne lub nie) są.
Moralność? Na koniec, czym jest delegat (względnie delegat ogólny), jeśli nie typem wskaźnika funkcji (odpowiednio opakowany + inteligentny + ogólny typ wskaźnika funkcji), co? Coś innego ! Zobacz to i tamto .
Cóż, naprawdę uproszczona wersja jest taka, że lambda jest po prostu skrótem dla funkcji anonimowej. Pełnomocnik może wykonywać znacznie więcej niż tylko funkcje anonimowe: takie rzeczy, jak zdarzenia, wywołania asynchroniczne i wiele łańcuchów metod.