7

I have written overloaded static TryParse methods for the following Nullable types: int?, short?, long?, double?, DateTime?, decimal?, float?, bool?, byte? and char?. Below is some of the implementation:

protected static bool TryParse(string input, out int? value)
{
    int outValue;
    bool result = Int32.TryParse(input, out outValue);
    value = outValue;
    return result;
}

protected static bool TryParse(string input, out short? value)
{
    short outValue;
    bool result = Int16.TryParse(input, out outValue);
    value = outValue;
    return result;
}

protected static bool TryParse(string input, out long? value)
{
    long outValue;
    bool result = Int64.TryParse(input, out outValue);
    value = outValue;
    return result;
}

The logic is the same in every method except that they use different types. Would it not be possible to use generics so that I don't need to have so much redundant code? The signature would look like this:

bool TryParse<T>(string input, out T value);

Thanks

Dave New
  • 38,496
  • 59
  • 215
  • 394
  • 1
    One reason a generic method for this is not suitable is not all `struct`s have `TryParse` methods, but you can't use a generic constraint to only allow compatible types at compile time. – Sam May 31 '13 at 07:42
  • By the way, you're missing the `?` after the second `T` in your proposed method signature. – Sam May 31 '13 at 08:03

3 Answers3

9

Would it not be possible to use generics so that I don't need to have so much redundant code?

You could do it with reflection, but that would be relatively slow. Otherwise, you could create a map from type to "method to use for that type" but it would be pretty ugly. Aside from anything else, it would never be truly generic - it would only work for types that provided a TryParse method of the right signature, which couldn't be known at compile-time.

I would personally consider changing the signature and behaviour, by the way. Currently even though the type of value is nullable, it will never have the null value at the end of the method, even if you return false. Why not make the return value the result of the parsing operation, returning null on failure?

protected static long? TryParseInt64(string input)
{
    long outValue;
    return Int64.TryParse(input, out outValue) ? (long?) outValue : null;
}
Jon Skeet
  • 1,421,763
  • 867
  • 9,128
  • 9,194
  • In that case, I'll stay away from generics. Regarding your suggestion, I need the signature to be in the form of `bool TryParse(string input, out object value)` so to be consistent with .NET's `TryParse` methods. – Dave New May 31 '13 at 07:22
  • @davenewza: Then you should *at least* change the behaviour so that `value` is `null` if `result` is `false`, to be consistent with the other `TryParse` methods: the `out` parameter should always have the default value of the type (which is `null` for nullable types) when it returns `false`. – Jon Skeet May 31 '13 at 07:26
  • Ah, absolutely. Thanks for pointing that out. – Dave New May 31 '13 at 07:27
  • Your method is wrong. It is supposed to parse a _Nullable_ `Int64`, so passing the `"null"` string is supposed to be success, and in this case `"null"` and `"foobar"` would yield the same result. – Ahmed KRAIEM May 31 '13 at 07:51
  • @AhmedKRAIEM, the method you're proposing functions differently to the one in the question. I think your comment should be aimed at the person who wrote the question; it's their requirements, not Jon's. – Sam May 31 '13 at 07:57
8

You could use the following generic extension method,

public static Nullable<TSource> TryParse<TSource>(this string input) where TSource : struct
{
    try
    {
        var result = Convert.ChangeType(input, typeof(TSource));
        if (result != null)
        {
            return (TSource)result;
        }
        return null;
    }
    catch (Exception)
    {
        return null;
    }
}

The following call will return the nullable parsed type.

string s = "510";
int? test = s.TryParse<int>();
//TryParse Returns 510 and stored in variable test.

string s = "TestInt";
int? test = s.TryParse<int>();
//TryParse Returns null and stored in variable test.
petchirajan
  • 4,152
  • 1
  • 18
  • 20
4

As an aside, you could refactor your code:

public static bool TryParse(string input, out int? value)
{
    return TryParse(input, Int32.TryParse, out value);
}

protected static bool TryParse(string input, out short? value)
{
    return TryParse(input, Int16.TryParse, out value);
}

protected static bool TryParse(string input, out long? value)
{
    return TryParse(input, Int64.TryParse, out value);
}

private static bool TryParse<T>(string input, TryParseFunc<T> tryParse, out T? value)
    where T : struct
{
    T outValue;
    bool result = tryParse(input, out outValue);
    value = outValue;
    return result;
}

private delegate bool TryParseFunc<T>(string input, out T value);
Sam
  • 40,644
  • 36
  • 176
  • 219
  • 3
    I like it. That's neat. It also makes the change I was suggesting very simple - you just use `value = result ? outValue : (T?) null` on the third line of the implementation part. – Jon Skeet May 31 '13 at 07:26
  • This is great. Thanks! Will also include the `null` default value as Jon suggested. – Dave New May 31 '13 at 07:39
  • 1
    There should be a badge for posting an answer that @JonSkeet himself approves in a comment. – Dennis May 31 '13 at 13:43