8
\$\begingroup\$

I have a method to parse strings. It can be used to parse a float using a specific culture and then get a new string with a specific out-culture. The same applies to ints, doubles and decimals.

The code that I've written is quite repetitive for each of the different parse methods which makes it hard to maintain (especially as I am just about to make the method a lot more complex).

Is it possible to make this code less repetitive?

if (mapping.ParseDecimal)
{
    decimal i;
    if (decimal.TryParse(value, numberStyle, inCulture, out i))
    {
        return i.ToString(outCulture);
    }
    else
    {
        if (logger.IsDebugEnabled)
            logger.DebugFormat("Could not parse value \"{0\" to a decimal using the culture \"{1}\".", value, inCulture);
    }
}
else if (mapping.ParseDouble)
{
    double i;
    if (double.TryParse(value, numberStyle, inCulture, out i))
    {
        return i.ToString(outCulture);
    }
    else
    {
        if (logger.IsDebugEnabled)
            logger.DebugFormat("Could not parse value \"{0\" to a double using the culture \"{1}\".", value, inCulture);
    }
}
else if (mapping.ParseFloat)
{
    float i;
    if (float.TryParse(value, numberStyle, inCulture, out i))
    {
        return i.ToString(outCulture);
    }
    else
    {
        if (logger.IsDebugEnabled)
            logger.DebugFormat("Could not parse value \"{0\" to a float using the culture \"{1}\".", value, inCulture);
    }
}
else if (mapping.ParseInt)
{
    int i;
    if (int.TryParse(value, numberStyle, inCulture, out i))
    {
        return i.ToString(outCulture);
    }
    else
    {
        if (logger.IsDebugEnabled)
            logger.DebugFormat("Could not parse value \"{0\" to a int using the culture \"{1}\".", value, inCulture);
    }
}
\$\endgroup\$
14
\$\begingroup\$

If repetition is your primary concern, you could try doing something like this:

public delegate string ParserMethod(string value, NumberStyles numberStyle, CultureInfo inCulture, CultureInfo outCulture);

public static class NumericParser
{
    public static readonly ParserMethod ParseInt = Create<int>(int.TryParse);
    public static readonly ParserMethod ParseFloat = Create<float>(float.TryParse);
    public static readonly ParserMethod ParseDouble = Create<double>(double.TryParse);
    public static readonly ParserMethod ParseDecimal = Create<decimal>(decimal.TryParse);

    public static Logger Logger { get; set; }

    delegate bool TryParseMethod<T>(string s, NumberStyles style, IFormatProvider provider, out T result);
    static ParserMethod Create<T>(TryParseMethod<T> tryParse) where T : IFormattable
    {
        return (value, numberStyle, inCulture, outCulture) =>
        {
            T result;
            if (tryParse(value, numberStyle, inCulture, out result))
            {
                return result.ToString(null, outCulture);
            }
            else
            {
                if (Logger != null && Logger.IsDebugEnabled)
                    Logger.DebugFormat("Could not parse value \"{0}\" to a {1} using the culture \"{2}\".", value, typeof(T).Name, inCulture);
                return "";
            }
        };
    }
}

This way, you only have to pass around the appropriate ParserMethod you want to use. In your case, you could map your different mapping values to the appropriate ParserMethod. And call it when needed.

\$\endgroup\$
  • \$\begingroup\$ Create<int>(int.TryParse); in what namespace is Create located? \$\endgroup\$ – jim Jul 29 '11 at 19:42
  • \$\begingroup\$ Create<T>() is the static method that you see at the end of the class. ;) I needed a nice way to create the ParserMethods and a factory fit the bill. \$\endgroup\$ – Jeff Mercado Jul 29 '11 at 19:46
  • \$\begingroup\$ ahhh...I see it now, thanks. thanks for the quick answer. \$\endgroup\$ – jim Jul 29 '11 at 19:49
4
\$\begingroup\$

I have a method that does something similar in that it takes a string and converts it to a given primitive type. It uses generics so there's less code.

    public static T ReadConfigItem<T>(string strValue, IFormatProvider formatProvider) where T : struct
    {
        T result;

        if (string.IsNullOrEmpty(strValue))
        {       
            result = (T)(Convert.ChangeType(false, typeof(T), formatProvider));
        }
        else
        {
            result = (T)(Convert.ChangeType(strValue, typeof(T), formatProvider));
        }

        return result;
    }

It can then be used like this:

    System.Globalization.CultureInfo c = new System.Globalization.CultureInfo("fr-FR");
    Console.WriteLine(ReadConfigItem<double>("90,99", c));
    Console.WriteLine(ReadConfigItem<double>("9", c));
    Console.WriteLine(ReadConfigItem<int>("6", c));
    Console.WriteLine(ReadConfigItem<double>(null, c));
    Console.WriteLine(ReadConfigItem<int>(null, c));
    Console.WriteLine(ReadConfigItem<bool>("true", c));
    Console.WriteLine(ReadConfigItem<bool>("True", c));
    Console.WriteLine(ReadConfigItem<bool>(null, c));

I'm in the UK and the output I get is:

90.99
9
6
0
0
True
True
False

If you wanted you could change this to an extension method on the string class.

Example of extension class:

public static class ConvertExtensions
    {
        public static T ConvertTo<T>(this string strValue, IFormatProvider formatProvider) where T : struct
        {
            T result;

            if (string.IsNullOrEmpty(strValue))
            {
                result = (T)(Convert.ChangeType(false, typeof(T), formatProvider));
            }
            else
            {
                result = (T)(Convert.ChangeType(strValue, typeof(T), formatProvider));
            }

            return result;
        }
    }

Usage:

CultureInfo c = new CultureInfo("fr-FR");    
Console.WriteLine("".ConvertTo<double>(c));
Console.WriteLine("True".ConvertTo<bool>(c));
Console.WriteLine("true".ConvertTo<bool>(c));
Console.WriteLine("".ConvertTo<bool>(c));
Console.WriteLine("".ConvertTo<decimal>(c));
\$\endgroup\$
  • \$\begingroup\$ This would have been the best method if I didn't have to use the Parse version :) +1 though for a really nice solution \$\endgroup\$ – Oskar Kjellin Jul 28 '11 at 12:17

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service, privacy policy and cookie policy

Not the answer you're looking for? Browse other questions tagged or ask your own question.