Generic TryParse
Asked Answered
G

25

224

I am trying to create a generic extension that uses 'TryParse' to check if a string is a given type:

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

this won't compile as it cannot resolve symbol 'TryParse'

As I understand, 'TryParse' is not part of any interface.

Is this possible to do at all?

Update:

Using the answers below I have come up with:

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

    return true;
}

It works quite well but I think using exceptions in that way doesn't feel right to me.

Update2:

Modified to pass type rather than use generics:

public static bool Is(this string input, Type targetType)
{
    try
    {
        TypeDescriptor.GetConverter(targetType).ConvertFromString(input);
        return true;
    }
    catch
    {
        return false;
    }
}
Gnosticize answered 2/6, 2010 at 21:16 Comment(8)
i think in this general case you will just have to deal with the exception kludge. you could add cases to check for things like ints or doubles and then use the specific TryParse methods, but you will still have to fall back on this to catch other types.Precept
The use of the generic is unnecessary. Just pass in the Type as a parameter. public static bool Is(this string input, Type targetType) . That way calling it looks a bit prettier: x.Is(typeof(int)) -VS- x.Is<int>()Pronounced
There is an IsValid method on the convertor for you to check if the conversion will have issues. I used the below method and seems to work fine. 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; }Uracil
@Uracil Thanks for showing an interest in this question, however your method wouldn't quite work as I wanted to check whether the string value was of a certain type rather than an object, although your method would be useful for object types (but would have to wrap the ConvertFrom(value) method in a try-catch block to catch the exceptions.Gnosticize
You should check if (targetType == null) because the first use of it in your code might throw but that exception would get swallowed by your catch.Luciusluck
For those of us who are not US-centric, you can use the GetConverter() overload with CultureInfo passing null as the context parameter. BTW TypeDescriptor comes from System.ComponentModel.Mcminn
All the primitives implement the following. So you could likely get away with removing the exception handler and using this. where T : struct, IComparable, IConvertible, IComparable<T>, IEquatable<T> While it is theoretically possible to still get an exception with this constraint, in practice it is so unlikely that it would ever happen.Sepulveda
Why does ReSharper tell me if (converter == null) is always false?Esculent
P
227

