2009-01-24 100 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; 
     } 
    } 
}