引言

最近GitHub上有个仓库火速拿到2656 stars,叫dgotnet/skills。我第一时间去看,发现它是一套让AI编码助手更懂.NET的“技能包”——其实就是一坨精心编写的Markdown文件,里面塞满了C#最佳实践、异步编程原则、依赖注入规则等内容。

作为一个天天跟提示词打交道的人,我一眼就看出这些文件可以直接拿来当System Prompt用。今天不废话,直接拆解最实用的一个skill,并附上你可直接复制的模板。

dotnet skills markdown files in github

1. 这个技能解决什么问题?

用AI生成.NET代码时,我经常遇到这些破事:

  • 生成SqlConnection忘记using,后面也不Dispose
  • 写了同步方法,明明能async却不用;
  • 依赖注入硬new,结果搞得一团浆糊;
  • 用了过时的HttpClient用法,导致socket耗尽。

这些不是AI能力不够,而是缺少上下文约束。dotnet/skills就是微软准备的标准答案集,它告诉AI:“写.NET代码时,必须遵守这些规则。” 你直接把这个规则喂给AI,它就不容易跑偏。

2. 核心思路

仓库里的每个skill都是一个Markdown文件,结构类似:

  • 标题和描述(明确场景)
  • 一组规则(列表形式)
  • 代码示例(正面样本)
  • 额外上下文(常见陷阱)

本质上就是一份针对性的系统提示词。我们把它抽取出来,放到ChatGPT、Claude或者Cursor的System Prompt里,或者作为项目的.cursorrules文件。

我挑了其中最有代表性的一个skill:dotnet-coding-best-practices(仓库里叫coding-best-practices.md)。它涵盖了异常处理、资源管理、异步模式等7大方面。

3. 完整提示词模板

下面是我基于原仓库整合的通用模板。你可以直接复制,放在AI助手的System Prompt首部。

markdown
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33
# .NET C# Coding Best Practices (AI Instructions)

You are an expert .NET developer. When generating C# code, strictly follow these rules:

1. **Resource Management**
   - Always wrap `IDisposable` in `using` statements. Never call `.Dispose()` manually unless you must.
   - Use `await using` for `IAsyncDisposable`.

2. **Async & Await**
   - Any I/O operation must be `async`. Never block with `.Result` or `.Wait()`.
   - Use `ValueTask` only in hot paths where allocation matters; otherwise use `Task`.

3. **Exception Handling**
   - Catch specific exceptions, never `Exception` unless at the top-level boundary.
   - Prefer `ArgumentException` / `ArgumentNullException` for parameter validation.

4. **Dependency Injection**
   - Never instantiate services with `new` inside a class; use constructor injection.
   - Register dependencies with the correct lifetime: Singleton, Scoped, Transient.

5. **Naming Conventions**
   - Public methods/properties: PascalCase. Private fields: `_camelCase`.
   - Avoid Hungarian notation or underscores for locals.

6. **Avoid Common Pitfalls**
   - Do not create multiple `HttpClient` instances; use `IHttpClientFactory`.
   - Do not use `DataTable` in new code unless required by legacy; prefer Dapper or EF Core.

7. **Code Structure**
   - Each file should have only one public type (class/interface).
   - Keep methods short (< 30 lines).

Provide code that compiles with .NET 8+ and follows modern C# idioms.

提示:如果你用Cursor或Copilot,可以把这段放到项目根目录的.cursorrulesSYSTEM_PROMPT.md里。

4. 效果演示:差Prompt vs 好Prompt

❌ 差Prompt(不加任何规则)

写一个C#类,用于从数据库读取用户列表。

AI可能输出这样:

csharp
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23
public class UserRepository
{
    private string connectionString;
    public UserRepository(string conn)
    {
        connectionString = conn;
    }
    public List<User> GetUsers()
    {
        SqlConnection conn = new SqlConnection(connectionString);
        conn.Open();
        SqlCommand cmd = new SqlCommand("SELECT * FROM Users", conn);
        SqlDataReader reader = cmd.ExecuteReader();
        List<User> users = new List<User>();
        while(reader.Read())
        {
            users.Add(new User { Id = (int)reader["Id"], Name = reader["Name"].ToString() });
        }
        reader.Close();
        conn.Close();
        return users;
    }
}

