it-swarm-pt.tech

Qual é a diferença entre lambdas e delegados no .NET Framework?

Fui muito perguntado sobre essa questão e pensei em solicitar algumas informações sobre a melhor forma de descrever a diferença.

66
ScottKoon

Eles são na verdade duas coisas muito diferentes. "Delegado" é, na verdade, o nome de uma variável que contém uma referência a um método ou um lambda, e um lambda é um método sem um nome permanente.

Lambdas são muito parecidos com outros métodos, exceto por algumas diferenças sutis.

  1. Um método normal é definido em um "statement" e amarrado a um nome permanente, enquanto um lambda é definido "on the fly" em um "expression" e não possui nome permanente.
  2. Alguns lambdas podem ser usados ​​com árvores de expressão .NET, enquanto os métodos não podem.

Um delegado é definido assim:

delegate Int32 BinaryIntOp(Int32 x, Int32 y);

Uma variável do tipo BinaryIntOp pode ter um método ou um labmda atribuído a ela, desde que a assinatura seja a mesma: dois argumentos Int32 e um retorno Int32.

Um lambda pode ser definido assim:

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

Outra coisa a notar é que, embora os tipos genéricos Func e Action sejam frequentemente considerados "tipos lambda", eles são como qualquer outro delegado. O interessante é que eles essencialmente definem um nome para qualquer tipo de delegado que você possa precisar (até 4 parâmetros, embora certamente você possa adicionar mais do seu próprio). Portanto, se você estiver usando uma grande variedade de tipos de delegado, mas nenhum mais de uma vez, poderá evitar sobrecarregar seu código com declarações de delegado usando Func e Action.

Aqui está uma ilustração de como Func e Action são "não apenas para lambdas":

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

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

Outra coisa útil a saber é que os tipos delegados (não os próprios métodos) com a mesma assinatura, mas com nomes diferentes, não serão implicitamente convertidos uns para os outros. Isso inclui os delegados Func e Action. No entanto, se a assinatura for idêntica, você poderá explicitamente converter entre elas.

Indo a milha extra .... Em funções C # são flexíveis, com o uso de lambdas e delegados. Mas o C # não tem "funções de primeira classe". Você pode usar o nome de uma função atribuído a uma variável delegada para criar essencialmente um objeto que represente essa função. Mas é realmente um truque de compilador. Se você iniciar uma instrução escrevendo o nome da função seguido de um ponto (por exemplo, tentar acessar o membro na função em si), verá que não há membros para fazer referência. Nem mesmo os de Object. Isso impede que o programador faça coisas úteis (e potencialmente perigosas, é claro), como adicionar métodos de extensão que podem ser chamados em qualquer função. O melhor que você pode fazer é estender a própria classe Delegate, que certamente também é útil, mas não tanto.

Atualização: Veja também resposta de Karg ilustrando a diferença entre delegados anônimos vs. métodos & lambdas.

Atualização 2: James Hart faz uma observação importante, embora muito técnica, de que lambdas e delegados não são entidades .NET (ou seja, o CLR não tem nenhum conceito de delegado ou lambda), mas sim construções de framework e linguagem.

79
Chris Ammerman

A questão é um pouco ambígua, o que explica a grande disparidade de respostas que você está obtendo.

Você realmente perguntou qual é a diferença entre lambdas e delegados no .NET framework; isso pode ser uma de várias coisas. Você está perguntando:

  • Qual é a diferença entre expressões lambda e delegados anônimos na linguagem C # (ou VB.NET)?

  • Qual é a diferença entre objetos System.Linq.Expressions.LambdaExpression e objetos System.Delegate no .NET 3.5?

  • Ou algo em algum lugar entre ou em torno desses extremos?

Algumas pessoas parecem estar tentando dar a você a resposta para a pergunta 'qual é a diferença entre expressões C # Lambda e .NET System.Delegate?', O que não faz muito sentido.

O framework .NET não entende, por si só, os conceitos de delegados anônimos, expressões lambda ou encerramentos - são todos definidos por especificações de linguagem. Pense em como o compilador C # converte a definição de um método anônimo em um método em uma classe gerada com variáveis ​​de membro para manter o estado de fechamento; para o .NET, não há nada anônimo sobre o delegado; é apenas anônimo para o programador C # escrevê-lo. Isso é igualmente verdadeiro para uma expressão lambda atribuída a um tipo de delegado.

