65.9K
CodeProject 正在变化。 阅读更多。
Home

使用 C# 构建菜单驱动的控制台应用程序

starIconstarIconstarIconstarIconstarIcon

5.00/5 (4投票s)

2024年5月11日

GPL3

8分钟阅读

viewsIcon

11909

了解如何使用 C# 和 .NET Framework 4.8 创建用户友好型控制台应用程序,具有菜单驱动交互和优雅退出选项。


图 1。 控制台应用程序显示选项菜单的屏幕截图。

目录

引言

本教程将使用 C# 和 .NET Framework 4.8 创建一个简单的控制台应用程序。我们的应用程序将向用户显示一个选项菜单,允许他们执行各种操作。我们将实现诸如显示菜单、处理用户输入以及确认退出应用程序等操作。

这个技巧/窍门是面向初学者的,以及那些刚刚开始进行 C# 编程语言练习的人,例如一年级计算机科学专业的学生。  对于初次学习编程的人来说,这个问题经常出现。


注意: 我意识到 C# 12 和 .NET Core 8 的存在。但是,作为个人喜好,我认为使用旧框架具有更多的教学价值。  我特别选择使用 .NET Framework 4.8 和 C# 7 来向学生展示诸如namespace等编程概念,这些概念在旧版本的 C# 中必须更明确地声明。  此外,由于这不是一个非常高级的教程,我们可以不必使用像 .NET Core 8 这样强大的框架,而 .NET Framework 在 Windows 上足以满足我的目的。


必备组件

  • 您的计算机上已安装 Visual Studio 2022。
  • 对 C# 编程概念有基本了解。

演练

步骤 1:创建新的控制台应用程序项目

  1. 打开 Visual Studio 2022。
  2. 点击 文件 菜单,指向 新建,然后点击 项目/解决方案
  3. 创建新项目 窗口中,搜索Console App (.NET Framework)
  4. 选择Console App (.NET Framework) 模板,然后点击 下一步
  5. 将项目命名为MenuDemo,然后选择一个保存位置。
  6. 点击创建 以创建项目。

步骤 2:添加枚举定义

  1. MenuDemo项目中,添加一个名为Enums.cs的新 C# 文件。

  2. 定义一个名为MenuChoices的枚举,以表示菜单选项。包括EatCandyGoFishingPlayBasketballExit的选项。使用Description属性为每个选项提供人类可读的描述。

using System.ComponentModel;

namespace MenuDemo
{
    public enum MenuChoices
    {
        [Description("Eat Candy")]
        EatCandy,
        
        [Description("Go Fishing")]
        GoFishing,
        
        [Description("Play Basketball")]
        PlayBasketball,
        
        [Description("Exit")]
        Exit
    }
}

列表 1。 MenuChoices枚举的定义。


什么是enum

在 C# 中,enum(“enumeration”的缩写)是一种特殊的数据类型,用于定义一组命名的整型常量。这些常量代表该enum类型的变量可以持有的有限的可能值列表。enum中的每个常量都有一个相关的整数值,默认情况下从 0 开始,后续常量递增 1。

为什么我们要使用enum

在我们的MenuDemo应用程序中,我们使用一个名为MenuChoicesenum来表示菜单中的不同可用选项。而不是使用原始整数值来表示每个选项(例如,“吃糖”为 0,“钓鱼”为 1,依此类推),我们使用像EatCandyGoFishing等描述性名称,使我们的代码更具可读性且自成体系。

使用enum的一些好处是

  1. 可读性:使用描述性名称可提高代码的可读性。当有人读取MenuChoices.EatCandy时,他们会立即理解预期的含义。

  2. 类型安全:枚举提供类型安全,这意味着您不能将任意值赋给枚举变量。您只能使用预定义的常量集。

  3. Intellisense 支持:Visual Studio 等 IDE 为枚举提供 IntelliSense 支持,使从选项列表中选择正确的值更加容易。

  4. 编译时检查:编译器会执行检查以确保枚举值被正确使用,从而减少运行时出错的可能性。

