【问题标题】:How to compact identical repeating Else If statements如何压缩相同的重复 Else If 语句
【发布时间】:2018-12-03 02:40:07
【问题描述】:

我正在试验用户可以通过基本的 ReadLine 输入进行导航的简单程序。在输入期间的任何给定时间,无论情况如何,都有几个命令始终可以访问,这是一个示例:

else if(input.ToLower() == "exit" || input.ToLower() == "leave")
{
    Console.Clear();
    ExitProgram.ExitProg();
    calcInput = false;
}
else if(input.ToLower() == "back" || input.ToLower() == "menu")
{
    TxtFun.CTxt("Returning to previous menu.");
    Console.ReadLine();
    Console.Clear();
    calcInput = false;
    calcLoop = false;
}
else
{
    TxtFun.CTxt("Invalid input.");
    Console.ReadLine();
    Console.Clear();

    calcInput = false;
}

上面是 2 If Else 语句,每次我要求用户输入时都会重复,然后检查它。当我多次嵌套用户输入时,这变得非常繁琐。

我的问题是,有没有办法将这些重复的 Else If 语句压缩到一个函数或一个单独的类中,以节省时间和(大量)空间,插入 If/ Else If 分支?

((如果有办法在最后包含返回“无效输入”的重复“Else”,则奖励积分,但这不是主要问题或目标。))

【问题讨论】:

  • 策略模式?开关盒?
  • 从函数之类的简单事物开始 - 到目前为止您尝试过什么?
  • if 声明在哪里?为什么要跳上elseif
  • finite-state machine 可能会有所帮助。

标签: c# if-statement coding-efficiency


【解决方案1】:

我在实现 REPL 接口时做了一些不同的事情。

使用Dictionary<string, Func<string, string>>(实际上,我使用一个封装了实际FuncAction 的类以及可用于通过生成help 文本来帮助提高应用程序可用性的描述) 使用忽略大小写的字符串比较并将您期望的输入添加到字典中,其中对象是您要执行的函数。

这也变成了自我记录,您也可以自动创建帮助文档!

如果您愿意,我明天可以快速发布一个示例。 -- 在下面添加代码示例的关键部分(来自 Program.cs 文件):

我们将用来捕获 REPL 命令信息的类:

    class ReplCommand
    {

        public string Command { get; set; }
        public string HelpText { get; set; }
        public Action<string> MethodToCall { get; set; }
        public int HelpSortOrder { get; set; }

    }

这是我们定义所有有效命令的地方

        static void PopulateCommands()
        {
            // Add your commands here
            AddCommand(new ReplCommand
            {
                Command = "MyCommand", // The command that the user will enter (case insensitive)
                HelpText = "This is the help text of my command", // Help text
                MethodToCall = MyCommand, // The actual method that we will trigger
                HelpSortOrder = 1 // The order in which the command will be displayed in the help
            });

            // Default Commands
            AddCommand(new ReplCommand
            {
                Command = "help",
                HelpText = "Prints usage information",
                MethodToCall = PrintHelp,
                HelpSortOrder = 100
            });
            AddCommand(new ReplCommand
            {
                Command = "quit",
                HelpText = "Terminates the console application",
                MethodToCall = Quit,
                HelpSortOrder = 101
            });

        }

        static void AddCommand(ReplCommand replCommand)
        {
            // Add the command into the dictionary to be looked up later
            _commands.Add(replCommand.Command, replCommand);
        }

这是程序的关键部分:

        // The dictionary where we will keep a list of all valid commands
        static Dictionary<string, ReplCommand> _commands = new Dictionary<string, ReplCommand>(StringComparer.CurrentCultureIgnoreCase);

        static void Main(string[] args)
        {
            // Create Commands
            PopulateCommands();

            // Run continuously until "quit" is entered
            while (true)
            {
                // Ask the user to enter their command
                Console.WriteLine("Please input your command and hit enter");
                // Capture the input
                string sInput = Console.ReadLine();
                // Search the input from within the commands
                if (_commands.TryGetValue(sInput, out ReplCommand c))
                {
                    // Found the command. Let's execute it
                    c.MethodToCall(sInput);
                }
                else
                {
                    // Command was not found, trigger the help text
                    PrintHelp(sInput);
                }
            }


        }

