Fastest way to check if a string can be parsed

前端 未结 5 1534
礼貌的吻别
礼貌的吻别 2021-01-01 14:38

I am parsing CSV files to lists of objects with strongly-typed properties. This involves parsing each string value from the file to an IConvertible type (

5条回答
  •  心在旅途
    2021-01-01 15:21

    If you have a known set of types to convert, you can do a series of if/elseif/elseif/else (or switch/case on the type name) to essentially distribute it to specialized parsing methods. This should be pretty fast. This is as described in @Fabio's answer.

    If you still have performance issues, you can also create a lookup table which will let you add new parsing methods as you need to support them:

    Given some basic parsing wrappers:

    public delegate bool TryParseMethod(string input, out T value);
    
    public interface ITryParser
    {
        bool TryParse(string input, out object value);
    }
    
    public class TryParser : ITryParser
    {
        private TryParseMethod ParsingMethod;
    
        public TryParser(TryParseMethod parsingMethod)
        {
            this.ParsingMethod = parsingMethod;
        }
    
        public bool TryParse(string input, out object value)
        {
            T parsedOutput;
            bool success = ParsingMethod(input, out parsedOutput);
            value = parsedOutput;
            return success;
        }
    }
    

    You can then setup a conversion helper which does the lookup and calls the appropriate parser:

    public static class DataConversion
    {
        private static Dictionary Parsers;
    
        static DataConversion()
        {
            Parsers = new Dictionary();
            AddParser(DateTime.TryParse);
            AddParser(Int32.TryParse);
            AddParser(Double.TryParse);
            AddParser(Decimal.TryParse);
            AddParser((string input, out string value) => {value = input; return true;});
        }
    
        public static void AddParser(TryParseMethod parseMethod)
        {
            Parsers.Add(typeof(T), new TryParser(parseMethod));
        }
    
        public static bool Convert(string input, out T value)
        {
            object parseResult;
            bool success = Convert(typeof(T), input, out parseResult);
            if (success)
                value = (T)parseResult;
            else
                value = default(T);
            return success;
        }
    
        public static bool Convert(Type type, string input, out object value)
        {
            ITryParser parser;
            if (Parsers.TryGetValue(type, out parser))
                return parser.TryParse(input, out value);
            else
                throw new NotSupportedException(String.Format("The specified type \"{0}\" is not supported.", type.FullName));
        }
    }
    

    Then usage might be like:

    //for a known type at compile time
    int value;
    if (!DataConversion.Convert("3", out value))
    {
        //log failure
    }
    
    //or for unknown type at compile time:
    object value;
    if (!DataConversion.Convert(myType, dataValue, out value))
    {
        //log failure
    }
    

    This could probably have the generics expanded on to avoid object boxing and type casting, but as it stands this works fine; perhaps only optimize that aspect if you have a measurable performance from it.

    EDIT: You can update the DataConversion.Convert method so that if it doesn't have the specified converter registered, it can fall-back to your TypeConverter method or throw an appropriate exception. It's up to you if you want to have a catch-all or simply have your predefined set of supported types and avoid having your try/catch all over again. As it stands, the code has been updated to throw a NotSupportedException with a message indicating the unsupported type. Feel free to tweak as it makes sense. Performance wise, maybe it makes sense to do the catch-all as perhaps those will be fewer and far between once you specify specialized parsers for the most commonly used types.

提交回复
热议问题