C#超级花哨的LINQiness

时间:2010-07-21 20:16:10

标签: c# linq dictionary lambda

我正在尝试编写一个动态类型的命令行处理器,其中我有一个字典,其中键是可能的参数,而成员是一个Action,其中字符串是在命令行上传递的参数之间的文本。希望能够通过添加params数组并在字典中编写动作来添加参数。

是的,我意识到这是一个过于复杂的实施,以简化维护,这是毫无意义的练习。大多只是试图强调自己学习更多linq。

这是我的字典:

    private static Dictionary<string[], Action<string>> _commandLineParametersProcessor = new Dictionary<string[], Action<string>>()
{
    {
        new string[] {"-l", "--l", "-log", "--log"},
        (logFile) =>  
            {
                _blaBla.LogFilePath = logFile;
            }
    },
    {
        new string[] { "-s", "--s", "-server", "--server" },
        (server) =>  
            {
                ExecuteSomething(server);
                _blaBla.Server = server;
            }
    }
};

采用string [] args的最优雅机制是什么,而不只是关联任何字典键数组中的成员,而是聚合((x,y)=&gt; string.Format(“{0} { 1}“,x,y))在任何键数组中包含()编辑的args []成员之间的元素序列(正在思考TakeWhile()在某种程度上适合这里),并将它们交给动作相应的键值成员。

我们已经无数次地编写了这些小命令行处理器,虽然显然一个简单的循环和切换总是绰绰有余,但这又是我所说的练习强调我的linq技能。所以请不要抱怨我过度工程,那部分是显而易见的。

更新: 为了使这可能更容易一些,这是一种非linq方式来做我正在寻找的东西(可能是不完美的,这只是它的翅膀):

Action<string> currentAction;
string currentActionParameter;
for(int i = 0; i < e.Args.Length; i++)
{
    bool isParameterSwitch = _commandLineParametersProcessor.Keys.Any((parameterChoices) => parameterChoices.Contains(e.Args[i]));

    if (isParameterSwitch)
    {
        if (!string.IsNullOrEmpty(currentActionParameter) && currentAction != null)
        {
            currentAction(currentActionParameter);

            currentAction = null;
            currentActionParameter = "";
        }
        currentAction = _commandLineParametersProcessor[_commandLineParametersProcessor.Keys.Single((parameterChoices) => parameterChoices.Contains(e.Args[i]))];
    }
    else
    {
        currentActionParameter = string.Format("{0} {1}", currentActionParameter, e.Args[i]);
    }
}

这不是一个完全糟糕的方法,我只是想知道是否有人可以使用linq或其他方式稍微简化它,虽然这可能是我猜的最简单的形式..

2 个答案:

答案 0 :(得分:2)

假设您知道每个命令都有相应的参数(因此'args'将始终采用

的格式
cmd arg (repeated)

你可以做这样荒谬的事......

var output = args.Select((value, idx) => new { Value = value, Group = idx / 2 })
            .GroupBy(x => x.Group)
            .Select(g => new 
             { 
                 Command = commands.FirstOrDefault(kvp => 
                    kvp.Key.Contains(g.First().Value)).Value, 
                 Argument = g.Last().Value 
             })
            .Where(c => c.Command != null)
            .Aggregate(
                new StringBuilder(), 
                (builder, value) => 
                { 
                    builder.AppendLine(value.Command(value.Argument)); 
                    return builder; 
                }).ToString();

但坦率地说,这是C#中最让我记忆深刻的一点,而且不是一个非常好的方法来教自己LINQ。尽管如此,它会做你要求的。

修改

刚刚意识到(感谢David B)您的密钥是string[],而不仅仅是string,所以我添加了一些更加迟钝的代码来处理

答案 1 :(得分:2)

借用Adam Robinson的答案的一半(+1 btw),但是意识到字词永远不会被键访问,而你只想运行Actions而不是构建一个字符串......

var inputCommands = args
    .Select((value, idx) => new { Value = value, Group = idx / 2 })
    .GroupBy(x => x.Group) 
    .Select(g => new  
    {  
      Command = g.First().Value,  
      Argument = g.Last().Value  
    }).ToList();

inputCommands.ForEach(x => 
{
  Action<string> theAction = 
  (
    from kvp in commands
    where kvp.Key.Contains(x.Command)
    select kvp.Value
  ).FirstOrDefault();
  if (theAction != null)
  {
    theAction(x.Argument);
  }
}

kvp.Key.Contains确实击败了词典的全部内容。我重新设计成Dictionary<string, Action<string>>。然后你可以说

inputCommands.ForEach(x => 
{
  if (commands.ContainsKey(x.Command))
  {
    commands[x.Command](x.Argument);
  }
}

PS:我记得我写的C#代码比我写的更多。


我必须承认你想要收集行动的可能性,而不是运行它们。这是代码:

var todo =
(
  from x in inputCommands
  let theAction = 
  (
    from kvp in commands
    where kvp.Key.Contains(x.Command)
    select kvp.Value
  ).FirstOrDefault()
  where theAction != null
  select new { TheAction = theAction, Argument = x.Argument }
).ToList();