TryParse generico


196

Sto cercando di creare un'estensione generica che utilizza "TryParse" per verificare se una stringa è un determinato tipo:

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

questo non verrà compilato in quanto non è possibile risolvere il simbolo 'TryParse'

A quanto ho capito, "TryParse" non fa parte di alcuna interfaccia.

È possibile farlo?

Aggiornare:

Usando le risposte qui sotto ho trovato:

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

    return true;
}

Funziona abbastanza bene ma penso che usare le eccezioni in quel modo non mi sembri giusto.

Update2:

Modificato per passare il tipo piuttosto che usare generici:

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

1
penso che in questo caso generale dovrai fare i conti con l'eccezione Kludge. potresti aggiungere casi per verificare cose come ints o doppi e quindi utilizzare i metodi TryParse specifici, ma dovrai comunque ricorrere a questo per catturare altri tipi.
Luca,

1
L'uso del generico non è necessario. Basta inserire il Tipo come parametro. public static bool Is (questo input stringa, digitare targetType). In questo modo chiamarlo sembra un po 'più bello: x.Is (typeof (int)) -VS- x.Is <int> ()
mikesigs

2
Esiste un metodo IsValid sul convertitore per verificare se la conversione avrà problemi. Ho usato il metodo seguente e sembra funzionare bene. 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; }
CastroXXL,

@CastroXXL Grazie per aver mostrato interesse per questa domanda, tuttavia il tuo metodo non funzionerebbe abbastanza poiché volevo verificare se il valore della stringa fosse di un certo tipo anziché di un oggetto, sebbene il tuo metodo sarebbe utile per i tipi di oggetto (ma devo avvolgere il ConvertFrom(value)metodo in un try-catchblocco per catturare le eccezioni.
Piers Myers,

2
Dovresti verificare se (targetType == null) perché il primo utilizzo di esso nel tuo codice potrebbe generare ma quell'eccezione verrebbe inghiottita dalla tua cattura.
Nick Strupat,

Risposte:


183

È necessario utilizzare la 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);
    }
}

3
Mi dispiace resuscitare, ma GetConverter restituisce null? Penso che se così fosse, probabilmente dovrebbe essere generata un'eccezione invece di fallire sostanzialmente in silenzio e restituire qualcos'altro. Quando l'ho provato sulla mia stessa classe (in cui non ho definito un typeconverter), ho ottenuto un convertitore da GetConverter, ma poi ConvertFromString ha lanciato NotSupportedException.
user420667

3
@ user420667, credo che dovresti controllare il risultato di CanConvertFrom (typeof (stringa)) prima di provare a convertire da stringa. TypeConverter potrebbe non supportare la conversione da stringa.
Reuben Bond il

3
È possibile aggiungere if (typeof (T) .IsEnum) {return (T) Enum.Parse (typeof (T), input); } [come collegamento abbastanza generico per tutti i tipi di Enum] prima di ottenere il convertitore. Suppongo che dipenda dalla frequenza con cui farai i tipi di Enum rispetto ai tipi più complessi.
Jesse Chisholm,

10
Non capisco perché questo sia contrassegnato come risposta e votato così tanto quando non implementa ciò che è stato richiesto: un generico Try Parse. Lo scopo principale dei metodi TryParse è che non generano eccezioni quando si tenta di eseguire l'analisi e hanno un impatto molto inferiore sulle prestazioni quando l'analisi non riesce e questa soluzione non riesce a fornire proprio questo.
Florin Dumitrescu,

2
Un problema di questo è che se T è un int e l'input è maggiore di int.MaxValue, genererà System.Exception w / System.OverFlowException come eccezione interna. Quindi, se ti aspetti una OverflowException, non la riceverai se non interroghi l'eccezione generata. Il motivo è che ConvertFromString genera una OverflowException e quindi il cast su T genera una System.Exception.
Trevor,

78

Di recente ho anche richiesto un TryParse generico. Ecco cosa mi è venuto in mente;

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

Quindi si tratta semplicemente di chiamare così:

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

