Универсальный Метод Tryparse
Я пытаюсь создать универсальное расширение, которое использует 'TryParse', чтобы проверить, является ли строка заданным типом:
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;
}
он работает довольно хорошо, но я думаю, что с помощью исключения в этом смысле не кажутся мне правильными.
Update2:
изменено для передачи типа, а не использовать дженерики:
public static bool Is(this string input, Type targetType)
{
try
{
TypeDescriptor.GetConverter(targetType).ConvertFromString(input);
return true;
}
catch
{
return false;
}
}
18 ответов:
вы должны использовать 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);
использование try / catches для управления потоком-это ужасная политика. Бросок исключения приводит к задержкам производительности, в то время как среда выполнения работает вокруг исключения. Вместо этого проверьте данные перед преобразованием.
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, вы можете использовать отражение и сделать это следующим образом:
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}); }
Это использует статический конструктор для каждого универсального типа, поэтому он должен выполнять только дорогостоящую работу при первом вызове его для данного типа. Он обрабатывает все типы в пространстве имен system, которые имеют методы TryParse. Он также работает с nullable версиями каждого из них (которые являются структурами), за исключением перечислений.
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; } }
вы не можете сделать это на общих видах.
что вы могли бы сделать, это создать интерфейс ITryParsable и использовать его для пользовательских типов, которые реализуют этот интерфейс.
Я думаю, что вы собираетесь использовать это с основными типами, такими как
intиDateTime. Вы не можете изменить эти типы для реализации новых интерфейсов.
вдохновленный решением, опубликованным здесь Чарли Брауном, я создал общий TryParse, используя отражение, которое необязательно выводит анализируемое значение:
/// <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, которое мне очень нравится, я создал версию, которая не использует методы расширения, но все еще имеет синглтон, сводя к минимуму необходимость делать отражение:/// <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)искатьTryParseилиParseметод, вызывая его, если вы его нашли.
Это моя попытка. Я сделал это как"упражнение". Я попытался сделать его таким же похожим на использование, как и существующий"преобразовать.ToX () " - одни etc. Но это один метод расширения:
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; } }
совсем немного опоздал на вечеринку, но вот что я придумал. Без исключений, однократное (по типу) отражение.
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; } } } }дополнительный класс необходим, потому что методы расширения не разрешены внутри универсальных классов. Это позволяет просто использовать, как показано ниже, и только попадает отражение при первом использовании типа.
"5643".ParseAs<int>()
вот еще один вариант.
Я написал класс, который легко зарегистрировать любое количество
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печать на консоль.класс:
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); } }
как ты и сказал,
TryParseне является частью интерфейса. Он также не является членом любого базового класса, так как это на самом делеstaticиstaticфункции не могут бытьvirtual. Таким образом, компилятор не может гарантировать, чтоTна самом деле есть элемент под названиемTryParse, Так это не работает.как сказал @Mark, вы можете создать свой собственный интерфейс и использовать пользовательские типы, но вам не повезло со встроенными типами.
Это вопрос общих ограничений. Поскольку у вас нет определенного интерфейса, вы застряли, если не будете следовать предложениям предыдущего ответа.
для получения документации по этому вопросу, проверьте следующую ссылку:
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
когда по этой ссылке: как вызвать статический метод в 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 адаптирован из Дэвида Эббо статьи (он бросал 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; } }