基于 SOC 职业分类
用 Codex 或 Claude 帮你安装 复制这段 Prompt,粘贴到 Codex、Claude 或其他助手里,让它检查 Skill 页面并帮你完成安装。
直接命令不会经过审查 Prompt;运行前请先检查来源。
npx skills add https://github.com/rudironsoni/Ghost --skill dotnet-csharp-coding-standards命令会保持在同一行。复制前请横向滚动并检查完整内容。
想先保存到本地?可下载 SkillsMP 当前能够提供的文件。
正在显示 SKILL.md
Rulesync CLI tool documentation - unified AI rule file management for various AI coding tools
Publishes .NET artifacts from Azure DevOps. NuGet push, containers to ACR, pipeline artifacts.
Configures artifacts output layout. UseArtifactsOutput, ArtifactsPath, impact on CI and Docker.
| name | dotnet-csharp-coding-standards |
| description | Defines baseline C# conventions loaded first -- naming, file layout, style rules. |
| allowed-tools | ["Read","Grep","Glob","Bash","Write","Edit"] |
Modern .NET coding standards based on Microsoft Framework Design Guidelines and C# Coding Conventions. This skill covers naming, file organization, and code style rules that agents should follow when generating or reviewing C# code.
Load this skill by default for any task that plans, designs, generates, modifies, or reviews C#/.NET code. Do not wait for explicit user wording such as "coding standards", "style", or "conventions". If code will be produced, this skill should be active before implementation starts. This skill is a baseline dependency that should be loaded before domain-specific C#/.NET skills.
Cross-references: [skill:dotnet-csharp-modern-patterns] for language feature usage, [skill:dotnet-csharp-async-patterns] for async naming conventions, [skill:dotnet-solid-principles] for SOLID, DRY, and SRP design principles at the class and interface level.
| Element | Convention | Example |
|---|---|---|
| Namespaces | PascalCase, dot-separated | MyCompany.MyProduct.Core |
| Classes, Records, Structs | PascalCase | OrderService, OrderSummary |
| Interfaces | I + PascalCase | IOrderRepository |
| Methods | PascalCase | GetOrderAsync |
| Properties | PascalCase | OrderDate |
| Events | PascalCase | OrderCompleted |
| Public constants | PascalCase | MaxRetryCount |
| Private fields | _camelCase | _orderRepository |
| Parameters, locals | camelCase | orderId, totalAmount |
| Type parameters | T or T + PascalCase | T, TKey, TValue |
| Enum members | PascalCase | OrderStatus.Pending |
Suffix async methods with Async:
// Correct
public Task<Order> GetOrderAsync(int id);
public ValueTask SaveChangesAsync(CancellationToken ct);
// Wrong
public Task<Order> GetOrder(int id); // missing Async suffix
public Task<Order> GetOrderTask(int id); // wrong suffix
Exception: Event handlers and interface implementations where the framework does not use the Async suffix (e.g., ASP.NET Core middleware InvokeAsync is already named by the framework).
Prefix booleans with is, has, can, should, or similar:
public bool IsActive { get; set; }
public bool HasOrders { get; }
public bool CanDelete(Order order);
Use plural nouns for collections:
public IReadOnlyList<Order> Orders { get; } // not OrderList
public Dictionary<string, int> CountsByName { get; } // descriptive
Each top-level type (class, record, struct, interface, enum) should be in its own file, named exactly as the type. Nested types stay in the containing type's file.
OrderService.cs -> public class OrderService
IOrderRepository.cs -> public interface IOrderRepository
OrderStatus.cs -> public enum OrderStatus
OrderSummary.cs -> public record OrderSummary
Always use file-scoped namespaces (C# 10+):
// Correct
namespace MyApp.Services;
public class OrderService { }
// Avoid: block-scoped namespace adds unnecessary indentation
namespace MyApp.Services
{
public class OrderService { }
}
Place using directives at the top of the file, outside the namespace. With <ImplicitUsings>enable</ImplicitUsings> (default in modern .NET), common namespaces are already imported. Only add explicit using statements for namespaces not covered by implicit usings.
Order of using directives:
System.* namespacesOrganize by feature or layer, matching namespace hierarchy:
src/MyApp/
Features/
Orders/
OrderService.cs
IOrderRepository.cs
OrderEndpoints.cs
Users/
UserService.cs
Infrastructure/
Persistence/
OrderRepository.cs
Always use braces for control flow, even for single-line bodies:
// Correct
if (order.IsValid)
{
Process(order);
}
// Avoid
if (order.IsValid)
Process(order);
Use expression bodies for single-expression members:
// Properties
public string FullName => $"{FirstName} {LastName}";
// Methods (single expression only)
public override string ToString() => $"Order #{Id}";
// Avoid for multi-statement methods -- use block body instead
var UsageUse var when the type is obvious from the right-hand side:
// Type is obvious: use var
var orders = new List<Order>();
var customer = GetCustomerById(id);
var name = "Alice";
// Type is not obvious: use explicit type
IOrderRepository repo = serviceProvider.GetRequiredService<IOrderRepository>();
decimal total = CalculateTotal(items);
Prefer pattern matching over null checks:
// Preferred
if (order is not null) { }
if (order is { Status: OrderStatus.Active }) { }
// Acceptable
if (order != null) { }
// Avoid
if (order is object) { }
if (!(order is null)) { }
Use null-conditional and null-coalescing operators:
var name = customer?.Name ?? "Unknown";
var orders = customer?.Orders ?? [];
items ??= [];
Prefer string interpolation over concatenation or string.Format:
// Preferred
var message = $"Order {orderId} totals {total:C2}";
// For complex interpolations, use raw string literals (C# 11+)
var json = $$"""
{
"id": {{orderId}},
"name": "{{name}}"
}
""";
// Avoid
var message = string.Format("Order {0} totals {1:C2}", orderId, total);
var message = "Order " + orderId + " totals " + total.ToString("C2");
Always specify access modifiers explicitly. Do not rely on defaults:
// Correct
public class OrderService
{
private readonly IOrderRepository _repo;
internal void ProcessBatch() { }
}
// Avoid: implicit internal class, implicit private field
class OrderService
{
readonly IOrderRepository _repo;
}
Follow the standard order:
access (public/private/protected/internal) -> static -> extern -> new ->
virtual/abstract/override/sealed -> readonly -> volatile -> async -> partial
public static readonly int MaxSize = 100;
protected virtual async Task<Order> LoadAsync() => await repo.GetDefaultAsync();
public sealed override string ToString() => Name;
These conventions implement SOLID and DRY principles at the code level. For comprehensive coverage with anti-patterns and fixes, see [skill:dotnet-solid-principles].
Seal classes that are not designed for inheritance. This improves performance (devirtualization) and communicates intent:
public sealed class OrderService(IOrderRepository repo)
{
// Not designed for inheritance
}
Only leave classes unsealed when you explicitly design them as base classes.
// Preferred: composition
public sealed class OrderProcessor(IValidator validator, INotifier notifier)
{
public async Task ProcessAsync(Order order)
{
await validator.ValidateAsync(order);
await notifier.NotifyAsync(order);
}
}
// Avoid: deep inheritance hierarchies
public class BaseProcessor { }
public class ValidatingProcessor : BaseProcessor { }
public class NotifyingValidatingProcessor : ValidatingProcessor { }
Keep interfaces focused. Prefer multiple small interfaces over one large one:
// Preferred
public interface IOrderReader
{
Task<Order?> GetByIdAsync(int id, CancellationToken ct = default);
Task<IReadOnlyList<Order>> GetAllAsync(CancellationToken ct = default);
}
public interface IOrderWriter
{
Task<Order> CreateAsync(Order order, CancellationToken ct = default);
Task UpdateAsync(Order order, CancellationToken ct = default);
}
// Avoid: one large interface with unrelated responsibilities
public interface IOrderRepository : IOrderReader, IOrderWriter { }
Accept CancellationToken as the last parameter in async methods. Use default as the default value for optional tokens:
public async Task<Order> GetOrderAsync(int id, CancellationToken ct = default)
{
return await _repo.GetByIdAsync(id, ct);
}
Always forward the token to downstream async calls. Never ignore a received CancellationToken.
Add XML docs to public API surfaces. Keep them concise:
/// <summary>
/// Retrieves an order by its unique identifier.
/// </summary>
/// <param name="id">The order identifier.</param>
/// <param name="ct">Cancellation token.</param>
/// <returns>The order, or <see langword="null"/> if not found.</returns>
public Task<Order?> GetByIdAsync(int id, CancellationToken ct = default);
Do not add XML docs to:
InternalsVisibleTo API)public string Name { get; })Configure these analyzers in Directory.Build.props or .editorconfig to enforce standards automatically:
<PropertyGroup>
<EnforceCodeStyleInBuild>true</EnforceCodeStyleInBuild>
<AnalysisLevel>latest-all</AnalysisLevel>
<TreatWarningsAsErrors>true</TreatWarningsAsErrors>
</PropertyGroup>
Key .editorconfig rules for C# style:
[*.cs]
csharp_style_namespace_declarations = file_scoped:warning
csharp_prefer_braces = true:warning
csharp_style_var_for_built_in_types = true:suggestion
csharp_style_var_when_type_is_apparent = true:suggestion
dotnet_style_require_accessibility_modifiers = always:warning
csharp_style_prefer_pattern_matching = true:suggestion
See [skill:dotnet-add-analyzers] for full analyzer configuration.
Conventions in this skill are grounded in publicly available content from:
required members (compile-time initialization safety), and var usage guidelines (readability-first). The language design team explicitly chose these features to reduce ceremony while maintaining safety. Source: https://github.com/dotnet/csharplang/tree/main/meetingsNote: This skill applies publicly documented design rationale. It does not represent or speak for the named sources.