CodeGym /Cursos /C# SELF /Tipos dinâmicos ( dynamic) e DLR

Tipos dinâmicos ( dynamic) e DLR

C# SELF
Nível 63, Lição 2
Disponível

1. Introdução

C# é uma linguagem de tipagem forte. Isso significa que, na etapa de compilação, a variável deve ter um tipo definido, e acessar membros de uma classe só é permitido de acordo com esse tipo. Isso é seguro, conveniente e claro para a IDE e para o compilador.

Mas às vezes precisamos acessar objetos cuja estrutura pode ser desconhecida em tempo de compilação — por exemplo, quando:

  • Está sendo executado código escrito em outra linguagem .NET (por exemplo IronPython ou IronRuby), onde a tipagem é dinâmica.
  • Ao trabalhar com objetos COM (por exemplo, com Microsoft Office via Interop).
  • Ao interagir com estruturas de dados dinâmicas: por exemplo, objetos JSON pouco formalizados.
  • Não queremos escrever um monte de código boilerplate, e é mais simples “confiar” no objeto e tentar chamar o método pelo nome.

Com a chegada em C# 4.0 da palavra-chave dynamic e da plataforma DLR, surgiu a possibilidade de escrever código que se comporta “como em Python, só que em C#”: a verificação acontece em tempo de execução, não na compilação. Com essa palavra mágica você pode instruir o compilador a ser menos rígido e só checar se existe o método/propriedade quando o programa realmente chegar naquele ponto.

O que é o DLR?

Dynamic Language Runtime (DLR) é uma extensão infraestrutural ao CLR (Common Language Runtime) que permite implementar suporte a linguagens dinâmicas (e comportamento dinâmico em linguagens estáticas). O DLR assume a execução de operações que não podem ser verificadas pelo compilador: por exemplo, chamada de método por nome quando o tipo só ficará conhecido em runtime. Graças ao DLR, o C# ganha um "modo dinâmico".

2. A palavra-chave dynamic: como ela funciona

Diferença em relação a object

Alguns iniciantes pensam que dynamic é só uma forma mais moderna de escrever object e ficar fazendo casts o tempo todo. Não é isso.

  • Uma variável do tipo object sempre exige cast explícito para um tipo concreto para chamar métodos (e o compilador verifica isso).
  • Uma variável do tipo dynamic dá uma ordem ao compilador: “confia em mim, eu sei que tipo será — vamos resolver em runtime!”. Se o método não existir — haverá uma exceção em tempo de execução.

Exemplo:


// Exemplo com object
object obj = "Privet, C#!";
// obj.ToUpper(); // Erro de compilação! O compilador não sabe que obj é uma string
string s = ((string)obj).ToUpper(); // Só assim

// Exemplo com dynamic
dynamic dyn = "Privet, C#!";
string upper = dyn.ToUpper(); // O compilador não reclama, a verificação será no runtime

Com dynamic você pode acessar propriedades e métodos inexistentes. Se eles não existirem — será lançada uma exceção do tipo RuntimeBinderException.

Uso num exemplo de aplicação

Suponha que no nosso mini-app exista um bloco que trabalha com dados JSON cuja estrutura pode mudar em tempo de execução. Com dynamic dá para acessar propriedades como se elas sempre existissem:


dynamic user = new System.Dynamic.ExpandoObject();
user.Name = "Vasya";
user.Age = 35;
Console.WriteLine($"Imya: {user.Name}, vozrast: {user.Age}");

3. DLR em ação: o que acontece por baixo do capô

Quando uma variável é declarada como dynamic, o compilador C# registra que qualquer acesso a métodos, propriedades, indexadores, operadores etc. nesse objeto ele não pode verificar antecipadamente.

Em vez disso, em cada chamada dinâmica o compilador insere código que faz uma requisição ao “dispatcher” do sistema. O DLR observa: será que existe mesmo aquele método ou propriedade que estamos acessando de forma despreocupada e otimista? Se sim — executa a chamada; se não — lança exceção.

Esquema visual


+------------------------+
| Código com dynamic     |
+----------+-------------+
           |
           v
+----------+-------------+
| O compilador C# insere |
| "consulta ao DLR"      |
+----------+-------------+
           |
           v
+----------+-------------+
| Em runtime o DLR busca |
| método/propriedade     |
+----------+-------------+
           |
      /---------\
      |         |
   encontrado  não encontrado
      |         |
      v         v
   chamada    exceção
   do método  (RuntimeBinderException)

O DLR cuida de buscar métodos e propriedades no objeto — e até faz cache das chamadas dinâmicas mais usadas!

Um pouco de história

O DLR foi criado para dar suporte a linguagens originalmente dinâmicas: IronPython, IronRuby, e também para facilitar a integração com objetos dinâmicos no C#. Hoje em dia é usado em objetos como ExpandoObject, DynamicObject, além de COM e dados dinamicamente serializados.

4. Comparação: static vs dynamic

Vamos comparar como ficam chamadas de propriedades e métodos nas paradigmas estático e dinâmico:

Método Declaração Verificação em compilação Comportamento em erro
object
object obj = ...
Exige cast para o tipo certo Erro de compilação ou InvalidCastException
dynamic
dynamic dyn = ...
Não, tudo em runtime RuntimeBinderException, se o método não existe
tipo estático
MyClass a = ...
Verificado pelo compilador Erro de compilação

