C# string Parsing to variable types

后端 未结 7 815
暖寄归人
暖寄归人 2021-02-02 04:55

I want to parse a string into a type easily, but I don\'t want to write wrapper code for each type, I just want to be able to do \"1234\".Parse() or the like and have it return

7条回答
  •  甜味超标
    2021-02-02 05:33

    My solution works for any type that implements the static method TryParse(string, out T), whether it's a class or a struct. Also, it will work for nullable structs, e.g.

    "1234".Parse() == 1234
    "asdf".Parse() == 0 // i.e. default(int)
    "1234".Parse() == 1234
    "asdf".Parse() == null
    "2001-02-03".Parse() == new DateTime(2009, 2, 3)
    

    and since System.Net.IPAddress has TryParse,

    "127.0.0.1".Parse().Equals(new IPAddress(new byte[] { 127, 0, 0, 1 }))
    

    I create the code with the System.Linq.Expressions framework, and then cache the created lambda. Since this is done in a generic static class with a specified type, this happens only once per type to parse.

    public static class StringExtensions
    {
        /// 
        /// Parse the  as a . If this cannot be achieved, return the default value of .
        /// 
        /// The type to parse into.
        /// The string to parse.
        /// The resultant  or the default of .
        /// 
        /// 
        /// "1234".Parse<int>() == 1234;
        /// "a".Parse<int>() == 0;
        /// "a".Parse<int?>() == null;
        /// "2010-01-01".Parse<DateTime?>() == new DateTime(2010, 1, 1)
        /// "2010-01-01a".Parse<DateTime?>() == null
        /// "127.0.0.1".Parse<System.Net.IPAddress>().Equals(new System.Net.IPAddress(new byte[] { 127, 0, 0, 1 }))
        /// "".Parse<System.Net.IPAddress>() == null
        /// 
        /// 
        public static T Parse(this string target)
        {
            return ParseHelper.Parse(target);
        }
    
        /// 
        /// Parse the  as a . If this cannot be achieved, return 
        /// 
        /// The type to parse into.
        /// The string to parse.
        /// The value to return if  could not be parsed.
        /// The resultant  or .
        /// 
        /// 
        /// "1234".Parse<int>(-1) == 1234;
        /// "a".Parse<int>(-1) == -1;
        /// "2010-01-01".Parse<DateTime?>(new DateTime(1900, 1, 1)) == new DateTime(2010, 1, 1)
        /// "2010-01-01a".Parse<DateTime?>(new DateTime(1900, 1, 1)) == new DateTime(1900, 1, 1)
        /// "127.0.0.1".Parse<System.Net.IPAddress>(new System.Net.IPAddress(new byte[] { 0, 0, 0, 0 })).Equals(new System.Net.IPAddress(new byte[] { 127, 0, 0, 1 }))
        /// "".Parse<System.Net.IPAddress>(new System.Net.IPAddress(new byte[] { 0, 0, 0, 0 })).Equals(new System.Net.IPAddress(new byte[] { 0, 0, 0, 0 }))
        /// 
        /// 
        public static T Parse(this string target, T defaultValue)
        {
            return ParseHelper.Parse(target, defaultValue);
        }
    
        private static class ParseHelper
        {
            private static readonly Func _parser;
    
            static ParseHelper()
            {
                Type type = typeof(T);
                bool isNullable = false;
                if (type.GetGenericArguments().Length > 0 && type.GetGenericTypeDefinition() == typeof(Nullable<>))
                {
                    isNullable = true;
                    type = type.GetGenericArguments()[0];
                }
    
                ParameterExpression target = Expression.Parameter(typeof(string), "target");
                ParameterExpression defaultValue = Expression.Parameter(typeof(T), "defaultValue");
                ParameterExpression result = Expression.Parameter(typeof(T), "result");
                if (isNullable)
                {
                    Type helper = typeof(NullableParseHelper<>);
                    helper = helper.MakeGenericType(typeof(T), type);
                    MethodInfo parseMethod = helper.GetMethod("Parse");
                    _parser = Expression.Lambda>(
                        Expression.Call(parseMethod, target, defaultValue),
                        target, defaultValue, result).Compile();
                }
                else
                {
                    MethodInfo tryParseMethod = (from m in typeof(T).GetMethods()
                                                 where m.Name == "TryParse"
                                                 let ps = m.GetParameters()
                                                 where ps.Count() == 2
                                                    && ps[0].ParameterType == typeof(string)
                                                    && ps[1].ParameterType == typeof(T).MakeByRefType()
                                                 select m).SingleOrDefault();
    
                    if (tryParseMethod == null)
                    {
                        throw new InvalidOperationException(string.Format("Cannot find method {0}.TryParse(string, out {0})", type.FullName));
                    }
                    _parser = Expression.Lambda>(
                        Expression.Condition(
                            Expression.Call(tryParseMethod, target, result),
                            result,
                            defaultValue),
                        target, defaultValue, result).Compile();
                }
            }
    
            public static T Parse(string target)
            {
                return _parser.Invoke(target, default(T), default(T));
            }
    
            public static T Parse(string target, T defaultValue)
            {
                return _parser.Invoke(target, defaultValue, default(T));
            }
    
            private static class NullableParseHelper where TBase : struct
            {
                private static readonly Func _parser;
    
                static NullableParseHelper()
                {
                    MethodInfo tryParseMethod = (from m in typeof(TBase).GetMethods()
                                                 where m.Name == "TryParse"
                                                 let ps = m.GetParameters()
                                                 where ps.Count() == 2
                                                    && ps[0].ParameterType == typeof(string)
                                                    && ps[1].ParameterType == typeof(TBase).MakeByRefType()
                                                 select m).SingleOrDefault();
    
                    if (tryParseMethod == null)
                    {
                        throw new InvalidOperationException(string.Format("Cannot find method {0}.TryParse(string, out {0})", typeof(TBase).FullName));
                    }
                    ParameterExpression target = Expression.Parameter(typeof(string), "target");
                    ParameterExpression defaultValue = Expression.Parameter(typeof(TBase?), "defaultValue");
                    ParameterExpression result = Expression.Parameter(typeof(TBase), "result");
                    _parser = Expression.Lambda>(
                        Expression.Condition(
                            Expression.Call(tryParseMethod, target, result),
                            Expression.ConvertChecked(result, typeof(TBase?)),
                            defaultValue),
                        target, defaultValue, result).Compile();
                }
    
                public static TBase? Parse(string target, TBase? defaultValue)
                {
                    return _parser.Invoke(target, defaultValue, default(TBase));
                }
            }
        }
    }
    

    pants!

提交回复
热议问题