2011-03-30 20 views
15

Empiezo a trabajar con objetos dinámicos en .Net y no puedo encontrar la manera de hacer algo.Obtener el tipo genérico de llamada al método en el objeto dinámico

Tengo una clase que hereda de DynamicObject y anulo el método TryInvokeMember.

p. Ej.

class MyCustomDynamicClass : DynamicObject 
{ 
    public override bool TryInvokeMember(InvokeMemberBinder binder, object[] args, out object result) 
    { 
     // I want to know here the type of the generic argument 
    } 
} 

Y dentro de ese método, quiero saber el tipo (si existe) de los argumentos genéricos en la invocación.

p. Ej. Si invoco el siguiente código, que desea obtener el valor de System.Boolean y System.Int32 dentro del método overrided de mi objeto dinámico

dynamic myObject = new MyCustomDynamicClass(); 
myObject.SomeMethod<bool>("arg"); 
myObject.SomeOtherMethod<int>("arg"); 

Actualmente, si pongo un punto de interrupción dentro del método overrided puedo conseguir el nombre del método que se invoca ("SomeMethod" y "SomeOtherMethod", y también los valores de los argumentos, pero no los tipos genéricos).

¿Cómo puedo obtener estos valores?

Gracias!

+0

Lo más probable es que necesite buscar el método utilizando la reflexión. MethodInfo proporciona acceso a los argumentos de tipo genérico. –

+0

El problema es que el método no existe, solo tengo acceso al objeto de enlace, que tiene una propiedad CallInfo, que no tiene ninguna información de genéricos. – willvv

+1

Ya sabes, he estado probando muestras de esto por un tiempo, y tampoco puedo encontrar dónde está la información genérica. Esta es realmente una muy buena pregunta. – Tejs

Respuesta

11

En realidad miré a través de la jerarquía de la carpeta y encontré una propiedad con los valores necesarios en los campos internos del objeto.

El problema es que la propiedad no está expuesta porque utiliza códigos/clases específicos de C#, por lo tanto, se debe acceder a las propiedades mediante Reflection.

He encontrado el código en este blog japonés: http://neue.cc/category/programming (no leo ningún japonés, por lo tanto, no estoy seguro de si el autor describe en realidad este mismo tema

Aquí está el fragmento:

var csharpBinder = binder.GetType().GetInterface("Microsoft.CSharp.RuntimeBinder.ICSharpInvokeOrInvokeMemberBinder"); 
var typeArgs = (csharpBinder.GetProperty("TypeArguments").GetValue(binder, null) as IList<Type>); 

typeArgs es una lista que contiene los tipos de los argumentos genéricos utilizados cuando se invoca el método.

Espero que esto ayude a alguien más.

+0

Buen trabajo :) No es bonito, pero cumple su función. –

+0

