Seguindo na série de novidades do C# 4.0 que vou abordar aqui no blog, quero agora dar uma olhada no dinamismo adicionado à linguagem.

Aviso: Antes de continuar, gostaria apenas de contar para vocês uma história triste. Os bits que estão disponíveis para download não são tão quentes quanto os bits apresentados no PDC. Sorry, mas é verdade. Isso quer dizer que algumas das coisas que vou mostrar aqui não vão compilar no build do CTP, e por isso, eu posso até errar alguma vírgula, já que não vou ter confirmação do compilador ou testes. Enfim, é o que dá trabalhar na borda das novidades de tecnologia, faz parte… Então vamos lá.

Não foi nesta versão (e não vai ser nunca) que o C# se tornou uma linguagem dinâmica como o Python ou o Ruby (implementados no .Net como IronPython e IronRuby). Mas agora, em poucas palavras, é possível ter uma variável tipada como “dynamic”, e chamar sobre ela qualquer método. Isso quer dizer que quando você coloca o ponto (.), nada aparece. Nada. E você pode digitar o que quiser depois do ponto. E compila. E esse negócio todo vai ser resolvido só na hora que o aplicativo estiver rodando (em runtime). Ou seja, não há verificação em tempo de compilação, o que é um dos contras de linguagens dinâmicas. Mas há vários pros, então não se preocupe. Além disso, você só usa se quiser.

