免建项目直接运行 C#:.NET 10 文件式应用(File-Based C# Apps)完整实战指南
【免费下载链接】skillsRepository for skills to assist AI coding agents with .NET and C#项目地址: https://gitcode.com/GitHub_Trending/skills17/skills
文件式 C# 应用(File-Based C# Apps)是 .NET 10 SDK 引入的能力,允许直接用dotnet hello.cs运行一个.cs文件,无需dotnet new console创建任何项目。本文基于当前仓库dotnet-advanced插件中的csharp-scriptsSkill(见 SKILL.md),完整讲解该能力的适用场景、#:指令系统、Unix shebang、AOT 下的 JSON 序列化方案、项目转换与旧版 SDK 降级路径,并辅以仓库评测用例与源码佐证,帮助你在语言实验、原型验证和小工具开发中彻底摆脱项目脚手架。
一、这个 Skill 解决什么问题
csharp-scripts是仓库 dotnet-advanced 插件下的四个高级技能之一(其余为dotnet-pinvoke、nuget-trusted-publishing、vectorization),定位是"针对小众场景的进阶 .NET/C# 技能"。它的核心主张是:当用户明确想要 C#/.NET 代码、但又不想创建项目时,用文件式应用来运行代码。
适用场景(When to Use)
- 快速验证某个 C# 概念、API 或语言特性,用一个文件式应用跑通即可;
- 在大项目里集成之前,先原型验证一段逻辑;
- 用一个入口文件加几个辅助
.cs文件拼装一个小工具。
不适用场景(When Not to Use)
- 用户要的是与语言无关的临时脚本、一次性计算,或 Shell/Python/PowerShell 风格的自动化;
- 用户需要完整项目、解决方案集成,或在现有应用中添加项目引用;
- 用户正工作在某个已存在的 .NET 解决方案中,想把代码加进去;
- 应用已经大到需要把项目结构、构建定制、测试或发布配置放进
.csproj。
一句话判断标准:代码量适合"文件"而非"项目"时用文件式应用;一旦需要构建定制、测试或发布配置,就应该迁移到.csproj。
唯一输入
| 输入 | 必填 | 说明 |
|---|---|---|
| C# 代码或意图 | 是 | 要运行的代码,或对文件式应用功能的描述 |
仓库证据:Skill 如何被评测
仓库在 tests/dotnet-advanced/csharp-scripts/eval.yaml 中为这个 Skill 提供了能力评测(type: capability)。评测刺激词是"测试 C# 语言特性":询问 C# 是否支持创建stackalloc的Span<nint>(native-sized 整数)。评测器断言:
- 命令以成功退出(exit-success);
- 输出匹配
(nint|nuint|native); - 输出包含
stackalloc; - 输出不匹配
dotnet new console—— 即 Agent 必须走dotnet <file>.cs文件式路径,而不是退化为建项目; - 评分标准明确写着:"使用
dotnet <file>.cs(文件式应用)运行测试,而不是用dotnet new console创建完整项目"。
这个评测用例从侧面印证了本 Skill 的设计意图:文件式应用不是"没项目时的临时凑合",而是被正式约定为 C# 语言实验的标准执行方式。
二、环境前提:SDK 版本与 Feature Band
文件式应用对 SDK 版本有硬性要求,开始前先执行:
dotnet --version判读规则:
- 文件式应用要求 .NET 10 或更高版本;
#:include、#:exclude以及跨文件的传递式(transitive)指令处理要求 SDK 10.0.300 或更高;- SDK 10.0.100 / 10.0.200 构建可以运行单文件应用,但不支持上述多文件指令;
- 版本低于 10 时,走本文第八节的"旧版 SDK 降级方案"。
注意这里要区分"运行时版本"和"SDK 功能带(feature band)":dotnet --version输出的完整版本号(如10.0.300)中,第二段300就是功能带,它决定了指令能力的边界。一个 10.0.200 的 SDK 依然是 .NET 10,但拿不到多文件指令。
作为环境参考:当前仓库根目录的 global.json 将 SDK 固定为11.0.100-preview(rollForward: latestMajor),高于 10.0.300 门槛,因此在该仓库环境中文件式应用的全部指令均可用。
三、最小工作流:写文件 → 运行 → 传参 → 清理
1. 写应用文件
创建一个使用顶级语句(top-level statements)的入口.cs文件,并放在任何现有项目目录之外,避免与.csproj冲突:
#!/usr/bin/env dotnet // hello.cs Console.WriteLine("Hello from a file-based app!"); var numbers = new[] { 1, 2, 3, 4, 5 }; Console.WriteLine($"Sum: {numbers.Sum()}");文件组织规范:
- 使用顶级语句,不要
Main方法、类或命名空间的样板代码; using指令放在文件顶部(如果有 shebang 行和#:指令,则放在它们之后);- 类型声明(类、record、enum)放在所有顶级语句之后。
2. 运行
dotnet hello.csSDK 会自动完成构建并运行,且结果会被缓存,后续重复运行很快。需要传参时,在--之后传入:
dotnet hello.cs -- arg1 arg2 "multi word arg"3. 清理
会话结束后删除应用文件;如需清掉缓存的构建产物:
dotnet clean hello.cs四、指令系统:#:开头的声明式配置
指令必须放在文件顶部(可选的 shebang 行之后),且必须在任何using指令或其他 C# 代码之前。所有指令都以#:开头。下面逐一说明六类指令。
#:package— NuGet 包引用
除非应用有意使用集中包管理(CPM),否则必须指定版本;当接受最新可用版本时用@*(接受预发布版则用@*-*):
#:package Humanizer@2.14.1 using Humanizer; Console.WriteLine("hello world".Titleize());#:property— MSBuild 属性
语法为#:property PropertyName=Value,可在文件内设置任意 MSBuild 属性:
#:property AllowUnsafeBlocks=true #:property PublishAot=false #:property NoWarn=CS0162MSBuild 表达式与属性函数同样受支持,例如:
#:property LogLevel=$([MSBuild]::ValueOrDefault('$(LOG_LEVEL)', 'Information'))常用属性速查:
| 属性 | 用途 |
|---|---|
AllowUnsafeBlocks=true | 启用unsafe代码 |
PublishAot=false | 关闭原生 AOT(默认开启) |
NoWarn=CS0162;CS0219 | 抑制特定警告 |
LangVersion=preview | 启用预览语言特性 |
InvariantGlobalization=false | 启用文化相关的全球化行为 |
#:project— 项目引用
按相对路径引用另一个项目:
#:project ../MyLibrary/MyLibrary.csproj#:ref— 文件式应用间的引用
当某个.cs文件应当编译进独立的程序集(而不是与入口文件同属一个编译单元)时,用#:ref建立类似"项目引用"的边界;普通的辅助文件共享同一程序集,应该用#:include。
#:property ExperimentalFileBasedProgramEnableRefDirective=true #:ref ../Shared/Formatter.cs Console.WriteLine(Formatter.Title("hello world"));使用要点:
- 被引用的文件会作为自己的虚拟项目编译,并作为项目引用加入;
- 如果被引用文件是没有顶级语句的库,需要在那个文件里加
#:property OutputType=Library; - 引用方要消费的成员应为
public,internal成员跨程序集不可见; #:ref是传递式的:被引用文件里可以再包含自己的#:ref及其他#:指令;- 相对路径按包含该指令的文件所在位置解析;
- 部分 SDK 构建需要
#:property ExperimentalFileBasedProgramEnableRefDirective=true;如果当前 SDK 不带该属性也接受#:ref,可以移除这行。
#:sdk— SDK 选择
覆盖默认的Microsoft.NET.Sdk,例如切换到 Web SDK:
#:sdk Microsoft.NET.Sdk.Web#:include与#:exclude— 多文件应用
仅 .NET SDK 10.0.300 及以后支持。用#:include把辅助源文件和受支持的资源纳入同一个虚拟项目,用#:exclude从 include 模式或默认条目集中剔除文件:
#!/usr/bin/env dotnet #:include Helpers.cs #:include Models/*.cs #:exclude Models/Generated/*.cs Console.WriteLine(Formatter.Title("hello world"));使用要点:
- 传给
dotnet的那个文件是入口点,顶级语句写在那里; - 类、record、enum 等声明放在被 include 的
.cs文件中; - 优先使用显式 glob(如
Helpers.cs、Models/*.cs),避免宽泛的递归 glob; - 路径按包含指令的文件所在目录解析;
- 非入口 C# 文件中的 include 指令同样会被处理:辅助文件可以声明自己的
#:package、#:property、#:sdk、#:project、#:ref、#:include或#:exclude; - 除非指令类型明确支持重复,避免跨文件重复声明指令——重复的
#:package、#:property、#:sdk、#:include、#:exclude会导致失败; - 使用
#:include时,在 Unix 系系统上给入口文件加 shebang(#!/usr/bin/env dotnet)以明确入口点;shebang 文件要求LF行尾且无 BOM。
一个典型的多文件布局:
scratch/ hello.cs Helpers.cs Models/ Person.cs#!/usr/bin/env dotnet // hello.cs #:include Helpers.cs #:include Models/*.cs var person = new Person("Ada"); Console.WriteLine(Formatter.Title(person.Name));// Helpers.cs static class Formatter { public static string Title(string value) => value.ToUpperInvariant(); }// Models/Person.cs record Person(string Name);五、Unix shebang:让.cs文件直接可执行
在 Unix 平台上,三步即可让.cs文件成为可执行文件:
文件第一行加 shebang:
#!/usr/bin/env dotnet Console.WriteLine("I'm executable!");设置执行权限:
chmod +x hello.cs直接运行:
./hello.cs
注意:shebang 文件必须使用LF行尾(不要CRLF);该指令在 Windows 上会被忽略。
六、AOT 默认开启:用源码生成 JSON 绕过反射限制
文件式应用默认启用原生 AOT。在 AOT 下,JsonSerializer.Serialize<T>(value)这类基于反射的 API 会在运行时失败,因此必须改用源码生成(source generation)的序列化:
using System.Text.Json; using System.Text.Json.Serialization; var person = new Person("Alice", 30); var json = JsonSerializer.Serialize(person, AppJsonContext.Default.Person); Console.WriteLine(json); var deserialized = JsonSerializer.Deserialize(json, AppJsonContext.Default.Person); Console.WriteLine($"Name: {deserialized!.Name}, Age: {deserialized.Age}"); record Person(string Name, int Age); [JsonSerializable(typeof(Person))] partial class AppJsonContext : JsonSerializerContext;要点:定义一个继承JsonSerializerContext的partial类,用[JsonSerializable]声明需要序列化的类型,然后通过AppJsonContext.Default.Person拿到强类型的JsonTypeInfo<Person>传给序列化 API。这正好与仓库另一个 Skill(system-text-json-net11)所强调的"优先强类型JsonTypeInfo<T>"方向一致。如果明确不需要 AOT,也可以通过#:property PublishAot=false关闭它(见第四节属性表)。
七、升级为完整项目:dotnet project convert
当文件式应用长到需要项目结构、构建定制、测试或发布配置时,一条命令即可原地转换:
dotnet project convert hello.cs转换后即可获得完整的.csproj项目,进入常规的dotnet run、dotnet build、dotnet test、dotnet publish工作流。
八、.NET 9 及更早版本的降级方案
如果 SDK 版本低于 10,文件式应用不可用。此时改用临时控制台项目:
mkdir -p /tmp/csharp-file-based-app && cd /tmp/csharp-file-based-app dotnet new console -o . --force用应用内容替换生成的Program.cs,然后用dotnet run运行;需要 NuGet 包时用dotnet add package <name>添加;用完删除该目录。
九、验证清单(Validation)
每次使用完文件式应用工作流,对照以下清单自查:
dotnet --version报告 10.0 或更高(否则走降级路径);- 如果应用使用了
#:include、#:exclude或来自被包含文件的传递式指令,dotnet --version报告 SDK 10.0.300 或更高; - 应用无错误编译(可用
dotnet build <file>.cs显式检查); dotnet <file>.cs输出符合预期;- 多文件应用包含所有必需的辅助文件,并排除了意外匹配的文件;
- 会话结束后应用文件和缓存产物已清理。
十、常见坑位速查表(Common Pitfalls)
| 坑位 | 解决方案 |
|---|---|
.cs文件位于含有.csproj的目录中 | 把应用移到项目目录之外,或使用dotnet run --file file.cs |
#:package未指定版本 | 指定版本:#:package PackageName@1.2.3,或用@*取最新 |
#:property语法错误 | 使用PropertyName=Value,=两侧无空格、不加引号:#:property AllowUnsafeBlocks=true |
| 指令放在了 C# 代码之后 | 所有#:指令必须紧跟可选 shebang 行之后,且在任何using指令或其他 C# 语句之前 |
| 辅助文件没有被编译 | 在入口文件加#:include Helper.cs或合适的 glob |
| 共享文件需要程序集边界 | 用#:ref Shared.cs而不是#:include Shared.cs;被引用文件若无入口点则加#:property OutputType=Library |
| 宽泛的 include 引入了无关文件 | 优先窄 include 模式,并用#:exclude排除生成文件、备份或实验文件 |
| 包含文件间出现重复指令 | 入口点与被包含 C# 文件中的 package、property、SDK、include、exclude 指令保持全局唯一 |
| 反射式 JSON 序列化失败 | 改用带JsonSerializerContext的源码生成 JSON(见第六节) |
| 出现意外的构建行为或版本错误 | 文件式应用会继承父目录的global.json、Directory.Build.props、Directory.Build.targets和nuget.config;若继承设置产生冲突,把应用移到隔离目录 |
十一、把 Skill 用对的验证方式
综合来看,csharp-scriptsSkill 的正确使用范式可以归纳为三条主线:
- 能力边界先行:先确认 SDK 版本满足门槛(10.0+,多文件指令需 10.0.300+),再决定是文件式应用还是降级到临时项目;
- 入口文件最小化:顶级语句 + 顶部指令 + 辅助文件
#:include,全部声明(类/record/enum)下沉到被包含文件; - 以评测为验收标准:仓库评测(eval.yaml)明确要求通过
dotnet <file>.cs运行实验代码并输出可验证的结果,而非创建dotnet new console项目——这正是本 Skill 与普通"写代码脚本"式技能的分水岭。
深入阅读可继续查看 Skill 原始定义 plugins/dotnet-advanced/skills/csharp-scripts/SKILL.md,以及插件整体定位 plugins/dotnet-advanced/README.md 和插件清单 plugins/dotnet-advanced/plugin.json。
【免费下载链接】skillsRepository for skills to assist AI coding agents with .NET and C#项目地址: https://gitcode.com/GitHub_Trending/skills17/skills
创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考