有一种方法,但它很丑- 不幸的是。总结一下它的关键点:
- 您将必须创建自己的解析器。
- 构造函数初始化和不变性消失了——你会明白为什么。
- 将值传递给您的对象的方式非常hacky - 至少在我看来。
- 你不能保证每个键出现一次
- 它是如此庞大和复杂,以至于您不妨说“不,让我们在每一行结尾拆分,然后拆分
=
,然后从那里取出它。”
这个解决方案不是我的——我从来没有想过这样的事情。取自Mike Hadlow 的博客。EasyNetQ ConnectionStringGrammar.cs中缺少的部分——令人惊讶的是,Mike 是该文件的贡献者。
首先,您创建您的属性值类型。几乎你已经在你的第一个片段上完成了这个,但不管它是:
static Parser<string> Text = Parse.AnyChar.Except(Parse.LineTerminator).Many().Text()
对于数字
static Parser<int> Number = Parse.Number.Select(int.Parse);
然后是创建键值解析器的方法
public static Parser<ThingsVisitor> CreateKeyValueParser<T>(
string keyName,
Parser<T> valueParser,
Expression<Func<Things, T>> getter)
{
return
from key in Parse.String(keyName).Token()
from separator in Parse.Char('=')
from value in valueParser
select (ThingsVisitor)(t =>
{
CreateSetter(getter)(t, value);
return t;
});
}
ThingsVisitor
只是一个别名:using ThingsVisitor = System.Func<Things, Things>;
. Things
在 leu 中MyClass
。CreateSetter()
从 getter 的表达式中提取 setter。我把它留给读者——如果你点击链接,这并不难。
然后你使用该方法来创建你的属性解析器
Parser<ThingsVisitor> Props = new List<Parser<ThingsVisitor>>
{
CreateKeyValueParser("OneThing", Text, t => t.OneThing),
CreateKeyValueParser("AnotherThing", Number, t => t.AnotherThing),
CreateKeyValueParser("YetAnotherThing", Text, t => t.YetAnotherThing)
}.Aggregate((a, b) => a.Or(b));
然后为整个输入定义解析器
Parser<IEnumerable<ThingsVisitor>> PropsParser =
from first in Props
from rest in Parse.LineEnd.Then(_ => Props).Many()
select new[] { first }.Concat(rest);
最后,您可以解析输入
Things things = new();
IEnumerable<ThingsVisitor> parser = PropsParser.Parse(@"OneThing=Foo
AnotherThing=32
YetAnotherThing=Baz");
parser.Aggregate(things, (thing, visitorFunction) => visitorFunction(thing));