2011-05-11 18 views
10

de varias fuentes en teh interwebs He obtenida esta función siguiente:Cómo utilizar la reflexión para obtener el método de extensión en el tipo genérico de

public static Nullable<T> TryParseNullable<T>(this Nullable<T> t, string input) where T : struct 
{ 
    if (string.IsNullOrEmpty(input)) 
     return default(T); 

    Nullable<T> result = new Nullable<T>(); 
    try 
    { 
     IConvertible convertibleString = (IConvertible)input; 
     result = new Nullable<T>((T)convertibleString.ToType(typeof(T), CultureInfo.CurrentCulture)); 
    } 
    catch (InvalidCastException) { } 
    catch (FormatException) { } 

    return result; 
} 

me he hecho en un método de extensión, y funciona muy bien si lo llamo directamente:

int? input = new int?().TryParseNullable("12345"); 

Mi problema ocurre cuando intento llamarlo usando la reflexión dentro del contexto de otra función genérica. SO está lleno de respuestas que describen cómo obtener MethodInfo de métodos genéricos y métodos estáticos, pero parece que no puedo juntarlos de la manera correcta.
he determinado correctamente que el tipo genérico pasado es en sí mismo un tipo genérico (Nullable<>), ahora quiero utilizar la reflexión para llamar al método TryParseNullable extensión en el Nullable<>:

public static T GetValue<T>(string name, T defaultValue) 
{ 
    string result = getSomeStringValue(name); 
    if (string.IsNullOrEmpty(result)) return defaultValue; 

    try 
    { 
     if (typeof(T).IsGenericType && typeof(T).GetGenericTypeDefinition() == typeof(Nullable<>)) 
     { 
      MethodInfo methodInfo; 

      //using the TryParse() of the underlying type works but isn't exactly the way i want to do it 
      //------------------------------------------------------------------------------------------- 
      NullableConverter nc = new NullableConverter(typeof(T)); 
      Type t = nc.UnderlyingType; 

      methodInfo = t.GetMethod("TryParse", BindingFlags.Public | BindingFlags.Static, Type.DefaultBinder, new[] { typeof(string), t.MakeByRefType() }, null); 
      if (methodInfo != null) 
      { 
       var inputParameters = new object[] { result, null }; 
       methodInfo.Invoke(null, inputParameters); 
       return (T) inputParameters[1]; 
      } 

      //start of the problem area 
      //------------------------- 

      Type ttype = typeof(T); 

      //this works but is undesirable (due to reference to class containing the static method): 
      methodInfo = typeof(ParentExtensionsClass).GetMethod("TryParseNullable", BindingFlags.Public | BindingFlags.Static); 
      if (methodInfo != null) 
       Console.WriteLine(methodInfo); 

      //standard way of getting static method, doesn't work (GetMethod() returns null): 
      methodInfo = ttype.GetMethod("TryParseNullable", BindingFlags.Public | BindingFlags.Static); 
      if (methodInfo != null) 
       Console.WriteLine(methodInfo); 

      //Jon Skeet's advised method, doesn't work in this case (again GetMethod() returns null): 
      //(see footnote for link to this answer) 
      methodInfo = ttype.GetMethod("TryParseNullable"); 
      methodInfo = methodInfo.MakeGenericMethod(ttype); 
      if (methodInfo != null) 
       Console.WriteLine(methodInfo); 

      //another random attempt (also doesn't work): 
      methodInfo = ttype.GetMethod("TryParseNullable", BindingFlags.Public | BindingFlags.Static, Type.DefaultBinder, new[] { typeof(string) }, null); 
      if (methodInfo != null) 
       Console.WriteLine(methodInfo); 
     } 

     // if we get this far, then we are not handling the type yet 
     throw new ArgumentException("The type " + defaultValue.GetType() + " is not yet supported by GetValue<T>.", "T"); 
    } 
    catch (Exception e) 
    { 
     [snip] 
    } 
} 

Puede alguien poner fuera de mi miseria?
El typeof(T) devuelve la información de tipo correcta, supongo que tal vez la estoy utilizando de forma incorrecta con la llamada GetMethod(), o no he especificado los parámetros correctos con la llamada al GetMethod().

1. Link to referenced Jon Skeet answer

+1

¿Está seguro de que el método de extensión se considera como parte del typeof (T)? Mi suposición sería que necesitas recuperar el método de la clase estática que implementa el método de extensión, punto. ¿Has intentado simplemente llamar a GetMethods() e inspeccionar lo que se devuelve? – dlev

+0

Gracias @dlev, esa fue la respuesta. Uno de los fundamentos básicos reales de los métodos de extensión y lo había pasado por alto. – slugster

+0

solo una nota sobre su lógica, ¿por qué devuelve 'default (T)' como 'T?' If 'input == null'? Parece que la lógica 'más correcta' sería devolver nulo. –

Respuesta

7

El problema es que los métodos de extensión no modifican el tipo que son 'que se extiende'. Lo que ocurre realmente detrás de escena es que el compilador traduce de forma transparente todas las llamadas que parecen realizadas en el objeto en cuestión a las llamadas a su método estático.

es decir.

int? input = new int?().TryParseNullable("12345"); 
// becomes... 
int? input = YourClass.TryParseNullable(new int?(), "12345"); 

A partir de ahí se hace evidente por qué no se muestra a través de la reflexión. Esto también explica por qué debe tener una directiva using para el espacio de nombres donde se define YourClass para que los métodos de extensión sean visibles para el compilador. En cuanto a cómo se puede acceder a esa información, no estoy seguro de que exista una forma de no ejecutar todos los tipos declarados (tal vez una lista filtrada de clases interesantes, si conoce ese tipo de información en tiempo de compilación) buscando para los métodos estáticos con ExtensionMethodAttribute ([ExtensionMethod]) definidos en ellos, y luego intenta analizar el MethodInfo para que la lista de parámetros funcione si funcionan en Nullable<>.

Cuestiones relacionadas