总之,枚举是一种方便的方式,可以定义一组相关的常量,并带有有意义的名称,从而提高代码的可读性、可维护性和可靠性。它们在具有固定选项集或状态的情况下特别有用,例如菜单、应用程序状态、一周中的几天等。


步骤 3:编写应用程序逻辑

  1. 打开解决方案资源管理器。

    • 为此,请点击 视图 菜单,然后点击 解决方案资源管理器

  2. 展开解决方案资源管理器中的所有项,找到Program.cs文件,然后双击它。

    • Program.cs文件现在将在编辑器中打开。

  3. Program类中的某个位置,在Main方法之后,添加代码以定义GetUserChoice 方法。

    • 此方法从控制台读取用户输入。
    • 它将输入解析为MenuChoices枚举值。
    • 如果输入与任何枚举值匹配,则返回相应的的值。
    • 如果输入无法解析为有效的枚举值,则返回MenuChoices.Unknown
    • 这是我的 GetUserChoice 方法的代码列表
      /// <summary>
      /// Reads user input from the console and parses it into a
      /// <see cref="T:MenuDemo.MenuChoices" /> enumeration value.
      /// </summary>
      /// <returns>
      /// The <see cref="T:MenuDemo.MenuChoices" /> enumeration value corresponding to
      /// the user input.
      /// If the input cannot be parsed into a valid enumeration value, returns
      /// <see cref="F:MenuDemo.MenuChoices.Unknown" />.
      /// </returns>
      /// <remarks>
      /// This method reads a line of text from the console input and attempts to parse
      /// it into a <see cref="T:MenuDemo.MenuChoices" /> enumeration value.
      /// <para />
      /// If the input matches any of the enumeration values, the corresponding
      /// enumeration value is returned.
      /// <para />
      /// If the input cannot be parsed into a valid enumeration value, the method
      /// returns <see cref="F:MenuDemo.MenuChoices.Unknown" />.
      /// </remarks>
      private static MenuChoices GetUserChoice()
      {
          var input = Console.ReadLine();
          return Enum.TryParse(input, out MenuChoices choice)
              ? choice
              : MenuChoices.Unknown;
      }
      
      列表 2。 GetUserChoice方法的代码。
  4. 现在,添加GetEnumDescription方法的定义代码

    • 如果可用,此方法将检索与给定enum值关联的描述。如果未找到Description属性,它将返回enum值的字符串表示形式。
    • 它首先使用反射获取enum值的字段信息。
    • 然后,它使用GetCustomAttribute方法检索与枚举值关联的DescriptionAttribute(如果存在)。
    • 如果找到DescriptionAttribute,则返回与之关联的描述值。
    • 如果未找到DescriptionAttribute,则返回枚举值本身的字符串表示形式。
    • 这是我的GetEnumDescription实现的一个示例列表
      /// <summary>
      /// Retrieves the description attribute value associated with the specified enum
      /// value.
      /// </summary>
      /// <param name="value">
      /// The <see langword="enum" /> value for which to retrieve the
      /// description.
      /// </param>
      /// <returns>
      /// The description associated with the <see langword="enum" /> value, if
      /// available; otherwise, the
      /// string representation of the <see langword="enum" /> value.
      /// </returns>
      /// <remarks>
      /// This method retrieves the description attribute value, if present, associated
      /// with the specified <see langword="enum" /> value.
      /// <para />
      /// If no description attribute is found, it returns the string representation of
      /// the <see langword="enum" /> value.
      /// </remarks>
      private static string GetEnumDescription(Enum value)
      {
          var field = value.GetType()
                           .GetField(value.ToString());
          var attribute = (DescriptionAttribute)Attribute.GetCustomAttribute(
              field, typeof(DescriptionAttribute)
          );
          return attribute == null ? value.ToString() : attribute.Description;
      }
      
      列表 3。 GetEnumDescription方法的代码。

      方法的目的
       
    • 此方法用于为枚举值提供人类可读的描述。
    • 当在用户界面或日志消息中显示enum值时,它特别有用。
    • 通过使用DescriptionAttribute来修饰枚举值,开发人员可以提供有意义的描述,从而提高代码的可读性。
  5. 定义DisplayMenu方法

    • 此方法显示用户的选项菜单。  每个选项被称为一个命令
    • 它遍历所有可用的菜单选项,不包括Unknown,并显示它们及其对应的编号。
    • 它提示用户输入他们的选择。
    • 这是我的DisplayMenu方法的版本列表
      /// <summary>
      /// Displays the menu of choices on the console.
      /// </summary>
      /// <remarks>
      /// This method iterates through all the available menu choices and displays them
      /// along with their corresponding numbers.
      /// <para />
      /// The numbering of the choices starts from <c>1</c>.
      /// </remarks>
      private static void DisplayMenu()
      {
          Console.WriteLine("Please choose an action:\n");
          var menuItemNumber = 1;
          foreach (MenuChoices choice in Enum.GetValues(typeof(MenuChoices)))
              if (choice != MenuChoices.Unknown)
              {
                  var description = GetEnumDescription(choice);
                  Console.WriteLine($"[{menuItemNumber}]:    {description}");
                  menuItemNumber++;
              }
       
          Console.Write("\nEnter your selection: ");
      }
      
      列表 4。 DisplayMenu方法的代码。

      方法的目的
       
    • 此方法在Main方法中调用,以向用户显示选项菜单。
    • 它通过提供清晰有序的可用选项展示来增强用户体验。
    • 以编号格式呈现选项有助于用户轻松识别和选择他们想要的选项。
    • menuItemNumber变量用于在每个选项旁边显示菜单项编号。
    • 它从 1 开始,表示第一个菜单选项。  通常,与enum的成员关联的整数从零开始。  从 1 开始提供更好的用户体验。
    • 在显示每个菜单选项后,menuItemNumber++menuItemNumber增加 1,以移至下一个菜单项。
    • 这确保每个菜单选项都显示一个唯一的、连续的编号,从 1 开始,使用户更容易找到和选择他们想要的选项。
  6. 定义Main方法:

    • 这是应用程序的入口点。
    • 它包含主要的应用程序逻辑,包括显示菜单、获取用户选择以及根据该选择执行操作。
    • 它会一直运行在一个无限循环中,直到用户选择退出应用程序。
    • 如果用户选择Exit,它会提示用户确认是否确定要退出。  输入响应后,用户必须按键盘上的ENTER键进行确认。
      • 如果确认是肯定的(Yy),则应用程序终止。
      • 如果用户输入任何其他内容,则控制台将被清除,并重新显示菜单。
        • 这是重要的网络安全漏洞缓解措施:这样做可以防止所谓的代码注入攻击
    • 在每个操作之后,它会提示用户按任意键继续,清除控制台,然后再次显示菜单。
    • 这是我的Main方法的版本列表
      /// <summary>
      /// The entry point of the application.
      /// </summary>
      /// <remarks>
      /// This method serves as the starting point of the console application.
      /// <para />
      /// It continuously displays a menu of choices to the user and executes the
      /// corresponding actions based on their selection.
      /// <para />
      /// The menu is displayed until the user decides to exit the application.
      /// </remarks>
      public static void Main()
      {
          while (true)
          {
              DisplayMenu();
              var choice = GetUserChoice();
       
              // Convert 1-based menu choice to 0-based index
              var choiceIndex = (int)choice - 1;
       
              // Check if choice is within the valid range
              if (choiceIndex >= 0 && choiceIndex < Enum
                      .GetValues(typeof(MenuChoices))
                      .Length) // Check against all menu items
                  // Perform action based on user choice index
                  switch (choiceIndex)
                  {
                      case (int)MenuChoices.EatCandy:
                          Console.WriteLine("You chose to Eat Candy.");
       
                          // Add your Eat Candy logic here
                          break;
       
                      case (int)MenuChoices.GoFishing:
                          Console.WriteLine("You chose to Go Fishing.");
       
                          // Add your Go Fishing logic here
                          break;
       
                      case (int)MenuChoices.PlayBasketball:
                          Console.WriteLine("You chose to Play Basketball.");
       
                          // Add your Play Basketball logic here
                          break;
       
                      case (int)MenuChoices.Exit:
                          Console.Write(
                              "Are you sure you want to exit the application? (Y/N): "
                          );
                          var confirmation = Console.ReadLine()
                              .ToUpper()[0];
                          Console.WriteLine();
                          if (confirmation == 'Y')
                          {
                              Console.WriteLine("Exiting the application...");
                              return; // Exit the Main method
                          }
       
                          Console.Clear();
                          continue;
       
                      default:
                          Console.WriteLine(
                              "Invalid choice. Please try again."
                          );
                          break;
                  }
              else
                  Console.WriteLine("Invalid choice. Please try again.");
       
              Console.WriteLine("Press any key to continue...");
              Console.ReadKey();
              Console.Clear(); // Clear the console for the next iteration
          }
      }
      
      列表 5。 Main方法的定义。

      方法的目的
       
    • Main方法作为 C# 控制台应用程序的入口点。
    • 当应用程序执行时,操作系统会查找Main方法来启动程序。
    • Main方法中的所有代码将在应用程序启动时执行。
    • 它是程序执行的起点。
    • Main方法包含应用程序的主要逻辑。
    • 它通常包括显示用户界面、处理用户输入以及根据用户选择执行各种操作等任务。
    • Main方法通常涉及控制流结构,如循环(whileforforeach)和条件语句(ifelseswitch)来控制应用程序内的执行流程。
    • Main方法负责确定应用程序何时应终止。
    • 它通常在一个循环中运行,直到满足特定条件,例如用户选择退出应用程序或完成某个任务。
    • Main方法还可能处理资源管理任务,例如打开和关闭文件、数据库连接或网络连接。
    • Main方法中可能还包含错误处理代码,例如try-catch块,以处理应用程序执行期间发生的异常

