8

Estoy buscando una forma de agregar miembros dinámicamente a un objeto dinámico. OK, supongo que se necesita una pequeña aclaración ...Agregar dinámicamente miembros a un objeto dinámico

Cuando se hace esto:

dynamic foo = new ExpandoObject(); 
foo.Bar = 42; 

La propiedad Bar se añadirá dinámicamente en tiempo de ejecución. Pero el código aún se refiere "estáticamente" a Bar (el nombre "Bar" está codificado) ... ¿Qué sucede si quiero agregar una propiedad en tiempo de ejecución sin saber su nombre en el momento de la compilación?

sé cómo hacer esto con un objeto dinámico personalizado (que en realidad hace unos meses blogged about it), utilizando los métodos de la clase DynamicObject, pero ¿cómo puedo hacer con cualquier objeto dinámico?

Probablemente podría utilizar la interfaz IDynamicMetaObjectProvider, pero no entiendo cómo usarla. Por ejemplo, ¿qué argumento debería pasar al método GetMetaObject? (espera un Expression)

Y, por cierto, ¿cómo se realiza la reflexión sobre los objetos dinámicos? La reflexión "Regular" y TypeDescriptor no muestran los miembros dinámicos ...

¡Cualquier observación se agradecerá!

+0

En C# 6.0, * puede ser * usted puede escribirlo como 'foo. $ Bar = 42;' :) No estoy seguro si está permitido para dinámico ... – nawfal

+0

@nawfal, en realidad, esa característica se ha caído .. .pero de todos modos, 'foo. $ Bar' es solo la abreviatura de' foo ["Bar"] ' –

+0

Thomas, no sabía acerca de la función que se descarta (me alegra), pero oh sí, por un momento Pasé por alto el requisito real de tu q. – nawfal

Respuesta

9

Lo que quiere es similar a las funciones getattr/setattr de Python. No hay una forma equivalente incorporada de hacerlo en C# o VB.NET. La capa externa del DLR (que se envía con IronPython e IronRuby en Microsoft.Scripting.dll) incluye un conjunto de API de alojamiento que incluye una API ObjectOperations que tiene métodos GetMember/SetMember. Podría usarlos pero necesitaría la dependencia adicional del DLR y un lenguaje basado en DLR.

Probablemente el enfoque más simple sería crear un CallSite con uno de los enlazadores C# existentes. Puede obtener el código para esto mirando el resultado de "foo.Bar = 42" en ildasm o reflector. Pero un simple ejemplo de esto sería:

object x = new ExpandoObject(); 
CallSite<Func<CallSite, object, object, object>> site = CallSite<Func<CallSite, object, object, object>>.Create(
      Binder.SetMember(
       Microsoft.CSharp.RuntimeBinder.CSharpBinderFlags.None, 
       "Foo", 
       null, 
       new[] { CSharpArgumentInfo.Create(CSharpArgumentInfoFlags.None, null) } 
      ) 
     ); 
site.Target(site, x, 42); 
Console.WriteLine(((dynamic)x).Foo); 
+0

Necesito tomarme un tiempo para asegurarme de que realmente entiendo lo que está haciendo este código, pero de todos modos está funcionando bien ... ¡Gracias! –

+0

¿Sería posible hacer esto <.Net 4.0 usando el DLR? Parece que el uso de la dinámica evitaría esto. – Firestrand

+0

El método anterior no debería funcionar correctamente. sitio llamado colocador requiere 2 Info de argumentos que se deben proporcionar en lugar de 1. La definición correcta será: new [] { CSharpArgumentInfo.Create (CSharpArgumentInfoFlags.None, null), CSharpArgumentInfo.Create (CSharpArgumentInfoFlags.None, null) } –

5

El marco de código abierto Dynamitey hará esto (disponible a través de nuget). Encapsula mientras aún almacena en caché el sitio de llamadas y el código de encuadernación que usó @Dino-Viehland.

Dynamic.InvokeSet(foo,"Bar",42); 

También puede llamar a muchos other kinds of c# binder too.

5

ExpandoObject implementa IDictionary < cadena, objeto > aunque explícitamente. Lo que esto significa es que simplemente puede enviar el ExpandoObject a IDictionary < cadena, objeto > y manipular el diccionario.

dynamic foo = new ExpandoObject(); 
foo.Bar = 42; 
food = (IDictionary<string,object>)foo; 
food["Baz"] = 54 
+0

¡Gracias! De hecho, ExpandoObject implementa IDictionary, como me di cuenta después, por lo que es claramente la solución más simple en ese caso. Sin embargo, el alcance de mi pregunta era más amplio: estaba buscando una solución que funcionara con cualquier objeto dinámico, no solo ExpandoObject –

1

Sé que esto es bastante una entrada antigua, pero que pensé en pasar a lo largo Miron Abramson solution sobre cómo puede crear su propio tipo y añadir propiedades en tiempo de ejecución - en caso de que alguien más por ahí está buscando algo similar.

Cuestiones relacionadas