TryParse genérico

Eu estou tentando criar uma extensão genérica que usa ‘TryParse’ para verificar se uma seqüência de caracteres é um determinado tipo:

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

isso não será compilado, pois não pode resolver o símbolo ‘TryParse’

Pelo que entendi, ‘TryParse’ não faz parte de nenhuma interface.

Isso é possível fazer em tudo?

Atualizar:

Usando as respostas abaixo eu inventei:

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

Funciona muito bem, mas acho que usar exceções dessa maneira não parece certo para mim.

Update2:

Modificado para passar o tipo em vez de usar genéricos:

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

    Você deve usar a class TypeDescriptor :

     public static T Convert(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); } } 

    Eu também precisei de um TryParse genérico recentemente. Aqui está o que eu inventei;

     public static T? TryParse(string value, TryParseHandler 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(string value, out T result); 

    Então é simplesmente uma questão de chamar assim:

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

    Usar try / catches para controle de stream é uma política terrível. Lançar uma exceção causa atrasos no desempenho enquanto o tempo de execução trabalha em torno da exceção. Em vez disso, valide os dados antes de converter.

     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); 

    Se você estiver usando o TryParse, você pode usar o reflexo e fazer assim:

     public static bool Is(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}); } 

    Isso usa um construtor estático para cada tipo genérico, portanto, ele só precisa fazer o trabalho caro na primeira vez em que você o chamar em um determinado tipo. Ele lida com todos os tipos no namespace do sistema que possuem methods TryParse. Ele também funciona com versões anuláveis ​​de cada uma delas (que são structs), exceto para enumerações.

      public static bool TryParse(this string Value, out t result) { return TryParser.TryParse(Value.SafeTrim(), out result); } private delegate bool TryParseDelegate(string value, out t result); private static class TryParser { private static TryParseDelegate parser; // Static constructor: static TryParser() { Type t = typeof(T); if (t.IsEnum) AssignClass(GetEnumTryParse()); else if (t == typeof(bool) || t == typeof(bool?)) AssignStruct(bool.TryParse); else if (t == typeof(byte) || t == typeof(byte?)) AssignStruct(byte.TryParse); else if (t == typeof(short) || t == typeof(short?)) AssignStruct(short.TryParse); else if (t == typeof(char) || t == typeof(char?)) AssignStruct(char.TryParse); else if (t == typeof(int) || t == typeof(int?)) AssignStruct(int.TryParse); else if (t == typeof(long) || t == typeof(long?)) AssignStruct(long.TryParse); else if (t == typeof(sbyte) || t == typeof(sbyte?)) AssignStruct(sbyte.TryParse); else if (t == typeof(ushort) || t == typeof(ushort?)) AssignStruct(ushort.TryParse); else if (t == typeof(uint) || t == typeof(uint?)) AssignStruct(uint.TryParse); else if (t == typeof(ulong) || t == typeof(ulong?)) AssignStruct(ulong.TryParse); else if (t == typeof(decimal) || t == typeof(decimal?)) AssignStruct(decimal.TryParse); else if (t == typeof(float) || t == typeof(float?)) AssignStruct(float.TryParse); else if (t == typeof(double) || t == typeof(double?)) AssignStruct(double.TryParse); else if (t == typeof(DateTime) || t == typeof(DateTime?)) AssignStruct(DateTime.TryParse); else if (t == typeof(TimeSpan) || t == typeof(TimeSpan?)) AssignStruct(TimeSpan.TryParse); else if (t == typeof(Guid) || t == typeof(Guid?)) AssignStruct(Guid.TryParse); else if (t == typeof(Version)) AssignClass(Version.TryParse); } private static void AssignStruct(TryParseDelegate del) where t: struct { TryParser.parser = del; if (typeof(t).IsGenericType && typeof(t).GetGenericTypeDefinition() == typeof(Nullable<>)) { return; } AssignClass(TryParseNullable); } private static void AssignClass(TryParseDelegate del) { TryParser.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(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 GetEnumTryParse() { 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), EnumTryParseMethod.MakeGenericMethod(type), false) as TryParseDelegate; if (result == null) return TryParseEnum; else return result; } private static bool TryParseNullable(string Value, out t? Result) where t: struct { t temp; if (TryParser.TryParse(Value, out temp)) { Result = temp; return true; } else { Result = null; return false; } } 

    Você não pode fazer isso em tipos gerais.

    O que você poderia fazer é criar uma interface ITryParsable e usá-la para tipos personalizados que implementam essa interface.

    Eu acho que você pretende usar isso com tipos básicos como int e DateTime . Você não pode alterar esses tipos para implementar novas interfaces.

    Inspirado na solução postada aqui por Charlie Brown, criei um TryParse genérico usando a reflection que, opcionalmente, gera o valor analisado:

     ///  /// 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. ///  /// The type to try and convert to. /// A string containing the value to try and convert. /// If the conversion was successful, the converted value of type T. /// If value was converted successfully, true; otherwise false. public static bool TryParse(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; } ///  /// 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. ///  /// The type to try and convert to. /// A string containing the value to try and convert. /// If value was converted successfully, true; otherwise false. public static bool TryParse(string value) where T : struct { T throwaway; var retVal = TryParse(value, out throwaway); return retVal; } 

    Pode ser chamado assim:

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

    Atualizar:
    Também graças à solução do YotaXP que eu realmente gosto, eu criei uma versão que não usa methods de extensão mas ainda tem um singleton, minimizando a necessidade de fazer reflexões:

     ///  /// Provides some extra parsing functionality for value types. ///  /// The value type T to operate on. public static class TryParseHelper 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); } } ///  /// 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. ///  /// A string containing the value to try and convert. /// If the conversion was successful, the converted value of type T. /// If value was converted successfully, true; otherwise false. public static bool TryParse(string value, out T result) { return tryParseCached(value, out result); } ///  /// 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. ///  /// A string containing the value to try and convert. /// If value was converted successfully, true; otherwise false. public static bool TryParse(string value) { T throwaway; return TryParse(value, out throwaway); } } 

    Chame assim:

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

    Que tal algo como isso?

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

     ///  /// Checks the specified value to see if it can be /// converted into the specified type. ///  /// 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. ///  ///  /// The value to check. /// The type that the value will be checked against. /// True if the value can convert to the given type, otherwise false.  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; } 

    Isso pode ser convertido em um método genérico facilmente.

      public static T Is(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; } 

    Quando eu queria fazer quase exatamente isso, eu tive que implementar da maneira mais difícil, dada a reflection. Dado T , reflita em typeof(T) e procure por um método TryParse ou Parse , invocando-o se você o encontrou.

    Esta é minha tentativa. Eu fiz isso como um “exercício”. Eu tentei torná-lo tão parecido para usar como o existente ” Convert.ToX () ” -ones etc. Mas este é o método de extensão:

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

    Um pouco atrasado para a festa, mas aqui está o que eu fiz. Sem exceções, uma vez (por tipo) de reflection.

     public static class Extensions { public static T? ParseAs(this string str) where T : struct { T val; return GenericHelper.TryParse(str, out val) ? val : default(T?); } public static T ParseAs(this string str, T defaultVal) { T val; return GenericHelper.TryParse(str, out val) ? val : defaultVal; } private static class GenericHelper { 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; } } } } 

    A class extra é necessária porque os methods de extensão não são permitidos dentro de classs genéricas. Isso permite um uso simples, conforme mostrado abaixo, e só atinge a reflection na primeira vez em que um tipo é usado.

     "5643".ParseAs() 

    Aqui está outra opção.

    Eu escrevi uma class que facilita o registro de qualquer número de manipuladores TryParse . Isso me permite fazer isso:

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

    Eu tenho 42 impressos no console.

    A turma é:

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

    Como você disse, o TryParse não faz parte de uma interface. Também não é membro de nenhuma class base, uma vez que, na verdade, static funções static e static não podem ser virtual . Portanto, o compilador não tem como garantir que T realmente tenha um membro chamado TryParse , então isso não funciona.

    Como o @Mark disse, você poderia criar sua própria interface e usar tipos personalizados, mas você está sem sorte para os tipos internos.

    Esta é uma questão de “restrições genéricas”. Como você não tem uma interface específica, fica preso, a menos que você siga as sugestões da resposta anterior.

    Para documentação sobre isso, verifique o seguinte link:

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

    Ele mostra como usar essas restrições e deve fornecer mais algumas dicas.

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

    ao seguir esta referência: Como invocar o método estático no C # 4.0 com tipo dynamic?

     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 cache = new Dictionary { {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))} }; ///  /// Allows access to static fields, properties, and methods, resolved at run-time. ///  public static dynamic StaticMembers(this Type type) { DynamicObject retVal; if (!cache.TryGetValue(type, out retVal)) return new StaticMembersDynamicWrapper(type); return retVal; } } } 

    E use da seguinte forma:

      public static T? ParseNumeric(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; } 

    Eu consegui algo que funciona assim

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

    Aqui está meu código

      public static class TryParseGeneric { //extend int public static dynamic TryParse( 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; } } 

    O StaticMembersDynamicWrapper é adaptado do artigo de David Ebbo (estava lançando um AmbiguousMatchException)

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

    Uma versão para obter descendentes do XDocument.

     public static T Get(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; } }