views:

258

answers:

1

Hello,

I want to make a generic string to numeric converter, and provide it as a string extension, so I wrote the following code:

public static bool TryParse<T>( this string text, out T result, IFormatProvider formatProvider ) where T : struct
try
{
   result = (T)Convert.ChangeType( text, typeof( T ), formatProvider );
   return true;
}
catch(...

I call it like this:

int value;
var ok = "123".TryParse(out value, NumberFormatInfo.CurrentInfo)

It works fine until I want to use a group separator: As I live in France, where the thousand separator is a space and the decimal separator is a comma, the string "1 234 567,89" should be equals to 1234567.89 (in Invariant culture). But, the function crashes!

When a try to perform a non generic conversion, like double.Parse(...), I can use an overload which accepts a NumberStyles parameter. I specify NumberStyles.Number and this time it works!

So, the questions are :

  • Why the parsing does not respect my NumberFormatInfo (where the NumberGroupSeparator is well specified to a space, as I specified in my OS)
  • How could I make work the generic version with Convert.ChangeTime, as it has no overload wich accepts a NumberStyles parameter ?
+1  A: 

Try specifying explicitly the culture "fr-FR":

CultureInfo c = new CultureInfo("fr-FR");
double d = 0;
if ("1 234 567,89".TryParse<double>(out d, c)) {
    Console.WriteLine(d);
}

EDIT: this example works properly:

static class Extension {
    public static bool TryParse<T>(this string text, out T result, IFormatProvider formatProvider) where T : struct {
        result = default(T);
        try {
            result = (T)Convert.ChangeType(text, typeof(T), formatProvider);
            return true;
        } catch {
            return false;
        }
    }

}

class Program {
    static void Main(string[] args) {
        CultureInfo c = new CultureInfo("fr-FR");
        double d = 0;
        // NumberGroupSeparator in fr-FR culture is space
        bool res = "123 456,78".TryParse<double>(out d, c);
        // Set separator as '.' and parse string with dots
        c.NumberFormat.NumberGroupSeparator = ".";
        res = "123.456,78".TryParse<double>(out d, c);
    }
}

Est-ce que ca marche comme ca? :)

Paolo Tedesco
I tried several cultures, and it's the same thing: the presence of a valid group separator throws an FormatException; although it works for the other things (decimal separator, date and time separators). For the thousand separator, you must specify a NumberStyles.AllowThousands value. But as i said before, the Convert.ChangeType have no overload with this kind of parameter.
Loic
It works with doubles, but try with integers: It will faill!the following code throws an format exception: var i = (int)Convert.ChangeType("1 234", typeof(int), new Culture("fr-FR");The following code works: var d = (double)Convert.ChangeType("1 234", typeof(double), new Culture("fr-FR");
Loic
@Loic: you are right, maybe Convert.ChangeType is not specifying NumberStyles.AllowThousands in its implementation. There's not much you can do about it, I'm afraid you'll have to handle it yourself, without using Convert.ChangeType.
Paolo Tedesco
I verify with Reflector: Convert.ChangeType calls ToInt32( ) on IConvertible. With a string on input, IConvertible.ToInt32( ) finishes to call Int32.Parse(...) with NumberStyles.Integer as parameter.NumberStyles.Integer does not allow thousand separator; NumberStyles.Number (use with double.Parse) allows thousand separator, that's why parsing to double works and parsing to int fails.I'll try another solution...
Loic