Para atingir o seu objetivo de fazer com que os atributos do dicionário Atributos sejam referenciados diretamente no objeto (como produto.Titulo), a melhor abordagem em C# é utilizar o DynamicObject.
Ao herdar de DynamicObject, você pode interceptar as tentativas de ler ou escrever em propriedades que não existem estaticamente na classe e redirecionar essas operações para o seu dicionário.
Aqui está como você pode refatorar o seu código para que o exemplo produto.Titulo funcione perfeitamente:
Código Refatorado
using System;
using System.Collections.Generic;
using System.Dynamic;
public abstract class ModeloBase : DynamicObject
{
public Guid Id { get; set; } = Guid.NewGuid();
public bool Ativo { get; set; } = true;
// O dicionário que guardará as propriedades dinâmicas
public Dictionary<string, object> Atributos { get; set; } = new Dictionary<string, object>();
// Intercepta a LEITURA da propriedade (ex: var x = produto.Titulo)
public override bool TryGetMember(GetMemberBinder binder, out object? result)
{
// Se existir no dicionário, retorna o valor. Se não, retorna null (ou lançar exceção)
if (Atributos.TryGetValue(binder.Name, out result))
{
return true;
}
result = null;
return true; // Retorna true para não quebrar a execução se a propriedade não existir
}
// Intercepta a ESCRITA da propriedade (ex: produto.Titulo = "...")
public override bool TrySetMember(SetMemberBinder binder, object? value)
{
Atributos[binder.Name] = value;
return true;
}
}
// Supondo que ModeloAuditado herde de ModeloBase
public abstract class ModeloAuditado : ModeloBase
{
public DateTime DataCriacao { get; set; } = DateTime.UtcNow;
}
public class Produto : ModeloAuditado
{
// Propriedades estáticas normais ainda funcionam aqui se você precisar delas
public decimal? Preco Fixo { get; set; }
}
Como usar no seu código
Para que o C# entenda que o objeto terá propriedades dinâmicas em tempo de execução, você precisa declarar a variável usando a palavra-chave dynamic:
class Program
{
static void Main()
{
// ATENÇÃO: É necessário tipar como 'dynamic' para habilitar a mágica
dynamic produto = new Produto();
// Atribuição direta (o TrySetMember entra em ação aqui)
produto.Titulo = "Titulo do produto 1";
produto.Preco = 99.90;
// Leitura direta (o TryGetMember entra em ação aqui)
Console.WriteLine($"ID: {produto.Id}"); // Propriedade estática normal
Console.WriteLine($"Título: {produto.Titulo}"); // Propriedade dinâmica do dicionário
Console.WriteLine($"Preço: {produto.Preco}"); // Propriedade dinâmica do dicionário
// Se você olhar o dicionário por trás das cenas, os dados estão lá:
Console.WriteLine($"No dicionário: {produto.Atributos["Titulo"]}");
}
}
Pontos importantes para ficar de olho:
Uso do
dynamic: Se você declarar comoProduto produto = new Produto();, o compilador do C# vai procurar a propriedadeTituloestática em tempo de compilação e vai dar erro. Usardynamic produtoavisa o compilador para "confiar em você" e resolver isso em tempo de execução.Performance: O uso de
DynamicObjectedynamictem um custo de performance ligeiramente maior do que propriedades normais por conta da resolução em tempo de execução (DLR - Dynamic Language Runtime), mas para cenários como entidades flexíveis/EAV (Entity-Attribute-Value), funciona muito bem.
Comentários