You should use the TypeDescriptor class:

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);
    }
}
Precept answered 2/6, 2010 at 21:23 Comment(7)
Sorry to resurrect, but does GetConverter return null? I think if it did then probably an exception should be thrown instead of essentially silently failing and return something else. When I tried it on my own class (in which I didn't define a typeconverter), I got a converter from GetConverter, but then the ConvertFromString threw a NotSupportedException.Suprematism
@user420667, I believe you should be checking the result of CanConvertFrom(typeof(string)) before attempting to convert from string. The TypeConverter may not support conversion from string.Spangler
You can add if (typeof(T).IsEnum) { return (T)Enum.Parse(typeof(T), input); } [as a fairly generic shortcut for all Enum types] before getting the Converter. I suppose it depends on how often you will be doing Enum types as opposed to more complex types.Persuader
I don't understand why this is marked as the answer and upvoted so much when it does not implement what was requested: a generic TryParse. The main purpose of TryParse methods is that they don't throw exceptions when attempting to perform the parsing and have a much lower impact on performance when the parsing fails and this solution fails to provide just that.Pyrochemical
One issue w/ this is that if T is an int and input is larger than int.MaxValue, it will throw a System.Exception w/ System.OverFlowException as the inner exception. So if you are expecting an OverflowException, you won't get it unless you interrogate the thrown Exception. The reason is that ConvertFromString throws an OverflowException, and then the cast to T throws a System.Exception.Chapfallen
I know I am late to this party, but this method is highly inefficient. Time consumption to answer by @Charlie Brown is 20x higher.Picture
Note: IntelliSense or perhaps Resharper seems to think if (converter != null) is unnecessary: "Expression is always true."Paschasia
M
88

I also required a generic TryParse recently. Here's what I came up with;

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

Then it's simply a matter of calling thusly:

var value = TryParse<int>("123", int.TryParse);
var value2 = TryParse<decimal>("123.123", decimal.TryParse);
Marginalia answered 1/7, 2011 at 22:11 Comment(6)
Just came across this post again months later and noticed while using it again that the method can't infer T from the handler, and we have to explicitly specify T when we call it. I'm curious, why can't it infer T?Luciusluck
In other words, why can't we do TryParse("42", Int32.TryParse)Luciusluck
Why would you want to use this function? If you know which function to call to parse the value, why not just call it directly? It already knows the right input type and there's no need for generics.This solution wouldn't work for types without a TryParseHandler.Wilmoth
@xxbbcc: I'd want to use this function because TryParse returns a boolean that indicates if the parse was successful. It returns your parsed value via an output parameter. Sometimes I just want to do something like this SomeMethod(TryParse<int>(DollarTextbox.Text, int.TryParse)) without creating an output variable to catch the result from int.TryParse. However, I do agree with Nick's sentiment about having the function infer the type.Gerta
Very efficient method. Highly recommended.Picture
I would recommend a default value as a third param. That fixes the problem where T cannot be inferred. Also, it allows for one to decide what value they want if the string value is invalid. For example, -1 might mean invalid. public static T TryParse<T>(string value, TryParseHandler<T> handler, T defaultValue)Sepulveda
T
36

Using try/catches for flow control is a terrible policy. Throw an exception causes performance lags while the runtime works around the exception. Instead validate the data before converting.

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);
Thibodeau answered 11/4, 2012 at 20:30 Comment(4)
I'm getting a Resharper notice that converter != null is always true, so it can be removed from the code.Feline
@Feline I don't always trust those ReSharper warnings. Often they can't see what happens at runtime.Elbring
@Elbring MSDN doesn't say that it can return null msdn.microsoft.com/en-us/library/ewtxwhzx.aspxHymnal
@Hymnal I was just sharing my experience with such R# warnings in general. I certainly didn't imply that it was wrong in this case.Elbring
S
14

If you are set on using TryParse, you can use reflection and do it like this:

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});
}
Spherule answered 2/6, 2010 at 22:0 Comment(6)
This is very cool, and it gets rid of the exceptions that I didn't like anyway. Still a bit convoluted though.Gnosticize
Nice solution, but any answer involving reflection (especially in a utility method that could easily be called from an inner loop) needs a disclaimer about performance. See: https://mcmap.net/q/19784/-how-costly-is-net-reflectionAnabas
Sigh. So the choices are (1) use exceptions for code flow control, (2) use reflection with its speed costs. I agree with @PiersMyers - neither choice is ideal. Good thing they both work. :)Persuader
I think you can replace the Type.GetType(string.Format(...)) with type.MakeByRefType().Hemicrania
the method only needs to be reflected once per type, not once per call. if you make this a generic class with a static member variable, then you can reuse the output of the first reflection.Weighting
@DrewNoakes your comment fixed this answer for me, as originally proposed I get a runtime exception.Emmalynne
C
8

How about something like this?

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

This can be converted to a generic method pretty easily.

 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;
}
Costly answered 2/6, 2010 at 21:27 Comment(3)
Does it matter whether you return true from the try block or return false from the catch block? I suppose not, but I still think using exceptions in this way feels wrong to me...Gnosticize
It doesn't matter whether you return from the catch block, this is the same. btw. Usually it is bad to have a generic catch clause: catch { }. However, in this case there is no alternative, because the .NET BaseNumberConverter throws the Exception base class in case of a conversion error. This is very unfortunate. In fact there are still quite a few places were the this base type is thrown. Hopefully Microsoft will fix these in a future version of the framework.Langsdon
No use made of the result of the conversion: code is redundant.Gorrian
C
7

This uses a static constructor for each generic type, so it only has to do the expensive work the first time you call it on a given type. It handles all the types in the system namespace which have TryParse methods. It also works with nullable versions of each of those (that are structs) except for enumerations.

    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;
        }
    }
Commeasure answered 23/5, 2013 at 16:54 Comment(0)
A
5

