Claude Code Plugins

Community-maintained marketplace

Feedback

C# 12 and .NET 8 coding standards including primary constructors, collection expressions, async patterns, null handling, and naming conventions. Use when writing or reviewing .NET 8 code.

Install Skill

1Download skill
2Enable skills in Claude

Open claude.ai/settings/capabilities and find the "Skills" section

3Upload to Claude

Click "Upload skill" and select the downloaded ZIP file

Note: Please verify skill by going through its instructions before using it.

SKILL.md

name dotnet8-standards
description C# 12 and .NET 8 coding standards including primary constructors, collection expressions, async patterns, null handling, and naming conventions. Use when writing or reviewing .NET 8 code.

.NET 8 Coding Standards

Overview

These standards ensure consistent, modern C# code across the codebase. Follow these patterns for all .NET 8 development.

C# 12 Syntax

Primary Constructors

Use primary constructors for dependency injection:

// Good - primary constructor
public class TaskService(ITaskRepository repository, ILogger<TaskService> logger)
{
    public async Task<TaskItem> GetByIdAsync(Guid id) => await repository.GetByIdAsync(id);
}

// Avoid - traditional constructor
public class TaskService
{
    private readonly ITaskRepository _repository;
    public TaskService(ITaskRepository repository) => _repository = repository;
}

Collection Expressions

Use collection expressions for initialization:

// Good
List<string> items = ["one", "two", "three"];
int[] numbers = [1, 2, 3];

// Avoid
var items = new List<string> { "one", "two", "three" };

Naming Conventions

Classes and Methods

  • PascalCase for classes, methods, properties: TaskService, GetAllAsync, IsCompleted
  • Async methods suffix with Async: GetByIdAsync, CreateAsync

Variables and Parameters

  • camelCase for local variables and parameters: taskId, pageSize
  • No Hungarian notation or prefixes
  • Descriptive names over abbreviations: cancellationToken not ct

Interfaces

  • Prefix with I: ITaskService, ITaskRepository

Async/Await Patterns

Always Use CancellationToken

// Good - accepts and passes CancellationToken
public async Task<List<TaskItem>> GetAllAsync(CancellationToken cancellationToken = default)
{
    return await repository.GetAllAsync(cancellationToken);
}

// Avoid - no cancellation support
public async Task<List<TaskItem>> GetAllAsync()
{
    return await repository.GetAllAsync();
}

ConfigureAwait

In library code, use ConfigureAwait(false):

await repository.GetAllAsync(cancellationToken).ConfigureAwait(false);

In ASP.NET Core controllers/services, it's optional (HttpContext flows automatically).

Null Handling

Nullable Reference Types

Enable nullable reference types (already in csproj):

// Explicit nullability
public string? Description { get; set; }  // Can be null
public string Title { get; set; } = string.Empty;  // Cannot be null

Null Checks

Use pattern matching for null checks:

// Good
if (task is null) return NotFound();
if (task is not null) Process(task);

// Avoid
if (task == null) return NotFound();

Property Initialization

Required Properties

Use required modifier for mandatory properties:

public class CreateTaskRequest
{
    public required string Title { get; init; }
    public string? Description { get; init; }
}

Init-Only Properties

Use init for immutable properties:

public class TaskItem
{
    public Guid Id { get; init; }
    public required string Title { get; init; }
    public DateTime CreatedAt { get; init; } = DateTime.UtcNow;
}

Records for DTOs

Use records for request/response DTOs:

public record CreateTaskRequest(string Title, string? Description);
public record TaskResponse(Guid Id, string Title, string? Description, bool IsCompleted);

File-Scoped Namespaces

Always use file-scoped namespaces:

// Good
namespace TaskApi.Services;

public class TaskService { }

// Avoid
namespace TaskApi.Services
{
    public class TaskService { }
}

Expression-Bodied Members

Use for single-line implementations:

// Good
public string FullName => $"{FirstName} {LastName}";
public override string ToString() => Title;

// Use block body for multi-line
public async Task<TaskItem?> GetByIdAsync(Guid id)
{
    var task = await repository.GetByIdAsync(id);
    logger.LogInformation("Retrieved task {Id}", id);
    return task;
}