重要
基于文件的应用是 .NET 10 的一项功能,它以预览版提供。 某些信息与可能在发布前修改的预发行版产品有关。 Microsoft对此处提供的信息不作任何明示或暗示的保证。
基于文件的应用 是包含在单个 *.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 run AsciiArt.cs
首次运行此程序时, dotnet 主机会从源文件生成可执行文件,将生成项目存储在临时文件夹中,然后运行创建的可执行文件。 可以通过再次键入 dotnet run AsciiArt.cs 来验证此体验。 这一次, dotnet 主机确定可执行文件是最新的,并运行可执行文件而不再次生成它。 看不到任何生成输出。
前面的步骤演示基于文件的应用不是脚本文件。 它们是使用临时文件夹中生成的项目文件的 C# 源文件。 生成程序时显示的输出行之一应如下所示(在 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 run 。
在 unix 上,可以直接运行基于文件的应用,在命令行而不是命令行 dotnet run上键入源文件名称。 需要进行两项更改:
设置对源文件 的执行 权限:
chmod +x AsciiArt.cs添加 shebang (
#!) 指令作为文件的第一行AsciiArt.cs:#!/usr/local/share/dotnet/dotnet run
dotnet在不同 unix 安装上的位置可能不同。 使用命令 whence dotnet 在本地环境中本地 dotnet 主机。
进行这两项更改后,可以直接从命令行运行程序:
./AsciiArt.cs
如果愿意,可以删除扩展,以便可以改为键入 ./AsciiArt 。 即使使用 Windows, #! 也可以将源文件添加到源文件。 Windows 命令行不支持 #!,但 C# 编译器允许在所有平台上基于文件的应用中使用该指令。
读取命令行参数
现在,将命令行上的所有参数写入输出。
将当前内容
AsciiArt.cs替换为以下代码:if (args.Length > 0) { string message = string.Join(' ', args); Console.WriteLine(message); }可以通过键入以下命令来运行此版本:
dotnet run AsciiArt.cs -- This is the command line.该
--选项指示应将所有以下命令参数传递给 AsciiArt 程序。This is the command line.参数作为字符串数组传递,其中每个字符串都是一个单词:This、、is、the和commandline.。
此版本演示了以下新概念:
- 命令行参数使用预定义变量
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将返回)通过在同一文件夹中创建新的文本文件来测试读取标准输入。 命名文件
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 run AsciiArt.cs或者,使用 PowerShell:
Get-Content input.txt | dotnet run AsciiArt.cs
现在,程序可以接受命令行参数或标准输入。
写入 ASCII Art 输出
接下来,添加支持 ASCII 艺术 、彩色.Console 的包。 若要将包添加到基于文件的程序中,请使用该 #:package 指令。
在AsciiArt.cs文件中的
#!指令后面添加以下指令:#:package Colorful.Console@1.2.15重要
上次更新本教程时,该版本
1.2.15是包的Colorful.Console最新版本。 检查包的 NuGet 页 以获取最新版本,以确保将包版本与最新的安全修补程序一起使用。更改调用
Console.WriteLine以改用方法的Colorful.Console.WriteAscii行:async Task WriteAsciiArt(AsciiMessageOptions options) { foreach (string message in options.Messages) { Colorful.Console.WriteAscii(message); await Task.Delay(options.Delay); } }运行程序,你会看到 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-beta6重要
2.0.0-beta6上次更新本教程时,版本是最新版本。 如果有较新版本可用,请使用最新版本来确保具有最新的安全包。 检查包的 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,参数是值的数组string。 此应用程序只有一个命令,因此请使用 根命令。创建根命令,并使用选项和参数对其进行配置。 将参数和选项添加到根命令:
RootCommand rootCommand = new("Ascii Art file-based program 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/local/share/dotnet/dotnet run
#:package Colorful.Console@1.2.15
#:package System.CommandLine@2.0.0-beta6
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 program 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 系统上使用该 #! 指令。 学习者可以在尝试 联机教程 之后以及构建基于项目的大型应用之前创建这些程序。 基于文件的应用也是命令行实用工具的绝佳平台。