C# 超花哨的 LINQiness

发布于 2024-09-10 16:48:16 字数 2023 浏览 2 评论 0 原文

我正在尝试编写一种动态排序的命令行处理器,其中我有一个字典,其中键是可能的参数,成员是一个操作,其中字符串是命令行上传递的参数之间的文本。希望能够通过添加 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 的最优雅的机制是什么,而不仅仅是关联属于任何字典键数组的成员,而是 Aggregate((x,y) => string.Format("{0} {1}", x, y)) 元素序列(认为 TakeWhile() 以某种方式适合这里)位于将在任何键数组中 Contain()ed 的 args[] 成员之间,并将它们传递到相应键的值成员的操作。

我们都已经无数次编写过这些小型命令行处理器,虽然显然简单的循环和切换总是绰绰有余,但正如我所说,这又是一次试图强调我的 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 或其他方式稍微简化它,尽管这可能是我猜的最简单的形式。

I'm trying to write a dynamic sort of command line processor where I have a dictionary with keys being possible parameters, and the member being an Action where the string is the text between the parameters passed on the command line. Want to be able to add parameters just by adding the params array, and writing the action in the dictionary.

Yes I realize this is a pointless exercise in overcomplicating implementation to simplify maintenance. Mostly just trying to stress myself to learn more linq.

Here's my dictionary:

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

What's the most elegant mechanism to take string[] args and not just correlate the members that fall within any of the dictionary key arrays, but Aggregate((x,y) => string.Format("{0} {1}", x, y)) the sequence of elements (was thinking TakeWhile() fits in here somehow) inbetween the args[] members that would be Contain()ed in any of the keys arrays, and handing them into the action of the respective key's value member.

We have all written these little command line processors countless times, and while obviously a simple loop and switch is always more than adequate, this is again as I said an exercise trying to stress my linq skills. So please no complaints that I'm overengineering, that part is obvious.

Update:
To make this maybe a little easier, here is a non-linq way of doing what I'm looking for (may be imperfect, this is just winging it):

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]);
    }
}

This is not an altogether bad approach, I just wonder if anyone can maybe simplify it a little using linq or otherwise, though this may be the simplest form i guess..

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(2

绿光 2024-09-17 16:48:16

借用 Adam Robinson 答案的一半(顺便说一句+1),但意识到字典永远不会通过键访问,并且您只想运行操作而不是构建字符串...

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< /code> 确实违背了字典的全部要点。我将其重新设计为 Dictionary>。然后你可以说

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

Borrowing half of Adam Robinson's answer (+1 btw), but realizing that the Dictionary will never be accessed by key, and you just want to run the Actions instead of building up a string...

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 really defeats the whole point of Dictionary. I'd re-design that to be a Dictionary<string, Action<string>>. Then you could say

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

PS: I can recall much more obtuse C# code that I have written than this.


I must admit the possibility that you want to collect the actions, instead of running them. Here is that code:

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();
巾帼英雄 2024-09-17 16:48:16

假设您知道每个命令都有一个相应的参数(因此“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[] 。 ,而不仅仅是一个字符串,所以我添加了一些甚至更迟钝的代码来处理这个问题。

Assuming you know that every command has a corresponding argument (so 'args' will always be in the format of

cmd arg (repeated)

You could do something ridiculous like this...

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

But that is, frankly, the most obtuse bit of C# that I can recall ever writing, and not a very good way to teach yourself LINQ. Nonetheless, it will do what you're asking.

EDIT

Just realized (thanks to David B) that your key is a string[], not just a string, so I added some even more obtuse code that deals with that.

~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文