完整的Program.cs文件

这是包含所有方法的完整Program.cs文件

using System;
using System.ComponentModel;
 
namespace MenuDemo
{
    /// <summary>
    /// Defines the behavior of the application.
    /// </summary>
    public static class Program
    {
        /// <summary>
        /// The entry point of the application.
        /// </summary>
        /// <remarks>
        /// This method serves as the starting point of the console application.
        /// <para />
        /// It continuously displays a menu of choices to the user and executes the
        /// corresponding actions based on their selection.
        /// <para />
        /// The menu is displayed until the user decides to exit the application.
        /// </remarks>
        public static void Main()
        {
            while (true)
            {
                DisplayMenu();
                var choice = GetUserChoice();
 
                // Convert 1-based menu choice to 0-based index
                var choiceIndex = (int)choice - 1;
 
                // Check if choice is within the valid range
                if (choiceIndex >= 0 && choiceIndex < Enum
                        .GetValues(typeof(MenuChoices))
                        .Length) // Check against all menu items
                    // Perform action based on user choice index
                    switch (choiceIndex)
                    {
                        case (int)MenuChoices.EatCandy:
                            Console.WriteLine("You chose to Eat Candy.");
 
                            // Add your Eat Candy logic here
                            break;
 
                        case (int)MenuChoices.GoFishing:
                            Console.WriteLine("You chose to Go Fishing.");
 
                            // Add your Go Fishing logic here
                            break;
 
                        case (int)MenuChoices.PlayBasketball:
                            Console.WriteLine("You chose to Play Basketball.");
 
                            // Add your Play Basketball logic here
                            break;
 
                        case (int)MenuChoices.Exit:
                            Console.Write(
                                "Are you sure you want to exit the application? (Y/N): "
                            );
                            var confirmation = Console.ReadLine()
                                .ToUpper()[0];
                            Console.WriteLine();
                            if (confirmation == 'Y')
                            {
                                Console.WriteLine("Exiting the application...");
                                return; // Exit the Main method
                            }
 
                            Console.Clear();
                            continue;
 
                        default:
                            Console.WriteLine(
                                "Invalid choice. Please try again."
                            );
                            break;
                    }
                else
                    Console.WriteLine("Invalid choice. Please try again.");
 
                Console.WriteLine("Press any key to continue...");
                Console.ReadKey();
                Console.Clear(); // Clear the console for the next iteration
            }
        }
 