Você pode resolver esse problema de não ter verificação em tempo de compilação com a utiliação de testes unitários! Muito bem, testes, testes, testes! Crie muitos testes. (Aliás, crie testes mesmo se você não for trabalhar com C# e suas capacidades dinâmicas. Testes são bons, fazem você crescer mais forte, morrer mais tarde, casar com uma pessoa legal, e além disso previne espinhas. E dá muita sorte.)

É muito parecido com o que o VB faz com Late Binding, mas é mais poderoso. Isso porque permite integrar vários modelos de chamada dinâmica, como Javascript, POCOs, COM, e outros que você quiser, já que é extensível.

Por exemplo, se estiver trabalhando estático, segue abaixo o código simples de uma calculadora:

    class Calculadora
    {
        public long Adicionar(int numero1, int numero2)
        {
            return numero1 + numero2;
        }
    }

Você faz sua soma em um windows form desta forma:

        private void butSomar_Click(object sender, EventArgs e)
        {
            Calculadora calc = new Calculadora();
            int numero1 = Convert.ToInt32(txtNumero1.Text);
            int numero2 = Convert.ToInt32(txtNumero2.Text);
            long resultado = calc.Adicionar(numero1, numero2);
            lblResultado.Text = "Resultado: " + resultado;
        }

Até aí tudo bem. Mas e se for dinamicamente? E se eu não souber o tipo? Bom, o C# de hoje já permite isso, com Reflection, e em grande parte é o que VB faz por baixo dos panos quando você faz late binding. Fica assim:

private void butSomarQuaseDinamico_Click(object sender, EventArgs e)
{
    object calc = new Calculadora();
    Type type = calc.GetType();
    int numero1 = Convert.ToInt32(txtNumero1.Text);
    int numero2 = Convert.ToInt32(txtNumero2.Text);
    long resultado = (long)type.InvokeMember("Adicionar",
        System.Reflection.BindingFlags.InvokeMethod,
        null,
        calc,
        new object[] { numero1, numero2 });
    lblResultado.Text = "Resultado: " + resultado;
}

Maior trabalhão, certo? Veja com C# 4.0 e tipos dinâmicos como isso facilita:

Novo método de adicionar:

        public dynamic Adicionar(dynamic numero1, dynamic numero2)
        {
            return numero1 + numero2;
        }

E novo método de chamada:

        private void butSomarDinamico_Click(object sender, EventArgs e)
        {
            dynamic calc = new Calculadora();
            dynamic numero1 = txtNumero1.Text;
            dynamic numero2 = txtNumero2.Text;
            long resultado = calc.Adicionar(numero1, numero2);
            lblResultado.Text = "Resultado: " + resultado;
        }

Bom, apesar de parecer que vai funcionar, na verdade, não vai. Alguns problemas: apesar de a avaliação de tipos dinâmicos ser feita em runtime, ainda assim o tal do tipo dinâmico ainda tem um tipo. Declará-lo como dinâmico significa apenas que o compilador não vai checar suas chamadas, mas em runtime isso vai ser feito. Neste caso, as variáveis numero1 e numero2, declaradas dinâmicas, são na realidade strings. E ao chamar o operador “+” sobre strings, o que acontece é uma concatenação, e não uma soma. Para resolver isso, você deve voltar a converter. Assim:

        private void butSomarDinamico_Click(object sender, EventArgs e)
        {
            dynamic calc = new Calculadora();
            dynamic numero1 = Convert.ToInt32(txtNumero1.Text);
            dynamic numero2 = Convert.ToInt32(txtNumero2.Text);
            long resultado = calc.Adicionar(numero1, numero2);
            lblResultado.Text = "Resultado: " + resultado;
        }

Dessa forma, a variavel numero1 é declarada dinâmica, mas no fundo, ela é um inteiro. Isso mostra o poder do dinamismo: o operador é chamado sobre o tipo que você passou, qualquer que seja ele. Se for uma string ele concatena, se for um inteiro ele soma. Isso é muito poderoso.

Observação: Não se anime muito com esse exemplo ainda, porque esse é um caso típico de bits não disponíveis ainda. Esse código vai dar um erro de compilação na linha que diz “return numero1 + numero2;”, porque os bits do CTP não implementaram ainda resolução de operadores. Esse exemplo da o seguinte erro: “Operator ‘+’ cannot be applied to operands of type ‘::dynamic’ and ‘::dynamic’ “. O próximo CTP (ou já o Beta) vai ter isso funcionando.

O mais engraçado disso tudo é que o C# continua sendo uma linguagem estática, mas com “aspectos” dinâmicos. A variável numero1, por exemplo, é “estáticamente tipada como dinâmica”. Ela possui um tipo, e esse tipo é “dynamic”. E esse tipo tem naturezas dinâmicas, e é tratado de forma diferente pelo compilador. Mas no fim das contas, um tipo dinâmico é um object com tratamento especial em compilação e em runtime. É isso.

Outra coisa importante de saber é que operações com objetos dinâmicos sempre retornam outro objeto dinâmico. Assim, se você chamar:

        public dynamic Somar10(dynamic numero)
        {
            var ret = numero + 10;
            return ret;
        }

A variável ret vai ser de tipo dinâmico. Tipos dinamicos se propagam, então tenha cuidado.

Acho que isso já dá pra entender o que é um tipo dinâmico. Pegando os exemplos da Microsoft, veja o que mais você pode fazer:

            dynamic d = RetornaObjetoRemoto();
            //chamando um método:
            d.M(7); 
            // setando propriedades:
            d.f = d.P; 
            // indexadores funcionando dinamicamente
            d["one"] = d["two"]; 
            // operadores:
            int i = d + 3; 
            // objeto dinamico é na verdade um delegate
            string s = d(5,7);

É claro que, neste caso, não seriam todas as operações possíveis, por causa do tipo. Você não tem indexadores em delegates, por exemplo… Mas esse código deve compilar com a versão final do C# 4.0 (a do CTP da erro nos operadores – novamente – e mais um erro nos indexadores – “Cannot apply indexing with [] to an expression of type ‘::dynamic’ “).

Outra coisa interessante é como é feita a resolução de uma chamada a um método que possui sobrecargas, com tipos dinâmicos. Assim, por exemplo, se tivermos um método sobrecarregado desta forma na nossa calculadora:

        public DateTime Adicionar(DateTime data, int dias)
        {
            return data.AddDays(dias);
        }

(Lembrem-se, já havia um outro método Adicionar que aceitava dois inteiros.)

Ao chamar a função Adicionar em um tipo dinâmico, ele vai ser resolvido de acordo com os valores de runtime. Assim:

            dynamic calc = new Calculadora();
            calc.Adicionar(1, 2);
            calc.Adicionar(DateTime.Now, 2);

A primeira chamada a Adicionar vai chamar o overload Adicionar(int, int), e a segunda vai chamar Adicionar(datetime, int). E essa resolução é feita em toda pelo framework. Legal, não é?

Se você quiser ver uma aplicação legal, veja o vídeo em que o Anders Hejlsberg apresenta o C# 4.0, mais ou menos no minuto 26, e você verá uma aplicação Silverlight que utiliza o Virtual Earth e integra C# no code-behind e javascript. No Javascript há isso:

Código Javascript de chamada do Virtual Earth

Note que Javascript é uma linguagem bastante dinâmica, e não há tipos declarados.

E no código em C#, há a chamada ao método javascript, através da Interoperabilidade (a parte: só o fato do Silverlight permitir essa interoperabilidade é o máximo, não é?):

Código C# do Silverlight que chama o código Javascript anterior

Vocês notaram no código C# as chamadas todas são feitas de uma maneira um pouco estranha, através de métodos “Invoke” ou “SetProperty”. Detalhe, tudo isso já é possível hoje, com C#3 e Silverlight.

Pois bem, o Anders começa então a aplicar um pouco do dinamismo. Ele aplica o método de extensão AsDynamic, que retorna estes tipos dinâmicos, nos dois objetos que eram trabalhados, um HtmlDocument e uma HtmlWindow. E faz então alguns ajustes, chamando diretamente os métodos que eram chamados via “Invoke”, e setando propriedades sem “SetProperty”. Fica assim:

Código C# do Silverlight modificado para usar as propridades dinâmicas do C# 4.0

Bem mais legível, certo? E como os métodos eram chamados com strings, e não havia verificação em tempo de compilação, nada mudou com relação a isso.

Em seguida, ele recorta o código Javascript, cola ele no código C# e faz alguns ajustes. Bem poucos, na verdade, e fica assim:

Código C#, migrado do antigo código Javascript, e todo trabalhado dinamicamente

A partir daí, este código C# passa a funcionar. Tudo dinâmico, e até meio “mágico”. É nesse tipo de aplicação, na interação com linguagens dinâmicas, ou objetos desconhecidos, que o custo das linguagens dinâmicas se paga.

É importante lembrar que todo este código traz uma dependência sobre o DLR, que é o Dynamic Language Runtime. O DLR vai vir junto com a próxima versão do .Net Framework e ainda não está concluído. Este fato, inclusive, é um dos problemas do pessoal do IronRuby, que também dependem do DLR, mas só podem progredir se o DLR já tiver implementado algumas coisas (o IronRuby é um projeto opensource). Além disso, por enquanto tenho ouvido falar de problemas de performance no DLR, o que está sendo endereçado pela Microsoft, e tudo isso leva tempo.

Assim que sair o novo CTP do VS 2010 e o suporte a tipos dinâmicos estiver implementado eu volto a falar do assunto. Enquanto isso, vou seguir nas outras novidades do C# 4.0.

Vocês gostaram dessa novidade? Onde mais vêem aplicações reais dessa nova funcionalidade do C#?

Giovanni Bassi

Arquiteto e desenvolvedor, agilista, escalador, provocador. É fundador e CSA da Lambda3. Programa porque gosta. Acredita que pessoas autogerenciadas funcionam melhor e por acreditar que heterarquia é mais eficiente que hierarquia. Foi reconhecido Microsoft MVP há mais de dez anos, dos mais de vinte que atua no mercado. Já palestrou sobre .NET, Rust, microsserviços, JavaScript, TypeScript, Ruby, Node.js, Frontend e Backend, Agile, etc, no Brasil, e no exterior. Liderou grupos de usuários em assuntos como arquitetura de software, Docker, e .NET.