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