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 (
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.