2011-03-14 11 views
23

Estoy implementando una función de propósito general para extraer un valor de un objeto dinámico provisto arbitrariamente, pero no sé cómo llamar a TryGetMember porque requiere un GetMemberBinder que es abstracto, por lo tanto no puedo crearlo. muestra ...¿Cómo llamar a DynamicObject.TryGetMember directamente?

public object GetValue(DynamicObject Source, string FieldName) 
{ 
    object Result = null; 
    GetMemberBinder Binder = x; // What object must be provided? 
    Binder.Name = FieldName; 
    if (Source.TryGetMember(Binder, out Result)) 
     return Result; 

    throw new Exception("The field '" + FieldName + "' not exists"); 
} 

¿Existe un descendiente de hormigón ya existente de GetMemberBinder listo para su uso? o una guía para crear mi propia implementación?

+0

Me falta algo ... ¿no es exactamente todo este método lo que el operador de asignación dinámica ya hace, excepto que es menos confiable? – Aaronaught

+5

Mi pregunta es acerca de obtener un valor de campo de un objeto dinámico sin saber a la vez cómo se llama ese campo. Por lo tanto, no puedo codificar, por ejemplo, "var MyValue = TheDynamicObject.TheField;" porque solo en tiempo de ejecución se obtiene el nombre del campo. Estoy escribiendo una clase de propósito general para tratar con objetos dinámicos provistos externamente. –

Respuesta

47

no estoy seguro si hay cualquier método en el marco que en realidad devuelve un GetMemberBinder, pero no importa - que no es la forma correcta para invocar un miembro dinámico por su nombre.

Lo que realmente necesita hacer es crear un sitio de llamadas. El método es el siguiente:

static object GetDynamicMember(object obj, string memberName) 
{ 
    var binder = Binder.GetMember(CSharpBinderFlags.None, memberName, obj.GetType(), 
     new[] { CSharpArgumentInfo.Create(CSharpArgumentInfoFlags.None, null) }); 
    var callsite = CallSite<Func<CallSite, object, object>>.Create(binder); 
    return callsite.Target(callsite, obj); 
} 

Tenga en cuenta que Binder.GetMember crea una CallSiteBinder, no un GetMemberBinder. Solo para ser 100% claro. Este método generará un RuntimeBinderException si falla la llamada interna al TryGetMember, por lo que no es necesario que verifique el resultado. Si no desea que las personas que llaman vean el RuntimeBinderException, envuélvalo en su propio try/catch.

El envío dinámico es complejo, al menos en relación con la reflexión sobre tipos estáticos. Como el CLR no se escribe de manera dinámica, C# tiene que crear una instancia de un compilador para averiguar cómo ejecutar el miembro/método. Eso es crear un sitio de llamadas. Por lo que yo sé, usted tiene para hacer esto, razón por la cual cada método Binder devuelve CallSiteBinder y no puede instanciar ninguno de los encuadernadores directamente.

Tenga en cuenta que el DLR realiza algún tipo de almacenamiento en caché de sitios de llamadas, pero no estoy seguro de si el almacenamiento en caché automático cubre este escenario. Es muy probable que desee guardar su sitio de llamadas para futuras llamadas para evitar la sobrecarga de la recopilación constante.

P.S. Si está utilizando (o puede usar) ExpandoObject en lugar de DynamicObject, tenga en cuenta que implementa IDictionary<string, object>, por lo que no necesita hacer nada de esto. Simplemente transfiéralo al tipo de diccionario y verifica si la propiedad existe. Solo usaría DynamicObject en ExpandoObject si estuviera haciendo algo mucho más complicado que simplemente agregar miembros en tiempo de ejecución, es decir, cambiar el comportamiento real en función del encuadernador de tiempo de ejecución.

+0

El almacenamiento en caché automático se almacena en el callsite y también en el callsitebinder, por lo que si no conserva al menos uno de ellos obtendrá un rendimiento bastante malo (lo he probado). Esa es una de las cosas que [interfaz improvisada] (http://code.google.com/p/impromptu-interface/) hace por usted, reutilizará el callsite que crea cada vez que puede. – jbtule

+0

Obviamente, usted sabe mucho sobre este tema, pero si continúa publicando ese enlace, va a [comenzar a recibir avisos de spam] (http://stackoverflow.com/faq#promotion). Por lo menos, agitaría menos plumas si no fuera siempre un hipervínculo. – Aaronaught

+0

Lo tendré en cuenta, pero es un marco de código abierto que está diseñado para resolver este problema específico que, en general, no es trivial. Este caso puede ser trivial, ya que los miembros de get siempre pueden tener la misma firma de func de callsite, por lo que todo lo que necesita es un diccionario de listas de llamadas indexadas por membername para solucionar este problema de caché. – jbtule

11

No llama directamente a TryGetMember, lo que necesita es usar las API dinámicas directamente para obtener el mismo efecto utilizando una carpeta de miembro de csharp y un sitio de llamada.

Esto se hace aún más fácil por el marco de código abierto Dynamitey (a través de nuget) ya que tiene un método estático que hace esto. Funciona para cualquier IDynamicMetaObjectProvider no solo DynamicObject y (funciona para los tipos regulares más rápido que la reflexión también).

return Dynamic.InvokeGet(Source, FieldName); 
+2

No entiendo por qué esta respuesta fue rechazada. Hace exactamente esto ahora ¿no? –

+0

@jbtule, sobre su foto de usuario ... ¡Agradable! ¿Mejor perfil? –

Cuestiones relacionadas