【问题标题】:Simplify multiple if statements that use if (string.contains())简化使用 if (string.contains()) 的多个 if 语句
【发布时间】:2014-05-06 15:17:34
【问题描述】:

我正在开发一个个人助理程序,我有一个名为 input_parse() 的方法,它查看输入字符串并检查与“命令”相对应的单词,伪代码如下所示。

if (input.contains("argA")
   {//execute command A}
if (input.contains("argB") && input.contains("argC"))
   {//execute command B}

有没有一种更简单的方法来解决这个问题,也许是使用命令的字符串数组,然后根据参数的索引对命令使用 switch 语句?

【问题讨论】:

  • 看起来很适合 Code Review
  • 不,没有更简单的方法。当然,您可以想出一种使用更少代码的方法,但实际上不会更简单
  • 你的情况是什么command?是某个方法、某个类还是只是一组操作?
  • 同意@SamIam。您可能可以使用一些花哨的正则表达式和捕获首先提取所有参数,但您仍然必须循环并使用 if 或 switch。
  • @Sergey 命令通常包括方法,例如检查天气、语音合成响应、检查 wolfram alpha 等。

标签: c# if-statement simplify


【解决方案1】:

您可以创建一个字典,其中值将是您的命令,键可以是您的字符串(参数名称)。

var myCommands = new Dictionary<string, Action>();
myCommands.Add("argA", new Action(() => Console.WriteLine("arga was passed")));
myCommands.Add("argB", new Action(() => Console.WriteLine("argb was passed")));

您可以通过字典的键来调用您的命令。因此,如果同时传递了 argA 和 argB,则两个命令都会被调用。

foreach (var key in myCommands.Keys)
{
  if (input.Contains(key))
  {
    myCommands[key]();
  }
}

这是最简单的方法,您不需要创建任何类结构或类似的东西。非常适合简单的控制台应用程序。

编辑

要交叉匹配参数,您可以定义以下字典

var myCommands new Dictionary<Func<string, bool>, Action>>();
myCommands.Add(new Func<string, bool>(i => i.Contains("argA")),new Action(() => Console.WriteLine("arga was passed"));
myCommands.Add(new Func<string, bool>(i => i.Contains("argB")),new Action(() => Console.WriteLine("arga was passed"));
myCommands.Add(new Func<string, bool>(i => i.Contains("argB") && e.Contains("argA")),new Action(() => Console.WriteLine("arga & argb was passed"));



foreach (var key in myCommands.Keys)
{
   if (key(input))
   {
      myCommands[key]();
   }
}

【讨论】:

  • 如果 B 和 C 都存在,则 OP 的代码涉及做某事;这不支持该操作。
  • 必须同意@Servy 在这一点上。字典不允许我与多个参数交叉引用,例如 if (input.contains("what is") && !input.contains("weather")) 我会以冲突告终。
  • 您可以将 Func 作为匹配参数的键,这将允许。这完全取决于您的偏好和应用程序的大小。如果它是大型企业,我会选择@Sergey 发布的内容。但如果它是一个小型控制台应用程序,并且您想保持简单,我会使用 Action、Func 委托。
【解决方案2】:

您有相互关联的命令和输入参数。对我来说,将这两件事封装到一个对象中并保持它们接近似乎是合乎逻辑的。您可以为此类对象创建接口

public interface ICommand
{
    bool CanGetArgumentsFrom(string input);
    void Execute();
}

以及实现它的命令

public class CommandA : ICommand
{
    public bool CanGetArgumentsFrom(string input)
    {
        return input.Contains("argA");
    }

    public void Execute()
    {
        /* execute command A */
    }
}

第二个命令

public class CommandB : ICommand
{
    public bool CanGetArgumentsFrom(string input)
    {
        return input.Contains("argB") && input.Contains("argC");
    }

    public void Execute()
    {
        /* execute command B */
    }
}

用法非常易读

ICommand[] commands = { new CommandA(), new CommandB() };

foreach(var command in commands)
   if (command.CanGetArgumentsFrom(input))
       command.Execute();

你甚至可以有方法

public bool TryExecuteCommand(string input)
{
    if (!CanGetArgumentsFrom(input))
        return false;

    Execute();
    return true;
}

那么执行会更加简单易读:

foreach(var command in commands)
    command.TryExecute(input);

如果您不想创建命名命令,那么您可以使用对象来验证输入并在所有必需参数都到位时执行一些操作

public class Command
{
    string[] arguments;
    Action action;

    public Command(Action action, params string[] arguments)
    {
        this.action = action;
        this.arguments = arguments;
    }

    public bool TryExecute(string input)
    {
        if (!arguments.All(input.Contains))
            return false;

        action();
        return true;
    }
}

现在使用这个类,您可以创建命令:

var commands = new Command[] {
    new Command(_ => /* execute A */, "argA"),
    new Command(_ => /* execute B */, "argB", "argC"),
};

【讨论】:

  • @Servy 当然,创建对象需要更多的工作,但是当命令和条件的数量增加时,它会在可维护性和可读性方面带来好处。如果只有两个命令,那么我会选择 OP 解决方案
  • 我认为这里的可维护性或可读性没有任何改进。我可以glance 看看 OP 的代码,看看它到底在做什么。我花了很长时间来解析您的解决方案,看看它在做什么,验证一切是否正确。当然,我会浏览一大堆不同的文件,而不是把所有东西都放在一个屏幕上。如果命令是从完全不同的地方创建的,执行完全不同的事情,由不同类型拥有,没有相关的条件检查等,这将是完全值得的,但事实并非如此。
  • 到目前为止,总共有 13 个可能的命令,但这个数量正在增长,并且可以扩展到多个命令。从长远来看,对象选项会让我更受益吗?这种方法还会导致多个类文件,例如(commandA.cs、commandB.cs 等),还是这些附加命令类嵌套在单个“命令”类文件中?
  • @user3538260 我会使用多个命令,这些命令将位于单独的文件中。您可以在解决方案中创建 Commands 文件夹以保留所有命令。为命令提供可读的名称。例如。检查天气命令。您甚至可以使用反射替换手动创建命令 - 从当前程序集中获取所有实现 ICommand 的类型并实例化它们。或者为此使用配置
  • @user3538260 好吧,一个 13 行的 if 语句仍然没有那么长。它仍然可以轻松放入屏幕中。那时你显然想确保每个块的主体只有一两行代码;如果还有更多工作要做,则调用另一种方法/类型,以便 if 块可以留在一个屏幕上。
【解决方案3】:

您可以使用IEnumerable.Any(用于“或”条件)或IEnumerable.All(用于“和”条件):

if (input.contains("argA"))
   {//execute command A}
if (new[] { "argB", "argC" }.All(input.Contains))
   {//execute command B}

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 2020-03-15
    • 2015-09-12
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多