问题:没有using,没有异步,没有异常处理,SqlConnection硬new,reader.Close手动调用不符现代实践。

✅ 好Prompt(加上上面的系统规则)

写一个C#类,用于从数据库读取用户列表。请遵循.NET最佳实践。

AI输出:

csharp
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32
public sealed class UserRepository
{
    private readonly string _connectionString;
    private readonly IHttpClientFactory? _httpClientFactory; // 如果不是必须的就不加

    public UserRepository(string connectionString)
    {
        ArgumentNullException.ThrowIfNull(connectionString);
        _connectionString = connectionString;
    }

    public async Task<IReadOnlyList<User>> GetUsersAsync(CancellationToken cancellationToken = default)
    {
        var users = new List<User>();
        await using var connection = new SqlConnection(_connectionString);
        await connection.OpenAsync(cancellationToken);
        
        const string sql = "SELECT Id, Name FROM Users";
        await using var command = new SqlCommand(sql, connection);
        await using var reader = await command.ExecuteReaderAsync(cancellationToken);
        
        while (await reader.ReadAsync(cancellationToken))
        {
            users.Add(new User
            {
                Id = reader.GetInt32(0),
                Name = reader.GetString(1)
            });
        }
        return users.AsReadOnly();
    }
}

对比明显:异步、using、参数验证、只读集合、CancellationToken支持。这就是dotnet/skills带来的直接提升。

code comparison before after using dotnet skills

5. 变体和注意事项

变体1:针对特定框架

如果你是写ASP.NET Core,可以从仓库里找aspnet-core.md的规则,替换掉通用规则。例如加上“Controller只负责协调,逻辑放到Service类”、“使用[ApiController]和FluentValidation”等。

变体2:针对特定项目风格

我的一个朋友团队强制要求使用Primary ConstructorFile-scoped namespaces,我就把规则里的命名约定改成C# 12风格,并加了一句“Prefer primary constructors for records and simple classes”。效果很好。

变体3:精简版(适合Token敏感场景)

如果AI上下文窗口有限,只保留前3条最关键的:资源管理、异步、DI。实测在Cursor里用这个精简版,代码质量提升同样明显。

⚠️ 注意事项

  • 不要一次塞太多规则:超过10条AI可能选择性忽略。优先只放项目真正需要的。
  • 规则与示例搭配:纯规则效果有限,最好在每个规则后面附一个2-3行的小例子。上面的模板里我没加,你可以自己扩展。
  • 定期更新:dotnet/skills仓库会随.NET新版本更新,比如.NET 9 Preview出来后会增加Primary Constructor等新特性。建议每月去仓库看一次Release。
  • 结合其他文件:如果你用Cursor,这个模板可以作为全局规则,再结合项目特定的文档生成效果更佳。

我的个人看法

微软开源这个skills仓库,本质上是在定义AI时代的.NET公约数。之前大家抱怨AI写的C#代码一股Java味,现在有了这份规范,AI终于能写出地道的.NET代码。

但我也发现一个问题:仓库里的规则偏保守,很多最新语法(比如collection expressionsraw string literals)没有收录。我建议你在使用模板时,自己加一条“Use C# 12 features where applicable”,让它跟上时代。

另外,这个仓库目前只覆盖了通用场景,像Blazor、MAUI、WinUI这些特定框架的技能还很少。如果你有经验,完全可以照着它的格式自己写一个“Blazor Best Practices.md”,贡献回去。

总结(其实不需要总结)

直接复制上面的模板到你的AI助手,让下一次生成的C#代码直接用await usingIHttpClientFactory。如果觉得好用,去GitHub给dotnet/skills点个star,也算感谢微软的分享。

如果你有自己魔改的”.NET技能”,欢迎在评论区贴出来,咱们交流一下。