2011-05-11 213 views
10

从各种渠道对德interwebs我已经收集到这个如下功能:如何使用反射来获取扩展方法上泛型类型

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; 
} 

我已经把它做成一个扩展方法,它工作得很好如果我直接打电话给我:

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

我尝试使用另一个通用函数的上下文中的反射来调用它时出现问题。 SO中有很多描述如何获得泛型方法和静态方法的MethodInfo的答案,但我似乎无法以正确的方式将它们组合在一起。
我已经正确地确定传递泛型类型本身是一个通用型(Nullable<>),现在我想使用反射来呼吁Nullable<>TryParseNullable扩展方法:

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] 
    } 
} 

有人可以把我出来我的痛苦?
typeof(T)返回正确的类型信息,我认为我可能在调用GetMethod()时错误地使用了它,或者我没有通过调用GetMethod()来指定正确的参数。

1. Link to referenced Jon Skeet answer

+1

你确定扩展方法被认为是typeof(T)的一部分吗?我的猜测是你需要从实现扩展方法的静态类中获取方法。你是否尝试过调用GetMethods()并检查返回的内容? – dlev 2011-05-11 04:28:14

+0

谢谢@dlev,那就是答案。扩展方法的真正基本原理之一,我忽略了它。 – slugster 2011-05-11 04:43:14

+0

只是关于你的逻辑的一个注释,如果'input == null'为什么要返回'default(T)'作为'T?'?看起来像'更正确的'逻辑将返回null。 – 2011-05-11 05:08:22

回答

7

的问题是,扩展方法不修改“”延伸他们的类型。幕后实际发生的事情是,编译器将所有似乎对该对象进行的调用透明地转换为调用静态方法。

即。

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

从那里它变得很明显,为什么它没有通过反射显示。这也解释了为什么您必须为名称空间提供using指令,其中YourClass定义为扩展方法对编译器可见。至于如何才能真正得到这些信息,我不确定是否有办法,没有运行所有声明的类型(如果你知道编译时的那些信息,也许是一组有趣的类的过滤列表)对于在其上定义的ExtensionMethodAttribute[ExtensionMethod])的静态方法,然后尝试解析MethodInfo以获取参数列表,以确定它们是否在Nullable<>上工作。