10

Is there a way to call TryParse dynamically? Some kind of:

public static bool TryParse<T>(string toConvert, out T result)

Of course one can use Typeonverters for this. However, an invalid conversion will result in an exception and I want to get rid of this.

  • Maybe you could use something from System.Reflection. I haven't dealt with generics with Reflection, but it might go like get type info, see if `TryParse()` exists, if so, `Invoke()`, otherwise return false. – cyanic Jul 14 '12 at 07:32
  • Related: http://stackoverflow.com/a/6160588/445517 – CodesInChaos Jul 14 '12 at 11:27
  • In particular look at http://stackoverflow.com/a/2961921/103167 Just add copying the result back out. – Ben Voigt Oct 17 '14 at 21:27

2 Answers2

16

You could call the TryParse method dynamically using Reflection. This way you won't get a time consuming Exception if the conversion fails.

This method is a slightly optimized version of this one.

    //Try Parse using Reflection
public static bool TryConvertValue<T>(string stringValue, out T convertedValue)
{
    var targetType = typeof(T);
    if (targetType == typeof(string))
    {
        convertedValue = (T)Convert.ChangeType(stringValue, typeof(T));
        return true;
    }
        var nullableType = targetType.IsGenericType &&
                       targetType.GetGenericTypeDefinition() == typeof (Nullable<>);
    if (nullableType)
    {
        if (string.IsNullOrEmpty(stringValue))
        {
            convertedValue = default(T);
            return true;
        }
            targetType = new NullableConverter(targetType).UnderlyingType;
    }

    Type[] argTypes = { typeof(string), targetType.MakeByRefType() };
    var tryParseMethodInfo = targetType.GetMethod("TryParse", argTypes);
    if (tryParseMethodInfo == null)
    {
        convertedValue = default(T);
        return false;
    }

    object[] args = { stringValue, null };
    var successfulParse = (bool)tryParseMethodInfo.Invoke(null, args);
    if (!successfulParse)
    {
        convertedValue = default(T);
        return false;
    }

    convertedValue = (T)args[1];
    return true;
}
Oliver Vogel
  • 1,988
  • 1
  • 20
  • 33
  • Good. A suggestion: In the `if (nullableType)` block, when you check `stringValue` for `null`, maybe you should say `string.IsNullOrEmpty(stringValue)` instead. The reason I think so is that `Nullable<>.ToString()` returns `""` when `HasValue` is false. So the empty string might be considered a valid string representation for e.g. `(int?)null`. – Jeppe Stig Nielsen Jul 14 '12 at 08:48
  • 1
    Thanks for the input. However, if you call this method with "" for converting a Nullable the out value will be null and the return value will be false. This is correct as "" should not be a valid value for parsing int?. If I would use string.IsNullOrEmpty the return value would be true, which would not be correct imho. – Oliver Vogel Jul 14 '12 at 09:09
  • 1
    That's completely OK. But it means that a `Nullable` will not "round-trip". Suppose you have: `int? i = null; string s = i.ToString(); int? tmp; bool canParse = TryConvertValue(s, out tmp);`. Then `canParse` will be false even if the string `s` comes directly from the `ToString` call of an `int?`. See also [MSDN ToString](http://msdn.microsoft.com/en-us/library/9hd15ket.aspx). – Jeppe Stig Nielsen Jul 14 '12 at 10:39
  • Another issue: The `out` parameter `convertedValue` should be of type `T`, not `object`. – Jeppe Stig Nielsen Jul 14 '12 at 10:40
  • I had a problem on how to determine the right overload of TryParse. Those lines helped me. Thanks! Type[] argTypes = { typeof(string), targetType.MakeByRefType() }; var tryParseMethodInfo = targetType.GetMethod("TryParse", argTypes); – Feras Taleb Aug 25 '19 at 08:43
2

You can write something like this:

public delegate bool TryParser<T>(string input, out T result);

public static bool TryParse<T>
     (string toConvert, out T result, TryParser<T> tryParser = null)
{
    if (toConvert == null)
        throw new ArgumentNullException("toConvert");

    // This whole block is only if you really need
    // it to work in a truly dynamic way. You can additionally consider 
    // memoizing the default try-parser on a per-type basis.
    if (tryParser == null)
    {
        var method = typeof(T).GetMethod
                 ("TryParse", new[] { typeof(string), typeof(T).MakeByRefType() });

        if (method == null)
            throw new InvalidOperationException("Type does not have a built in try-parser.");

        tryParser = (TryParser<T>)Delegate.CreateDelegate
            (typeof(TryParser<T>), method);
    }

    return tryParser(toConvert, out result);
}

And then call it like:

int result;
bool success = TryParse("123", out result);

I really wouldn't recommend this unless you have some scenario that requires it.

Ani
  • 111,048
  • 26
  • 262
  • 307