3
Mi sono appena imbattuto in questo post mesi dopo e ho notato mentre lo utilizzavo di nuovo che il metodo non può inferire Tdal gestore e dobbiamo specificare esplicitamente Tquando lo chiamiamo. Sono curioso, perché non può inferire T?
Nick Strupat,

25
Perché vorresti usare questa funzione? Se sai quale funzione chiamare per analizzare il valore, perché non chiamarlo direttamente? Conosce già il giusto tipo di input e non c'è bisogno di generici. Questa soluzione non funzionerebbe per i tipi senza TryParseHandler.
xxbbcc,

2
@xxbbcc: vorrei usare questa funzione perché TryParse restituisce un valore booleano che indica se l'analisi ha avuto esito positivo. Restituisce il valore analizzato tramite un parametro di output. A volte voglio solo fare qualcosa del genere SomeMethod(TryParse<int>(DollarTextbox.Text, int.TryParse))senza creare una variabile di output per catturare il risultato int.TryParse. Tuttavia, sono d'accordo con il sentimento di Nick di avere la funzione inferire il tipo.
Walter Stabosz,

1
Metodo molto efficiente. Altamente raccomandato.
Vladimir Kocjancic,

3
Consiglierei un valore predefinito come terzo parametro. Ciò risolve il problema in cui T non può essere dedotto. Inoltre, consente di decidere quale valore desiderano se il valore della stringa non è valido. Ad esempio, -1 potrebbe significare non valido. public static T TryParse <T> (valore stringa, gestore TryParseHandler <T>, T defaultValue)
Rhyous

33

L'uso di try / catches per il controllo del flusso è una politica terribile. Il lancio di un'eccezione provoca ritardi nelle prestazioni mentre il runtime funziona attorno all'eccezione. Convalida invece i dati prima della conversione.

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

2
Ricevo un avviso Resharper che converter != nullè sempre vero, quindi può essere rimosso dal codice.
ErikE

5
@ErikE Non mi fido sempre di quegli avvisi ReSharper. Spesso non riescono a vedere cosa succede in fase di esecuzione.
ProfK,

1
@ProfK MSDN non dice che si può restituire null msdn.microsoft.com/en-us/library/ewtxwhzx.aspx
Danio

@danio Stavo solo condividendo la mia esperienza con tali avvisi R # in generale. Certamente non ho insinuato che fosse sbagliato in questo caso.
ProfK

14

Se sei impostato su TryParse, puoi usare reflection e farlo in questo modo:

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

Questo è molto bello e si sbarazza delle eccezioni che non mi sono piaciute comunque. Comunque un po 'contorto.
Piers Myers,

6
Bella soluzione, ma qualsiasi risposta che implichi una riflessione (specialmente in un metodo di utilità che potrebbe essere facilmente chiamato da un circuito interno) richiede una dichiarazione di non responsabilità in merito alle prestazioni. Vedi: stackoverflow.com/questions/25458/how-costly-is-net-reflection
Patrick M

Sospiro. Quindi le scelte sono (1) usare le eccezioni per il controllo del flusso di codice, (2) usare la riflessione con i suoi costi di velocità. Sono d'accordo con @PiersMyers - nessuna delle due scelte è l'ideale. Meno male che funzionano entrambi. :)
Jesse Chisholm il

Penso che puoi sostituire il Type.GetType(string.Format(...))con type.MakeByRefType().
Drew Noakes,

3
il metodo deve essere riflesso solo una volta per tipo, non una volta per chiamata. se rendi questa una classe generica con una variabile membro statica, puoi riutilizzare l'output del primo riflesso.
Andrew Hill,

7

Questo utilizza un costruttore statico per ogni tipo generico, quindi deve solo fare il lavoro costoso la prima volta che lo chiami su un determinato tipo. Gestisce tutti i tipi nello spazio dei nomi di sistema che hanno metodi TryParse. Funziona anche con versioni nullable di ognuna di quelle (che sono strutture) ad eccezione delle enumerazioni.

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

6

Che ne dici di questo?

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

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

Questo può essere convertito in un metodo generico abbastanza 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;
}

