通用TryParse

我想创build一个通用的扩展使用TryParse来检查一个string是否是给定的types:

public static bool Is<T>(this string input) { T notUsed; return T.TryParse(input, out notUsed); } 

这将不会编译,因为它不能解决符号'TryParse'

据我所知,“TryParse”不是任何接口的一部分。

这是可能做到的吗?

更新:

使用下面的答案我已经想出了:

 public static bool Is<T>(this string input) { try { TypeDescriptor.GetConverter(typeof(T)).ConvertFromString(input); } catch { return false; } return true; } 

它工作得很好,但我认为以这种方式使用exception并不适合我。

UPDATE2:

修改为传递types而不是使用generics:

 public static bool Is(this string input, Type targetType) { try { TypeDescriptor.GetConverter(targetType).ConvertFromString(input); return true; } catch { return false; } } 

你应该使用TypeDescriptor类:

 public static T Convert<T>(this string input) { try { var converter = TypeDescriptor.GetConverter(typeof(T)); if(converter != null) { // Cast ConvertFromString(string text) : object to (T) return (T)converter.ConvertFromString(input); } return default(T); } catch (NotSupportedException) { return default(T); } } 

我最近还需要一个通用的TryParse。 这就是我想出来的。

 public static T? TryParse<T>(string value, TryParseHandler<T> handler) where T : struct { if (String.IsNullOrEmpty(value)) return null; T result; if (handler(value, out result)) return result; Trace.TraceWarning("Invalid value '{0}'", value); return null; } public delegate bool TryParseHandler<T>(string value, out T result); 

那么这只是一个打电话问题:

 var value = TryParse<int>("123", int.TryParse); var value2 = TryParse<decimal>("123.123", decimal.TryParse); 

尝试/捕捉stream量控制是一个可怕的政策。 抛出一个exception会导致性能滞后,而运行时则会影响exception。 而是在转换之前validation数据。

 var attemptedValue = "asdfasdsd"; var type = typeof(int); var converter = TypeDescriptor.GetConverter(type); if (converter != null && converter.IsValid(attemptedValue)) return converter.ConvertFromString(attemptedValue); else return Activator.CreateInstance(type); 

如果你使用TryParse,你可以使用reflection,像这样做:

 public static bool Is<T>(this string input) { var type = typeof (T); var temp = default(T); var method = type.GetMethod( "TryParse", new[] { typeof (string), Type.GetType(string.Format("{0}&", type.FullName)) }); return (bool) method.Invoke(null, new object[] {input, temp}); } 

这为每个generics使用了一个静态构造函数,因此只需要在给定types上第一次调用昂贵的工作。 它处理具有TryParse方法的系统名称空间中的所有types。 除了枚举之外,它也可以与每个结构体(可以是结构体)的可为空版本一起工作。

  public static bool TryParse<t>(this string Value, out t result) { return TryParser<t>.TryParse(Value.SafeTrim(), out result); } private delegate bool TryParseDelegate<t>(string value, out t result); private static class TryParser<T> { private static TryParseDelegate<T> parser; // Static constructor: static TryParser() { Type t = typeof(T); if (t.IsEnum) AssignClass<T>(GetEnumTryParse<T>()); else if (t == typeof(bool) || t == typeof(bool?)) AssignStruct<bool>(bool.TryParse); else if (t == typeof(byte) || t == typeof(byte?)) AssignStruct<byte>(byte.TryParse); else if (t == typeof(short) || t == typeof(short?)) AssignStruct<short>(short.TryParse); else if (t == typeof(char) || t == typeof(char?)) AssignStruct<char>(char.TryParse); else if (t == typeof(int) || t == typeof(int?)) AssignStruct<int>(int.TryParse); else if (t == typeof(long) || t == typeof(long?)) AssignStruct<long>(long.TryParse); else if (t == typeof(sbyte) || t == typeof(sbyte?)) AssignStruct<sbyte>(sbyte.TryParse); else if (t == typeof(ushort) || t == typeof(ushort?)) AssignStruct<ushort>(ushort.TryParse); else if (t == typeof(uint) || t == typeof(uint?)) AssignStruct<uint>(uint.TryParse); else if (t == typeof(ulong) || t == typeof(ulong?)) AssignStruct<ulong>(ulong.TryParse); else if (t == typeof(decimal) || t == typeof(decimal?)) AssignStruct<decimal>(decimal.TryParse); else if (t == typeof(float) || t == typeof(float?)) AssignStruct<float>(float.TryParse); else if (t == typeof(double) || t == typeof(double?)) AssignStruct<double>(double.TryParse); else if (t == typeof(DateTime) || t == typeof(DateTime?)) AssignStruct<DateTime>(DateTime.TryParse); else if (t == typeof(TimeSpan) || t == typeof(TimeSpan?)) AssignStruct<TimeSpan>(TimeSpan.TryParse); else if (t == typeof(Guid) || t == typeof(Guid?)) AssignStruct<Guid>(Guid.TryParse); else if (t == typeof(Version)) AssignClass<Version>(Version.TryParse); } private static void AssignStruct<t>(TryParseDelegate<t> del) where t: struct { TryParser<t>.parser = del; if (typeof(t).IsGenericType && typeof(t).GetGenericTypeDefinition() == typeof(Nullable<>)) { return; } AssignClass<t?>(TryParseNullable<t>); } private static void AssignClass<t>(TryParseDelegate<t> del) { TryParser<t>.parser = del; } public static bool TryParse(string Value, out T Result) { if (parser == null) { Result = default(T); return false; } return parser(Value, out Result); } } private static bool TryParseEnum<t>(this string Value, out t result) { try { object temp = Enum.Parse(typeof(t), Value, true); if (temp is t) { result = (t)temp; return true; } } catch { } result = default(t); return false; } private static MethodInfo EnumTryParseMethod; private static TryParseDelegate<t> GetEnumTryParse<t>() { Type type = typeof(t); if (EnumTryParseMethod == null) { var methods = typeof(Enum).GetMethods( BindingFlags.Public | BindingFlags.Static); foreach (var method in methods) if (method.Name == "TryParse" && method.IsGenericMethodDefinition && method.GetParameters().Length == 2 && method.GetParameters()[0].ParameterType == typeof(string)) { EnumTryParseMethod = method; break; } } var result = Delegate.CreateDelegate( typeof(TryParseDelegate<t>), EnumTryParseMethod.MakeGenericMethod(type), false) as TryParseDelegate<t>; if (result == null) return TryParseEnum<t>; else return result; } private static bool TryParseNullable<t>(string Value, out t? Result) where t: struct { t temp; if (TryParser<t>.TryParse(Value, out temp)) { Result = temp; return true; } else { Result = null; return false; } } 

一般types你不能这样做。

你可以做的是创build一个接口ITryParsable并将其用于实现此接口的自定义types。

我想,虽然你打算用这种基本types如intDateTime 。 你不能改变这些types来实现新的接口。

受Charlie Brown发布的解决scheme的启发,我使用reflection创build了一个通用的TryParse,可选地输出parsing值:

 /// <summary> /// Tries to convert the specified string representation of a logical value to /// its type T equivalent. A return value indicates whether the conversion /// succeeded or failed. /// </summary> /// <typeparam name="T">The type to try and convert to.</typeparam> /// <param name="value">A string containing the value to try and convert.</param> /// <param name="result">If the conversion was successful, the converted value of type T.</param> /// <returns>If value was converted successfully, true; otherwise false.</returns> public static bool TryParse<T>(string value, out T result) where T : struct { var tryParseMethod = typeof(T).GetMethod("TryParse", BindingFlags.Static | BindingFlags.Public, null, new [] { typeof(string), typeof(T).MakeByRefType() }, null); var parameters = new object[] { value, null }; var retVal = (bool)tryParseMethod.Invoke(null, parameters); result = (T)parameters[1]; return retVal; } /// <summary> /// Tries to convert the specified string representation of a logical value to /// its type T equivalent. A return value indicates whether the conversion /// succeeded or failed. /// </summary> /// <typeparam name="T">The type to try and convert to.</typeparam> /// <param name="value">A string containing the value to try and convert.</param> /// <returns>If value was converted successfully, true; otherwise false.</returns> public static bool TryParse<T>(string value) where T : struct { T throwaway; var retVal = TryParse(value, out throwaway); return retVal; } 

它可以这样调用:

 string input = "123"; decimal myDecimal; bool myIntSuccess = TryParse<int>(input); bool myDecimalSuccess = TryParse<decimal>(input, out myDecimal); 

更新:
还要感谢YotaXP的解决scheme,我真的很喜欢,我创build了一个不使用扩展方法但仍然有单例的版本,最大限度地减less了reflection的需要:

 /// <summary> /// Provides some extra parsing functionality for value types. /// </summary> /// <typeparam name="T">The value type T to operate on.</typeparam> public static class TryParseHelper<T> where T : struct { private delegate bool TryParseFunc(string str, out T result); private static TryParseFunc tryParseFuncCached; private static TryParseFunc tryParseCached { get { return tryParseFuncCached ?? (tryParseFuncCached = Delegate.CreateDelegate(typeof(TryParseFunc), typeof(T), "TryParse") as TryParseFunc); } } /// <summary> /// Tries to convert the specified string representation of a logical value to /// its type T equivalent. A return value indicates whether the conversion /// succeeded or failed. /// </summary> /// <param name="value">A string containing the value to try and convert.</param> /// <param name="result">If the conversion was successful, the converted value of type T.</param> /// <returns>If value was converted successfully, true; otherwise false.</returns> public static bool TryParse(string value, out T result) { return tryParseCached(value, out result); } /// <summary> /// Tries to convert the specified string representation of a logical value to /// its type T equivalent. A return value indicates whether the conversion /// succeeded or failed. /// </summary> /// <param name="value">A string containing the value to try and convert.</param> /// <returns>If value was converted successfully, true; otherwise false.</returns> public static bool TryParse(string value) { T throwaway; return TryParse(value, out throwaway); } } 

像这样调用它:

 string input = "987"; decimal myDecimal; bool myIntSuccess = TryParseHelper<int>.TryParse(input); bool myDecimalSuccess = TryParseHelper<decimal>.TryParse(input, out myDecimal); 

这样的事情呢?

http://madskristensen.net/post/Universal-data-type-checker.aspx ( 存档 )

 /// <summary> /// Checks the specified value to see if it can be /// converted into the specified type. /// <remarks> /// The method supports all the primitive types of the CLR /// such as int, boolean, double, guid etc. as well as other /// simple types like Color and Unit and custom enum types. /// </remarks> /// </summary> /// <param name="value">The value to check.</param> /// <param name="type">The type that the value will be checked against.</param> /// <returns>True if the value can convert to the given type, otherwise false. </returns> public static bool CanConvert(string value, Type type) { if (string.IsNullOrEmpty(value) || type == null) return false; System.ComponentModel.TypeConverter conv = System.ComponentModel.TypeDescriptor.GetConverter(type); if (conv.CanConvertFrom(typeof(string))) { try { conv.ConvertFrom(value); return true; } catch { } } return false; } 

这可以很容易地转换为一个通用的方法。

  public static T Is<T>(this string input) { if (string.IsNullOrEmpty(value)) return false; var conv = System.ComponentModel.TypeDescriptor.GetConverter(typeof(T)); if (conv.CanConvertFrom(typeof(string))) { try { conv.ConvertFrom(value); return true; } catch { } } return false; } 

当我想要做这件事情时,我不得不用艰难的方式来实现它。 给定T ,反映typeof(T)并寻找一个TryParseParse方法,如果你find它就调用它。

这是我的尝试。 我做了一个“练习”。 我试图使它与现有的“ Convert.ToX() ”类似,但是这个是扩展方法:

  public static bool TryParse<T>(this String str, out T parsedValue) { try { parsedValue = (T)Convert.ChangeType(str, typeof(T)); return true; } catch { parsedValue = default(T); return false; } } 

晚会晚了很多,但这是我想出来的。 没有例外,一次性(每种types)reflection。

 public static class Extensions { public static T? ParseAs<T>(this string str) where T : struct { T val; return GenericHelper<T>.TryParse(str, out val) ? val : default(T?); } public static T ParseAs<T>(this string str, T defaultVal) { T val; return GenericHelper<T>.TryParse(str, out val) ? val : defaultVal; } private static class GenericHelper<T> { public delegate bool TryParseFunc(string str, out T result); private static TryParseFunc tryParse; public static TryParseFunc TryParse { get { if (tryParse == null) tryParse = Delegate.CreateDelegate( typeof(TryParseFunc), typeof(T), "TryParse") as TryParseFunc; return tryParse; } } } } 

额外的类是必需的,因为在generics类中不允许扩展方法。 这允许简单的使用,如下所示,只有在第一次使用types时才会reflection。

 "5643".ParseAs<int>() 

正如你所说, TryParse不是一个接口的一部分。 它也不是任何给定基类的成员,因为它实际上是staticstatic函数不能是virtual 。 所以,编译器没有办法确保T实际上有一个叫TryParse的成员,所以这是行不通的。

正如@Mark所说的,你可以创build你自己的界面并使用自定义types,但是对于内置types来说,运气不佳。

这是另一种select。

我写了一个类,可以很容易地注册任何数量的TryParse处理程序。 它让我这样做:

 var tp = new TryParser(); tp.Register<int>(int.TryParse); tp.Register<decimal>(decimal.TryParse); tp.Register<double>(double.TryParse); int x; if (tp.TryParse("42", out x)) { Console.WriteLine(x); }; 

我得到42打印到控制台。

这个class是:

 public class TryParser { public delegate bool TryParseDelegate<T>(string s, out T result); private Dictionary<Type, Delegate> _tryParsers = new Dictionary<Type, Delegate>(); public void Register<T>(TryParseDelegate<T> d) { _tryParsers[typeof(T)] = d; } public bool Deregister<T>() { return _tryParsers.Remove(typeof(T)); } public bool TryParse<T>(string s, out T result) { if (!_tryParsers.ContainsKey(typeof(T))) { throw new ArgumentException("Does not contain parser for " + typeof(T).FullName + "."); } var d = (TryParseDelegate<T>)_tryParsers[typeof(T)]; return d(s, out result); } } 

这是一个“通用约束”的问题。 由于您没有特定的界面,因此除非您按照上一个答案的build议,否则您将被卡住。

有关这方面的文档,请查看以下链接:

http://msdn.microsoft.com/en-us/library/ms379564(VS.80).aspx

它告诉你如何使用这些约束,并应该给你更多的线索。

http://blogs.msdn.com/b/davidebb/archive/2009/10/23/using-c-dynamic-to-call-static-members.aspx借用;

当下面这个参考: 如何在dynamictypes的C#4.0中调用静态方法?

 using System; using System.Collections.Generic; using System.Dynamic; using System.Linq; using System.Reflection; namespace Utils { public class StaticMembersDynamicWrapper : DynamicObject { private Type _type; public StaticMembersDynamicWrapper(Type type) { _type = type; } // Handle static methods public override bool TryInvokeMember(InvokeMemberBinder binder, object[] args, out object result) { var methods = _type .GetMethods(BindingFlags.FlattenHierarchy | BindingFlags.Static | BindingFlags.Public) .Where(methodInfo => methodInfo.Name == binder.Name); var method = methods.FirstOrDefault(); if (method != null) { result = method.Invoke(null, args); return true; } result = null; return false; } } public static class StaticMembersDynamicWrapperExtensions { static Dictionary<Type, DynamicObject> cache = new Dictionary<Type, DynamicObject> { {typeof(double), new StaticMembersDynamicWrapper(typeof(double))}, {typeof(float), new StaticMembersDynamicWrapper(typeof(float))}, {typeof(uint), new StaticMembersDynamicWrapper(typeof(uint))}, {typeof(int), new StaticMembersDynamicWrapper(typeof(int))}, {typeof(sbyte), new StaticMembersDynamicWrapper(typeof(sbyte))} }; /// <summary> /// Allows access to static fields, properties, and methods, resolved at run-time. /// </summary> public static dynamic StaticMembers(this Type type) { DynamicObject retVal; if (!cache.TryGetValue(type, out retVal)) return new StaticMembersDynamicWrapper(type); return retVal; } } } 

并使用它如下:

  public static T? ParseNumeric<T>(this string str, bool throws = true) where T : struct { var statics = typeof(T).StaticMembers(); if (throws) return statics.Parse(str); T retval; if (!statics.TryParse(str, out retval)) return null; return retval; } 

我设法得到这样的东西

  var result = "44".TryParse<int>(); Console.WriteLine( "type={0}, value={1}, valid={2}", result.Value.GetType(), result.Value, result.IsValid ); 

这是我的代码

  public static class TryParseGeneric { //extend int public static dynamic TryParse<T>( this string input ) { dynamic runner = new StaticMembersDynamicWrapper( typeof( T ) ); T value; bool isValid = runner.TryParse( input, out value ); return new { IsValid = isValid, Value = value }; } } public class StaticMembersDynamicWrapper : DynamicObject { private readonly Type _type; public StaticMembersDynamicWrapper( Type type ) { _type = type; } // Handle static properties public override bool TryGetMember( GetMemberBinder binder, out object result ) { PropertyInfo prop = _type.GetProperty( binder.Name, BindingFlags.FlattenHierarchy | BindingFlags.Static | BindingFlags.Public ); if ( prop == null ) { result = null; return false; } result = prop.GetValue( null, null ); return true; } // Handle static methods public override bool TryInvokeMember( InvokeMemberBinder binder, object [] args, out object result ) { var methods = _type .GetMethods( BindingFlags.FlattenHierarchy | BindingFlags.Static | BindingFlags.Public ).Where( methodInfo => methodInfo.Name == binder.Name ); var method = methods.FirstOrDefault(); if ( method == null ) { result = null; return false; } result = method.Invoke( null, args ); return true; } } 

StaticMembersDynamicWrapper改编自David Ebbo的文章 (这是抛出一个AmbiguousMatchException)

 public static T Get<T>(string val) { return (T) TypeDescriptor.GetConverter(typeof (T)).ConvertFromInvariantString(val); } 

从XDocument获取后代的版本。

 public static T Get<T>(XDocument xml, string descendant, T @default) { try { var converter = TypeDescriptor.GetConverter(typeof (T)); if (converter != null) { return (T) converter.ConvertFromString(xml.Descendants(descendant).Single().Value); } return @default; } catch { return @default; } }