O que o .NETDOESunderstand é a idéia de um delegado - um tipo que descreve uma assinatura de método, cujas instâncias representam chamadas vinculadas a métodos específicos em objetos específicos ou chamadas não vinculadas a um método específico em um determinado tipo que pode ser invocado contra qualquer objeto desse tipo, onde o dito método adere à referida assinatura. Esses tipos todos herdam de System.Delegate.

O .NET 3.5 também introduz o namespace System.Linq.Expressions, que contém classes para descrever expressões de código - e que também pode representar chamadas vinculadas ou não ligadas a métodos em tipos ou objetos particulares. As instâncias de LambdaExpression podem então ser compiladas em delegados reais (em que um método dinâmico baseado na estrutura da expressão é codenegado e um ponteiro delegado para ele é retornado).

Em C # você pode produzir instâncias de System.Expressions.Expression tipos, atribuindo uma expressão lambda a uma variável do referido tipo, que irá produzir o código apropriado para construir a expressão em tempo de execução.

Claro, se você estava perguntando qual é a diferença entre expressões lambda e métodos anônimos em C #, afinal de contas, tudo isso é praticamente irrelevante, e nesse caso a principal diferença é a brevidade, que se inclina para delegados anônimos quando você não se preocupa com parâmetros e não planeja retornar um valor, e para lambdas quando você quer digitar parâmetros inferidos e tipos de retorno.

E expressões lambda suportam geração de expressão.

27
James Hart

Uma diferença é que um delegado anônimo pode omitir parâmetros enquanto um lambda deve corresponder à assinatura exata. Dado:

public delegate string TestDelegate(int i);

public void Test(TestDelegate d)
{}

você pode chamá-lo das quatro maneiras a seguir (observe que a segunda linha tem um representante anônimo que não possui nenhum parâmetro):

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

Você não pode passar uma expressão lambda que não tenha parâmetros ou um método que não tenha parâmetros. Estes não são permitidos:

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

Delegados são equivalentes a ponteiros de função/ponteiros de método/callbacks (faça a sua escolha), e lambdas são funções anônimas bem simplificadas. Pelo menos é o que eu digo às pessoas.

13
Dan Shield

Eu não tenho muita experiência com isso, mas a maneira que eu descreveria é que um delegado é um wrapper em torno de qualquer função, enquanto uma expressão lambda é uma função anônima.

3
chessguy

Um delegado é sempre basicamente um ponteiro de função. Um lambda pode se transformar em um delegado, mas também pode se transformar em uma árvore de expressões LINQ. Por exemplo,

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

A primeira linha produz um delegado, enquanto o segundo produz uma árvore de expressão.

3
Curt Hagenlocher

Um delegado é uma referência a um método com uma lista de parâmetros e um tipo de retorno específicos. Pode ou não incluir um objeto.

Uma expressão lambda é uma forma de função anônima.

2
Peter Ritchie

lambdas são simplesmente açúcar sintático em um delegado. O compilador acaba convertendo lambdas em delegados.

Estes são os mesmos, eu acredito:

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

Um delegado é uma assinatura de função; algo como 

delegate string MyDelegate(int param1);

O delegado não implementa um corpo. 

O lambda é uma chamada de função que corresponde à assinatura do delegado. Para o delegado acima, você pode usar qualquer um dos;

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

O tipo Delegate é mal nomeado, no entanto; criar um objeto do tipo Delegate na verdade cria uma variável que pode conter funções - sejam elas lambdas, métodos estáticos ou métodos de classe.

2
Steve Cooper

Está bem claro que a questão deveria ser "qual é a diferença entre lambdas e anônimo delegados?" De todas as respostas aqui, apenas uma pessoa acertou - a principal diferença é que os lambdas podem ser usados ​​para criar árvores de expressão, bem como delegados.

Você pode ler mais no MSDN: http://msdn.Microsoft.com/en-us/library/bb397687.aspx

2
Philip Beber

Um delegado é uma fila de ponteiros de função, chamando um delegado pode invocar vários métodos. Um lambda é essencialmente uma declaração de método anônima que pode ser interpretada pelo compilador de forma diferente, dependendo do contexto em que é usado.