Importa se si restituisce true dal blocco try o si restituisce false dal blocco catch? Suppongo di no, ma penso ancora che usare le eccezioni in questo modo mi sembri sbagliato ...
Piers Myers,

3
Non importa se si ritorna dal blocco catch, questo è lo stesso. btw. Di solito è male avere una clausola catch generico: catch { }. Tuttavia, in questo caso non esiste alternativa, poiché .NET BaseNumberConvertergenera la Exceptionclasse di base in caso di errore di conversione. Questo è molto sfortunato. In effetti ci sono ancora alcuni posti dove viene lanciato questo tipo di base. Speriamo che Microsoft risolva questi problemi in una versione futura del framework.
Steven

Grazie Steven, non avrei potuto dirlo meglio.
Bob

Nessun uso fatto del risultato della conversione: il codice è ridondante.
BillW,

4

Non puoi farlo su tipi generali.

Quello che potresti fare è creare un'interfaccia ITryParsable e usarla per tipi personalizzati che implementano questa interfaccia.

Immagino però che tu abbia intenzione di usarlo con tipi base come inte DateTime. Non è possibile modificare questi tipi per implementare nuove interfacce.


1
Mi chiedo se funzionerebbe usando la parola chiave dinamica in .net 4?
Pierre-Alain Vigeant,

@Pierre: questo non funzionerà di default in C # con la dynamicparola chiave, perché non funzionerà con la digitazione statica. Puoi creare il tuo oggetto dinamico in grado di gestirlo, ma non è predefinito.
Steven,

4

Ispirato alla soluzione pubblicata qui da Charlie Brown, ho creato un TryParse generico usando la riflessione che opzionalmente genera il valore analizzato:

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

Può essere chiamato così:

string input = "123";
decimal myDecimal;

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

Aggiornamento:
anche grazie alla soluzione di YotaXP che mi piace molto, ho creato una versione che non utilizza metodi di estensione ma ha ancora un singleton, riducendo al minimo la necessità di riflettere:

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

Chiamalo così:

string input = "987";
decimal myDecimal;

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

3

Un po 'tardi alla festa, ma ecco cosa mi è venuto in mente. Nessuna eccezione, riflessione una tantum (per 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;
            }
        }
    }
}

La classe aggiuntiva è richiesta perché i metodi di estensione non sono consentiti all'interno delle classi generiche. Ciò consente un utilizzo semplice, come mostrato di seguito, e colpisce la riflessione solo la prima volta che si utilizza un tipo.

"5643".ParseAs<int>()

3

Ecco un'altra opzione.

Ho scritto una classe che semplifica la registrazione di un numero qualsiasi di TryParsegestori. Mi permette di fare questo:

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

Vengo 42stampato sulla console.

La classe è:

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

Mi piace questo, ma come lo faresti senza generici. Il caso d'uso è riflessione, ovviamente.
Sinaesthetic

Ho aggiunto un metodo sovraccarico che fa hacking di riflessione. Se c'è un modo più elegante di affrontarlo, sono tutti gli occhi lol gist.github.com/dasjestyr/90d8ef4dea179a6e08ddd85e0dacbc94
Sinaesthetic

2

Quando volevo fare quasi esattamente questa cosa, dovevo implementarla nel modo più duro, data la riflessione. Dato T, rifletti typeof(T)e cerca un metodo TryParseo Parse, invocandolo se lo hai trovato.


Questo è quello che stavo per suggerire.
Steven Evers,

2

Questo è il mio tentativo. L'ho fatto come un "esercizio". Ho provato a renderlo simile da usare come le attuali " Convert.ToX () " -ones ecc. Ma questo è il metodo di estensione:

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

Lo svantaggio principale rispetto a questo TypeConverter.ConvertFrom()è che la classe di origine deve fornire la conversione del tipo, il che significa generalmente che non è possibile supportare la conversione in tipi personalizzati.
Ian Goldby,

1

Come hai detto, TryParsenon fa parte di un'interfaccia. Inoltre, non è un membro di una data classe di base poiché in realtà è statice le staticfunzioni non possono esserlo virtual. Quindi, il compilatore non ha modo di assicurare che in Trealtà abbia un membro chiamato TryParse, quindi non funziona.