Quite a bit late to the party, but here's what I came up with. No exceptions, one-time (per type) reflection.

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

The extra class is required because extention methods are not permitted inside generic classes. This allows simple usage, as shown below, and only hits reflection the first time a type is used.

"5643".ParseAs<int>()
Antiseptic answered 7/8, 2014 at 20:40 Comment(0)
C
4

You can't do it on general types.

What you could do is to create an interface ITryParsable and use it for custom types that implement this interface.

I guess though that you intend to use this with basic types like int and DateTime. You can't change these types to implement new interfaces.

Catarrhine answered 2/6, 2010 at 21:18 Comment(2)
I wonder if that would work by using the dynamic keyword in .net 4?Hairpin
@Pierre: This won't work by default in C# with the dynamic keyword, because it won't work on static typing. You can create your own dynamic object that can handle this, but it is not default.Langsdon
C
4

Inspired by the solution posted here by Charlie Brown, I created a generic TryParse using reflection that optionally outputs the parsed value:

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

It can be called thus:

string input = "123";
decimal myDecimal;

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

Update:
Also thanks to YotaXP's solution which I really like, I created a version that doesn't use extension methods but still has a singleton, minimizing the need to do reflection:

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

Call it like this:

string input = "987";
decimal myDecimal;

bool myIntSuccess = TryParseHelper<int>.TryParse(input);
bool myDecimalSuccess = TryParseHelper<decimal>.TryParse(input, out myDecimal);
Cream answered 26/9, 2014 at 8:46 Comment(0)
R
4

You can do this with the INumber<T> interface:

public static bool Is<T>(string input)
    where T : INumber<T>
{
    return T.TryParse(input, CultureInfo.InvariantCulture, out _);
}

This interface was introduced in .NET 7 as part of generic maths, which also allow you to do calculations on generic primitive types.

This can also be used experimentally in .NET 6 by adding the System.Runtime.Experimental NuGet package and then <EnablePreviewFeatures>true</EnablePreviewFeatures> to any PropertyGroup of your project.

Row answered 29/7, 2022 at 14:55 Comment(1)
Oo, that does look interesting, let's hope it does make it to .NET 7. Thanks for the info.Gnosticize
L
3

Here's another option.

I wrote a class that makes it easy to register any number of TryParse handlers. It lets me do this:

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

I get 42 printed to the console.

The class is:

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);
    }
}
Lumpish answered 26/2, 2016 at 2:37 Comment(3)
I like this, but how would you do it without generics. Use case being reflection, of course.Unadvised
I added an overloaded method that does some reflection hackery. If there's a more elegant way to address it, I'm all eyes lol gist.github.com/dasjestyr/90d8ef4dea179a6e08ddd85e0dacbc94Unadvised
You could remove the generics from the delegate but keep it on the method signatures so you get the type inference. You can also remove the dictionary keyed by type by defining static generic storage: private static class Storage<T> { public static TryParseDelegate<T> Delegate; } and then set that from your methods.Expurgatory
H
2

When I wanted to do almost this exact thing, I had to implement it the hard way, given reflection. Given T, reflect on typeof(T) and look for a TryParse or Parse method, invoking it if you've found it.

Hebetic answered 2/6, 2010 at 21:26 Comment(1)
This is what I was going to suggest.Minus
G
2

This is my try. I did it as an "exercise". I tried to make it as similar to use as the existing "Convert.ToX()"-ones etc. But this one is extension method:

    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; }
    }
Gouache answered 21/5, 2014 at 13:13 Comment(1)
The main disadvantage of this compared with TypeConverter.ConvertFrom() is that the source class has to provide the type conversion, which generally means you cannot support conversion to custom types.Hackbut
P
1

As you said, TryParse is not part of an interface. It is also not a member of any given base class since it's actually static and static functions can't be virtual. So, the compiler has no way of assuring that T actually has a member called TryParse, so this doesn't work.

As @Mark said, you could create your own interface and use custom types, but you're out of luck for the built-in types.

Prenatal answered 2/6, 2010 at 21:25 Comment(0)
V
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);
    }
}
Vue answered 20/3, 2019 at 17:4 Comment(0)
C
1

