Posts Marcados C#

C#, Closures e Resharper

Olá pessoal! Tivemos um problema esta semana, em tese de origem simples, mas que nos tomou um tempo enorme de debug. Quero compartilhar aqui para tentar evitar que outros sigam pelo mesmo caminho!

Para começar, imaginem este código ilustrativo abaixo. Uma rotina simples para enumerar duas vezes por uma coleção e retornar a mesma, modificada. Claro que isto é somente representativo de um código real, mas imaginem qualquer função que por alguma razão modifica elementos de uma lista e retorna a mesma ou um subconjunto da mesma. Trivial, certo? O resultado esperado desta execução são duas linhas com o número “2”.

class MinhaClase {
    public decimal Valor { get; set; }
}
    
class Program {
    static IList<MinhaClase> Calcular(IList<MinhaClase> classes) {
        foreach (var minhaClase in classes) {
            minhaClase.Valor = 1;
        }
        foreach (var minhaClase in classes) {
            minhaClase.Valor++;
        }
        return classes;
    }
    static void Main(string[] args) {
        var classes = new[] {
                                new MinhaClase { Valor = 0}, 
                                new MinhaClase { Valor = 0}, 
                                new MinhaClase { Valor = 1}
                            };
        var result = Calcular(classes.Where(c => c.Valor != 1).ToList());
        foreach (var minhaClase in result) {
                Console.WriteLine(minhaClase.Valor.ToString());
        }
        Console.ReadLine();
    }
}

Todo o problema começa quando a gente tenta refatorar e melhorar o código. Como todo bom desenvolvedor, nós usamos o Resharper, que é uma ferramenta fantástica da JetBrains para nos ajudar a gerar um bom código. Na White Fox, nós a usamos praticamente todo o tempo, ela nos poupa muito esforço de código e ajuda a evitar problemas graves de maneira completamente automática. Porém, neste caso, como a rotina “Calcular” simplesmente enumera, o Resharper vai sugerir que a gente mude o IList<> para sua classe base, um IEnumerable<>. A regra seguida por ele faz sentido pois se só estamos usando métodos da classe base, não seria necessário usar a classe derivada na assinatura. O código gerado então após a refatoração fica da seguinte forma:

class MinhaClase {
    public decimal Valor { get; set; }
}
    
class Program {
    static IEnumerable<MinhaClase> Calcular(IEnumerable<MinhaClase> classes) {
        foreach (var minhaClase in classes) {
            minhaClase.Valor = 1;
        }
        foreach (var minhaClase in classes) {
            minhaClase.Valor++;
        }
        return classes;
    }
    static void Main(string[] args) {
        var classes = new[] {
                                new MinhaClase { Valor = 0}, 
                                new MinhaClase { Valor = 0}, 
                                new MinhaClase { Valor = 1}
                            };
        var result = Calcular(classes.Where(c => c.Valor != 1));
        foreach (var minhaClase in result) {
                Console.WriteLine(minhaClase.Valor.ToString());
        }
        Console.ReadLine();
    }
}

Depois deste refactor, o resultado do programa não deveria mudar, certo? Errado! Os mais experientes irão saber que o resultado da execução deste segundo código é uma lista vazia!

A explicação para isto tem a ver com o suporte do C# para closures (ver este post, que explica o que são de maneira simples, ou esta explicação mais acadêmica aqui). As operações LINQ simplesmente geram predicados para uma closure que é executada no momento que a coleção é acessada. Tem a ver também com a diferença básica entre um IList<> e um IEnumerable<>, já que num IList<>, um GetEnumerator (executado em um foreach) sempre aponta pra coleção interna, já existente… Já em um IEnumerable<>, o GetEnumerator vai sempre acionar o container para criar coleção e aí enumerar os elementos… Isto significa que a coleção de um IEnumerable<> produzido por uma closure vai ser gerada A CADA foreach, e não uma única vez. Ou seja, cada foreach da rotina “Calcular” executa a closure, bem como o foreach do resultado final. Como neste caso a closure é um filtro de Valor != 1, no segundo foreach da rotina “Calcular” e no último (onde ele imprime o resultado no console) ele simplesmente vai retornar 0 elementos!

