2010-02-01 3 views
3

Я пишу простой парсер, который будет принимать строку формата 20100101,1,2,foo и создать экземпляр следующего класса:Попытка написать приятный анализатор

public class Foo 
{ 
    public DateTime TheDate { get; set; } 
    public int TheFirstInt { get; set; } 
    public int TheSecondInt { get; set; } 
    public string TheString { get; set; } 
} 

Я хотел бы иметь возможность заявить о своих парсерах для каждого свойства в виде массива (например) Func<>s, чтобы сделать код более читаемым (с точки зрения корреляции элементов в строке с используемым кодом разбора).

// 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 
}; 

Я тогда хотел, чтобы иметь возможность перебирать анализаторы, свойство FooClass и значение в fooItems в одном цикле:

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 
} 

Однако, это, конечно, не будет работать, потому что:

  • Это не решает, как вы могли бы перебрать свойства fooInstance
  • Не имеет значения для литья значений, проанализированных

Любые идеи о том, как написать «приятный» парсер, как это?

+1

Первое, что я подумал, когда увидел, что этот вопрос был «Простите меня, кажется, что введенный вами ввод содержит«; » к которому я не привык ». –

ответ

2

Я хотел бы попробовать использовать Action с вместо Func с и установить свойства напрямую:

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); 
+0

Ты избил меня. +1 –

0

Вы должны использовать отражение: например:

fooInstance.GetType().GetProperty("SomeProp").SetValue(fooInstance, "SomeProp", val); 
2

Я знаю, что это не отвечая на ваш вопрос напрямую, но если вы обнаружите, что ваш «язык» становится намного сложнее, я рекомендую использовать Иронию для его анализа: http://www.codeplex.com/irony

Если y наш язык будет оставаться в плоском формате (например, CSV), то это стоит посмотреть на http://www.filehelpers.com/

В вашем примере, вы бы просто нужно аннотировать класс:

[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; } 
} 

затем разобрать его с :

FileHelperEngine engine = new FileHelperEngine(typeof(Foo)); 
Foo[] fs = engine.ReadFile("FileIn.txt") as Foo[]; 
+0

@ Rob: По моему сценарию я счастлив использовать подход, предложенный MartinStettner, но я буду держать FileHelpers в виду для более сложных сценариев в будущем. –