Exemplo para clarear:


// Tipagem estática
MyUser u = new MyUser();
u.PrintInfo(); // Verificado pelo compilador

// dynamic
dynamic du = new MyUser();
du.PrintInfo(); // O compilador não verifica, encontra o método só em runtime

// object
object ou = new MyUser();
// ou.PrintInfo(); // Erro de compilação
((MyUser)ou).PrintInfo(); // Precisa fazer cast explícito

Métodos estáticos e dynamic


dynamic d = "Hello";
Console.WriteLine(d.Length); // 5 — tudo certo

Console.WriteLine(d.FakeMethod()); // RuntimeBinderException em tempo de execução

Mas cuidado! Se você digitar errado ou o nome do método não existir — isso só vai aparecer em tempo de execução. Exemplo clássico: "bug em produção".

5. Exemplos práticos: dinâmica em tarefas reais

Trabalhando com ExpandoObject


using System.Dynamic;

dynamic person = new ExpandoObject();
person.Name = "Sergey";
person.Greet = (Action)(() => Console.WriteLine($"Privet, {person.Name}!"));

person.Greet(); // Vai imprimir: Privet, Sergey!

Aqui adicionamos em tempo de execução a propriedade Name e até um delegado Greet. Muito parecido com trabalhar com objetos em JavaScript.

Binding a estruturas JSON


using System.Dynamic;
using Newtonsoft.Json;

string json = @"{""name"": ""Andrey"", ""age"": 30}";
dynamic obj = JsonConvert.DeserializeObject
  
   (json); Console.WriteLine(obj.name); // Andrey Console.WriteLine(obj.age); // 30 
  

Repare: se você acessar obj.surname — vai dar erro em tempo de execução.

COM-Interop (Automação do Office)


dynamic excel = Activator.CreateInstance(Type.GetTypeFromProgID("Excel.Application"));
excel.Visible = true; // "Vemos" a propriedade, mesmo que o compilador não conheça o tipo

6. Nuances úteis

DLR, dynamic e uso com outras linguagens .NET

  • IronPython/IronRuby: dá pra instanciar objetos dessas linguagens e chamar métodos a partir do C# via dynamic.
  • Objetos COM: wrapper automático de métodos e propriedades via dynamic.
  • Objetos dinâmicos customizados: implementando a interface IDynamicMetaObjectProvider, você pode criar sua classe com chamadas dinâmicas.

Exemplo de integração com IronPython


using IronPython.Hosting;
using Microsoft.Scripting.Hosting;

ScriptEngine engine = Python.CreateEngine();
dynamic py = engine.Execute("class Test: pass\nTest()");
py.x = 42;
Console.WriteLine(py.x); // 42

Como implementar seus próprios tipos dinâmicos

Geralmente é mais fácil herdar de DynamicObject.


using System.Dynamic;

class MyDynamic : DynamicObject
{
    public override bool TryGetMember(GetMemberBinder binder, out object result)
    {
        if (binder.Name == "Ping")
        {
            result = "Pong!";
            return true;
        }
        result = null;
        return false;
    }
}

dynamic d = new MyDynamic();
Console.WriteLine(d.Ping); // Pong!
Console.WriteLine(d.Miss); // null (não lança exceção)

Quando dynamic faz mal

  • Reduz performance (chamadas dinâmicas são mais lentas que estáticas).
  • Complica debugging e manutenção.
  • Perde a maioria das facilidades da IDE: autocomplete, refactoring, find usages.
  • Pode levar a erros que só aparecem para o usuário ou no servidor.

Quando realmente usar dynamic

  • Integração com bibliotecas externas onde é impossível ou inviável escrever wrappers (COM, Python, JavaScript).
  • Manipular estruturas fracamente tipadas (JSON/XML/ExpandoObject).
  • Para escrever frameworks, metaprogramação e APIs flexíveis que devem ser dinâmicas por design.

Em todos os outros casos é melhor usar tipos estáticos e aproveitar a ajuda do compilador :-)

7. Erros típicos ao trabalhar com dynamic e DLR

Erro №1: Acessar membros inexistentes.
Chamar método ou propriedade inexistente em um dynamic leva a RuntimeBinderException em runtime, o que é difícil de pegar durante o desenvolvimento.

Erro №2: Ignorar checagem de tipos.
Sem checar tipos antes de chamar (por exemplo, if (obj is IDictionary<string, object>)) você pode ter erros inesperados em runtime.

Erro №3: Usar dynamic em APIs públicas.
Tipos dinâmicos tornam o código mais difícil de manter, já que IDEs e analisadores não conseguem sugerir membros corretamente, reduzindo a legibilidade.

Erro №4: Abusar de dynamic em código de alta performance.
Chamadas dinâmicas são mais lentas que estáticas e aumentam overhead. Use dynamic só quando necessário.

2
Tarefa
C# SELF, nível 63, lição 2
Bloqueado
Uso simples de `dynamic`
Uso simples de `dynamic`
Comentários
TO VIEW ALL COMMENTS OR TO MAKE A COMMENT,
GO TO FULL VERSION