Onde erramos? Os mais experientes vão notar que erramos na hora que tiramos o .ToList() do final da closure que define o filtro. A razão da remoção foi que como a rotina “Calcular” agora aceita um IEnumerable<>, o ToList() não seria mais necessário. Sem o ToList(), a closure não é mais executada naquele momento e sim passada para a variável e aí toda a rotina Calcular para de funcionar. Basta colocar o .ToList() de volta que tudo passa a funcionar novamente. Este erro, aparentemente óbvio, na prática é muito fácil de ser cometido, pois o uso de expressões LINQ é cada vez mais comum e, em um código complexo de negócios, se a função sendo chamada tem parametros IEnumerable<>, uma closure pode acabar sendo enviada inadivertidamente.

Portanto, neste caso, o Resharper não só facilita os erros, como pode ainda produzir alguns muito difíceis de identificar (no nosso casso, foram horas gastas até achar onde estava a closure sendo passada). Claro que desligamos esta sugestão na nossa configuração do Resharper e o problema foi resolvido. É importante ressaltar que, mesmo apesar deste caso, continuamos a achar o Resharper uma ferramenta essencial de trabalho para nós.

Outro ponto que merece ser comentado, é que pesquisando este problema, entendemos melhor o funcionamento do IEnumerable<> e o IList<>. Como o IEnumerable<> sempre tem que gerar a coleção a cada GetEnumerator(), podemos ter um desempenho pior das coleções, na maioria dos cenários, comparado a um IList<>. Só isto já seria motivo suficiente para favorecer o uso do IList<> ao invés do IEnumerable<>.

Bom, espero ter ajudado a poupar o tempo de outras pessoas ao deparar com problemas similares. Por causa deste problema, acabamos tendo que conhecer bem mais sobre closures e coleções no .NET, assim, no final, a existência deste bug até que foi benéfica!

Até a próxima!

, ,

4 Comentários

Repositórios e Expressões Lambda

Esta semana fizemos mais uma grande melhoria na nossa camada de domínio, incorporando expressões lambda .Net nas nossas consultas e parâmetros de ordenação de repositórios. Retomando o post sobre a camada de domínio, os repositórios são utilizados para tirar a dependência da aplicação dos detalhes de persistência de objetos e para ser uma representação abstrata de uma coleção de entidades de domínio.

A idéia é que eles facilitem a busca de entidades específicas ou implementem métodos mais complexos para a execução de consultas. Como no nosso caso usamos o NHibernate para camada de persistência, nós temos duas opções de efetuar estas consultas, os Criterias (DetachedCriteria ou expressões ICriterion simples) ou o HQL, que é uma linguagem de consulta parecida com SQL só que composta pelas entidades de domínio.

Consultas HQL são baseadas em strings (ver exemplo abaixo do nosso sistema de ServiceDesk), o que é extremamente flexível porém não suporta intellisense nem é compilada (o que pode facilitar erros no caso de refactors). Mas, para o HQL não temos muita opção, a alternativa que seria usar o NH Linq ainda não é viável (pelo que eu tenho visto, ainda não suporta muitas coisas).


public int TotalInbox(User user) {
   const string hql =
       @"select count(*) from Incident i inner join i.Allocation a
         inner join a.SupportGroup s inner join s.Users u 
         inner join i.Company c
         where i.ClosedOn is null and a.SupportUser is null
         and i.State != isnull(c.WaitingUserConfirmationState,-1) 
         and i.State != isnull(c.WaitingUserInformationState, -1) 
         and u = ?";
   return (int) ExecuteScalar<long>(hql, user);
}

O uso de Criterias é mais simples e é indicado para consultas com restrições diretas na entidade base (ou nas que possuem relacionamento direto com a entidade base). Porém, nos criterias é necessário especificar o nome da propriedade como string. Novamente, sem intellisense e sujeito a problemas de refactor. Como o uso de Criterias é muito mais freqüente, nós automatizamos na nossa camada de domínio a geração de uma classe estática com todas as propriedades das entidades de domínio (usando arquivos .tt de um editor T4). Desta forma, temos intellisense e no caso de refactor que cause alguma quebra, teremos um erro de compilação (já que as classes são sempre regeradas). E, conforme falado no post da camada de domínio, construímos ainda uma fluent interface para facilitar a escrita. O exemplo abaixo mostra isto em funcionamento.

