HiveBrain v1.2.0
Get Started
← Back to all entries
snippetcsharpModerate

Parse a specified arithmetic type from string

Submitted by: @import:stackexchange-codereview··
0
Viewed 0 times
parsetypefromstringspecifiedarithmetic

Problem

This class has the responsibility of parsing a string to one of the primitive types: int, double, float, decimal...

While creating it, I have noticed that it'll be terribly ugly.

It basically should perform a TryParse of each primitive type and return the first that returns true, along with the parsed value.

public static class PrimitiveParser
{
    public static bool TryParse(Type targetType, string sourceValue, out object result)
    {
        result = null;

        if (targetType == typeof(int))
        {
            int intResult;
            var parseResult = int.TryParse(sourceValue, out intResult);
            {
                result = intResult;
                return parseResult;
            }
        }
        if (targetType == typeof(double))
        {
            double doubleResult;
            var parseResult = double.TryParse(sourceValue, out doubleResult);
            {
                result = doubleResult;
                return parseResult;
            }
        }
        if (targetType == typeof(float))
        {
            float floatResult;
            var parseResult = float.TryParse(sourceValue, out floatResult);
            {
                result = floatResult;
                return parseResult;
            }
        }

        /// ...

        return false;
    }
}

Solution

I'd prefer to use a dictionary for this.

private delegate bool TryParseDelegate(string text, out T value);

private static readonly Dictionary TryParsers = new Dictionary
{
    { typeof(int), (TryParseDelegate)int.TryParse },
    { typeof(float), (TryParseDelegate)float.TryParse },
    { typeof(double), (TryParseDelegate)double.TryParse }
};


The TryParse method:

public static bool TryParse(string value, out T returnValue)
{
    // Check if we have a parse method for T in the dictionary.
    object parserObj;
    if (TryParsers.TryGetValue(typeof(T), out parserObj))
    {
        TryParseDelegate parser = (TryParseDelegate)parserObj;
        return parser(value, out returnValue);
    }
    // If no, fallback to IConvertible.
    if (typeof(IConvertible).IsAssignableFrom(typeof(T)))
    {
        try
        {
            returnValue = (T)Convert.ChangeType(value, typeof(T));
            return true;
        }
        catch { }
    }
    // No luck.
    returnValue = default(T);
    return false;
}


This approach support exensibility.

For example, imagine we have a class A:

public class A
{
    public static readonly A Zero = new A("Zero");
    public static readonly A One = new A("One");

    public readonly string Text;

    private A(string text)
    {
        Text = text;
    }

    public static bool TryParse(string text, out A value)
    {
        switch (text.ToLower())
        {
            case "zero":
                value = Zero;
                break;
            case "one":
                value = One;
                break;
            default:
                value = null;
                return false;
        }
        return true;
    }

    public override string ToString()
    {
        return Text;
    }
}


Then the only thing you have to do is to append an item to the dictionary:

{ typeof(A), (TryParseDelegate)A.TryParse }


Let's test it:

int i;
double d;
A a;

if (TryParse("123", out i))
    Console.WriteLine($"i={i}"); // Prints i=123
if (TryParse("123.456", out d))
    Console.WriteLine($"d={d}"); // Prints d=123.456
if (TryParse("ONE", out a))
    Console.WriteLine($"a={a}"); // Prints a=One

Code Snippets

private delegate bool TryParseDelegate<T>(string text, out T value);

private static readonly Dictionary<Type, object> TryParsers = new Dictionary<Type, object>
{
    { typeof(int), (TryParseDelegate<int>)int.TryParse },
    { typeof(float), (TryParseDelegate<float>)float.TryParse },
    { typeof(double), (TryParseDelegate<double>)double.TryParse }
};
public static bool TryParse<T>(string value, out T returnValue)
{
    // Check if we have a parse method for T in the dictionary.
    object parserObj;
    if (TryParsers.TryGetValue(typeof(T), out parserObj))
    {
        TryParseDelegate<T> parser = (TryParseDelegate<T>)parserObj;
        return parser(value, out returnValue);
    }
    // If no, fallback to IConvertible.
    if (typeof(IConvertible).IsAssignableFrom(typeof(T)))
    {
        try
        {
            returnValue = (T)Convert.ChangeType(value, typeof(T));
            return true;
        }
        catch { }
    }
    // No luck.
    returnValue = default(T);
    return false;
}
public class A
{
    public static readonly A Zero = new A("Zero");
    public static readonly A One = new A("One");

    public readonly string Text;

    private A(string text)
    {
        Text = text;
    }

    public static bool TryParse(string text, out A value)
    {
        switch (text.ToLower())
        {
            case "zero":
                value = Zero;
                break;
            case "one":
                value = One;
                break;
            default:
                value = null;
                return false;
        }
        return true;
    }

    public override string ToString()
    {
        return Text;
    }
}
{ typeof(A), (TryParseDelegate<A>)A.TryParse }
int i;
double d;
A a;

if (TryParse("123", out i))
    Console.WriteLine($"i={i}"); // Prints i=123
if (TryParse("123.456", out d))
    Console.WriteLine($"d={d}"); // Prints d=123.456
if (TryParse("ONE", out a))
    Console.WriteLine($"a={a}"); // Prints a=One

Context

StackExchange Code Review Q#148566, answer score: 11

Revisions (0)

No revisions yet.