De hecho, encontré una captura en este código. solo funciona con la versión completa del framework, y necesito usarlo en Silverlight, por lo tanto, todavía me falta una respuesta real :( – willvv

3

El marco de código abierto Dynamitey puede llamar propiedades que sean internas/protegidas/privadas utilizando el DLR y, por lo tanto, funciona con Silverlight. Pero se vuelve un poco complicado con los miembros explícitos de la interfaz ya que tiene que usar el nombre completo real del miembro en el tipo, en lugar del nombre del miembro de la interfaz. Por lo que puede hacer:

var typeArgs = Dynamic.InvokeGet(binder, "Microsoft.CSharp.RuntimeBinder.ICSharpInvokeOrInvokeMemberBinder.TypeArguments") 
    as IList<Type>; 
+0

¡Esto es genial! – albertjan

+0

Hola, esto no funciona porque el encuadernador no es de tipo ICSharpInvokeOrInvokeMemberBinder pero si cambia TypeArguments a m_typeArguments funciona – albertjan

+0

Ugh, tiene razón, aunque m_typeArguments funciona, devuelve un tipo diferente a esa versión de propiedad, por lo que eliminé el ejemplo de fundición de pato de interfaz. – jbtule

3

Un poco de google y tengo solución bastante genérica para Mono y .NET:

/// <summary>Framework detection and specific implementations.</summary> 
public static class FrameworkTools 
{ 
    private static bool _isMono = Type.GetType("Mono.Runtime") != null; 

    private static Func<InvokeMemberBinder, IList<Type>> _frameworkTypeArgumentsGetter = null; 

    /// <summary>Gets a value indicating whether application is running under mono runtime.</summary> 
    public static bool IsMono { get { return _isMono; } } 

    static FrameworkTools() 
    { 
     _frameworkTypeArgumentsGetter = CreateTypeArgumentsGetter(); 
    } 

    private static Func<InvokeMemberBinder, IList<Type>> CreateTypeArgumentsGetter() 
    { 
     if (IsMono) 
     { 
      var binderType = typeof(Microsoft.CSharp.RuntimeBinder.RuntimeBinderException).Assembly.GetType("Microsoft.CSharp.RuntimeBinder.CSharpInvokeMemberBinder"); 

      if (binderType != null) 
      { 
       ParameterExpression param = Expression.Parameter(typeof(InvokeMemberBinder), "o"); 

       return Expression.Lambda<Func<InvokeMemberBinder, IList<Type>>>(
        Expression.TypeAs(
         Expression.Field(
          Expression.TypeAs(param, binderType), "typeArguments"), 
         typeof(IList<Type>)), param).Compile(); 
      } 
     } 
     else 
     { 
      var inter = typeof(Microsoft.CSharp.RuntimeBinder.RuntimeBinderException).Assembly.GetType("Microsoft.CSharp.RuntimeBinder.ICSharpInvokeOrInvokeMemberBinder"); 

      if (inter != null) 
      { 
       var prop = inter.GetProperty("TypeArguments"); 

       if (!prop.CanRead) 
        return null; 

       var objParm = Expression.Parameter(typeof(InvokeMemberBinder), "o"); 

       return Expression.Lambda<Func<InvokeMemberBinder, IList<Type>>>(
        Expression.TypeAs(
         Expression.Property(
          Expression.TypeAs(objParm, inter), 
          prop.Name), 
         typeof(IList<Type>)), objParm).Compile(); 
      } 
     } 

     return null; 
    } 

    /// <summary>Extension method allowing to easyly extract generic type arguments from <see cref="InvokeMemberBinder"/>.</summary> 
    /// <param name="binder">Binder from which get type arguments.</param> 
    /// <returns>List of types passed as generic parameters.</returns> 
    public static IList<Type> GetGenericTypeArguments(this InvokeMemberBinder binder) 
    { 
     // First try to use delegate if exist 
     if (_frameworkTypeArgumentsGetter != null) 
      return _frameworkTypeArgumentsGetter(binder); 

     if (_isMono) 
     { 
      // In mono this is trivial. 

      // First we get field info. 
      var field = binder.GetType().GetField("typeArguments", BindingFlags.Instance | 
       BindingFlags.Public | BindingFlags.NonPublic | BindingFlags.Static); 

      // If this was a success get and return it's value 
      if (field != null) 
       return field.GetValue(binder) as IList<Type>; 
     } 
     else 
     { 
      // In this case, we need more aerobic :D 

      // First, get the interface 
      var inter = binder.GetType().GetInterface("Microsoft.CSharp.RuntimeBinder.ICSharpInvokeOrInvokeMemberBinder"); 

      if (inter != null) 
      { 
       // Now get property. 
       var prop = inter.GetProperty("TypeArguments"); 

       // If we have a property, return it's value 
       if (prop != null) 
        return prop.GetValue(binder, null) as IList<Type>; 
      } 
     } 

     // Sadly return null if failed. 
     return null; 
    } 
} 

divertirse. Por cierto, Impromptu es genial, pero no puedo usarlo.

Cuestiones relacionadas