none
convert string to T , usfull RRS feed

  • General discussion

  •     public static class StringConverter
        {
            /// <summary>
            /// converts a string to an parsed instance of T  
            /// </summary>
            /// <typeparam name="T">convert to this type</typeparam>
            /// <param name="value">string value</param>
            /// <returns>instance of T from the string value</returns>
            public static T To<T>(this string value) where T : IConvertible
            {
                var t = typeof(T);
                if (t.Namespace != "System" || t.Module.Name != "mscorlib.dll")//make sure it's .net IConvertible type, not user's
                {
                    return default(T);
                }

                var type = Type.GetTypeCode(t);

                //handle speciel cases
                if (type == TypeCode.Empty)
                    return (T)(object)null;
                if (type == TypeCode.DBNull)
                    return (T)(object)DBNull.Value;

                if (string.IsNullOrEmpty(value) && type != TypeCode.String && type != TypeCode.Object)
                    return default(T);//return empty if string empty and not null

                //convert to type
                switch (type)
                {
                    case TypeCode.String:
                    case TypeCode.Object:
                        return (T)(object)value;

                    case TypeCode.Boolean:
                        bool boolean;
                        bool.TryParse(value, out boolean);
                        return (T)(object)boolean;

                    case TypeCode.Char:
                        return (T)(object)value[0];

                    case TypeCode.SByte:
                        return (T)(object)sbyte.Parse(value, NumberStyles.Integer, Thread.CurrentThread.CurrentCulture);
                    case TypeCode.Byte:
                        return (T)(object)byte.Parse(value, NumberStyles.Integer, Thread.CurrentThread.CurrentCulture);
                    case TypeCode.Int16:
                        return (T)(object)short.Parse(value, NumberStyles.Integer, Thread.CurrentThread.CurrentCulture);
                    case TypeCode.UInt16:
                        return (T)(object)ushort.Parse(value, NumberStyles.Integer, Thread.CurrentThread.CurrentCulture);
                    case TypeCode.Int32:
                        return (T)(object)int.Parse(value, NumberStyles.Integer, Thread.CurrentThread.CurrentCulture);
                    case TypeCode.UInt32:
                        return (T)(object)uint.Parse(value, NumberStyles.Integer, Thread.CurrentThread.CurrentCulture);
                    case TypeCode.Int64:
                        return (T)(object)long.Parse(value, NumberStyles.Integer, Thread.CurrentThread.CurrentCulture);
                    case TypeCode.UInt64:
                        return (T)(object)ulong.Parse(value, NumberStyles.Integer, Thread.CurrentThread.CurrentCulture);

                    case TypeCode.Single:
                        return (T)(object)float.Parse(value, NumberStyles.Float | NumberStyles.AllowThousands, Thread.CurrentThread.CurrentCulture);
                    case TypeCode.Double:
                        return (T)(object)double.Parse(value, NumberStyles.Float | NumberStyles.AllowThousands, Thread.CurrentThread.CurrentCulture);
                    case TypeCode.Decimal:
                        return (T)(object)decimal.Parse(value, NumberStyles.Number, Thread.CurrentThread.CurrentCulture);

                    case TypeCode.DateTime:
                        return (T)(object)DateTime.Parse(value, Thread.CurrentThread.CurrentCulture);
                }

                return default(T);
            }
        }

        public static class StringConverter
        {
            /// <summary>
            /// converts a string to an parsed instance of T   
            /// </summary>
            /// <typeparam name="T">convert to this type</typeparam>
            /// <param name="value">string value</param>
            /// <returns>instance of T from the string value</returns>
            public static T To<T>(this string value) where T : IConvertible
            {
                var t = typeof(T);
                if (t.Namespace != "System" || t.Module.Name != "mscorlib.dll")//make sure it's .net IConvertible type, not user's
                {
                    return default(T);
                }
    
                var type = Type.GetTypeCode(t);
    
                //handle speciel cases
                if (type == TypeCode.Empty)
                    return (T)(object)null;
                if (type == TypeCode.DBNull)
                    return (T)(object)DBNull.Value;
    
                if (string.IsNullOrEmpty(value) && type != TypeCode.String && type != TypeCode.Object)
                    return default(T);//return empty if string empty and not null
    
                //convert to type
                switch (type)
                {
                    case TypeCode.String:
                    case TypeCode.Object:
                        return (T)(object)value;
    
                    case TypeCode.Boolean:
                        bool boolean;
                        bool.TryParse(value, out boolean);
                        return (T)(object)boolean;
    
                    case TypeCode.Char:
                        return (T)(object)value[0];
    
                    case TypeCode.SByte:
                        return (T)(object)sbyte.Parse(value, NumberStyles.Integer, Thread.CurrentThread.CurrentCulture);
                    case TypeCode.Byte:
                        return (T)(object)byte.Parse(value, NumberStyles.Integer, Thread.CurrentThread.CurrentCulture);
                    case TypeCode.Int16:
                        return (T)(object)short.Parse(value, NumberStyles.Integer, Thread.CurrentThread.CurrentCulture);
                    case TypeCode.UInt16:
                        return (T)(object)ushort.Parse(value, NumberStyles.Integer, Thread.CurrentThread.CurrentCulture);
                    case TypeCode.Int32:
                        return (T)(object)int.Parse(value, NumberStyles.Integer, Thread.CurrentThread.CurrentCulture);
                    case TypeCode.UInt32:
                        return (T)(object)uint.Parse(value, NumberStyles.Integer, Thread.CurrentThread.CurrentCulture);
                    case TypeCode.Int64:
                        return (T)(object)long.Parse(value, NumberStyles.Integer, Thread.CurrentThread.CurrentCulture);
                    case TypeCode.UInt64:
                        return (T)(object)ulong.Parse(value, NumberStyles.Integer, Thread.CurrentThread.CurrentCulture);
    
                    case TypeCode.Single:
                        return (T)(object)float.Parse(value, NumberStyles.Float | NumberStyles.AllowThousands, Thread.CurrentThread.CurrentCulture);
                    case TypeCode.Double:
                        return (T)(object)double.Parse(value, NumberStyles.Float | NumberStyles.AllowThousands, Thread.CurrentThread.CurrentCulture);
                    case TypeCode.Decimal:
                        return (T)(object)decimal.Parse(value, NumberStyles.Number, Thread.CurrentThread.CurrentCulture);
    
                    case TypeCode.DateTime:
                        return (T)(object)DateTime.Parse(value, Thread.CurrentThread.CurrentCulture);
                }
    
                return default(T);
            }
        }

    • Edited by igalk474 Thursday, June 21, 2012 10:58 AM
    Tuesday, June 19, 2012 9:22 AM