上面定义的每条评论的具体实现:

        static void MyCommand(string input)
        {
            Console.WriteLine($"MyCommand has been executed by the input '{input}'");
        }

        static void PrintHelp(string input)
        {
            // Unless the input that got us here is 'help', display the (wrong) command that was
            // entered that got us here
            if (input?.ToLowerInvariant() != "help")
            {
                // Display the wrong command
                Console.WriteLine($"Command '{input}' not recognized. See below for valid commands");
            }

            // Loop through each command from a list sorted by the HelpSortOrder
            foreach (ReplCommand c in _commands.Values.OrderBy(o => o.HelpSortOrder))
            {
                // Print the command and its associated HelpText
                Console.WriteLine($"{c.Command}:\t{c.HelpText}");
            }
        }

        static void Quit(string input)
        {
            System.Environment.Exit(0);
        }

    }

}

这是complete Program.cs file的链接。

我已将完整的代码库上传到我的GitHub Repo

【讨论】:

  • 使用字典的好主意,我对他们还是新手(基本上所有东西)。我相信我会尝试使用用户 Leo 发布的一些内容作为答案,但是如果您可以发布您提到的内容的示例,那也很棒。干杯!
  • 嗨@NathanialLake,首先,恭喜您开始使用。我已经用代码 sn-ps 和我的 GitHub 存储库的链接更新了我的答案,其中包含一个完整的工作示例。请随时询问您可能需要的任何说明,祝您好运!
【解决方案2】:

我通常会在这种情况下尝试重构,直到我满意为止

每次我要求用户输入时都重复,然后检查它

然后首先将其包装在一个函数/方法中,这样您只需将其维护在一个地方

private void ProcessInput(string input)
{
    if(string.IsNullOrEmpty(input)) throw new ArgumentException();

    input = input.Trim().ToLower();

    if(input == "exit" || input == "leave")
    {
        Console.Clear();
        ExitProgram.ExitProg();
        calcInput = false;
    }
    else if(input == "back" || input == "menu")
    {
        TxtFun.CTxt("Returning to previous menu.");
        Console.ReadLine();
        Console.Clear();
        calcInput = false;
        calcLoop = false;
    }
    else
    {
        TxtFun.CTxt("Invalid input.");
        Console.ReadLine();
        Console.Clear();

        calcInput = false;
    }    
}

然后重构每个if 块...

private void ProcessExitInput(string input)
{
    if(input == "exit" || input == "leave")
    {
        Console.Clear();
        ExitProgram.ExitProg();
        calcInput = false;
    }
}

private void ProcessMenuInput(string input)
{
    if(input == "back" || input == "menu")
    {
        TxtFun.CTxt("Returning to previous menu.");
        Console.ReadLine();
        Console.Clear();
        calcInput = false;
        calcLoop = false;
    }
}

private void ProcessDefaultInput(string input)
{
    if(input != "back" && input != "menu" && input != "exit" && input != "leave")
    {
        TxtFun.CTxt("Returning to previous menu.");
        Console.ReadLine();
        Console.Clear();
        calcInput = false;
        calcLoop = false;
    }
}

现在你的ProcessInput 方法变得更小了...

private void ProcessInput(string input)
{
    if(string.IsNullOrEmpty(input)) throw new ArgumentException();

    input = input.Trim().ToLower();

    ProcessExitInput(input);
    ProcessMenuInput(input);
    ProcessDefaultInput(input);
}

您甚至可以使用switch/case 块代替if/else 块。甚至将if 条件重构为单独的对象...

class InputHandler
{
    public static bool IsExitInput(string input)
    {
        return input == "exit" || input == "leave";
    }
}

您还可以创建一个 InputFactory 对象,该对象根据特定条件 (input) 返回特定的 IInputProcessor 接口实现。有点像...

public interface IInputProcessor
{
    void Process();
}

public class ExitInputProcessor : IInputProcessor
{

    public void Process()
    {
        //process the exit command input
    }

}

并制作一个InputFactory 对象以根据当前输入返回所需的实现

您可以做任何有助于您以后组织和维护该代码的事情,并且对于您应该如何编写代码没有绝对的答案。 一个提示,测试驱动开发通常有助于编写更清晰和可维护的代码

【讨论】:

  • 谢谢里奥!我是一个自学成才的初学者,这篇文章很有帮助。干杯!
猜你喜欢
  • 2019-05-06
  • 1970-01-01
  • 1970-01-01
  • 2022-01-28
  • 2012-12-15
  • 1970-01-01
  • 2012-04-25
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多