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.

HttpValueCollection Pública

Neste momento estou desenvolvendo um serviço WCF que receberá o conteúdo através de uma requisição HTTP POST. A questão é que o WCF não suporta o conteúdo RAW do POST do HTTP (que envia a requisição com o content type definido como “application/x-www-form-urlencoded”), o que nos obriga a mudar ligeiramente o contrato para suportar um Stream ao invés dos parametros tradicionais e, via método ParseQueryString da classe HttpUtility, fazer o parser do Stream para os parametros que a operação irá trabalhar.

Infelizmente isso me obriga a referenciar o Assembly System.Web.dll em uma aplicação console (que é o host do serviço), que me parece não fazer muito sentido. Este é mais um motivo para votarem na sugestão do Paulo Morgado.

ComVisibleAttribute

Este atributo está presente desde a versão 1.0 do .NET Framework. Ele tem a finalidade de controlar a acessibilidade de um membro ou tipo para o mundo COM. Já falei sobre ele aqui.

Esse atributo recebe em seu construtor um valor booleano indicando tal acessibilidade, com o valor padrão definido como True. Sendo assim, quando este atributo é omitido, assume-se que ele será exposto ao mundo COM. Só que há uma mudança no tipo de projeto Class Library entre a versão 1.x para a versão 2.0, mais precisamente no Visual Studio 2005. Quando criamos um projeto do tipo Class Library agora, esse atributo vem explicitamente definido como False, no arquivo AssemblyInfo.cs, impedindo que qualquer membro dentro deste Assembly seja visível.

Com isso, sempre que precisar criar um Assembly para hospedá-lo no COM+ ou até mesmo para interoperar com o mundo não gerenciado e, estiver criando sob o Visual Studio 2005, atente-se e defina este atributo como True.

ServiceController.ExecuteCommand

Quando criamos Windows Services sob a plataforma .NET, podemos sobrescrever o método OnCustomCommand fornecido pela classe ServiceBase para que, via ServiceController (classe que permite a interceptação da execução do serviço), possamos disparar esse método customizado e, conseqüentemente, executarmos uma determinada tarefa dentro do mesmo.

protected override void OnCustomCommand(int command) { …. }

Com isso, podemos simplesmente em uma aplicação cliente, instanciarmos a classe ServiceController, especificarmos o serviço e a máquina onde ele reside e, finalmente, invocar o método ExecuteCommand, algo como é mostrado no exemplo abaixo:

new ServiceController(“MeuServico”, “.”).ExecuteCommand(N);

Onde o parametro N representa o número inteiro que será passado para o método OnCustomCommand. A questão é que temos um detalhe a ser considerado aqui: se esse valor inteiro for menor que 128, uma exceção do tipo Win32Exception com a seguinte mensagem é atirada: Access denied. Isso ocorre porque provavelmente valores menores que 128 são reservados para o próprio sistema operacional.

Se não me engano, nas versões 1.x do .NET Framework, a exceção atirada era algo como “parametro incorreto”, o que me parece mais significativo para o caso.

ManualResetEvent vs. AutoResetEvent

Ambas as classes que são temas do post tem basicamente a mesma finalidade, ou seja, efetuar a sinalização entre threads. Isso permite que uma determinada thread notifique as outras threads que ela finalizou ou liberou um determinado recurso e, conseqüentemente, essas outras threads poderão dar seqüencia na execução.

Leve em consideração o seguinte código:

delegate void Executor();

private static XXX _resetEvent = new XXX(false);

static void Main(string[] args)
{
    new Executor(Teste1).BeginInvoke(null, null);
    new Executor(Teste2).BeginInvoke(null, null);
    new Executor(Teste3).BeginInvoke(null, null);

    Console.WriteLine(“Fim”);
    Console.ReadLine();
}

static void Teste1()
{
    Console.WriteLine(“Teste1 – Antes”);

    Thread.Sleep(5000); //Simula um processamento pesado
    _resetEvent.Set();
   
    Console.WriteLine(“Teste1 – Depois”);
}

static void Teste2()
{
    Console.WriteLine(“Teste2 – Antes”);
    _resetEvent.WaitOne();
    Console.WriteLine(“Teste2 – Depois”);
}

static void Teste3()
{
    Console.WriteLine(“Teste3 – Antes”);
    _resetEvent.WaitOne();
    Console.WriteLine(“Teste3 – Depois”);
}

