csharp-dotnet

Modern C# and .NET development patterns including async programming, LINQ, and ASP.NET Core.

Safety Notice

This listing is imported from skills.sh public index metadata. Review upstream SKILL.md and repository scripts before running.

Copy this and send it to your AI assistant to learn

Install skill "csharp-dotnet" with this command: npx skills add miles990/claude-software-skills/miles990-claude-software-skills-csharp-dotnet

C# & .NET

Overview

Modern C# and .NET development patterns including async programming, LINQ, and ASP.NET Core.

Modern C# Features

Records and Init-Only Properties

// Record type (immutable by default) public record User( string Id, string Email, string Name, DateTime CreatedAt );

// With-expressions for immutable updates var user = new User("1", "test@example.com", "Test User", DateTime.UtcNow); var updated = user with { Name = "Updated Name" };

// Record with validation public record ValidatedUser { public required string Id { get; init; } public required string Email { get; init; } public required string Name { get; init; }

public ValidatedUser()
{
    // Validation in constructor
}

// Custom validation
public static ValidatedUser Create(string email, string name)
{
    if (!email.Contains("@"))
        throw new ArgumentException("Invalid email");

    return new ValidatedUser
    {
        Id = Guid.NewGuid().ToString(),
        Email = email,
        Name = name
    };
}

}

// Init-only properties public class Config { public required string ConnectionString { get; init; } public int Timeout { get; init; } = 30; }

Pattern Matching

// Type patterns public string Describe(object obj) => obj switch { string s => $"String of length {s.Length}", int i when i > 0 => $"Positive integer: {i}", int i => $"Non-positive integer: {i}", IEnumerable<int> list => $"Integer list with {list.Count()} items", null => "Null value", _ => "Unknown type" };

// Property patterns public decimal CalculateDiscount(Order order) => order switch { { Total: > 1000, Customer.IsPremium: true } => order.Total * 0.2m, { Total: > 1000 } => order.Total * 0.1m, { Customer.IsPremium: true } => order.Total * 0.05m, _ => 0 };

// List patterns (C# 11) public string DescribeList(int[] numbers) => numbers switch { [] => "Empty", [var single] => $"Single element: {single}", [var first, var second] => $"Two elements: {first}, {second}", [var first, .. var middle, var last] => $"First: {first}, Last: {last}, Middle count: {middle.Length}", };

// Positional patterns with deconstruct public record Point(int X, int Y);

public string Quadrant(Point point) => point switch { (0, 0) => "Origin", (> 0, > 0) => "First quadrant", (< 0, > 0) => "Second quadrant", (< 0, < 0) => "Third quadrant", (> 0, < 0) => "Fourth quadrant", (_, 0) or (0, _) => "On an axis" };

Nullable Reference Types

#nullable enable

public class UserService { // Non-nullable (compiler ensures not null) public User GetUser(string id) { return _repository.Find(id) ?? throw new NotFoundException($"User {id} not found"); }

// Nullable return
public User? FindUser(string id)
{
    return _repository.Find(id);
}

// Null handling
public string GetDisplayName(User? user)
{
    // Null-conditional
    var name = user?.Name;

    // Null-coalescing
    return user?.Name ?? "Anonymous";

    // Null-coalescing assignment
    // user ??= CreateDefaultUser();
}

// Null-forgiving operator (use sparingly)
public void ProcessUser(User? user)
{
    // When you know it's not null but compiler doesn't
    var name = user!.Name;
}

}

// Required members (C# 11) public class RequiredUser { public required string Id { get; init; } public required string Email { get; init; } public string? Nickname { get; init; } }

Async Programming

using System.Threading.Tasks; using System.Threading;

public class AsyncService { // Basic async method public async Task<User> GetUserAsync(string id, CancellationToken ct = default) { var response = await _httpClient.GetAsync($"/users/{id}", ct); response.EnsureSuccessStatusCode(); return await response.Content.ReadFromJsonAsync<User>(ct) ?? throw new InvalidOperationException("Null response"); }

// Parallel execution
public async Task&#x3C;IReadOnlyList&#x3C;User>> GetUsersAsync(IEnumerable&#x3C;string> ids)
{
    var tasks = ids.Select(id => GetUserAsync(id));
    return await Task.WhenAll(tasks);
}

// With error handling
public async Task&#x3C;Result&#x3C;User>> SafeGetUserAsync(string id)
{
    try
    {
        var user = await GetUserAsync(id);
        return Result&#x3C;User>.Success(user);
    }
    catch (Exception ex)
    {
        return Result&#x3C;User>.Failure(ex.Message);
    }
}

// ValueTask for potentially synchronous operations
public ValueTask&#x3C;User?> GetCachedUserAsync(string id)
{
    if (_cache.TryGetValue(id, out var user))
    {
        return ValueTask.FromResult&#x3C;User?>(user);
    }

    return new ValueTask&#x3C;User?>(FetchAndCacheUserAsync(id));
}

// Async enumerable (IAsyncEnumerable)
public async IAsyncEnumerable&#x3C;User> StreamUsersAsync(
    [EnumeratorCancellation] CancellationToken ct = default)
{
    var page = 1;
    while (true)
    {
        var users = await FetchPageAsync(page, ct);
        if (!users.Any()) yield break;

        foreach (var user in users)
        {
            yield return user;
        }
        page++;
    }
}

