Adicionar propriedades C # dinamicamente em tempo de execução

89

Sei que existem algumas perguntas que tratam disso, mas as respostas geralmente seguem as linhas de recomendar um Dicionário ou Coleção de parâmetros, o que não funciona na minha situação.

Estou usando uma biblioteca que funciona por meio da reflexão para fazer muitas coisas inteligentes com objetos com propriedades. Isso funciona com classes definidas, bem como classes dinâmicas. Preciso dar um passo adiante e fazer algo nesse sentido:

public static object GetDynamicObject(Dictionary<string,object> properties) {
    var myObject = new object();
    foreach (var property in properties) {
        //This next line obviously doesn't work... 
        myObject.AddProperty(property.Key,property.Value);
    }
    return myObject;
}

public void Main() {
    var properties = new Dictionary<string,object>();
    properties.Add("Property1",aCustomClassInstance);
    properties.Add("Property2","TestString2");

    var myObject = GetDynamicObject(properties);

    //Then use them like this (or rather the plug in uses them through reflection)
    var customClass = myObject.Property1;
    var myString = myObject.Property2;

}

A biblioteca funciona bem com um tipo de variável dinâmica, com propriedades atribuídas manualmente. No entanto, não sei quantas ou quais propriedades serão adicionadas de antemão.

Paul Grimshaw
fonte

Respostas:

105

Já deu uma olhada na ExpandoObject?

consulte: http://blogs.msdn.com/b/csharpfaq/archive/2009/10/01/dynamic-in-c-4-0-introducing-the-expandoobject.aspx

Do MSDN:

A classe ExpandoObject permite adicionar e excluir membros de suas instâncias em tempo de execução e também definir e obter valores desses membros. Essa classe oferece suporte à vinculação dinâmica, que permite usar a sintaxe padrão, como sampleObject.sampleMember, em vez de uma sintaxe mais complexa, como sampleObject.GetAttribute ("sampleMember").

Permitindo que você faça coisas legais como:

dynamic dynObject = new ExpandoObject();
dynObject.SomeDynamicProperty = "Hello!";
dynObject.SomeDynamicAction = (msg) =>
    {
        Console.WriteLine(msg);
    };

dynObject.SomeDynamicAction(dynObject.SomeDynamicProperty);

Com base no seu código real, você pode estar mais interessado em:

public static dynamic GetDynamicObject(Dictionary<string, object> properties)
{
    return new MyDynObject(properties);
}

public sealed class MyDynObject : DynamicObject
{
    private readonly Dictionary<string, object> _properties;

    public MyDynObject(Dictionary<string, object> properties)
    {
        _properties = properties;
    }

    public override IEnumerable<string> GetDynamicMemberNames()
    {
        return _properties.Keys;
    }

    public override bool TryGetMember(GetMemberBinder binder, out object result)
    {
        if (_properties.ContainsKey(binder.Name))
        {
            result = _properties[binder.Name];
            return true;
        }
        else
        {
            result = null;
            return false;
        }
    }

    public override bool TrySetMember(SetMemberBinder binder, object value)
    {
        if (_properties.ContainsKey(binder.Name))
        {
            _properties[binder.Name] = value;
            return true;
        }
        else
        {
            return false;
        }
    }
}

Assim, você só precisa:

var dyn = GetDynamicObject(new Dictionary<string, object>()
    {
        {"prop1", 12},
    });

Console.WriteLine(dyn.prop1);
dyn.prop1 = 150;

Derivar de DynamicObject permite que você crie sua própria estratégia para lidar com essas solicitações de membros dinâmicos, cuidado , pois há monstros aqui: o compilador não será capaz de verificar muitas de suas chamadas dinâmicas e você não obterá o intellisense, então apenas mantenha isso em mente.

Clint
fonte
40

Obrigado @Clint pela ótima resposta:

Só queria destacar como foi fácil resolver isso usando o objeto Expando:

    var dynamicObject = new ExpandoObject() as IDictionary<string, Object>;
    foreach (var property in properties) {
        dynamicObject.Add(property.Key,property.Value);
    }
Paul Grimshaw
fonte
4

você pode desserializar sua string json em um dicionário e, em seguida, adicionar novas propriedades e serializá-la.

 var jsonString = @"{}";

        var jsonDoc = JsonSerializer.Deserialize<Dictionary<string, object>>(jsonString);

        jsonDoc.Add("Name", "Khurshid Ali");

        Console.WriteLine(JsonSerializer.Serialize(jsonDoc));
Khurshid Ali
fonte