delegar palavra-chave x notação lambda


Respostas:


140

Resposta curta: não.

Resposta mais longa que pode não ser relevante:

  • Se você atribuir o lambda a um tipo de delegado (como Funcou Action), receberá um delegado anônimo.
  • Se você atribuir o lambda a um tipo de Expressão, receberá uma árvore de expressão em vez de um delegado anônimo. A árvore de expressão pode ser compilada para um delegado anônimo.

Edit: Aqui estão alguns links para expressões.

  • System.Linq.Expression.Expression (TDelegate) (comece aqui).
  • O Linq na memória com representantes (como System.Func) usa System.Linq.Enumerable . O Linq to SQL (e qualquer outra coisa) com expressões usa System.Linq.Queryable . Confira os parâmetros nesses métodos.
  • Uma explicação de ScottGu . Em poucas palavras, o Linq in-memory produzirá alguns métodos anônimos para resolver sua consulta. O Linq to SQL produzirá uma árvore de expressão que representa a consulta e depois a converterá em T-SQL. O Linq to Entities produzirá uma árvore de expressão que representa a consulta e depois a converterá em SQL apropriado da plataforma.

3
Um tipo de expressão? Isso soa como um novo território para mim. Onde posso descobrir mais sobre os tipos de expressão e o uso de árvores de expressão em C #?
MojoFilter

2
Mesmo mais resposta - há razões complicados porque eles são conversíveis em diferentes tipos de delegado, também :)
Jon Skeet

Observe que o lambda só pode ser atribuído a um tipo de expressão, se for uma expressão lambda.
Micha Wiedenmann 8/16

125

Gosto da resposta de Amy, mas achei que seria pedante. A pergunta diz: "Uma vez compilado" - o que sugere que ambas as expressões tenham sido compilados. Como eles poderiam compilar, mas com um sendo convertido em um delegado e outro em uma árvore de expressão? É complicado - você precisa usar outro recurso de métodos anônimos; o único que não é compartilhado por expressões lambda. Se você especificar um método anônimo sem especificar uma lista de parâmetros , ele será compatível com qualquer tipo de delegado retornando nulo e sem nenhum outparâmetro. Armado com esse conhecimento, devemos ser capazes de construir duas sobrecargas para tornar as expressões completamente inequívocas, mas muito diferentes.

Mas um desastre acontece! Pelo menos com o C # 3.0, não é possível converter uma expressão lambda com um corpo de bloco em uma expressão - nem converter uma expressão lambda com uma atribuição no corpo (mesmo que seja usada como valor de retorno). Isso pode mudar com o C # 4.0 e o .NET 4.0, que permitem que mais seja expresso em uma árvore de expressão. Então, em outras palavras, com os exemplos que o MojoFilter deu, os dois quase sempre serão convertidos na mesma coisa. (Mais detalhes em um minuto.)

Podemos usar o truque de delegar parâmetros se mudarmos os corpos um pouco:

using System;
using System.Linq.Expressions;

public class Test
{
    static void Main()
    {
        int x = 0;
        Foo( () => x );
        Foo( delegate { return x; } );
    }

    static void Foo(Func<int, int> action)
    {
        Console.WriteLine("I suspect the anonymous method...");
    }

    static void Foo(Expression<Func<int>> func)
    {
        Console.WriteLine("I suspect the lambda expression...");
    }
}

Mas espere! Podemos diferenciar os dois mesmo sem usar árvores de expressão, se formos espertos o suficiente. O exemplo abaixo usa as regras de resolução de sobrecarga (e o truque de correspondência de delegado anônimo) ...

using System;
using System.Linq.Expressions;

public class Base
{
    public void Foo(Action action)
    {
        Console.WriteLine("I suspect the lambda expression...");
    }
}

public class Derived : Base
{
    public void Foo(Action<int> action)
    {
        Console.WriteLine("I suspect the anonymous method...");
    }
}

class Test
{
    static void Main()
    {
        Derived d = new Derived();
        int x = 0;
        d.Foo( () => { x = 0; } );
        d.Foo( delegate { x = 0; } );
    }
}

Ai. Lembre-se de crianças, toda vez que você sobrecarrega um método herdado de uma classe base, um gatinho começa a chorar.


9
Peguei minha pipoca e li a coisa toda. Essa é uma distinção em que eu provavelmente nunca pensaria, mesmo se estivesse olhando bem na cara.
MojoFilter

27
Eu sabia um pouco disso, mas devo parabenizá-lo por sua capacidade de comunicá-lo aos humanos.
Amy B

1
Para qualquer pessoa interessada nas alterações no .NET 4.0 (com base no CTP) - consulte marcgravell.blogspot.com/2008/11/future-expressions.html . Observe que o C # 4.0 não faz nada de novo ainda, tanto quanto eu posso dizer.
Marc Gravell

4
Jon, você é demais. Erik, para ser um verdadeiro fã do Skeet, você deve estar inscrito no seu stack overflow rss como eu. Coloque o stackoverflow.com/users/22656 no seu leitor de feeds.
Paul Batum

3
@RoyiNamir: Se você usar um método anônimo sem uma lista de parâmetros, é compatível com qualquer tipo de delegado com parâmetros que não sejam de ref / out, desde que o tipo de retorno seja compatível. Basicamente, você está dizendo "Eu não ligo para os parâmetros". Observe que nãodelegate { ... } é o mesmo que - este último é compatível apenas com um tipo de delegado sem parâmetros. delegate() { ... }
Jon Skeet

2

Nos dois exemplos acima, não há diferença, zero.

A expressão:

() => { x = 0 }

é uma expressão Lambda com corpo de instrução, portanto, não pode ser compilada como uma árvore de expressão. Na verdade, nem compila porque precisa de um ponto e vírgula após 0:

() => { x = 0; } // Lambda statement body
() => x = 0      // Lambda expression body, could be an expression tree. 

6
Certamente isso significa que há uma diferença de "one irá compilar, o outro não";)
Jon Skeet

2

Amy B está correta. Observe que pode haver vantagens em usar árvores de expressão. O LINQ to SQL examinará a árvore de expressão e a converterá em SQL.

Você também pode fazer truques com lamdas e árvores de expressão para passar efetivamente os nomes dos membros da classe para uma estrutura de uma maneira segura para refatoração. Moq é um exemplo disso.


-1

Há uma diferença

Exemplo:

var mytask = Task.Factory.StartNew(() =>
{
    Thread.Sleep(5000);
    return 2712;
});
mytask.ContinueWith(delegate
{
    _backgroundTask.ContinueTask(() =>lblPercent.Content = mytask.Result.ToString(CultureInfo.InvariantCulture));
});   

E substituo por lambda: (erro)

var mytask = Task.Factory.StartNew(() =>
{
    Thread.Sleep(5000);
    return 2712;
});
mytask.ContinueWith(()=>
{
    _backgroundTask.ContinueTask(() =>lblPercent.Content = mytask.Result.ToString(CultureInfo.InvariantCulture));
});

~ Lambda errado falhou apenas porque a assinatura do parâmetro do método não corresponde.
Jack Wang

-1

Alguns princípios aqui.

Este é um método anônimo

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

Como os métodos anônimos não têm nomes, precisamos de um representante no qual possamos atribuir esses dois métodos ou expressões. por exemplo

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

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

O mesmo com a expressão lambda. Geralmente precisamos de um delegado para usá-los

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

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

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

bool result = checkStudentAge ( Student Object);
Ao utilizar nosso site, você reconhece que leu e compreendeu nossa Política de Cookies e nossa Política de Privacidade.
Licensed under cc by-sa 3.0 with attribution required.