Resultados para quando XXX = ManualResetEvent
Fim
Teste1 – Antes
Teste2 – Antes
Teste3 – Antes
Teste1 – Depois
Teste3 – Depois
Teste2 – Depois

Resultados para quando XXX = AutoResetEvent
Fim
Teste1 – Antes
Teste2 – Antes
Teste3 – Antes
Teste1 – Depois
Teste3 – Depois

Como sabemos, o método BeginInvoke fornecido pela instancia do delegate permite a chamado para o método que ele aponta de forma assíncrona, ou seja, será criada uma worker thread para cada um deles; sendo assim, os processos acontecerão paralelamente e o reset event está aqui para garantir a sincronização das informações, ou melhor, destes métodos.

Quando utilizamos o método WaitOne do ManualResetEvent nos métodos 2 e 3, eles aguardarão um sinal que, por sua vez, será dado através do método Set, também do ManualResetEvent. Enquanto isso não acontecer, os métodos 2 e 3 não darão sequencia no processamento. Se repararmos o resultado final, as mensagens (writelines) que a aparecem depois do método WaitOne retornará, isso quer dizer que ele ficará “travado” até receber o sinal para prosseguir. Uma vez recebido, todos aqueles que estiveremos pendentes serão executados.

Já com um AutoResetEvent, o processo é muito semelhante, com a exceção que quando o método Set for chamado, apenas uma thread da fila será executada. Isso explica o motivo pelo qual a mensagem do método 2 (“Teste2 – Depois“) não aparece no resultado final.

Bem, isso é uma das N possibilidades que o .NET fornece para efetuar a sincronização através de sinalização. Para aqueles que querem se aprofundar, vejam o namespace System.Threading.

Invocando uma URL com conteúdo compactado

Há algum tempo eu escrevi sobre como compactar o output de uma página ASP.NET. O principal benefício desta técnica é compactar o resultado para que menos dados sejam transportados entre o cliente e o servidor. Quem faz a descompactação do conteúdo é o browser, ou melhor, a versão 1.1 do HTTP.

Mas quando precisamos invocar a URL dinamicamente, via qualquer outro tipo de aplicação, como um serviço Windows, fica a nosso encargo criar entradas no cabeçalho para indicar que a aplicação suporta compactação e, além disso, precisamos identificar se o conteúdo está ou não compactado e, caso esteja, devemos recorrer a um código semelhante ao que está no artigo para ter acesso ao conteúdo em seu formato legível.

As keys utilizadas são Accept-encoding (remessa) e Content-Encoding (retorno). O código abaixo exibe a forma que voce pode proceder para indicar a requisição que a tua aplicação suporta o conteúdo compactado e, quando receber, voce pode verificar se o conteúdo está ou não compactado:

HttpWebRequest req = (HttpWebRequest)WebRequest.Create(URL);
req.Headers.Add(“Accept-encoding“, “gzip”);

using (req as IDisposable)
{
    using (HttpWebResponse resp = (HttpWebResponse)req.GetResponse())
    {
        Debug.WriteLine(resp.Headers[“Content-Encoding“]);
    }
}

Observação: De nada adianta voce informar a requisição que a sua aplicação suporta a compactação de conteúdo se a página/aplicação que está tentando acessar não suportar essa técnica.

Por dentro da Base Classe Library (BCL)

Bem, a idéia era criar um livro, mas por questões de tempo não consegui dar a atenção merecida ao mesmo. Como recentemente saiu a versão 3.X do .NET Framework, eu acredito que não seria estratégico para alguma editora publicar algo em versão 2.0 se já estamos na versão 3.5. Sim, nós sabemos que o 2.0 é o núcleo, mas…

Sendo assim, eu decidi publicar os capítulos de forma gratuíta no meu site. Como não tive tempo nem ninguém para fazer a correção do conteúdo, peço desculpas antecipadamente e desejo que sejam toleráveis com relação a possíveis erros de portugues (concordancia, acentuação, etc.), e também a possíveis erros técnicos. Além disso, também havia alguns namespaces que estavam no escopo para falar, mas que não consegui, como é o caso do System.Transactions e o System.Threading. Talvez em uma próxima oportunidade. Todos exemplos estão em Visual Basic .NET e Visual C#.

Voce pode acessar o índice através deste endereço. ATENÇÃO: Os arquivos estão em formato XPS. Caso não tenha o visualizador, você poderá baxiá-lo através deste link.