0

I have the code below for parsing different types. However I realized when deploying to Azure the double etc was different on my local PC. My best bet is due to CultureInfo. I would like to add CultureInfo.InvariantCulture to the code below. However I have not found a way to do this via the delegate approach. Any pointers how to fix this? Or is it not possible for all the types and I need to handle each type differently?

using System;
using System.Collections.Generic;
using System.Text;

namespace Common.Parse
{
    public delegate bool ParseDelegate<T>(string s, out T result);

    public static class TryParseExtensions
    {
        public static int? TryParseOrDefaultInt32(this string value)
        {
            return TryParseOrDefault<int>(value, int.TryParse);
        }

        public static Int16? TryParseOrDefaultInt16(this string value)
        {
            return TryParseOrDefault<Int16>(value, Int16.TryParse);
        }

        public static Int64? TryParseOrDefaultInt64(this string value)
        {
            return TryParseOrDefault<Int64>(value, Int64.TryParse);
        }

        public static byte? TryParseOrDefaultByte(this string value)
        {
            return TryParseOrDefault<byte>(value, byte.TryParse);
        }

        public static bool? TryParseOrDefaultBoolean(this string value)
        {
            return TryParseOrDefault<bool>(value, bool.TryParse);
        }

        public static Single? TryParseOrDefaultSingle(this string value)
        {
            return TryParseOrDefault<Single>(value, Single.TryParse);
        }

        public static Double? TryParseOrDefaultDouble(this string value)
        {
            return TryParseOrDefault<Double>(value, Double.TryParse);
        }

        public static float? TryParseOrDefaultFloat(this string value)
        {
            return TryParseOrDefault<float>(value, float.TryParse);
        }

        public static Decimal? TryParseOrDefaultDecimal(this string value)
        {
            return TryParseOrDefault<Decimal>(value, Decimal.TryParse);
        }

        public static DateTime? TryParseOrDefaultDateTime(this string value)
        {
            return TryParseOrDefault<DateTime>(value, DateTime.TryParse);
        }

        public static int TryParseInt32(this string value)
        {
            return TryParse<int>(value, int.TryParse);
        }

        public static Int16 TryParseInt16(this string value)
        {
            return TryParse<Int16>(value, Int16.TryParse);
        }

        public static Int64 TryParseInt64(this string value)
        {
            return TryParse<Int64>(value, Int64.TryParse);
        }

        public static byte TryParseByte(this string value)
        {
            return TryParse<byte>(value, byte.TryParse);
        }

        public static bool TryParseBoolean(this string value)
        {
            return TryParse<bool>(value, bool.TryParse);
        }

        public static Single TryParseSingle(this string value)
        {
            return TryParse<Single>(value, Single.TryParse);
        }

        public static Double TryParseDouble(this string value)
        {
            return TryParse<Double>(value, Double.TryParse);
        }

        public static Decimal TryParseDecimal(this string value)
        {
            return TryParse<Decimal>(value, Decimal.TryParse);
        }

        public static DateTime TryParseDateTime(this string value)
        {
            return TryParse<DateTime>(value, DateTime.TryParse);
        }

        public static T TryParse<T>(this string value, ParseDelegate<T> parse) where T : struct
        {
            T result;
            parse(value, out result);
            return result;
        }

        public static T? TryParseOrDefault<T>(this string value, ParseDelegate<T> parse) where T : struct
        {
            T result;
            var succeed = parse(value, out result);

            if (succeed)
            {
                return result;
            }

            return null;
        }
    }
}
Thomas Segato
  • 4,567
  • 11
  • 55
  • 104
  • See also https://stackoverflow.com/questions/468791/is-there-a-way-of-setting-culture-for-a-whole-application-all-current-threads-a – Ian Mercer Mar 22 '21 at 19:59

2 Answers2

0

You will have to add delegates having NumberStyles and IFormatProvider parameters.

But the question is, why do you need to make the delegate approach public? For the rare cases that are not covered by your non-delegate methods, you could also use an approach based on Convert.ChangeType(Object, Type, IFormatProvider) instead.

public static T? TryParseOrDefault<T>(this string value) where T : struct
{
    try {
        return (T)Convert.ChangeType(value, typeof(T), CultureInfo.InvariantCulture);
    catch {
        return null;
    }
}

Also note that Single and float are the same. TryParseOrDefaultFloat is redundant.

Olivier Jacot-Descombes
  • 104,806
  • 13
  • 138
  • 188
0

Unfortunately you won't be able to solve it with a single delegate, because the types that your parser handles don't share a TryParse with a common signature that accepts an IFormatProvider (in your case, CultureInfo.InvariantCulture).

For instance, the DateTime and Double culture-aware parsing methods have different parameters order and type:

// Note the DateTimeStyles type
bool DateTime.TryParse(string? s, IFormatProvider? provider, DateTimeStyles styles, out DateTime result);

// Note the different order and NumberStyles type
bool Double.TryParse(string? s, NumberStyles style, IFormatProvider? provider, out double result);

Have you considered specifying the culture at the thread level? Something like this:

Thread.CurrentThread.CurrentCulture = new CultureInfo("de-DE");

Just beware that it won't affect only this class, but the entire thread.

If this is part of an ASP.NET application, you can even change the culture at the application level.

Alan Lacerda
  • 758
  • 1
  • 6
  • 12