Você pode obter um delegado que aponte para a expressão lambda como um método, lançando-o em um delegado ou, se estiver passando-o como um parâmetro para um método que espera um tipo de delegado específico, o compilador o converterá para você. Usando-o dentro de uma instrução LINQ, o lambda será traduzido pelo compilador em uma árvore de expressão em vez de simplesmente um delegado.

A diferença é que um lambda é uma maneira breve de definir um método dentro de outra expressão, enquanto um delegado é um tipo de objeto real.

1
justin.m.chase

Os delegados são realmente apenas digitação estrutural para funções. Você poderia fazer a mesma coisa com a digitação nominal e implementar uma classe anônima que implementa uma interface ou classe abstrata, mas isso acaba sendo muito código quando apenas uma função é necessária.

Lambda vem da idéia de lambda calculus da Alonzo Church nos anos 1930. É uma maneira anônima de criar funções. Eles se tornam especialmente úteis para compor funções

Então, enquanto alguns podem dizer que lambda é açúcar sintático para os delegados, eu diria que os delegados são uma ponte para facilitar as pessoas em lambdas em c #.

1
Steve g

Eu suponho que a sua pergunta diz respeito ao c # e não ao .NET, devido à ambigüidade da sua pergunta, pois o .NET não fica sozinho - isto é, sem c # - compreensão de delegados e expressões lambda.

Um ( normal , em oposição ao chamado genérico delegados, cf later) delegado deve ser visto como um tipo de c ++ typedef de um tipo de ponteiro de função, para instância em c ++:

R (*thefunctionpointer) ( T ) ;

typedef é do tipo thefunctionpointer, que é o tipo de ponteiros para uma função que recebe um objeto do tipo T e retorna um objeto do tipo R. Você usaria assim:

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

onde thefunction seria uma função com um T e retornando um R.

Em c # você iria para

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

e você usaria assim:

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

onde thefunction seria uma função com um T e retornando um R. Isso é para os delegados, os chamados delegados normais.

Agora, você também tem delegados genéricos em c #, que são delegados que são genéricos, ou seja, que são "modelos" por assim dizer, usando assim uma expressão c + +. Eles são definidos assim:

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

E você pode usá-los assim:

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

onde thefunction2 é uma função que toma como argumento e retorna um double.

Agora imagine que em vez de thefunction2 eu gostaria de usar uma "função" que não é definida por enquanto, por uma declaração, e que eu nunca irei usar mais tarde. Então o c # nos permite usar o expressão desta função. Por expressão, quero dizer a expressão "matemática" (ou funcional, para se ater aos programas), por exemplo: para um double x eu vou associar o doublex*x. Em matemática você escreve isto usando o símbolo de látex "\ mapsto" . Em c #, a notação funcional foi emprestada: =>. Por exemplo :

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

(double x) => x * x é uma expressão . Não é um tipo, enquanto os delegados (genéricos ou não) são.

Moralidade? No final, o que é um delegado (resp. Delegado genérico), se não um tipo de ponteiro de função (resp. Empacotado + inteligente + tipo de ponteiro de função genérico), hein? Algo mais ! Veja this e that .

0

Aqui está um exemplo que eu coloquei por algum tempo no meu blog coxo. Digamos que você queira atualizar um rótulo de um segmento de trabalho. Eu tenho 4 exemplos de como atualizar esse rótulo de 1 a 50 usando delegados, delegados anon e 2 tipos de 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

Alguns básicos aqui. "Delegado" é, na verdade, o nome de uma variável que contém uma referência a um método ou a um lambda

Este é um método anônimo - (String testString) => {Console.WriteLine (testString); };

Como o método anônimo não possui nenhum nome, precisamos de um delegado no qual possamos atribuir ambos os métodos ou expressões. Por exemplo.

delegate void PrintTestString (string testString); // declara um delegado

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


Mesmo com a expressão lambda. Normalmente precisamos delegar para usá-los

s => s.Age> someValue && s.Age <someValue // retornará verdadeiro/falso

Podemos usar um delegado func para usar essa expressão.

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

bool result = checkStudentAge (Student Object);

0
Yogesh Prajapati

Lambdas são versões simplificadas de delegados. Eles têm algumas das propriedades de um encerramento como delegados anônimos, mas também permitem que você use digitação implícita. Um lambda como este:

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

é muito mais conciso do que o que você pode fazer com um delegado:

something.Sort(sortMethod);
...

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