TryParse genérico

196

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?

Atualizar:

Usando as respostas abaixo, criei:

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;
    }
}
Piers Myers
fonte
1
Eu acho que, neste caso geral, você terá que lidar com a exceção kludge. você pode adicionar casos para verificar itens como duplos ou ints e, em seguida, usar os métodos TryParse específicos, mas ainda precisará recorrer a ele para detectar outros tipos.
luke
1
O uso do genérico é desnecessário. Basta passar o Type como parâmetro. public static bool Is (esta entrada de sequência, digite targetType). Dessa forma, chamando parece um pouco mais bonita: x.Is (typeof (int)) CONTRA a x.Is <int> ()
mikesigs
2
Existe um método IsValid no conversor para você verificar se a conversão terá problemas. Eu usei o método abaixo e parece funcionar bem. protected Boolean TryParse<T>(Object value, out T result) { result = default(T); var convertor = TypeDescriptor.GetConverter(typeof(T)); if (convertor == null || !convertor.IsValid(value)) { return false; } result = (T)convertor.ConvertFrom(value); return true; }
precisa saber é o seguinte
@CastroXXL Obrigado por mostrar interesse nesta questão, no entanto, seu método não funcionaria, pois eu queria verificar se o valor da string era de um determinado tipo e não um objeto, embora seu método seja útil para tipos de objetos (mas seria tem que envolver o ConvertFrom(value)método em uma try-catchquadra para pegar as exceções.
Piers Myers
2
Você deve verificar se (targetType == null) porque o primeiro uso dele no seu código pode ser lançado, mas essa exceção seria engolida por sua captura.
Nick Strupat

Respostas:

183

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);
    }
}
Lucas
fonte
3
Desculpe ressuscitar, mas GetConverter retorna nulo? Eu acho que se isso acontecesse, provavelmente uma exceção deveria ser lançada em vez de essencialmente falhar silenciosamente e retornar outra coisa. Quando tentei na minha própria classe (na qual não defini um conversor de tipo), recebi um conversor de GetConverter, mas o ConvertFromString lançou uma NotSupportedException.
User420667
3
@ user420667, acredito que você deve verificar o resultado de CanConvertFrom (typeof (string)) antes de tentar converter da string. O TypeConverter pode não suportar conversão de string.
Reuben de Bond
3
Você pode adicionar if (typeof (T) .IsEnum) {return (T) Enum.Parse (typeof (T), input); } [como um atalho bastante genérico para todos os tipos de enum] antes de obter o conversor. Suponho que depende da frequência com que você fará os tipos Enum, em oposição aos tipos mais complexos.
Jesse Chisholm
10
Não entendo por que isso é marcado como resposta e muito votado quando não implementa o que foi solicitado: um Try Parse genérico . O principal objetivo dos métodos TryParse é que eles não gerem exceções ao tentar executar a análise e tenham um impacto muito menor no desempenho quando a análise falhar e esta solução falhar em fornecer exatamente isso.
Florin Dumitrescu
2
Um problema com isso é que, se T é um int e a entrada é maior que int.MaxValue, ele lançará um System.Exception com System.OverFlowException como exceção interna. Portanto, se você espera uma OverflowException, não a receberá, a menos que interrogue a exceção lançada. O motivo é que ConvertFromString lança uma OverflowException e, em seguida, a conversão para T lança uma System.Exception.
Trevor
78

Também precisei de um TryParse genérico recentemente. Aqui está o que eu criei;

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);
Charlie Brown
fonte
3
Apenas deparei com este post novamente meses depois e percebemos ao usá-lo novamente que o método não pode inferir Tdo manipulador, e precisamos especificar explicitamente Tquando o chamamos. Estou curioso, por que não pode inferir T?
perfil completo de Nick Strupat
25
Por que você gostaria de usar esta função? Se você sabe qual função chamar para analisar o valor, por que não chamá-lo diretamente? Ele já sabe o tipo de entrada correto e não há necessidade de genéricos. Essa solução não funcionaria para tipos sem um TryParseHandler.
Xxbbcc
2
@xxbbcc: Eu gostaria de usar esta função porque o TryParse retorna um booleano que indica se a análise foi bem-sucedida. Retorna seu valor analisado através de um parâmetro de saída. Às vezes, eu só quero fazer algo assim SomeMethod(TryParse<int>(DollarTextbox.Text, int.TryParse))sem criar uma variável de saída para capturar o resultado int.TryParse. No entanto, eu concordo com o sentimento de Nick sobre ter a função inferir o tipo.
Walter Stabosz 13/09/2013
1
Método muito eficiente. Altamente recomendado.
Vladimir Kocjancic
3
Eu recomendaria um valor padrão como um terceiro parâmetro. Isso corrige o problema em que T não pode ser inferido. Além disso, permite decidir qual o valor que eles querem se o valor da string for inválido. Por exemplo, -1 pode significar inválido. public estático T TryParse <T> (valor da sequência, TryParseHandler <T> manipulador, T defaultValue)
Rhyous
33

