2010-06-13 48 views
40

Estoy buscando una forma de modificar propiedades en un objeto dynamic C# 4.0 con el nombre de la propiedad conocida solo en tiempo de ejecución.Cómo establecer una propiedad de un objeto dinámico C# 4 cuando tiene el nombre en otra variable

¿Hay una manera de hacer algo como (ExpandoObject se usa sólo como un ejemplo, esto podría ser cualquier clase que implemente IDynamicMetaObjectProvider):

string key = "TestKey"; 
dynamic e = new ExpandoObject(); 
e[key] = "value"; 

lo que sería equivalente a:

dynamic e = new ExpandoObject(); 
e.TestKey = "value"; 

¿O es la única manera de avanzar?

+0

La reflexión es probablemente su propia solución, a menos que ponga sus propiedades en un hashmap para que puedan determinarse en tiempo de ejecución. –

+0

posible duplicado de [Agregar propiedades desconocidas (en tiempo de diseño) a un ExpandoObject] (http://stackoverflow.com/questions/2974008/adding-unknown-at-design-time-properties-to-an-expandoobject) – nawfal

Respuesta

16

No muy fácil, no. La reflexión no funciona, ya que asume un modelo de tipo normal, que es , no, el rango completo de dynamic. Si en realidad solo estás hablando con objetos regulares, entonces usa la reflexión aquí. De lo contrario, supongo que es posible que desee realizar una ingeniería inversa del código que el compilador emite para una asignación básica, y modificarlo para que tenga un nombre de miembro flexible. Sin embargo, seré sincero: esta no es una opción atractiva; un simple:

dynamic foo = ... 
foo.Bar = "abc"; 

se traduce en:

if (<Main>o__SiteContainer0.<>p__Site1 == null) 
{ 
    <Main>o__SiteContainer0.<>p__Site1 = CallSite<Func<CallSite, object, string, object>>.Create(Binder.SetMember(CSharpBinderFlags.None, "Bar", typeof(Program), new CSharpArgumentInfo[] { CSharpArgumentInfo.Create(CSharpArgumentInfoFlags.None, null), CSharpArgumentInfo.Create(CSharpArgumentInfoFlags.Constant | CSharpArgumentInfoFlags.UseCompileTimeType, null) })); 
} 
<Main>o__SiteContainer0.<>p__Site1.Target(<Main>o__SiteContainer0.<>p__Site1, foo, "abc"); 

Si desea un enfoque que funciona para ambos objetos dinámicos y no dinámicos: FastMember es útil para esto, y funciona ya sea en el tipo o nivel de objeto:

// could be static or DLR 
var wrapped = ObjectAccessor.Create(obj); 
string propName = // something known only at runtime 
Console.WriteLine(wrapped[propName]); 

disponible en Nuget, y muy optimizado para dinámica y no dinámica sc enarios.

+0

Gracias Marc - ¡Pensé que me había perdido algo obvio, pero obviamente no! :) ¿Tienes algún enlace a un buen ejemplo de cómo hacer la emisión del código para que pueda probarlo? ¡Si puedo hacerlo funcionar, parece algo de trabajo compartido! –

+0

@Kieran - depende de lo que necesites. No sonaba como si necesitara una emisión (el código que publiqué fue solo una muestra de lo feo que es). –

+0

Downvoted porque esto no explica que un expandoObject use un diccionario como su almacén de respaldo y se puede usar exactamente como el OP sugerido originalmente. No hay necesidad de reflexión O emitir en un expandoObject. Dicho esto, @MarcGravell está en lo cierto al decir que para los objetos dinámicos no expandidos (así como para los objetos regulares asignados a una var dinámica) no existe una solución limpia. –

3

Mi marco de código abierto Dynamitey tiene métodos para invocar basándose en nombres de cadenas utilizando el DLR. Hace el trabajo de almacenar en caché sitios de enlace y lo simplifica a una llamada de método. también funciona más rápido que la reflexión en objetos no dinámicos también.

Dynamic.InvokeSet(e, "TestKey", "value"); 
50

Paul Sasik respondido a una pregunta similar en C# 4.0 Dynamic vs Expando... where do they fit?

using System; 
using System.Dynamic; 

class Program 
{ 
    static void Main(string[] args) 
    { 
     dynamic expando = new ExpandoObject(); 
     var p = expando as IDictionary<String, object>; 
     p["A"] = "New val 1"; 
     p["B"] = "New val 2"; 

     Console.WriteLine(expando.A); 
     Console.WriteLine(expando.B); 
    } 
} 
0

fast-member puede encajar el proyecto de ley - que parece que genera el IL sobre la marcha, pero lo almacena en caché por lo que es muy rápido después del primer uso .

6

Para agregar a la respuesta de Jonas, no tiene que crear una nueva var p. Use este enfoque en su lugar:

using System; 
using System.Dynamic; 

class Program 
{ 
    static void Main(string[] args) 
    { 
     dynamic expando = new ExpandoObject(); 
     ((IDictionary<String, object>)expando)["A"] = "New val 1"; 
     ((IDictionary<String, object>)expando)["B"] = "New val 2"; 

     Console.WriteLine(expando.A); 
     Console.WriteLine(expando.B); 
    } 
} 
Cuestiones relacionadas