All replies

  • Hmm.  I'd have to disagree.

    • The declaration allows any IConvertable type to be supplied, but does not handle user-created classes that implement IConvertable.  No exception is raised.
    • For boolean conversions, the code ignores the return value from TryParse which indicates a successful conversion.
    • For numeric conversions, the code performs unnecessary boxing/unboxing of the result; UInt64 values are handled incorrectly; and undocumented exceptions are raised when argument is non-numeric;

    In general, while use of the code may save you some typing, using it will make your project harder to maintain over the long run.  IMO, you'd be better off sticking to more precise conversions already available in the framework.  It will better convey the intent of the operation to anyone who reads it.


    This signature unintentionally left blank.


    • Edited by Nick F. _ Tuesday, June 19, 2012 12:37 PM
    Tuesday, June 19, 2012 12:36 PM
  • thanks, i will improve those,

    for bool , it was on purpose  so that invalid input returns the default false, otherwise it will return true/false

    the boxing is neccesery , it wont let it return int as T, but when it's an objectit will always be able to convert to T

    and it will compile

    i will fix those problems( mainly the UInt64 , user's Iconvertible, maintenability & comments)

    it's as precise as system.convert.to...  class functions and convert.changetype

    and it's faster and lite

    with this extension method you can use it like this:

    "123".To<int>();

    thanks

    Tuesday, June 19, 2012 8:11 PM
  • I think you miss my point about maintainability.

    If I write:

       x = Convert.ToInt32(someValue);

    pretty much anyone familliar with .NET will immediately understand exactly what this line of code is doing.  Those that do not can highlight in the IDE and hit F1, access the MSDN documentation and see what Convert.ToInt32 does.

    On the other hand to understand:

       x = someValue.To<Int32>();

    requires a knowledge of your custom extension and/or access to your source or documentation.  As the author, you have that knowledge.  Someone filling in for you will not.

    As to it being faster.  I tested this claim using the program below.  My test repeatedly reports that the extension code is the slowest of the three methods.

    class Program
    {
        static void Main(string[] args)
        {
            string value = "1,123.0";
            long start, stop;
            Stopwatch watch = new Stopwatch();
            double x;
            int i, loop = 1000000;
    
            Thread.CurrentThread.Priority = ThreadPriority.Highest;
    
            x = 0.0;
            watch.Reset();
            watch.Start();
            for (i = 0; i < loop; i++)
                x = x + value.To<double>();
            watch.Stop();
            Console.Out.WriteLine("{0} ticks", watch.ElapsedTicks);
    
            x = 0.0;
            watch.Reset();
            watch.Start();
            for (i = 0; i < loop; i++)
                x = x + double.Parse(value, NumberStyles.Float | NumberStyles.AllowThousands, Thread.CurrentThread.CurrentCulture);
            watch.Stop();
            Console.Out.WriteLine("{0} ticks", watch.ElapsedTicks);
    
            x = 0.0;
            watch.Reset();
            watch.Start();
            for (i = 0; i < loop; i++)
                x = x + Convert.ToDouble(value);
            Console.Out.WriteLine("{0} ticks", watch.ElapsedTicks);
        }
    }
    


    This signature unintentionally left blank.


    • Edited by Nick F. _ Wednesday, June 20, 2012 12:30 PM Stopwatched
    Wednesday, June 20, 2012 10:38 AM
  • I think you miss my point about maintainability.

    If I write:

       x = Convert.ToInt32(someValue);

    pretty much anyone familliar with .NET will immediately understand exactly what this line of code is doing.  Those that do not can highlight in the IDE and hit F1, access the MSDN documentation and see what Convert.ToInt32 does.

    On the other hand to understand:

       x = someValue.To<Int32>();

    requires a knowledge of your custom extension and/or access to your source or documentation.  As the author, you have that knowledge.  Someone filling in for you will not.

    As to it being faster.  I tested this claim using the program below.  My test repeatedly reports that the extension code is the slowest of the three methods.

        class Program
        {
            [DllImport("Kernel32.dll")]
            private static extern bool QueryPerformanceCounter(
                out long lpPerformanceCount);
    
            static void Main(string[] args)
            {
                string value = "1,123.0";
                long start, stop;
                double x;
                int i, loop = 1000000;
                
                Thread.CurrentThread.Priority = ThreadPriority.Highest;
    
                x = 0.0; 
                QueryPerformanceCounter(out start);
                for (i = 0; i < loop; i++)
                    x = x + value.To<double>();
                QueryPerformanceCounter(out stop);
                Console.Out.WriteLine("{0} ticks", stop - start);
    
                x = 0.0;
                QueryPerformanceCounter(out start);
                for (i = 0; i < loop; i++)
                    x = x + double.Parse(value, NumberStyles.Float | NumberStyles.AllowThousands, Thread.CurrentThread.CurrentCulture);
                QueryPerformanceCounter(out stop);
                Console.Out.WriteLine("{0} ticks", stop - start);
    
                x = 0.0;
                QueryPerformanceCounter(out start);
                for (i = 0; i < loop; i++)
                    x = x + Convert.ToDouble(value);
                QueryPerformanceCounter(out stop);
                Console.Out.WriteLine("{0} ticks", stop - start);
            }
        }


    This signature unintentionally left blank.


    Using the Stopwatch .NET wrapper of QueryPerformanceCounter would make your code more readable.
    Wednesday, June 20, 2012 12:05 PM
  • Good suggestion.  Been using QueryPerformanceCounter since compilers were 16bit and only went up hill  ;-)


    This signature unintentionally left blank.

    Wednesday, June 20, 2012 12:35 PM
  • thanks

    try to test it's performance against

    Convert.ChangeType

    with type or typecode

    as it was ment to replace it

    because convert.todouble & double.parse happens at the end, and the same in changetype

    it's only a part of the function and doesn't do the same

    the function in the first post was updated


    • Edited by igalk474 Thursday, June 21, 2012 10:58 AM
    Thursday, June 21, 2012 10:56 AM
  • I took your suggestion, and the updated extension code and re-ran the performance test using the updated program below.

    Here are the results for a typical run on my laptop.  (lower numbers = faster code)

    value.To<double>  :  1,787,648 ticks
    dobule.Parse      :    578,134 ticks
    Convert.ToDouble  :    582,840 ticks
    Convert.ChangeType:    659,083 ticks

    As shown here, the extension method is significantly slower than the microsoft provided conversion.  So while your code may work very well in your specific project, I still do not see a compelling reason to chose this kind of conversion over the ones that already exist in the .NET framework.

    static void Main(string[] args)
    {
        string value = "1,234,567.89";
        Stopwatch watch = new Stopwatch();
        double x;
        int i, loop = 1000000;
    
        Thread.CurrentThread.Priority = ThreadPriority.Highest;
    
        x = 0.0;
        watch.Reset();
        watch.Start();
        for (i = 0; i < loop; i++)
            x = x + value.To<double>();
        watch.Stop();
        Console.Out.WriteLine("value.To<double>  : {0,10:N0} ticks", watch.ElapsedTicks);
    
        x = 0.0;
        watch.Reset();
        watch.Start();
        for (i = 0; i < loop; i++)
            x = x + double.Parse(value, NumberStyles.Float | NumberStyles.AllowThousands, Thread.CurrentThread.CurrentCulture);
        watch.Stop();
        Console.Out.WriteLine("dobule.Parse      : {0,10:N0} ticks", watch.ElapsedTicks);
    
        x = 0.0;
        watch.Reset();
        watch.Start();
        for (i = 0; i < loop; i++)
            x = x + Convert.ToDouble(value);
        watch.Stop();
        Console.Out.WriteLine("Convert.ToDouble  : {0,10:N0} ticks", watch.ElapsedTicks);
    
        x = 0.0;
        watch.Reset();
        watch.Start();
        for (i = 0; i < loop; i++)
            x = x + (double)Convert.ChangeType(value, TypeCode.Double, Thread.CurrentThread.CurrentCulture);
        watch.Stop();
        Console.Out.WriteLine("Convert.ChangeType: {0,10:N0} ticks", watch.ElapsedTicks);
    }


    This signature unintentionally left blank.


    • Edited by Nick F. _ Friday, June 22, 2012 12:17 PM formatting
    Friday, June 22, 2012 12:16 PM
  • thanks!

    i will probably use ChangeType as it is , or with extension method like this:

    public static T To<T>(this string value) where T : IConvertible
    {

            return (T)Convert.ChangeType(value, typeof(T), Thread.CurrentThread.CurrentCulture);

    }

    Sunday, June 24, 2012 1:02 PM