2009-01-24 47 views
7

我的特殊問題:如何從類型獲取TryParse方法?

我有一個字符串,它指定在配置類的aribitrary類型

Config.numberType = "System.Foo";

其中Foo就像DecimalDouble

我使用Type.GetType(Config.numberType)類型返回相應的類型。

如何從該類型獲得可以使用的編號System.Foo.TryParse()

一些進一步的相關查詢

  • TryParse()可以從System.Foo.TryParse()以及foo.TryParse()進行訪問。這是否意味着foo是C#中的某種類?這對我來說似乎很奇怪,int,double等實際上不只是修飾符關鍵字。
  • 如何在這些情況下聲明變量? - var沒有普遍使用,似乎也就是隻在局部範圍等
+0

`var`當前只在方法的範圍內可用。有關詳細信息,請參閱http://stackoverflow.com/q/4461597/1086351 – fcrick 2012-04-19 18:32:45

回答

20

正如許多人所說 - 沒有直接的途徑。我想到一個比較接近的選項是TypeConverter

Type type = typeof(double); 
    string text = "123.45"; 

    object value = TypeDescriptor.GetConverter(type) 
     .ConvertFromInvariantString(text); 

當然,你可能需要try/catch來處理異常。這就是人生。

+1

請注意,與大多數選項不同,這是可擴展和自我維護的;許多標準和定製類型都有關聯的轉換器。 – 2009-01-24 21:40:00

7

如何從類型到被 能夠使用,System.Foo.TryParse()?

您需要使用反射來查找,然後調用靜態TryParse()方法。並非所有類型都實現此方法 - 因此,如果缺失,您必須決定如何處理它。您還可以使用System.Convert將字符串轉換爲任意類型,假設該字符串實際上是該類型的值的有效表示形式其中實現了轉換。

的TryParse()可從 System.Foo.TryParse(訪問)以及 foo.TryParse()。這是否意味着foo是 C#中的某種類?

intdouble等都是aliasesSystem.Int32System.Double,等等 - 他們是C#語言,這將是他們沒有不舒服的冗長的一部分。

如何在 這些情況下聲明變量?

不知道類型在編譯時,你將被迫宣告,並與您的數據爲object/System.Object工作。 C#4.0將會引入實際的動態類型,這些動態類型將會爲你處理一些乏味的反射工作,但是現在你被困在手工中。請注意,如果您使用諸如linked to by Sebastian Sedlak等技術的方法with a parametrized type argument and returnTryParse()中的use System.Convert,則可以輕鬆實現編寫可與靜態類型一起工作的客戶端代碼的功能......只要它們匹配,或者可以從您正在解析的類型轉換爲。

+0

除TryParse是靜態的; `dynamic`適用於實例。 – 2009-01-24 21:39:13

+0

@Marc:我的意思是作爲他最後一個問題的回答(當你不知道編譯時的類型時,你可以使用什麼類型的變量)。但是,對於他的第一個問題,「動態」對他的幫助不大。代碼塞巴斯蒂安連接看起來像一個體面的方式來實現這一點。 – Shog9 2009-01-24 22:17:49

5

編輯:我刪除了通用實現,並清理了這個響應,以更好地適應最初陳述的問題。

注:Marc Gravell的答案可能是最簡潔的,如果你只是想要給定類型的解析值。下面的答案顯示瞭如何獲取方法(即MethodInfo對象以及如何調用它)。

以下應該工作,至少對於實現公共靜態布爾的TryParse(字符串,T值)類型:

public static class Parsing 
{ 
    static MethodInfo findTryParseMethod(Type type) 
    { 
     //find member of type with signature 'static public bool TryParse(string, out T)' 
     BindingFlags access = BindingFlags.Static | BindingFlags.Public; 
     MemberInfo[] candidates = type.FindMembers(
      MemberTypes.Method, 
      access, 
      delegate(MemberInfo m, object o_ignored) 
      { 
       MethodInfo method = (MethodInfo)m; 
       if (method.Name != "TryParse") return false; 
       if (method.ReturnParameter.ParameterType != typeof(bool)) return false; 
       ParameterInfo[] parms = method.GetParameters(); 
       if (parms.Length != 2) return false; 
       if (parms[0].ParameterType != typeof(string)) return false; 
       if (parms[1].ParameterType != type.MakeByRefType()) return false; 
       if (!parms[1].IsOut) return false; 

       return true; 

      }, null); 

     if (candidates.Length > 1) 
     { 
      //change this to your favorite exception or use an assertion 
      throw new System.Exception(String.Format(
       "Found more than one method with signature 'public static bool TryParse(string, out {0})' in type {0}.", 
       type)); 
     } 
     if (candidates.Length == 0) 
     { 
      //This type does not contain a TryParse method - replace this by your error handling of choice 
      throw new System.Exception(String.Format(
       "Found no method with signature 'public static bool TryParse(string, out {0})' in type {0}.", 
       type)); 
     } 
     return (MethodInfo)candidates[0]; 
    } 