// Consuming async enumerable
public async Task ProcessAllUsersAsync()
{
    await foreach (var user in StreamUsersAsync())
    {
        await ProcessUserAsync(user);
    }
}

}

// Channels for producer-consumer public class ChannelExample { private readonly Channel<Message> _channel = Channel.CreateBounded<Message>(100);

public async Task ProduceAsync(Message message)
{
    await _channel.Writer.WriteAsync(message);
}

public async Task ConsumeAsync(CancellationToken ct)
{
    await foreach (var message in _channel.Reader.ReadAllAsync(ct))
    {
        await ProcessMessageAsync(message);
    }
}

}

LINQ

using System.Linq;

public class LinqExamples { // Query syntax public IEnumerable<User> GetActiveUsers(IEnumerable<User> users) { return from user in users where user.IsActive orderby user.Name select user; }

// Method syntax (more common)
public IEnumerable&#x3C;string> GetActiveUserEmails(IEnumerable&#x3C;User> users)
{
    return users
        .Where(u => u.IsActive)
        .OrderBy(u => u.Name)
        .Select(u => u.Email);
}

// Grouping
public IDictionary&#x3C;string, List&#x3C;User>> GroupByDomain(IEnumerable&#x3C;User> users)
{
    return users
        .GroupBy(u => u.Email.Split('@')[1])
        .ToDictionary(g => g.Key, g => g.ToList());
}

// Join
public IEnumerable&#x3C;OrderWithUser> JoinOrdersWithUsers(
    IEnumerable&#x3C;Order> orders,
    IEnumerable&#x3C;User> users)
{
    return orders.Join(
        users,
        order => order.UserId,
        user => user.Id,
        (order, user) => new OrderWithUser(order, user));
}

// Aggregate
public OrderStats GetOrderStats(IEnumerable&#x3C;Order> orders)
{
    return new OrderStats(
        Count: orders.Count(),
        Total: orders.Sum(o => o.Amount),
        Average: orders.Average(o => o.Amount),
        Max: orders.Max(o => o.Amount)
    );
}

// SelectMany (flatten)
public IEnumerable&#x3C;OrderItem> GetAllItems(IEnumerable&#x3C;Order> orders)
{
    return orders.SelectMany(o => o.Items);
}

// Complex pipeline
public IEnumerable&#x3C;UserSummary> GetTopCustomers(
    IEnumerable&#x3C;User> users,
    IEnumerable&#x3C;Order> orders)
{
    return orders
        .GroupBy(o => o.UserId)
        .Select(g => new
        {
            UserId = g.Key,
            TotalSpent = g.Sum(o => o.Amount),
            OrderCount = g.Count()
        })
        .OrderByDescending(x => x.TotalSpent)
        .Take(10)
        .Join(users, x => x.UserId, u => u.Id, (x, u) => new UserSummary(
            u.Name,
            x.TotalSpent,
            x.OrderCount
        ));
}

}

Dependency Injection

using Microsoft.Extensions.DependencyInjection;

// Service registration public static class ServiceCollectionExtensions { public static IServiceCollection AddApplicationServices(this IServiceCollection services) { // Transient - new instance each time services.AddTransient<IEmailService, EmailService>();

    // Scoped - one per request
    services.AddScoped&#x3C;IUserRepository, UserRepository>();

    // Singleton - one instance for app lifetime
    services.AddSingleton&#x3C;ICacheService, RedisCacheService>();

    // Factory registration
    services.AddScoped&#x3C;IDbConnection>(sp =>
    {
        var config = sp.GetRequiredService&#x3C;IConfiguration>();
        return new SqlConnection(config.GetConnectionString("Default"));
    });

    // Options pattern
    services.Configure&#x3C;EmailOptions>(configuration.GetSection("Email"));

    return services;
}

}

// Constructor injection public class UserService { private readonly IUserRepository _repository; private readonly IEmailService _emailService; private readonly ILogger<UserService> _logger;

