Use when working with .cs files, .NET projects, implementing C# features, advising on C#/.NET architecture and patterns, or answering questions about C#/.NET development. Covers async patterns, dependency injection, LINQ, and testing conventions.
This skill inherits all available tools. When active, it can use any tool Claude has access to.
Apply these patterns when working with C# code. When both developing-csharp and testing-xunit apply, testing-xunit takes precedence in test files.
| Element | Convention | Example |
|---|---|---|
| Classes, Structs | PascalCase | UserService |
| Interfaces | IPascalCase | IUserRepository |
| Methods, Properties | PascalCase | GetUserById |
| Private fields | _camelCase | _userRepository |
| Parameters, locals | camelCase | userId |
| Async methods | PascalCase + Async | GetUserByIdAsync |
src/
├── MyApp.Api/Controllers/
├── MyApp.Core/Entities/, Interfaces/, Services/
├── MyApp.Infrastructure/Repositories/, Data/
tests/
├── MyApp.UnitTests/
├── MyApp.IntegrationTests/
Register services in Program.cs:
builder.Services.AddScoped<IUserRepository, UserRepository>();
builder.Services.AddSingleton<ICacheService, RedisCacheService>();
builder.Services.AddTransient<IEmailSender, SmtpEmailSender>();
| Lifetime | Use For |
|---|---|
| Singleton | Shared state, caches, configuration |
| Scoped | Per-request state, DbContext, repositories |
| Transient | Stateless services, lightweight operations |
Inject via constructor:
public class UserService : IUserService
{
private readonly IUserRepository _userRepository;
private readonly ILogger<UserService> _logger;
public UserService(IUserRepository userRepository, ILogger<UserService> logger)
{
_userRepository = userRepository;
_logger = logger;
}
}
Always use Async suffix. Always await or return the task. Use ConfigureAwait(false) in libraries. Support CancellationToken.
public async Task<User> GetUserAsync(int id, CancellationToken ct = default)
{
return await _context.Users.FirstOrDefaultAsync(u => u.Id == id, ct);
}
Do not use async void except for event handlers. Do not block with .Result or .Wait(). Do not ignore tasks with _ = ProcessAsync().
Prefer method syntax:
var activeUsers = users
.Where(u => u.IsActive)
.OrderBy(u => u.LastName)
.Select(u => new UserDto(u.Id, u.FullName))
.ToList();
Materialize once to avoid multiple enumeration:
var activeUsers = users.Where(u => u.IsActive).ToList();
var count = activeUsers.Count;
var first = activeUsers.First();
Use custom exception hierarchy:
public class DomainException : Exception { }
public class NotFoundException : DomainException { }
public class ValidationException : DomainException
{
public IReadOnlyList<string> Errors { get; }
}
Result pattern alternative:
public class Result<T>
{
public bool IsSuccess { get; }
public T? Value { get; }
public string? Error { get; }
public static Result<T> Success(T value) => new(value);
public static Result<T> Failure(string error) => new(error);
}
public class UserServiceTests
{
private readonly Mock<IUserRepository> _mockRepo;
private readonly UserService _sut;
public UserServiceTests()
{
_mockRepo = new Mock<IUserRepository>();
_sut = new UserService(_mockRepo.Object);
}
[Fact]
public async Task GetByIdAsync_WithValidId_ReturnsUser()
{
var expectedUser = new User { Id = 1, Name = "John" };
_mockRepo.Setup(r => r.GetByIdAsync(1)).ReturnsAsync(expectedUser);
var result = await _sut.GetByIdAsync(1);
Assert.NotNull(result);
Assert.Equal("John", result.Name);
}
}
public class AppDbContext : DbContext
{
public DbSet<User> Users => Set<User>();
protected override void OnModelCreating(ModelBuilder modelBuilder)
{
modelBuilder.ApplyConfigurationsFromAssembly(typeof(AppDbContext).Assembly);
}
}
Avoid the repository pattern with Entity Framework.
var for obvious typesreadonly fieldsrecord for DTOs: public record UserDto(int Id, string Name);using statements for disposable resources"" not string.Emptyint[] numbers = [1, 2, 3];