Usar try / catchs para controle de fluxo é 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 da conversão.

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);
Sites de reflexão
fonte
2
Estou recebendo um aviso do Resharper que converter != nullé sempre verdadeiro, para que possa ser removido do código.
ErikE
5
@ErikE Eu nem sempre confio nesses avisos do ReSharper. Frequentemente, eles não conseguem ver o que acontece no tempo de execução.
ProfK 27/10
1
Não @ProfK MSDN não dizer que ele pode retornar nulo msdn.microsoft.com/en-us/library/ewtxwhzx.aspx
Danio
@danio Eu estava apenas compartilhando minha experiência com esses avisos de R # em geral. Eu certamente não sugeri que estava errado neste caso.
ProfK 10/03/19
14

Se você estiver usando o TryParse, poderá usar a reflexão e fazer o seguinte:

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});
}
Joseph Sturtevant
fonte
Isso é muito legal e se livra das exceções que eu não gostei de qualquer maneira. Ainda um pouco complicado, no entanto.
Piers Myers
6
Solução agradável, mas qualquer resposta que envolva reflexão (especialmente em um método utilitário que possa ser chamado facilmente de um loop interno) precisa de um aviso sobre desempenho. Veja: stackoverflow.com/questions/25458/how-costly-is-net-reflection
Patrick M
Suspiro. Portanto, as opções são (1) usar exceções para controle de fluxo de código, (2) usar reflexão com seus custos de velocidade. Concordo com @PiersMyers - nenhuma das opções é ideal. Ainda bem que os dois trabalham. :)
Jesse Chisholm
Eu acho que você pode substituir o Type.GetType(string.Format(...))com type.MakeByRefType().
Drew Noakes
3
o método precisa ser refletido apenas uma vez por tipo, não uma vez por chamada. se você criar uma classe genérica com uma variável de membro estática, poderá reutilizar a saída da primeira reflexão.
Andrew Hill
7

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 chama em um determinado tipo. Ele lida com todos os tipos no espaço para nome do sistema que possuem métodos TryParse. Também funciona com versões anuláveis ​​de cada uma delas (que são estruturas), exceto para enumerações.

    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;
        }
    }
Bryce Wagner
fonte
6

Que tal algo como isso?

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

/// <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 para um método genérico com bastante facilidade.

 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;
}
Prumo
fonte
Importa se você retorna true do bloco try ou retorna false do bloco catch? Suponho que não, mas ainda acho que usar exceções dessa maneira me parece errado ...
Piers Myers
3
Não importa se você retorna do bloco catch, é o mesmo. btw. Normalmente, é ruim ter uma cláusula catch genérica: catch { }. No entanto, nesse caso, não há alternativa, porque o .NET BaseNumberConverterlança a Exceptionclasse base em caso de erro de conversão. Isso é muito lamentável. De fato, ainda existem alguns lugares onde esse tipo de base é lançado. Espero que a Microsoft os corrija em uma versão futura do framework.
Steven
Obrigado Steven, não poderia ter dito melhor.
Bob
Não adianta o resultado da conversão: o código é redundante.
BillW
4

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

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

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

Mark Byers
fonte
1
Gostaria de saber se isso funcionaria usando a palavra-chave dinâmica em .net 4?
Pierre-Alain Vigeant
@ Pierre: Isso não funcionará por padrão em C # com a dynamicpalavra - chave, porque não funcionará na digitação estática. Você pode criar seu próprio objeto dinâmico que pode lidar com isso, mas não é o padrão.
Steven
4

Inspirado na solução postada aqui por Charlie Brown, criei um TryParse genérico usando 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);

Atualização:
Também graças à solução do YotaXP de que realmente gosto, criei uma versão que não usa métodos de extensão, mas ainda possui um singleton, minimizando a necessidade de refletir:

/// <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);
Jez
fonte
3

Um pouco tarde para a festa, mas aqui está o que eu criei. Sem exceções, reflexão única (por tipo).

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 métodos de extensão não são permitidos dentro de classes genéricas. Isso permite o uso simples, como mostrado abaixo, e apenas atinge a reflexão na primeira vez que um tipo é usado.

"5643".ParseAs<int>()
YotaXP
fonte
3

Aqui está outra opção.

Eu escrevi uma classe que facilita o registro de qualquer número de TryParsemanipuladores. 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);
};

Sou 42impresso no console.

