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





5.00/5 (4投票s)
了解如何使用 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:创建新的控制台应用程序项目
- 打开 Visual Studio 2022。
- 点击 文件 菜单,指向 新建,然后点击 项目/解决方案。
- 在创建新项目 窗口中,搜索
Console App (.NET Framework)
。 - 选择
Console App (.NET Framework)
模板,然后点击 下一步。 - 将项目命名为
MenuDemo
,然后选择一个保存位置。 - 点击创建 以创建项目。
步骤 2:添加枚举定义
-
在
MenuDemo
项目中,添加一个名为Enums.cs
的新 C# 文件。 -
定义一个名为
MenuChoices
的枚举,以表示菜单选项。包括EatCandy
、GoFishing
、PlayBasketball
和Exit
的选项。使用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
应用程序中,我们使用一个名为MenuChoices
的enum
来表示菜单中的不同可用选项。而不是使用原始整数值来表示每个选项(例如,“吃糖”为 0,“钓鱼”为 1,依此类推),我们使用像EatCandy
、GoFishing
等描述性名称,使我们的代码更具可读性且自成体系。
使用enum
的一些好处是
-
可读性:使用描述性名称可提高代码的可读性。当有人读取
MenuChoices.EatCandy
时,他们会立即理解预期的含义。 -
类型安全:枚举提供类型安全,这意味着您不能将任意值赋给枚举变量。您只能使用预定义的常量集。
-
Intellisense 支持:Visual Studio 等 IDE 为枚举提供 IntelliSense 支持,使从选项列表中选择正确的值更加容易。
-
编译时检查:编译器会执行检查以确保枚举值被正确使用,从而减少运行时出错的可能性。
总之,枚举是一种方便的方式,可以定义一组相关的常量,并带有有意义的名称,从而提高代码的可读性、可维护性和可靠性。它们在具有固定选项集或状态的情况下特别有用,例如菜单、应用程序状态、一周中的几天等。
步骤 3:编写应用程序逻辑
-
打开解决方案资源管理器。
-
为此,请点击 视图 菜单,然后点击 解决方案资源管理器。
-
-
展开解决方案资源管理器中的所有项,找到
Program.cs
文件,然后双击它。-
Program.cs
文件现在将在编辑器中打开。
-
-
在
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
方法的代码。
-
现在,添加
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
来修饰枚举值,开发人员可以提供有意义的描述,从而提高代码的可读性。
- 如果可用,此方法将检索与给定
-
定义
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 开始,使用户更容易找到和选择他们想要的选项。
-
定义
Main
方法:- 这是应用程序的入口点。
- 它包含主要的应用程序逻辑,包括显示菜单、获取用户选择以及根据该选择执行操作。
- 它会一直运行在一个无限循环中,直到用户选择退出应用程序。
- 如果用户选择
Exit
,它会提示用户确认是否确定要退出。 输入响应后,用户必须按键盘上的ENTER
键进行确认。- 如果确认是肯定的(
Y
或y
),则应用程序终止。 - 如果用户输入任何其他内容,则控制台将被清除,并重新显示菜单。
- 这是重要的网络安全漏洞缓解措施:这样做可以防止所谓的代码注入攻击。
- 如果确认是肯定的(
- 在每个操作之后,它会提示用户按任意键继续,清除控制台,然后再次显示菜单。
- 这是我的
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
方法通常涉及控制流结构,如循环(while
、for
、foreach
)和条件语句(if
、else
、switch
)来控制应用程序内的执行流程。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:运行应用程序
- 按键盘上的
F5
,或点击菜单栏上的调试 菜单,然后点击 开始调试 来运行应用程序。- Visual Studio 应该会自动将您的应用程序构建成一个
.exe
文件,然后为您启动该.exe
文件。 - 您看到的内容应该类似于(尽管可能不完全相同)图 1。
- Visual Studio 应该会自动将您的应用程序构建成一个
- 按照屏幕上的说明与菜单进行交互。
- 测试每个菜单选项,并验证应用程序是否按预期运行。
结论
恭喜!您已成功使用 .NET Framework 4.8 在 C# 中创建了一个菜单驱动的控制台应用程序。您已学会了如何显示菜单、处理用户输入以及实现执行前确认操作等功能。继续探索和尝试 C#,以进一步提高您的编程技能。