Trying to write a pleasant parser
I am writing a simple parser that will take a string of the format 20100101,1,2,foo
and create an instance of the following class:
public class Foo
{
public DateTime TheDate { get; set; }
public int TheFirstInt { get; set; }
public int TheSecondInt { get; set; }
public string TheString { get; set; }
}
I would like to be able to declare my parsers for each property as an开发者_运维百科 array of (for example) Func<>s
in an attempt to make the code more readable (from a point of view of correlating items in the string with the parsing code that is used).
// Production code would contain parsers with error checking etc.
Func<string, object>[] parsers = new Func<string, object>[]
{
s => DateTime.ParseExact(s, "yyyyMMdd", CultureInfo.InvariantCulture),
s => int.Parse(s),
s => int.Parse(s),
s => s
};
I would then like to be able to iterate through the parsers, properties of FooClass
and values in fooItems
in one loop:
Foo fooInstance = new Foo();
string[] fooItems = fooString.Split(',');
for (int i = 0; i < parsers.Length; i++)
{
fooInstance.Properties[i] = parsers[i](fooItems[i]);
// range-checking and error handling excluded from this example
}
However, this of course won't work because:
- It doesn't address how you might be able to iterate through the properties of
fooInstance
- It doesn't deal with casting of values parsed
Any thoughs on how to write a "pleasant" parser like this?
I'd try to use Action
s instead of Func
s and set the properties directly:
Action<string, FooClass>[] actions = new Action<string, FooClass>[] {
(s, c) => c.TheDate = DateTime.ParseExact(s, "yyyyMMdd", CultureInfo.InvariantCulture),
(s, c) => c.TheFirstInt = Int32.Parse(s)
// ...
}
for (int i = 0; i < fooItems.Length; ++i)
actions[i](fooItems[i], fooInstance);
I know this isn't answering your question directly, but if you find your "language" gets much more complicated then I recommend using Irony to parse it: http://www.codeplex.com/irony
If your language is going to stay in a flat format (like CSV), then it's worth looking at http://www.filehelpers.com/
In your example, you'd just need to annotate your class:
[DelimitedRecord(",")]
public class Foo
{
[FieldConverter(ConverterKind.Date, "yyyyMMdd")]
public DateTime TheDate { get; set; }
public int TheFirstInt { get; set; }
public int TheSecondInt { get; set; }
public string TheString { get; set; }
}
then parse it with:
FileHelperEngine engine = new FileHelperEngine(typeof(Foo));
Foo[] fs = engine.ReadFile("FileIn.txt") as Foo[];
You need to use reflection : eg :
fooInstance.GetType().GetProperty("SomeProp").SetValue(fooInstance, "SomeProp", val);
精彩评论