【问题标题】:C# Super fancy LINQinessC# 超级花哨的 LINQiness
【发布时间】:2010-07-21 20:16:09
【问题描述】:

我正在尝试编写一种动态排序的命令行处理器,其中我有一个字典,其中键是可能的参数,成员是一个 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[] 参数的最优雅的机制是什么,而不仅仅是关联任何字典键数组中的成员,而是 Aggregate((x,y) => 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 或其他方式简化它,尽管这可能是我猜的最简单的形式..

【问题讨论】:

  • LINQ 可能不是这类问题的正确答案...
  • 我不清楚您要做什么。您能否举一个具体的例子来说明“聚合 args[] 成员之间的元素序列,这些元素将包含在任何键数组中”位是什么意思?
  • 有时使用锤子是因为您想了解更多关于锤子的知识,但这并不意味着螺丝刀真的是更好的工具。我不确定 LINQ 会在这里做什么。您有一组由字符串数组键入的操作,并且您想要...什么?使用用户输入的可能匹配键数组中任何字符串的字符串从字典中选择并执行操作?嗯,实际上,这确实有道理。好吧,也许这里可以使用 LINQ ,但我对 LINQ 的了解不够,无法向您展示如何...
  • 作为参数开关的 args 之间的聚合指的是命令行类似于: bla.exe --log some log file.log --s myserver.com args 数组将有 3 个成员需要连接才能传递到 --log 的操作中,并且只有一个用于 --s 操作

标签: c# linq dictionary lambda


【解决方案1】:

借用 Adam Robinson 的一半答案(+1 顺便说一句),但意识到永远不会通过键访问字典,并且您只想运行 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&lt;string, Action&lt;string&gt;&gt;。那你可以说

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();

【讨论】:

  • 这个想法只是执行动作,尽管据我所知,你仍然缺少参数开关之间的 args 连接,例如bla.exe -l some junk for l --server server.com "some junk for l" 需要传递给操作 whos 键包含 -l 在字典中,尽管在 args 数组中是 3 个成员,什么的可变性需要连接作为参数是什么让这真的很棘手。
【解决方案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,所以我添加了一些更钝的代码来处理它。

【讨论】:

  • 我可以肯定地说,这是我所记得阅读中最迟钝的C#。 :D
猜你喜欢
  • 2013-10-28
  • 1970-01-01
  • 2012-09-13
  • 2011-07-24
  • 2015-07-15
  • 2010-12-09
  • 2012-07-19
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多