A turma é:

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);
    }
}
Enigmatividade
fonte
Eu gosto disso, mas como você faria isso sem genéricos. Caso de uso sendo reflexão, é claro.
Sinaesthetic
Eu adicionei um método sobrecarregado que faz alguns truques de reflexão. Se houver uma maneira mais elegante de resolvê-lo, estou de olho lol gist.github.com/dasjestyr/90d8ef4dea179a6e08ddd85e0dacbc94
Sinaesthetic
2

Quando eu queria fazer quase exatamente isso, tive que implementá-lo da maneira mais difícil, considerando a reflexão. Dado T, refletir sobre typeof(T)e olhar para uma TryParseou Parsemétodo, invocando-se que você o encontrou.

JSB ձոգչ
fonte
Isto é o que eu ia sugerir.
Steven Evers
2

Esta é a minha tentativa. Eu fiz isso como um "exercício". Tentei torná-lo tão semelhante ao uso como os existentes " 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; }
    }
W0lfw00ds
fonte
A principal desvantagem disso TypeConverter.ConvertFrom()é que a classe de origem precisa fornecer a conversão de tipo, o que geralmente significa que você não pode suportar a conversão para tipos personalizados.
Ian Goldby
1

Como você disse, TryParsenão faz parte de uma interface. Também não é membro de nenhuma classe base, pois é realmente statice staticfunções não podem ser virtual. Portanto, o compilador não tem como garantir que Trealmente tenha um membro chamado TryParse, portanto isso não funciona.

Como o @Mark disse, você pode criar sua própria interface e usar tipos personalizados, mas não tem sorte com os tipos internos.

Donnie
fonte
1
public static class Primitive
{
    public static DateTime? TryParseExact(string text, string format, IFormatProvider formatProvider = null, DateTimeStyles? style = null)
    {
        DateTime result;
        if (DateTime.TryParseExact(text, format, formatProvider, style ?? DateTimeStyles.None, out result))
            return result;
        return null;
    }

    public static TResult? TryParse<TResult>(string text) where TResult : struct
    {
        TResult result;
        if (Delegates<TResult>.TryParse(text, out result))
            return result;
        return null;
    }

    public static bool TryParse<TResult>(string text, out TResult result) => Delegates<TResult>.TryParse(text, out result);

    public static class Delegates<TResult>
    {
        private delegate bool TryParseDelegate(string text, out TResult result);

        private static readonly TryParseDelegate _parser = (TryParseDelegate)Delegate.CreateDelegate(typeof(TryParseDelegate), typeof(TResult), "TryParse");

        public static bool TryParse(string text, out TResult result) => _parser(text, out result);
    }
}
ChaosPandion
fonte
0

Esta é uma questão de 'restrições genéricas'. Como você não possui uma interface específica, fica paralisado, a menos que 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.

Simon
fonte
0

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 chamar o método estático no C # 4.0 com tipo dinâmico?

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

E use-o da seguinte maneira:

  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;
  }
GregC
fonte
0

Eu consegui algo que funciona assim

    var result = "44".TryParse<int>();

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

Aqui está o meu código

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

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


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

Com o TypeDescriptoruso da classe de TryParsemaneira relacionada:

public static bool TryParse<T>(this string input, out T parsedValue)
{
    parsedValue = default(T);
    try
    {
        var converter = TypeDescriptor.GetConverter(typeof(T));
        parsedValue = (T)converter.ConvertFromString(input);
        return true;
    }
    catch (NotSupportedException)
    {
        return false;
    }
}
Vova
fonte
Embora esse código possa resolver a questão, incluindo uma explicação de como e por que isso resolve o problema realmente ajudaria a melhorar a qualidade da sua postagem e provavelmente resultará em mais votos positivos. Lembre-se de que você está respondendo à pergunta dos leitores no futuro, não apenas à pessoa que está perguntando agora. Por favor edite sua resposta para adicionar explicações e dar uma indicação do que limitações e premissas se aplicam.
bip duplo
0

Usando as informações acima, é isso que desenvolvi. É possível converter o objeto diretamente, caso contrário, ele será convertido em uma string e chamará o método TryParse para o tipo de objeto desejado.

Eu cache os métodos em um dicionário, pois cada um deles é encontrado para reduzir a carga de busca do método.

