Skip to main content

ExpandoObject | Classe Livre | CSharp

  • ExpandoObject | Classe Livre | CSharp

O que é o ExpandoObject

O ExpandoObject é um tipo presente no namespace System.Dynamic que permite criar objetos dinâmicos em tempo de execução.

  • Permite adicionar e remover propriedades dinamicamente
  • Não possui estrutura fixa (diferente de classes tradicionais)
  • Funciona em conjunto com o tipo dynamic
  • Resolve membros em runtime, não em compile-time
dynamic user = new ExpandoObject();

user.Name = "John Doe";
user.Age = 42;
user.HomeTown = "New York";

Nesse cenário, user não tem um contrato definido. As propriedades são criadas conforme são atribuídas.


Como funciona internamente

O ExpandoObject implementa:

  • IDictionary<string, object>
  • IDynamicMetaObjectProvider

Isso significa que:

  • Internamente ele funciona como um dicionário chave/valor
  • A resolução de membros (user.Name) é convertida para acesso por chave
var expando = new ExpandoObject() as IDictionary<string, object>;

expando["Name"] = "John Doe";
expando["Age"] = 42;

Equivalente ao uso com dynamic.


Adicionando e removendo propriedades

Adicionar

dynamic obj = new ExpandoObject();
obj.Title = "Developer";

Remover

var dict = (IDictionary<string, object>)obj;
dict.Remove("Title");

Iterando sobre propriedades

Como ele é um dicionário, você pode iterar facilmente:

dynamic obj = new ExpandoObject();
obj.Name = "Ana";
obj.Age = 30;

foreach (var kvp in (IDictionary<string, object>)obj)
{
Console.WriteLine($"{kvp.Key}: {kvp.Value}");
}

Cenários de uso

1. Serialização dinâmica (JSON)

Muito usado quando você não conhece o schema em tempo de compilação:

dynamic response = new ExpandoObject();
response.status = "ok";
response.data = new { id = 1, name = "Test" };

return Results.Ok(response);

2. Integrações externas

  • APIs com payload variável
  • Webhooks
  • Dados parcialmente estruturados

3. Construção de objetos flexíveis

Substitui rapidamente DTOs em cenários simples:

dynamic dto = new ExpandoObject();
dto.Id = Guid.NewGuid();
dto.Timestamp = DateTime.UtcNow;

Vantagens

  • Alta flexibilidade
  • Redução de boilerplate (sem criar classes)
  • Útil para prototipação e dados dinâmicos

Desvantagens e cuidados

1. Sem segurança de tipo

  • Não há validação em compile-time
  • Erros só aparecem em runtime
Console.WriteLine(user.Nam); // erro só em execução

2. Performance inferior

Uso de dynamic implica:

  • Reflection
  • Binding dinâmico
  • Overhead de runtime

O uso de dynamic tem custo relevante em performance e deve ser evitado em hot paths


3. Difícil manutenção

  • Código menos previsível
  • Menor suporte de IDE (autocomplete, refactor)

4. Não substitui modelagem de domínio

  • Não deve ser usado em regras de negócio críticas
  • Prefira classes tipadas para domínio

Comparação com alternativas

Classe tipada

class User
{
public string Name { get; set; }
public int Age { get; set; }
}
  • Segurança de tipo
  • Melhor performance
  • Melhor manutenção

Dictionary<string, object>

var dict = new Dictionary<string, object>();
dict["Name"] = "John";
  • Sem sintaxe dinâmica (dict["Name"])
  • Mais explícito
  • Melhor previsibilidade

ExpandoObject

dynamic user = new ExpandoObject();
user.Name = "John";
  • Sintaxe fluida
  • Menos explícito
  • Mais risco em runtime

Boas práticas

  • Use apenas em camadas externas (ex: API, integração)
  • Evite em domínio e regras críticas
  • Combine com validação manual quando necessário
  • Prefira tipos fortes quando o schema for conhecido
  • Evite uso em loops intensivos ou cenários de alta performance

Quando usar

  • Estrutura de dados desconhecida em tempo de compilação
  • Protótipos rápidos
  • Transformações dinâmicas (ex: mapping JSON)
  • Integrações flexíveis

Quando não usar

  • Core do domínio
  • Sistemas críticos
  • Código que exige alta performance
  • APIs públicas com contrato estável