I put a bunch of ideas here together and ended up with a very short solution.

This is an extension method on a string

enter code here

I made it with the same foot print as the TryParse methods on the numeric types

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

    }

'''

Converge answered 14/9, 2019 at 19:29 Comment(1)
float testValue = 0; if ( "1234".TryParse<float>( out testValue ) ) { doSomethingGood(); } else { handleTheBadness(); }Converge
S
1

T.TryParse ... why?

I see no benefit in having such generic TryParse function. There are too many different strategies for parsing and converting data between different types, with possible conflicting behavior. How could this function know which strategy to pick in a context-free fashion?

  • classes with dedicated TryParse functions could be called
  • classes with dedicated Parse functions could be wrapped with try-catch and bool result
  • classes with operator overloads, how would you let them handle the parsing?
  • type descriptors are built-in using Convert.ChangeType. This API is customisable at runtime. Does your function require default behavior or allow for customization?
  • should you allow any mapping framework to try to parse for you?
  • how would you handle conflicts in the above?
Swordplay answered 14/9, 2019 at 19:56 Comment(0)
F
0

This is a question of 'generic constraints'. Because you don't have a specific interface then you are stuck unless you follow the suggestions of the previous answer.

For documentation on this, check the following link:

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

It shows you how to use these constraints and should give you some more clues.

Falconry answered 2/6, 2010 at 21:22 Comment(0)
S
0

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

when following this reference: How to invoke static method in C#4.0 with dynamic type?

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

And use it as follows:

  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;
  }
Sarsenet answered 30/4, 2011 at 16:52 Comment(0)
C
0

I managed to get something that works like this

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

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

Here's my code

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

The StaticMembersDynamicWrapper is adapted from David Ebbo's article (it was throwing an AmbiguousMatchException)

Cathryncathy answered 22/10, 2011 at 17:51 Comment(0)
B
0
public static T Get<T>(string val)
{ 
    return (T) TypeDescriptor.GetConverter(typeof (T)).ConvertFromInvariantString(val);
}
Buckle answered 29/10, 2013 at 12:23 Comment(0)
K
0

With TypeDescriptor class usage in TryParse related manner:

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;
    }
}
Kob answered 22/5, 2019 at 11:25 Comment(1)
While this code may solve the question, including an explanation of how and why this solves the problem would really help to improve the quality of your post, and probably result in more up-votes. Remember that you are answering the question for readers in the future, not just the person asking now. Please edit your answer to add explanations and give an indication of what limitations and assumptions apply.Hypercorrection
M
0

Using the information above, this is what I developed. It will convert the object directly is possible, otherwise it will convert the object to a string and call the TryParse method for the desired object type.

I cache the methods in a dictionary as each is encountered to reduce the method fetching load.

It's possible to test if the object can be directly converted to the target type, which would further reduce the string conversion part. But I'll leave that out for now.

    /// <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;
    }
Muntin answered 6/7, 2019 at 21:55 Comment(1)
I had to add another function to support enumerations. Seems Enum parsing requires the "where T : struct" attribute, and I want this to work on anything convertible. (probably should add an convertible attribute to the type). However, some of the following suggestions look simpler (thus better).Muntin
B
0

I used the answer from Charlie Brown for an extension method of the JObject from Json.NET.

public static class JObjectExtension
{
    public delegate bool TryParseHandler<T>(string value, out T result);

    public static bool TryParsePropertyAs<T>(
        this JObject jObject,
        string propertyName,
        TryParseHandler<T> parser,
        out T value)
    {
        value = default;

        if (!jObject.TryGetValue(propertyName, out var jToken)) return false;

        if (!parser(jToken.Value<string>(), out T result)) return false;

        value = result;

        return true;
    }
}
Boaten answered 20/6, 2022 at 9:0 Comment(0)
S
-2

A version for getting descendants from 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;
    }
}
Septivalent answered 1/10, 2013 at 17:47 Comment(0)

© 2022 - 2024 — McMap. All rights reserved.