É possível testar se o objeto pode ser convertido diretamente no tipo de destino, o que reduziria ainda mais a parte de conversão da string. Mas vou deixar isso de fora por enquanto.

    /// <summary>
    /// Used to store TryParse converter methods
    /// </summary>
    private static readonly Dictionary<Type, MethodInfo> TypeConverters = new Dictionary<Type, MethodInfo>();

    /// <summary>
    /// Attempt to parse the input object to the output type
    /// </summary>
    /// <typeparam name="T">output type</typeparam>
    /// <param name="obj">input object</param>
    /// <param name="result">output result on success, default(T) on failure</param>
    /// <returns>Success</returns>
    public static bool TryParse<T>([CanBeNull] object obj, out T result)
    {
        result = default(T);

        try
        {
            switch (obj)
            {
                // don't waste time on null objects
                case null: return false;

                // if the object is already of type T, just return the value
                case T val:
                    result = val;
                    return true;
            }

            // convert the object into type T via string conversion
            var input = ((obj as string) ?? obj.ToString()).Trim();
            if (string.IsNullOrEmpty(input)) return false;

            var type = typeof (T);
            Debug.WriteLine($"Info: {nameof(TryParse)}<{type.Name}>({obj.GetType().Name}=\"{input}\")");

            if (! TypeConverters.TryGetValue(type, out var method))
            {
                // get the TryParse method for this type
                method = type.GetMethod("TryParse",
                    new[]
                    {
                        typeof (string),
                        Type.GetType($"{type.FullName}&")
                    });

                if (method is null)
                    Debug.WriteLine($"FAILED: Cannot get method for {type.Name}.TryParse()");

                // store it so we don't have to do this again
                TypeConverters.Add(type, method);
            }

            // have to keep a reference to parameters if you want to get the returned ref value
            var parameters = new object[] {input, null};
            if ((bool?) method?.Invoke(null, parameters) == true)
            {
                result = (T) parameters[1];
                return true;
            }                
        }
        catch (Exception ex)
        {
            Debug.WriteLine(ex);
        }

        return false;
    }
B Duffy
fonte
Eu tive que adicionar outra função para suportar enumerações. Parece que a análise de enum requer o atributo "where T: struct", e eu quero que isso funcione em qualquer coisa conversível. (provavelmente deve adicionar um atributo conversível ao tipo). No entanto, algumas das sugestões a seguir parecem mais simples (portanto, melhores).
B Duffy
0

Coloquei um monte de idéias aqui e acabei com uma solução muito curta.

Este é um método de extensão em uma string

enter code here

Fiz isso com a mesma pegada dos métodos TryParse nos tipos numéricos

    /// <summary>
    /// string.TryParse()
    /// 
    /// This generic extension method will take a string
    ///     make sure it is not null or empty
    ///     make sure it represents some type of number e.g. "123" not "abc"
    ///     It then calls the appropriate converter for the type of T
    /// </summary>
    /// <typeparam name="T">The type of the desired retrunValue e.g. int, float, byte, decimal...</typeparam>
    /// <param name="targetText">The text to be converted</param>
    /// <param name="returnValue">a populated value of the type T or the default(T) value which is likely to be 0</param>
    /// <returns>true if the string was successfully parsed and converted otherwise false</returns>
    /// <example>
    /// float testValue = 0;
    ///  if ( "1234".TryParse<float>( out testValue ) )
    ///  {
    ///      doSomethingGood();
    ///  }
    ///  else
    ///  {
    ///      handleTheBadness();
    ///  }
    /// </example>
    public static bool TryParse<T>(this string targetText, out T returnValue )
    {
        bool returnStatus = false;

        returnValue = default(T);

        //
        // make sure the string is not null or empty and likely a number...
        // call whatever you like here or just leave it out - I would
        // at least make sure the string was not null or empty  
        //
        if ( ValidatedInputAnyWayYouLike(targetText) )
        {

            //
            // try to catch anything that blows up in the conversion process...
            //
            try
            {
                var type = typeof(T);
                var converter = TypeDescriptor.GetConverter(type);

                if (converter != null && converter.IsValid(targetText))
                {
                    returnValue = (T)converter.ConvertFromString(targetText);
                    returnStatus = true;
                }

            }
            catch
            {
                // just swallow the exception and return the default values for failure
            }

        }

        return (returnStatus);

    }

'' '

JD Hicks
fonte
float testValue = 0; if ("1234" .TryParse <float> (fora testValue)) {doSomethingGood (); } else {handleTheBadness (); }
JD Hicks
0

T.TryParse ... por quê?

Não vejo nenhum benefício em ter essa TryParsefunção genérica . Existem muitas estratégias diferentes para analisar e converter dados entre tipos diferentes, com possível comportamento conflitante. Como essa função poderia saber qual estratégia escolher de maneira livre de contexto?

  • classes com funções TryParse dedicadas podem ser chamadas
  • classes com funções de análise dedicadas podem ser agrupadas com resultado try-catch e bool
  • classes com sobrecargas de operador, como você os deixaria lidar com a análise?
  • descritores de tipo são incorporados usando Convert.ChangeType. Essa API é personalizável em tempo de execução. Sua função requer comportamento padrão ou permite personalização?
  • você deve permitir que qualquer estrutura de mapeamento tente analisar por você?
  • como você lidaria com os conflitos acima?

fonte
-2

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