c# - toparse - tryparse syntax




TryParse genérico (13)

Aqui está outra opção.

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

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

Eu tenho 42 impressos no console.

A aula é:

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

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

Como você disse, o TryParse não faz parte de uma interface. Também não é membro de nenhuma classe 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 é 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<T>(this String str, out T parsedValue)
    {
        try
        {
            parsedValue = (T)Convert.ChangeType(str, typeof(T));
            return true;
        }

        catch { parsedValue = default(T); return false; }
    }

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.


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

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

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

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

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

/// <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;
}

Pode ser chamado assim:

string input = "123";
decimal myDecimal;

bool myIntSuccess = TryParse<int>(input);
bool myDecimalSuccess = TryParse<decimal>(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 métodos de extensão mas ainda tem um singleton, minimizando a necessidade de fazer reflexões:

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

Chame assim:

string input = "987";
decimal myDecimal;

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

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


Que tal algo como isso?

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

/// <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;
  }

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

 public static bool Is<T>(this string value)
 {
    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;
}

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

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

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

"5643".ParseAs<int>()

Uma versão para obter descendentes do 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;
    }
}

Você deve usar a classe 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);
    }
}

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.


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






tryparse