public long TotalClosed(User user) {
    return Query.Where(Restrictions.Eq(PN.User, user))
           .And(Restrictions.IsNotNull(PN.ClosedOn)).Count();
}

Esta solução estava bastante adequada até aparecer a alternativa de usarmos expressões lambda ao invés de gerar as classes estáticas. Existem algumas iniciativas já fazendo isto, como o projeto NHLambdaExtensions. Neste caso específico, porém, eles estão abordando isto como uma biblioteca à parte para a geração de Criterias do NH e não como algo integrado aos repositórios.

Felizmente, na nossa camada de domíno, os repositórios já são tipados para a entidade de domínio que eles representam. Desta foram, fica simples gerar as expressões. O exemplo abaixo mostra a mesma rotina acima, agora usando expressões lamdas.

public long TotalClosed(User user) {
    return 
      Query.Where(i => i.User == user && i.ClosedOn != null).Count();
}

Como pode ser visto, bem mais legível e intuitivo. E sem a necessidade de se usar classes estáticas de apoio! Ainda temos algum trabalho a ser feito, pois não é simples traduzir todos os tipos de lambda para restrições válidas. Mas para os casos mais simples como o acima, está já 100% funcionando.

Outro ponto interessante é que as funções que aceitam lambda podem ser expostos para camadas superiores de domínio, já que usar uma expressão lambda não causa dependência para a camada de persistência. Isto diminui de maneira significativa o próprio tamanho da implementação dos repositórios.

Finalmente, a utilização deste tipo de solução acaba sendo algo tão flexível que estamos passando a adotar em vários outros cenários. Na definição de classes de ordenação para consultas, na identificação de campos usados como atributos em formulários e controllers e assim por diante. Realmente algo muito simples que está aí desde o lançamento da versão 3.5 do .Net mas que demorou para  a “ficha cair” achar uma maneira legal de aplicar na nossa infraestrutura.

Quem quiser saber algum detalhe mais técnico da implementação basta me mandar um email. Até a próxima.

, , , , ,

Deixe um comentário

Interlúdio – Boxing

Pequena pausa na série. Programando hoje, apanhei tristemente de uma coisa ultra simples. Estava aprimorando o meu gerador de forms dinâmicos na parte de carga de dados do post. A partir dos parâmetros trazidos, ele preenche a instância do objeto desejado, salvando automaticamente se necessário.

Imaginem um objeto com uma prorpriedade value qualquer (Id p. ex, que é Int32). Como o dynamic form preenche cada propriedade por reflection, no final eu tenho sempre dois objetcs.  Algo tipo assim:

object oldValue = myOldObject.Id;
object newValue = myNewObject.Id;

Só que quando eu comparo (oldValue == newValue) ele retorna sempre false, mesmo que o Id seja o mesmo! Óbvio, por causa do boxing, no == ele compara as referências e aí como não é o mesmo objeto, sempre retorna false.  O que não foi tão óbvio foi como sair desta encrenca já que, em tese, eu deveria fazer o unboxing primeiro, só que como eu só tenho o type por reflection, isto não seria fácil.

Depois de perder quase meia hora e já pensando no pior, achei um post sobre o System.Object.Equal. Ele faz a comparação do conteúdo  e aí funciona para value types. Aí o código fica simplesmente:

System.Object.Equal(oldValue, newValue)

Simples, certo? Mas não óbvio e resolvi postar aqui pra facilitar a vida de outras pessoas que tenham a mesma dificuldade!

Até breve!

Hoje apanhei (pra variar) com uma coisa simples….  Todos devem conhecer o conceito de boxing e unboxing (se não vejam pra ontem!! rsrs), mas no caso de reflection, eu apanhei com algo bobo.
Imaginem um objeto com uma prorpriedade int (Id p. ex). Eu fiz um dynamic form para preencher automaticamente, só que como é por reflection, no final eu tenho dois objetcs.  Algo tipo assim:
object oldValue = myOldObject.Id;
object newValue = myNewObject.Id;
Só que quando eu comparo (oldValue == newValue) ele retorna sempre false, mesmo que o Id seja o mesmo! Óbvio, por causa do boxing, no == ele compara as referencias e aí como não é o mesmo objeto, sempre retorna false. ? O que não é óbvio era como sair desta encrenca já que