public UserService(
    IUserRepository repository,
    IEmailService emailService,
    ILogger&#x3C;UserService> logger)
{
    _repository = repository;
    _emailService = emailService;
    _logger = logger;
}

public async Task&#x3C;User> CreateUserAsync(CreateUserRequest request)
{
    _logger.LogInformation("Creating user {Email}", request.Email);

    var user = new User(request.Email, request.Name);
    await _repository.AddAsync(user);
    await _emailService.SendWelcomeEmailAsync(user);

    return user;
}

}

// Primary constructor (C# 12) public class UserServiceNew( IUserRepository repository, IEmailService emailService, ILogger<UserServiceNew> logger) { public async Task<User> CreateUserAsync(CreateUserRequest request) { logger.LogInformation("Creating user {Email}", request.Email); // Use repository, emailService directly return null!; } }

Generic Patterns

// Generic repository public interface IRepository<T> where T : class, IEntity { Task<T?> FindAsync(string id); Task<IReadOnlyList<T>> FindAllAsync(); Task AddAsync(T entity); Task UpdateAsync(T entity); Task DeleteAsync(string id); }

public class Repository<T> : IRepository<T> where T : class, IEntity { private readonly DbContext _context;

public Repository(DbContext context)
{
    _context = context;
}

public async Task&#x3C;T?> FindAsync(string id)
{
    return await _context.Set&#x3C;T>().FindAsync(id);
}

public async Task&#x3C;IReadOnlyList&#x3C;T>> FindAllAsync()
{
    return await _context.Set&#x3C;T>().ToListAsync();
}

public async Task AddAsync(T entity)
{
    await _context.Set&#x3C;T>().AddAsync(entity);
    await _context.SaveChangesAsync();
}

// ...

}

// Generic constraints public class Service<TEntity, TId> where TEntity : class, IEntity<TId>, new() where TId : struct { // ... }

// Covariance and contravariance public interface IProducer<out T> { T Produce(); }

public interface IConsumer<in T> { void Consume(T item); }

Error Handling

// Result type pattern public readonly struct Result<T> { public T? Value { get; } public string? Error { get; } public bool IsSuccess => Error is null;

private Result(T value)
{
    Value = value;
    Error = null;
}

private Result(string error)
{
    Value = default;
    Error = error;
}

public static Result&#x3C;T> Success(T value) => new(value);
public static Result&#x3C;T> Failure(string error) => new(error);

public TResult Match&#x3C;TResult>(
    Func&#x3C;T, TResult> success,
    Func&#x3C;string, TResult> failure) =>
    IsSuccess ? success(Value!) : failure(Error!);

}

// Custom exceptions public class DomainException : Exception { public string Code { get; }

public DomainException(string code, string message)
    : base(message)
{
    Code = code;
}

}

public class ValidationException : DomainException { public IDictionary<string, string[]> Errors { get; }

public ValidationException(IDictionary&#x3C;string, string[]> errors)
    : base("VALIDATION_ERROR", "Validation failed")
{
    Errors = errors;
}

}

// Exception filter middleware public class ExceptionMiddleware { private readonly RequestDelegate _next; private readonly ILogger<ExceptionMiddleware> _logger;

public async Task InvokeAsync(HttpContext context)
{
    try
    {
        await _next(context);
    }
    catch (ValidationException ex)
    {
        context.Response.StatusCode = 400;
        await context.Response.WriteAsJsonAsync(new { ex.Code, ex.Errors });
    }
    catch (NotFoundException ex)
    {
        context.Response.StatusCode = 404;
        await context.Response.WriteAsJsonAsync(new { ex.Code, ex.Message });
    }
    catch (Exception ex)
    {
        _logger.LogError(ex, "Unhandled exception");
        context.Response.StatusCode = 500;
        await context.Response.WriteAsJsonAsync(new { Code = "INTERNAL_ERROR" });
    }
}

}

Related Skills

  • [[backend]] - ASP.NET Core development

  • [[database]] - Entity Framework Core

  • [[testing]] - xUnit, NUnit

Source Transparency

This detail page is rendered from real SKILL.md content. Trust labels are metadata-based hints, not a safety guarantee.

Related Skills

Related by shared tags or category signals.

Coding

code-quality

No summary provided by upstream source.

Repository SourceNeeds Review
Coding

game-development

No summary provided by upstream source.

Repository SourceNeeds Review
Coding

devops-cicd

No summary provided by upstream source.

Repository SourceNeeds Review
Coding

flame-game-dev

No summary provided by upstream source.

Repository SourceNeeds Review