2011-06-27 14 views
7

Deseo acceder a una propiedad en un objeto mientras aprovecho el mecanismo de enlace DLR.Invocar un miembro de un objeto dinámico con un nombre definido en el tiempo de ejecución en un String

  • no puedo utilizar el mecanismo de unión nativo (dynamic palabra clave en C#), porque no sé el nombre de la propiedad en tiempo de compilación;
  • No puedo usar la reflexión porque solo recupera información de tipo estático;
  • fundición a un IDictionary<string, object>, que yo sepa, solo resuelve el caso de las clases dinámicas que eligen implementar esa interfaz (como ExpandoObject).

Aquí está el código de demostración:

static void Main(string[] args) 
    { 
     dynamic obj = new System.Dynamic.ExpandoObject(); 
     obj.Prop = "Value"; 
     // C# dynamic binding. 
     Console.Out.WriteLine(obj.Prop); 
     // IDictionary<string, object> 
     Console.Out.WriteLine((obj as IDictionary<string, object>)["Prop"]); 
     // Attempt to use reflection. 
     PropertyInfo prop = obj.GetType().GetProperty("Prop"); 
     Console.Out.WriteLine(prop.GetValue(obj, new object[] { })); 
     Console.In.ReadLine(); 
    } 

Respuesta

5

Finalmente pude hacerlo utilizando el encuadernador de tiempo de ejecución C#. (Creo que debería ser posible hacerlo con una implementación más simple de encuadernador, pero no pude escribir uno funcional, probablemente escribir una implementación "simple" ya sea bastante difícil).

using Microsoft.CSharp.RuntimeBinder; 

    private class TestClass 
    { 
     public String Prop { get; set; } 
    } 

    private static Func<object, object> BuildDynamicGetter(Type targetType, String propertyName) 
    { 
     var rootParam = Expression.Parameter(typeof(object)); 
     var propBinder = Microsoft.CSharp.RuntimeBinder.Binder.GetMember(CSharpBinderFlags.None, propertyName, targetType, new[] { CSharpArgumentInfo.Create(CSharpArgumentInfoFlags.None, null) }); 
     DynamicExpression propGetExpression = Expression.Dynamic(propBinder, typeof(object), 
      Expression.Convert(rootParam, targetType)); 
     Expression<Func<object, object>> getPropExpression = Expression.Lambda<Func<object, object>>(propGetExpression, rootParam); 
     return getPropExpression.Compile(); 
    } 

    static void Main(string[] args) 
    { 
     dynamic root = new TestClass { Prop = "StatValue" }; 

     Console.Out.WriteLine(root.Prop); 
     var dynGetter = BuildDynamicGetter(root.GetType(), "Prop"); 
     Console.Out.WriteLine(dynGetter(root)); 

     root = new System.Dynamic.ExpandoObject(); 
     root.Prop = "ExpandoValue"; 

     Console.WriteLine(BuildDynamicGetter(root.GetType(), "Prop").Invoke(root)); 
     Console.Out.WriteLine(root.Prop); 
     Console.In.ReadLine(); 
    } 

Salida:

StatValue 
StatValue 
ExpandoValue 
ExpandoValue 
0

Se podría utilizar C# dynamic con la compilación en tiempo de ejecución para acomodar el requisito del nombre de propiedad desconocida.

Un ejemplo:

dynamic d = new ExpandoObject(); 

d.Value = 7; 

var helper = "" + 
    "using System; " + 
    "public class Evaluator " + 
    "{{ " + 
    " public object Eval(dynamic d) {{ return d.{0}; }} " + 
    "}}"; 

var references = new string[] 
{ 
    "System.dll", 
    "System.Core.dll", 
    "Microsoft.CSharp.dll" 
}; 

var parameters = new CompilerParameters(references, "Test.dll"); 
var compiler = new CSharpCodeProvider(); 

var results = compiler.CompileAssemblyFromSource(
    parameters, 
    String.Format(helper, "Value")); 

dynamic exp = Activator.CreateInstance(
    results.CompiledAssembly.GetType("Evaluator")); 

Console.WriteLine(exp.Eval(d)); 

Esto funciona, pero dudo que esta es la mejor opción y si necesita invocar el método que puede ser un poco más complejo.

0

Si subclase DynamicObject puede anular TrySetMember para almacenar las propiedades por nombre en un diccionario local y anular TryGetIndex para permitir la recuperación de ellos utilizando el nombre de la propiedad como una cadena. Hay un buen ejemplo de esto en la página DynamicObject en MSDN.

Esta no es la manera más rápida de hacerlo, ya que no aprovecha el despacho dinámico y los lugares de llamadas (y otras cosas que no entiendo del todo) tan bien como podría, pero funcionará. Terminará luciendo así:

dynamic myDynamicClass = new MyDynamic(); 

// Uses your TrySetMember() override to add "Value" to the internal dictionary against the key "Prop": 
myDynamicClass.Prop = "Value"; 

// Uses your TryGetIndex override to return the value of the internal dictionary entry for "Prop": 
object value = myDynamicClass["Prop"]; 
+0

cosa es que estoy trabajando bajo el supuesto de que no controlo el objeto dinámico que estoy consumiendo - puede ser cualquier objeto que implemente IDynamicMetaObjectProvider. –

0

Ésta es básicamente una variante de esta pregunta: Dynamically adding members to a dynamic object pero en vez de querer añadir que estés deseando conseguir los miembros - por lo que sólo cambia el aglutinante y la firma de el sitio de llamada. Parece que ya has descubierto cómo hacer la carpeta.

1

El marco de código abierto ImpromptuInterface hará esto (disponible a través de nuget). Utiliza las mismas llamadas de API que el compilador de C#, también se ejecuta más rápido que la reflexión en objetos no dinámicos.

Console.Out.WriteLine(Impromptu.InvokeGet(obj,"Prop")); 
Cuestiones relacionadas