        /// <summary>
        /// Displays the menu of choices on the console.
        /// </summary>
        /// <remarks>
        /// This method iterates through all the available menu choices and displays them
        /// along with their corresponding numbers.
        /// <para />
        /// The numbering of the choices starts from <c>1</c>.
        /// </remarks>
        private static void DisplayMenu()
        {
            Console.WriteLine("Please choose an action:\n");
            var menuItemNumber = 1;
            foreach (MenuChoices choice in Enum.GetValues(typeof(MenuChoices)))
                if (choice != MenuChoices.Unknown)
                {
                    var description = GetEnumDescription(choice);
                    Console.WriteLine($"[{menuItemNumber}]:    {description}");
                    menuItemNumber++;
                }
 
            Console.Write("\nEnter your selection: ");
        }
 
        /// <summary>
        /// Retrieves the description attribute value associated with the specified enum
        /// value.
        /// </summary>
        /// <param name="value">
        /// The <see langword="enum" /> value for which to retrieve the
        /// description.
        /// </param>
        /// <returns>
        /// The description associated with the <see langword="enum" /> value, if
        /// available; otherwise, the
        /// string representation of the <see langword="enum" /> value.
        /// </returns>
        /// <remarks>
        /// This method retrieves the description attribute value, if present, associated
        /// with the specified <see langword="enum" /> value.
        /// <para />
        /// If no description attribute is found, it returns the string representation of
        /// the <see langword="enum" /> value.
        /// </remarks>
        private static string GetEnumDescription(Enum value)
        {
            var field = value.GetType()
                             .GetField(value.ToString());
            var attribute = (DescriptionAttribute)Attribute.GetCustomAttribute(
                field, typeof(DescriptionAttribute)
            );
            return attribute == null ? value.ToString() : attribute.Description;
        }
 