, , , , ,

Deixe um comentário

Response.Flush()

Nestes últimos 2 dias eu perdi um tempo impressionante com algo que em tese deveria ter sido extremamente simples de fazer, tipo 5 minutos…  A idéia era fazer um página que iria rodar uma operação longa ir mostrando o andamento da operação, algo como “Passo 1 executado”, “Passo 2 executado” e assim por diante…

Como a regra de negócio estava pronta, achei que fosse trivial ir gerando as tags <p> e ir mostrando o andamento com flushs… Não poderia estar mais errado! Inicialmente tentei usar o HtmlWriter do Render pra ir fazendo os Writes intercalados com os Response.Flush() (fiz um teste com Sleep() pra simular o processamento). Mas não funcionou, ele mostrava a página só no final, com todos os passos de uma vez.

Aí comecei o processo conhecido de buscar no google alguma dica ou esperiência pra entender o que estava errado. Nem precisa falar que o help da MSDN era inútil, aliás copiei e colei o exemplo deles e mesmo comportamento. Comecei a ver que este é um problema para muitas pessoas, aparentemente em algumas situações o Response.Flush() simplesmente não funciona e um mesmo código que funciona para um (como o exemplo da MSDN) não funciona pra outros.

Claro que deve ter algo na página ou no ambiente que faz com que este probelma apareça. Gastei umas boas 4 horas tentando eliminar o que era, utilizando as dicas que achei de pessoas no google para tentar mapear o que era. Mas a questão é que parecia que vários tipos de problemas acabavam se sobrepondo, o principal sendo um próprio entendimento do que o Flush() deveria fazer. Na minha visão (e na visão de várias pessoas que eu vi), ele deveria simplesmente fazer com que o conteúdo gerado até ali fosse para o browser. Mas até sobre isto haviam dúvidas. E aí vinham várias receitas, pra ligar ou desligar buffer, pra fazer com código <% %> intercalado na página ou em determinado evendo em code-behind e até para fazer várias vezes em sequência o comando Flush() para que ele funcionasse! Nenhuma alternativa funcionou pra mim, fui dormir para tentar de novo no dia seguinte.

No dia seguinte, mais 2h. O tempo gasto nisto já estava absurdo e poderia até ter feito com javascript simples, mas vocês sabem, vira questão de honra!  Continuando as busca no google, achei um comentário dizendo que uma instrução específica, faria toda a diferença: Response.BufferOutput = false. E neste cenário, realmente funcionou, mas somente no código aspx direto! Já que neste caso estava funcionando, comecei a tentar isolar o que realmente causava o probelma. Mas não tive sucesso, qualquer pequena alteração fazia com que o código deixasse de funcionar. Desisti, o código final ficou desta forma:

—————————————————————————————————————–

<!DOCTYPE html PUBLIC “-//W3C//DTD XHTML 1.0 Transitional//EN”  “http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd”><html  >
<head><title>Correçao de Pesquisa</title></head>
<body style=”font-family: Calibri, Tahoma, Arial”>
<p>Correção em Progresso. Aguarde por favor….</p>
<%  Response.Buffer = false;
Response.BufferOutput = false;
for (var i = 0; i < 10; i++) {
%><span>teste</span><br /><%
Response.Flush();
System.Threading.Thread.Sleep(1000);
} %>
</body>
</html>
—————————————————————————————————————–
Impressionante que este código não funciona se for colocado no evento Load ou Render da página. De fato deve ter alguma coisa no ambiente que impede isto, mas tive que dar um basta no tempo perdido para este problema. Ah, mais um ponto interessante, a tag SPAN funciona melhor do que a tag P – que mesmo com o código funcionando dá uma impressão de agrupamento ao ser mostrado no browser.

Enfim, é impressionante como perdemos tempo com coisas triviais. Esta do Response.Flush é mais um exemplo. Fazer software é muito bom, mas este tipo de coisa não é muito!

, , , ,

3 Comentários