基于文件的应用是在一个单独的*.cs文件中运行并构建的程序,而无需相应的项目(*.csproj)文件。 基于文件的应用非常适合学习 C#,因为它们的复杂性较低:整个程序存储在单个文件中。 基于文件的应用也可用于生成命令行实用工具。 在 Unix 平台上,可以使用 (shebang) #!运行基于文件的应用。
在本教程中,你将:
- 创建基于文件的应用。
- 添加 Unix shebang (
#!) 支持。 - 读取命令行参数。
- 处理标准输入。
- 生成 ASCII 艺术输出。
- 处理命令行参数。
- 使用分析的命令行结果。
- 测试最终应用程序。
构建一个基于文件的应用程序,该应用程序将文本写成 ASCII 艺术风格。 该应用包含在单个文件中,使用 NuGet 包并实现核心功能。
先决条件
- .NET 10 SDK。 从.NET 下载网站下载。
- Visual Studio Code。 从 Visual Studio Code 主页下载它。
- (可选)适用于 Visual Studio Code 的 C# DevKit 扩展。 从 Visual Studio Code 市场下载它。
创建基于文件的应用
-
打开 Visual Studio Code 并创建一个名为
AsciiArt.cs的新文件。 输入以下文本:Console.WriteLine("Hello, world!"); -
保存文件。 然后,在 Visual Studio Code 中打开集成终端并键入:
dotnet AsciiArt.cs
首次运行此程序时, dotnet 主机会从源文件生成可执行文件,将生成项目存储在临时文件夹中,然后运行创建的可执行文件。 可以通过再次键入 dotnet AsciiArt.cs 来验证此体验。 这一次, dotnet 主机确定可执行文件是最新的,并运行可执行文件,而无需再次生成它。 你看不到任何构建输出。
前面的步骤演示基于文件的应用不是脚本文件。 它们是 C# 源文件,dotnet 主机通过在临时文件夹中使用生成的项目文件来构建这些文件。 生成程序时显示的输出行之一应如下所示(在 Windows 上):
AsciiArt succeeded (7.3s) → AppData\Local\Temp\dotnet\runfile\AsciiArt-85c58ae0cd68371711f06f297fa0d7891d0de82afde04d8c64d5f910ddc04ddc\bin\debug\AsciiArt.dll
在 Unix 平台上,输出文件夹类似于:
AsciiArt succeeded (7.3s) → Library/Application Support/dotnet/runfile/AsciiArt-85c58ae0cd68371711f06f297fa0d7891d0de82afde04d8c64d5f910ddc04ddc/bin/debug/AsciiArt.dll
该输出会告知你放置临时文件和生成输出的位置。 在本教程中,每当编辑源文件时,dotnet 主机都会在可执行文件运行前对其进行更新。
基于文件的应用是常规 C# 程序。 唯一的限制是必须在一个源文件中写入它们。 可以使用顶级语句或经典 Main 方法作为入口点。 可以声明任何类型:类、接口和结构。 可以在基于文件的应用中构建算法,就像在任何 C# 程序中一样。 甚至可以声明多个命名空间来组织代码。 如果发现基于文件的应用对于单个文件来说太大,则可以将其转换为基于项目的程序,并将源拆分为多个文件。 基于文件的应用是一种出色的原型制作工具。 可以开始尝试以最少的开销来证明概念和生成算法。
Unix shebang (#!) 支持
注释
仅在 Unix 平台上支持 #! 指令。 Windows 没有类似的指令可以直接执行 C# 程序。 在 Windows 上,必须在命令行上使用 dotnet 。
在 Unix 上,只需使用源文件名称即可直接执行基于文件的应用。 需要进行两项更改:
-
设置源文件的执行权限:
chmod +x AsciiArt.cs -
添加一个 shebang (
#!) 指令作为AsciiArt.cs文件的第一行:#!/usr/bin/env -S dotnet --这个 shebang 使用 env 在 PATH 中查找 dotnet。-S 使 env 将 dotnet 与 — 作为独立参数传递。– 确保用户参数直接传给应用,避免被 dotnet 误解为自身选项。
提示
如果前面的 shebang 不起作用,请尝试
#!/usr/bin/env dotnet或dotnet的确切位置,例如#!/usr/local/share/dotnet/dotnet --。
进行这两项更改后,可以直接运行程序:
./AsciiArt.cs
如果愿意,可以删除扩展,以便可以改为键入 ./AsciiArt 。 即使在 Windows 上,您也可以将 #! 添加到您的源文件中。 Windows 不支持 #!,但 C# 编译器允许在所有平台上基于文件的应用中使用该指令。
读取命令行参数
现在,将命令行上的所有参数写入输出。
-
将当前内容
AsciiArt.cs替换为以下代码:if (args.Length > 0) { string message = string.Join(' ', args); Console.WriteLine(message); } -
可以通过键入以下命令来运行此版本:
dotnet AsciiArt.cs -- This is the command line.— 表示后续命令行参数传给 AsciiArt 程序。示例 This is the command line. 会作为字符串数组传入,包含 This、is、the、command、line. 五个词。
此版本演示了以下新概念:
- 预定义变量
args将命令行参数传递给程序。 变量args是字符串数组:string[]. 如果长度args为 0,则未提供任何参数。 否则,参数列表中的每个单词都存储在数组中的相应条目中。 - 该方法使用
string.Join指定的分隔符将多个字符串联接到单个字符串中。 在这种情况下,分隔符是一个空格。 - Console.WriteLine 将字符串写入标准输出控制台,后跟一个新行。
处理标准输入
前面的代码正确处理命令行参数。 现在,添加代码来处理从标准输入(stdin)而不是命令行参数读取输入。
-
将以下
else子句添加到你在前面的代码中添加的if语句中:else { while (Console.ReadLine() is string line && line.Length > 0) { Console.WriteLine(line); } }上述代码不断读取控制台输入,直到遇到空行或 null。输入流结束时,Console.ReadLine() 返回 null。 Console.ReadLine
-
通过在同一文件夹中创建新的文本文件来测试读取标准输入。 命名文件
input.txt并添加以下行:Hello from ... dotnet! You can create file-based apps in .NET 10 and C# 14 Have fun writing useful utilities将行保持简短,以便在添加使用 ASCII 艺术的功能时正确设置格式。
-
再次运行程序。
使用 bash:
cat input.txt | dotnet AsciiArt.cs或者,使用 PowerShell:
Get-Content input.txt | dotnet AsciiArt.cs
现在,程序可以接受命令行参数或标准输入。
生成 ASCII 艺术输出
接下来,添加一个支持 ASCII 艺术的包Colorful.Console。 若要将包添加到基于文件的应用中,请使用 #:package 该指令。
-
在 AsciiArt.cs 中,将下面的指令添加到 #! 指令之后:
#:package Colorful.Console@1.2.15重要
上次更新本教程时,该版本
1.2.15是包的Colorful.Console最新版本。 检查包的 NuGet 页 以获取最新版本,以确保将包版本与最新的安全修补程序一起使用。 -
更改调用
Console.WriteLine的行,使其改用Colorful.Console.WriteAscii方法:Colorful.Console.WriteAscii(line); -
运行该程序。 你看到的是 ASCII 艺术输出,而不是回显文本。
处理命令行选项
接下来,添加命令行分析。 当前版本将每个单词写入不同的输出行。 添加的命令行参数支持两个功能:
-
引用应写在一行上的多个单词:
AsciiArt.cs "This is line one" "This is another line" "This is the last line" -
添加一个选项
--delay以在每个行之间暂停:AsciiArt.cs --delay 1000
用户可以同时使用这两个参数。
大多数命令行应用程序都需要分析命令行参数才能有效地处理选项、命令和用户输入。 该 System.CommandLine 库 提供用于处理命令、子命令、选项和参数的综合功能。 您可以专注于应用程序的功能,而不是解析命令行输入的机制。
该 System.CommandLine 库提供几个关键优势:
- 自动帮助文本生成和验证。
- 支持 POSIX 和 Windows 命令行约定。
- 内置选项卡补全功能。
- 跨应用程序的一致解析行为。
-
添加
System.CommandLine包。 在现有包指令后面添加此指令:#:package System.CommandLine@2.0.0重要
在本教程上次更新时,
2.0.0是最新版本。 如果有较新版本可用,请使用最新版本来确保具有最新的安全包。 检查包的 NuGet 页 以获取最新版本,以确保将包版本与最新的安全修补程序一起使用。 -
在文件顶部添加必要的 using 语句(在
#!和#:package指令之后):using System.CommandLine; using System.CommandLine.Parsing; -
定义延迟选项和消息参数。 添加以下代码以创建
CommandLine.Option和CommandLine.Argument对象来表示命令行选项和参数:Option<int> delayOption = new("--delay") { Description = "Delay between lines, specified as milliseconds.", DefaultValueFactory = parseResult => 100 }; Argument<string[]> messagesArgument = new("Messages") { Description = "Text to render." };在命令行应用程序中,选项通常以(双短划线)开头
--,并且可以接受参数。 该--delay选项接受一个整数参数,该参数指定延迟(以毫秒为单位)。
messagesArgument定义了如何将选项后的任何剩余令牌解析为文本。 每个标记将成为数组中的一个单独的字符串,但文本可以引用以在一个标记中包含多个单词。 例如,"This is one message"变为单个令牌,而This is four tokens变为四个单独的令牌。前面的代码定义了整数类型的 –delay 选项,以及字符串数组类型的消息参数。应用只有一个命令,因此使用根命令。
-
创建根命令,并使用选项和参数对其进行配置。 将参数和选项添加到根命令:
RootCommand rootCommand = new("Ascii Art file-based app sample"); rootCommand.Options.Add(delayOption); rootCommand.Arguments.Add(messagesArgument); -
添加代码以分析命令行参数并处理任何错误。 此代码验证命令行参数,并将分析的参数存储在对象中 System.CommandLine.ParseResult :
ParseResult result = rootCommand.Parse(args); foreach (ParseError parseError in result.Errors) { Console.Error.WriteLine(parseError.Message); } if (result.Errors.Count > 0) { return 1; }
前面的代码验证所有命令行参数。 如果验证失败,应用会将错误写入控制台并退出。
使用分析的命令行结果
现在,完成应用以使用已分析的选项并写入输出。 首先,定义用于保存已分析选项的记录。 基于文件的应用可以包括类型声明,例如记录和类。 它们必须位于所有顶级语句和本地函数之后。
-
添加声明
record以存储消息和延迟选项值:public record AsciiMessageOptions(string[] Messages, int Delay); -
在记录声明之前添加以下本地函数。 此方法处理命令行参数和标准输入,并返回新的记录实例:
async Task<AsciiMessageOptions> ProcessParseResults(ParseResult result) { int delay = result.GetValue(delayOption); List<string> messages = [.. result.GetValue(messagesArgument) ?? Array.Empty<string>()]; if (messages.Count == 0) { while (Console.ReadLine() is string line && line.Length > 0) { Colorful.Console.WriteAscii(line); await Task.Delay(delay); } } return new([.. messages], delay); } -
创建一个本地函数,以指定的延迟生成 ASCII 艺术图案。 此函数在记录中写入每个消息,并在每条消息之间指定延迟:
async Task WriteAsciiArt(AsciiMessageOptions options) { foreach (string message in options.Messages) { Colorful.Console.WriteAscii(message); await Task.Delay(options.Delay); } } -
将前面编写的 if 子句替换为以下代码,以处理命令行参数并输出:
var parsedArgs = await ProcessParseResults(result); await WriteAsciiArt(parsedArgs); return 0;
你创建了一个 record 类型,该类型为分析的命令行选项和参数提供结构。 新的本地函数创建记录的实例,并使用记录来写入 ASCII 艺术输出。
测试最终应用程序
通过运行多个不同的命令来测试应用程序。 如果遇到问题,下面是与所生成内容进行比较的已完成示例:
#!/usr/bin/env dotnet
#:package Colorful.Console@1.2.15
#:package System.CommandLine@2.0.0
using System.CommandLine;
using System.CommandLine.Parsing;
Option<int> delayOption = new("--delay")
{
Description = "Delay between lines, specified as milliseconds.",
DefaultValueFactory = parseResult => 100
};
Argument<string[]> messagesArgument = new("Messages")
{
Description = "Text to render."
};
RootCommand rootCommand = new("Ascii Art file-based app sample");
rootCommand.Options.Add(delayOption);
rootCommand.Arguments.Add(messagesArgument);
ParseResult result = rootCommand.Parse(args);
foreach (ParseError parseError in result.Errors)
{
Console.Error.WriteLine(parseError.Message);
}
if (result.Errors.Count > 0)
{
return 1;
}
var parsedArgs = await ProcessParseResults(result);
await WriteAsciiArt(parsedArgs);
return 0;
async Task<AsciiMessageOptions> ProcessParseResults(ParseResult result)
{
int delay = result.GetValue(delayOption);
List<string> messages = [.. result.GetValue(messagesArgument) ?? Array.Empty<string>()];
if (messages.Count == 0)
{
while (Console.ReadLine() is string line && line.Length > 0)
{
// <WriteAscii>
Colorful.Console.WriteAscii(line);
// </WriteAscii>
await Task.Delay(delay);
}
}
return new([.. messages], delay);
}
async Task WriteAsciiArt(AsciiMessageOptions options)
{
foreach (string message in options.Messages)
{
Colorful.Console.WriteAscii(message);
await Task.Delay(options.Delay);
}
}
public record AsciiMessageOptions(string[] Messages, int Delay);
在本教程中,你学习了如何生成基于文件的应用,其中在单个 C# 文件中生成程序。 这些程序不使用项目文件,并且可以在 Unix 系统上使用该 #! 指令。 学习者可以在尝试 联机教程 之后以及构建基于项目的大型应用之前创建这些程序。 基于文件的应用也是命令行实用工具的绝佳平台。











暂无评论内容