        /// <summary>
        /// Reads user input from the console and parses it into a
        /// <see cref="T:MenuDemo.MenuChoices" /> enumeration value.
        /// </summary>
        /// <returns>
        /// The <see cref="T:MenuDemo.MenuChoices" /> enumeration value corresponding to
        /// the user input.
        /// If the input cannot be parsed into a valid enumeration value, returns
        /// <see cref="F:MenuDemo.MenuChoices.Unknown" />.
        /// </returns>
        /// <remarks>
        /// This method reads a line of text from the console input and attempts to parse
        /// it into a <see cref="T:MenuDemo.MenuChoices" /> enumeration value.
        /// <para />
        /// If the input matches any of the enumeration values, the corresponding
        /// enumeration value is returned.
        /// <para />
        /// If the input cannot be parsed into a valid enumeration value, the method
        /// returns <see cref="F:MenuDemo.MenuChoices.Unknown" />.
        /// </remarks>
        private static MenuChoices GetUserChoice()
        {
            var input = Console.ReadLine();
            return Enum.TryParse(input, out MenuChoices choice)
                ? choice
                : MenuChoices.Unknown;
        }
    }
}

列表 6。 我们Program.cs文件的最终内容。

步骤 4:运行应用程序

  1. 按键盘上的F5,或点击菜单栏上的调试 菜单,然后点击 开始调试 来运行应用程序。
    • Visual Studio 应该会自动将您的应用程序构建成一个.exe文件,然后为您启动该.exe文件。
    • 您看到的内容应该类似于(尽管可能不完全相同)图 1
  2. 按照屏幕上的说明与菜单进行交互。
  3. 测试每个菜单选项,并验证应用程序是否按预期运行。

结论

恭喜!您已成功使用 .NET Framework 4.8 在 C# 中创建了一个菜单驱动的控制台应用程序。您已学会了如何显示菜单、处理用户输入以及实现执行前确认操作等功能。继续探索和尝试 C#,以进一步提高您的编程技能。

© . All rights reserved.