2011-05-11 228 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<>上工作。