Come ha detto @Mark, potresti creare la tua interfaccia e utilizzare tipi personalizzati, ma sei sfortunato per i tipi integrati.


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

0

Questa è una questione di "vincoli generici". Perché non hai un'interfaccia specifica, allora sei bloccato a meno che non segui i suggerimenti della risposta precedente.

Per la documentazione su questo, controlla il seguente link:

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

Ti mostra come usare questi vincoli e dovrebbe darti qualche indizio in più.


0

Preso in prestito da http://blogs.msdn.com/b/davidebb/archive/2009/10/23/using-c-dynamic-to-call-static-members.aspx

quando si segue questo riferimento: Come richiamare il metodo statico in C # 4.0 con tipo dinamico?

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 usalo come segue:

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

0

Sono riuscito a ottenere qualcosa che funziona così

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

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

Ecco il mio codice

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

Lo StaticMembersDynamicWrapper è adattato da David Ebbo articolo (che stava gettando un AmbiguousMatchException)


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

0

Con l' TypeDescriptorutilizzo della classe in TryParsemodo correlato:

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

Sebbene questo codice possa risolvere la domanda, inclusa una spiegazione di come e perché questo risolva il problema, contribuirebbe davvero a migliorare la qualità del tuo post e probabilmente a dare più voti positivi. Ricorda che stai rispondendo alla domanda per i lettori in futuro, non solo per la persona che chiede ora. Si prega di modificare la risposta per aggiungere spiegazioni e dare un'indicazione di ciò si applicano le limitazioni e le assunzioni.
doppio segnale acustico

0

Utilizzando le informazioni di cui sopra, questo è quello che ho sviluppato. Convertirà direttamente l'oggetto è possibile, altrimenti convertirà l'oggetto in una stringa e chiamerà il metodo TryParse per il tipo di oggetto desiderato.

Memorizzo nella cache i metodi in un dizionario man mano che si incontrano ciascuno per ridurre il carico di recupero del metodo.

È possibile verificare se l'oggetto può essere convertito direttamente nel tipo di destinazione, il che ridurrebbe ulteriormente la parte di conversione della stringa. Ma lo lascerò fuori per ora.

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

Ho dovuto aggiungere un'altra funzione per supportare le enumerazioni. Sembra che l'analisi di Enum richieda l'attributo "where T: struct" e voglio che funzioni su qualsiasi cosa convertibile. (probabilmente dovrebbe aggiungere un attributo convertibile al tipo). Tuttavia, alcuni dei seguenti suggerimenti sembrano più semplici (quindi migliori).
B Duffy,

0

Ho messo insieme un sacco di idee qui e ho finito con una soluzione molto breve.

Questo è un metodo di estensione su una stringa

enter code here

L'ho realizzato con la stessa impronta dei metodi TryParse sui tipi numerici

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

    }

'''


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

0

T. TryParse ... perché?

Non vedo alcun vantaggio nell'avere una TryParsefunzione così generica . Esistono troppe strategie diverse per l'analisi e la conversione dei dati tra tipi diversi, con possibili comportamenti contrastanti. Come può questa funzione sapere quale strategia scegliere in modo privo di contesto?

  • classi con funzioni TryParse dedicate potrebbero essere chiamate
  • le classi con funzioni Parse dedicate potrebbero essere racchiuse con risultato try-catch e bool
  • classi con sovraccarichi dell'operatore, come lasceresti che gestiscano l'analisi?
  • i descrittori di tipo sono integrati usando Convert.ChangeType. Questa API è personalizzabile in fase di esecuzione. La tua funzione richiede un comportamento predefinito o consente la personalizzazione?
  • dovresti consentire a qualsiasi framework di mappatura di provare ad analizzare per te?
  • come gestiresti i conflitti di cui sopra?

-2

Una versione per ottenere discendenti da 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;
    }
}
Utilizzando il nostro sito, riconosci di aver letto e compreso le nostre Informativa sui cookie e Informativa sulla privacy.
Licensed under cc by-sa 3.0 with attribution required.