C # Super Fancy LINQiness - PullRequest
       5

C # Super Fancy LINQiness

1 голос
/ 22 июля 2010

Я пытаюсь написать динамический вид процессора командной строки, где у меня есть словарь с ключами, являющимися возможными параметрами, и членом, являющимся действием, где строка является текстом между параметрами, передаваемыми в командной строке.Хотите иметь возможность добавлять параметры, просто добавляя массив 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;
            }
    }
};

Какой самый элегантный механизм для получения строковых [] аргументов, а не только для корреляции членов, которые попадают влюбой из ключевых массивов словаря, но Aggregate ((x, y) => string.Format ("{0} {1}", x, y)) последовательность элементов (считая, что TakeWhile () как-то здесь подходит)между членами args [], которые будут содержать Contain () ed в любом из массивов ключей, и передать их в действие соответствующего значения элемента ключа.

Мы все написали эти маленькие процессоры командной строки бесчисленное количество рази, хотя очевидно, что простой цикл и переключение всегда более чем адекватны, это снова, как я сказал, упражнение, пытающееся подчеркнуть мои навыки 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 ]

2 голосов
/ 22 июля 2010

Занимая половину ответа Адама Робинсона (+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 действительно побеждает весь смысл словаря. Я бы изменил дизайн, чтобы он стал 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();
2 голосов
/ 22 июля 2010

Предполагая, что вы знаете, что у каждой команды есть соответствующий аргумент (поэтому '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. Тем не менее, он будет делать то, что вы просите.

EDIT

Только что понял (благодаря Дэвиду Б), что ваш ключ - string[], а не просто string, поэтому я добавил еще более тупой код, который с этим справляется.

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...