Agregar propiedades de C # dinámicamente en tiempo de ejecución

89

Sé que hay algunas preguntas que abordan esto, pero las respuestas generalmente siguen la línea de recomendar un Diccionario o una Colección de parámetros, lo cual no funciona en mi situación.

Estoy usando una biblioteca que funciona mediante la reflexión para hacer muchas cosas inteligentes con objetos con propiedades. Esto funciona con clases definidas, así como con clases dinámicas. Necesito dar un paso más y hacer algo en este 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;

}

La biblioteca funciona bien con un tipo de variable dinámica, con propiedades asignadas manualmente. Sin embargo, no sé cuántas o qué propiedades se agregarán de antemano.

Paul Grimshaw
fuente

Respuestas:

105

¿Ha echado un vistazo a ExpandoObject?

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

Desde MSDN:

La clase ExpandoObject le permite agregar y eliminar miembros de sus instancias en tiempo de ejecución y también establecer y obtener valores de estos miembros. Esta clase admite el enlace dinámico, que le permite usar una sintaxis estándar como sampleObject.sampleMember en lugar de una sintaxis más compleja como sampleObject.GetAttribute ("sampleMember").

Permitiéndote hacer cosas interesantes como:

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

dynObject.SomeDynamicAction(dynObject.SomeDynamicProperty);

Según su código real, es posible que le interese más:

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;
        }
    }
}

De esa forma solo necesitas:

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

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

La derivación de DynamicObject le permite idear su propia estrategia para manejar estas solicitudes de miembros dinámicos, tenga cuidado de que haya monstruos aquí: el compilador no podrá verificar muchas de sus llamadas dinámicas y no obtendrá intellisense, así que siga eso en mente.

Clint
fuente
39

Gracias @Clint por la gran respuesta:

Solo quería resaltar lo fácil que fue resolver esto usando el Objeto Expando:

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

podría deserializar su cadena json en un diccionario y luego agregar nuevas propiedades y luego serializarla.

 var jsonString = @"{}";

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

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

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