    public static bool TryParse(Type t, string s, out object val) 
    { 
     MethodInfo method = findTryParseMethod(t); //can also cache 'method' in a Dictionary<Type, MethodInfo> if desired 
     object[] oArgs = new object[] { s, null }; 
     bool bRes = (bool)method.Invoke(null, oArgs); 
     val = oArgs[1]; 
     return bRes; 
    } 

    //if you want to use TryParse in a generic syntax: 
    public static bool TryParseGeneric<T>(string s, out T val) 
    { 
     object oVal; 
     bool bRes = TryParse(typeof(T), s, out oVal); 
     val = (T)oVal; 
     return bRes; 
    } 
} 

使用下面的測試代碼:

 public bool test() 
    { 
     try 
     { 
      object oVal; 
      bool b = Parsing.TryParse(typeof(int), "123", out oVal); 
      if (!b) return false; 
      int x = (int)oVal; 
      if (x!= 123) return false; 
     } 
     catch (System.Exception) 
     { 
      return false; 
     } 

     try 
     { 
      int x; 
      bool b = Parsing.TryParseGeneric<int>("123", out x); 
      if (!b) return false; 
      if (x != 123) return false; 
     } 
     catch (System.Exception) 
     { 
      return false; 
     } 


     try 
     { 
      object oVal; 
      bool b = Parsing.TryParse(typeof(string), "123", out oVal); 
      //should throw an exception (//no method String.TryParse(string s, out string val) 
      return false; 
     } 
     catch (System.Exception) 
     { 
      //should throw an exception 
     } 

     return true; 
    } 
} 

而且在使用你的情況:

//input: string s, Config 
Type tNum = Type.GetType(Config.numberType);  
object oVal; 
bool ok = Parsing.TryParse(tNum, s, out oVal); 
//oVal is now of type tNum and its value is properly defined if ok == true 

關於使用var:你可能會誤解var的作用:它是不是「變體」類型(類型對象已被用於此類型),但將該類型的聲明語法移至作業的右側。下面的聲明是等價的:

var i = 1; //the compiler infers the type from the assignment, type of i is int. 
int i = 1; //type of i is int via declaration 

var的主要用途是允許創建匿名類型

var anon = new { Name = "abc", X = 123 }; 
1

而另一good link to this problem。該網站的源代碼格式非常糟糕,所以我把它放在這裏。希望作者不會起訴我。

public static T Parse<T>(string s) 
{ 
    Type t = typeof(T); 
    // Attempt to execute the Parse method on the type if it exists. 
    MethodInfo parse = t.GetMethod("Parse", new Type[] { typeof(string) }); 

    if (parse != null) 
    { 
     try 
     { 
      return (T)parse.Invoke(null, new object[] { s }); 
     } 
     catch (Exception ex) 
     { 
      throw ex.InnerException; 
     } 
    } 
    else 
    { 
     throw new MethodAccessException(String.Format("The Parse method does not exist for type {0}.", t.Name)); 
    } 
} 

public static bool TryParse<T>(string s, out T result) 
{ 
    return TryParse<T>(s, false, out result); 
} 

public static bool TryParse<T>(string s, bool throwException, out T result) 
{ 
    result = default(T); 
    Type t = typeof(T); 
    T type = default(T); 

    // Look for the TryParse method on the type. 
    MethodInfo tryParse = t.GetMethod("TryParse", new Type[] { typeof(string), Type.GetType(t.FullName + "&") }); 
    if (tryParse != null) 
    { 
     // Try parse exists. Call it. 
     Object[] ps = new Object[2]; 
     ps[0] = s; 

     bool isSuccess = (bool)tryParse.Invoke(type, ps); 

     if (isSuccess) 
      result = (T)ps[1]; 

     return isSuccess; 
    } 
    else 
    { 
     // TryParse does not exist. Look for a Parse method. 
     try 
     { 
      result = Parse<T>(s); 
      return true; 
     } 
     catch 
     { 
      if (throwException) 
       throw; 

      return false; 
     } 
    } 
}