Func vs. Expression


Há algum tempo eu comentei sobre a evolução dos delegates, passando pelas versões 1.0, 2.0 e 3.0 do C#. Sabemos que a partir da versão 3.0 temos uma nova forma de expressar os delegates: expressões lambda. Neste modelo, não há mais necessidade de criar um método adicional ou um método anonimo para executar uma determinada tarefa.

Com a vinda do LINQ, novos delegates também foram introduzidos dentro do namespace System, através do Assembly System.Core.dll:

public delegate TR Func<TR>();
public delegate TR Func<T0, TR>(T0 a0);
public delegate TR Func<T0, T1, TR>(T0 a0, T1 a1);
public delegate TR Func<T0, T1, T2, TR>(T0 a0, T1 a1, T2 a2);
public delegate TR Func<T0, T1, T2, T3, TR>(T0 a0, T1 a1, T2 a2, T3 a3);

Esta família de delegates genéricos servem para construir delegates “on-the-fly”, eliminando a necessidade de criá-los explicitamente. TR representa o resultado do delegate (nunca podendo ser void); depois temos outras versões do mesmo podendo, no máximo, termos quatro parametros de entrada. Em uma operação de soma, poderíamos utilizar o terceiro delegate, como por exemplo:

Func<int, int, int> exemplo = (v1, v2) => v1 + v2;
int resultado = exemplo(2, 3);

Ao compilar este código, o compilador do C# criará: um método que retorna um número inteiro e, no corpo do mesmo, terá o cálculo a ser realizado (v1 + v2) e um delegate que apontará para esse método recém criado; além disso, ele converterá a expressão lambda em um método anonimo, fazendo o uso do delegate criado anteriormente que, neste momento, apontará para o método que fará a soma dos números. O código acima é compilado para:

private static void Main(string[] args)
{
    Func<int, int, int> exemplo1 = delegate (int v1, int v2) {
        return v1 + v2;
    };
    int resultado = exemplo1(2, 3);
}

[CompilerGenerated]
private static Func<int, int, int> CS$<>9__CachedAnonymousMethodDelegate1;
 
[CompilerGenerated]
private static int <Main>b__0(int v1, int v2)
{
    return (v1 + v2);
}

Uma outra alternativa é a utilização da classe Expression<TDelegate>, contida dentro do namespace System.Linq.Expressions. Essa classe deve ser tipificada com o mesmo delegate que utilizamos acima e, ao invés de converter a expressão lambda em um código IL que avalia a expressão, irá transformá-la em uma árvore de objetos IL, representando a expressão. Se utilizarmos o mesmo exemplo, veremos que o código mudará:

Expression<Func<int, int, int>> exemplo1 = (v1, v2) => v1 + v2;
int resultado = exemplo1.Compile()(2, 3);

Neste caso, não podemos invocar diretamente o delegate por ele não é um delegate. Essa classe fornece um método chamado Compile que, ao invocá-lo, retorna o delegate especificado na sua criação (Func<int, int, int>) e, a partir daí, podemos utilizá-lo da forma tradicional. Como o compilador lida de forma diferente quando utilizamos a classe Expression<TDelegate>, o código IL gerado para ele corresponde à:

private static void Main(string[] args)
{
    ParameterExpression CS$0$0000;
    ParameterExpression CS$0$0001;

    int resultado = 
        Expression.Lambda<Func<int, int, int>>(
            Expression.Add(
                CS$0$0000 = Expression.Parameter(typeof(int), “v1”)
                , CS$0$0001 = Expression.Parameter(typeof(int), “v2”)
            )
        , new ParameterExpression[] { CS$0$0000, CS$0$0001 })
        .Compile()(2, 3);
}

Como podemos ver, as expressões lambdas podem ser representadas como código (delegates) ou como dados (árvore de expressões). É importante lembrar que uma expressão é um tipo de Abstract Syntax Tree (AST), que é uma estrutura de dados que representa um código já analisado. Essa técnica nos dá a habilidade de converter/traduzir um determinado código em outro, como é o caso do LINQ to SQL, que transforma essas árvores de expressão em linguagem T-SQL.

Anúncios

Deixe uma resposta

Preencha os seus dados abaixo ou clique em um ícone para log in:

Logotipo do WordPress.com

Você está comentando utilizando sua conta WordPress.com. Sair / Alterar )

Imagem do Twitter

Você está comentando utilizando sua conta Twitter. Sair / Alterar )

Foto do Facebook

Você está comentando utilizando sua conta Facebook. Sair / Alterar )

Foto do Google+

Você está comentando utilizando sua conta Google+. Sair / Alterar )

Conectando a %s