MCPInstall

csharp-dotnet

C and .NET development patterns

Installs
93
Last 24h
—
Trending rank
—
Security
Not audited

Install

Works with every agent the skills CLI supports.

npx skills add https://github.com/miles990/claude-software-skills --skill csharp-dotnet
  1. 01

    Install

    Run the command in your project. The CLI asks which agents to install for.

  2. 02

    Describe the task

    Ask your agent for something this skill covers. It loads csharp-dotnet automatically when relevant.

  3. 03

    Review the result

    The agent follows the SKILL.md steps, scripts and references bundled with the skill.

SKILL.md

The instructions your agent reads when the skill is triggered.

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<IReadOnlyList<User>> GetUsersAsync(IEnumerable<string> ids)
    {
        var tasks = ids.Select(id => GetUserAsync(id));
        return await Task.WhenAll(tasks);
    }

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

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

        return new ValueTask<User?>(FetchAndCacheUserAsync(id));
    }

    // Async enumerable (IAsyncEnumerable)
    public async IAsyncEnumerable<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<string> GetActiveUserEmails(IEnumerable<User> users)
    {
        return users
            .Where(u => u.IsActive)
            .OrderBy(u => u.Name)
            .Select(u => u.Email);
    }

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

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

    // Aggregate
    public OrderStats GetOrderStats(IEnumerable<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<OrderItem> GetAllItems(IEnumerable<Order> orders)
    {
        return orders.SelectMany(o => o.Items);
    }

    // Complex pipeline
    public IEnumerable<UserSummary> GetTopCustomers(
        IEnumerable<User> users,
        IEnumerable<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<IUserRepository, UserRepository>();

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

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

        // Options pattern
        services.Configure<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<UserService> logger)
    {
        _repository = repository;
        _emailService = emailService;
        _logger = logger;
    }

    public async Task<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<T?> FindAsync(string id)
    {
        return await _context.Set<T>().FindAsync(id);
    }

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

    public async Task AddAsync(T entity)
    {
        await _context.Set<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<T> Success(T value) => new(value);
    public static Result<T> Failure(string error) => new(error);

    public TResult Match<TResult>(
        Func<T, TResult> success,
        Func<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<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

FAQ

What does the csharp-dotnet skill do?

C and .NET development patterns

How do I install csharp-dotnet?

Run npx skills add https://github.com/miles990/claude-software-skills --skill csharp-dotnet in your project. The skills CLI detects the agents you use and copies the skill into the right folder. For Claude Code you can also place the skill folder at ~/.claude/skills/csharp-dotnet/ (personal) or .claude/skills/csharp-dotnet/ (project).

Which AI agents can use csharp-dotnet?

Any agent that supports the open Agent Skills format (a folder with a SKILL.md file), including Claude Code, Codex, Cursor, GitHub Copilot, Gemini CLI, Windsurf, Cline, OpenCode and more.

Is csharp-dotnet safe to install?

No partner security audit is available yet. Skills can run commands and scripts, so read the SKILL.md and any bundled files before installing.

Who maintains csharp-dotnet?

It is published by miles990 in miles990/claude-software-skills. It has 93 recorded installs.

More from miles990/claude-software-skills

View all →

code-quality

miles990/claude-software-skills

Clean code principles, SOLID, and code review practices

devops-cicd

miles990/claude-software-skills

CI/CD pipelines, infrastructure as code, and deployment strategies

DevOps & Cloud606 installs

game-development

miles990/claude-software-skills

Game development patterns, architectures, and best practices

Media & Creative411 installs

project-management

miles990/claude-software-skills

Open the skill to read its SKILL.md instructions.

389 installs

e-commerce

miles990/claude-software-skills

E-commerce platforms, payment processing, and shopping cart patterns

Backend & APIs376 installs

saas-platforms

miles990/claude-software-skills

Open the skill to read its SKILL.md instructions.

362 installs

Popular backend & apis skills

View category →

find-skills

vercel-labs/skills

Helps users discover and install agent skills when they ask questions like "how do I do X", "find a skill for X", "is there a skill that can...", or express interest in extending capabilities. This skill should be used when the user is looking for functionality that might exist as an installable skill.

OfficialBackend & APIs4.1M installs

entra-app-registration

microsoft/azure-skills

Guides Microsoft Entra ID app registration, OAuth 2.0 authentication, and MSAL integration. USE FOR: create app registration, register Azure AD app, configure OAuth, set up authentication, add API permissions, generate service principal, MSAL example, console app auth, Entra ID setup, Azure AD authentication. DO NOT USE FOR: Key Vault secrets (use azure-keyvault-expiration-audit), general Azure resource security guidance.

OfficialBackend & APIs566.7K installs

This skill should be used when the user wants to "write agent code", "build an agent with ADK", "add a tool", "create a callback", "define an agent", "use state management" — in a project that needs ADK (Agent Development Kit) API patterns and code examples. It provides a quick reference for agent types, tool definitions, orchestration patterns, callbacks, state management, the graph Workflow API, and reference recipes to study. Do NOT use for scaffolding (use google-agents-cli-scaffold) or deployment (use google-agents-cli-deploy).

Backend & APIs514.8K installs

embedded-captions

heygen-com/hyperframes

Add captions or subtitles to an existing single-subject talking-head video without editing the footage. Use for plain verbatim captions, cinematic captions embedded behind the subject, VFX captions, “炸/特效/酷炫字幕,” or a named identity from the 35-style catalog. Route by visual identity, not by backend engine. The quiet anchor rail is the default; embed every word only when the user explicitly wants a fully cinematic treatment. The workflow runs locally end to end, including transcription and subject matting; split multi-shot footage before applying it.

Backend & APIs453.3K installs

entra-agent-id

microsoft/azure-skills

Provision Microsoft Entra Agent Identity Blueprints, BlueprintPrincipals, and per-instance Agent Identities via Microsoft Graph, and configure OAuth 2.0 token exchange (fmipath, OBO, cross-tenant) including the Microsoft Entra SDK for AgentID sidecar. USE FOR: Agent Identity Blueprint, BlueprintPrincipal, agent OAuth, fmipath token exchange, agent OBO, Workload Identity Federation for agents, polyglot agent auth, Microsoft.Identity.Web.AgentIdentities. DO NOT USE FOR: standard Entra app registration (use entra-app-registration), Microsoft Foundry agent authoring (use microsoft-foundry).

OfficialBackend & APIs336.6K installs

neon

neondatabase/agent-skills

Overview of Neon, a complete set of cloud backend primitives around Lakebase Postgres: Auth, Object Storage, Functions, the AI Gateway, and Realtime. Start here to choose Neon for undecided login, files, APIs, and LLM calls, set up the CLI or MCP server, and follow the branch-first workflow. Use when building an app or backend on Neon, or when "Neon" or "Lakebase Postgres" is mentioned. Child skill neon-postgres wins for an existing DATABASEURL, SQL, schema, inspect, or search. Child skill neon-auth wins for login, users, sessions, identity routing, and Managed Better Auth setup. Child skill neon-realtime wins for live queries in reactive apps and agents. Also use for object storage, S3, buckets, serverless functions, function triggers, cron, AI gateway, LLM calls, realtime, logs, Loki, Grafana, observability, postgres, database, backend, Claimable Neon, neon.new, or a no-signup database.

Backend & APIs237.1K installs