smithery/thapaliyabikendra

csharp-advanced-patterns

Master advanced C# patterns including records, pattern matching, async/await, LINQ, and performance optimization for .NET 10. Use when: (1) implementing complex C# patterns, (2) optimizing performance, (3) refactoring legacy code, (4) writing modern idiomatic C#.

Installation

$ npx skills add smithery/thapaliyabikendra --skill csharp-advanced-patterns

Similar popular skills

Related neighbors and high-traction skills in the same topics — useful to compare before installing.

Also in this package

Other skills from smithery/thapaliyabikendra · top by installs.

npx skills add smithery/thapaliyabikendra

Browse all from smithery/thapaliyabikendra

More details

Agent compatibility

Declared targets from SKILL.md / docs. Unmarked agents are not listed — the skill may still install via the CLI.

Claude Code Not declared
Cursor Not declared
Codex Not declared
GitHub Copilot Not declared
Windsurf Not declared
Gemini CLI Not declared
Cline Not declared
OpenCode Not declared

Package contents

Files included with this skill beyond the listing page.

  • skill md SKILL.md 9,412 B
  • docs SUMMARY.md 295 B

History

  1. First recorded snapshot · 0 installs

SKILL.md

C# Advanced Patterns

Advanced C# language patterns and .NET 10 features for elegant, performant code.

When to Use

  • Implementing complex business logic with pattern matching
  • Optimizing async/await usage
  • Writing performant code with Span<T>/Memory<T>
  • Refactoring legacy code to modern C#
  • Creating immutable DTOs with records

Modern C# Features (.NET 10)

Records for DTOs

// Immutable DTO with required properties
public record CreatePatientDto
{
    public required string FirstName { get; init; }
    public required string LastName { get; init; }
    public required string Email { get; init; }
    public DateTime DateOfBirth { get; init; }
}

// Positional record with deconstruction
public record PatientDto(Guid Id, string FullName, string Email);

// Usage
var (id, name, email) = patient;

Pattern Matching

// Switch expression for status handling
public string GetStatusMessage(AppointmentStatus status) => status switch
{
    AppointmentStatus.Scheduled => "Your appointment is confirmed",
    AppointmentStatus.Completed => "Thank you for visiting",
    AppointmentStatus.Cancelled => "Your appointment was cancelled",
    AppointmentStatus.NoShow => "You missed your appointment",
    _ => throw new ArgumentOutOfRangeException(nameof(status))
};

// Property pattern matching
public decimal CalculateDiscount(Patient patient) => patient switch
{
    { Age: > 65 } => 0.20m,
    { IsVeteran: true } => 0.15m,
    { Visits: > 10 } => 0.10m,
    _ => 0m
};

// List patterns (.NET 7+)
public string DescribeList(int[] numbers) => numbers switch
{
    [] => "Empty",
    [var single] => $"Single: {single}",
    [var first, .., var last] => $"First: {first}, Last: {last}",
};

Primary Constructors

// Class with primary constructor
public class PatientService(
    IRepository<Patient, Guid> repository,
    ILogger<PatientService> logger)
{
    public async Task<Patient> GetAsync(Guid id)
    {
        logger.LogInformation("Getting patient {Id}", id);
        return await repository.GetAsync(id);
    }
}

Collection Expressions

// Modern collection initialization
int[] numbers = [1, 2, 3, 4, 5];
List<string> names = ["Alice", "Bob", "Charlie"];
Span<int> span = [1, 2, 3];

// Spread operator
int[] combined = [..numbers, 6, 7, 8];

Async/Await Patterns

Proper Async with Cancellation

public async Task<PatientDto> GetPatientAsync(
    Guid id,
    CancellationToken cancellationToken = default)
{
    var patient = await _repository
        .GetAsync(id, cancellationToken);

    return ObjectMapper.Map<Patient, PatientDto>(patient);
}

Parallel Processing with SemaphoreSlim

public async Task ProcessPatientsAsync(
    IEnumerable<Guid> patientIds,
    CancellationToken ct)
{
    var semaphore = new SemaphoreSlim(10); // Max 10 concurrent
    var tasks = patientIds.Select(async id =>
    {
        await semaphore.WaitAsync(ct);
        try
        {
            await ProcessPatientAsync(id, ct);
        }
        finally
        {
            semaphore.Release();
        }
    });

    await Task.WhenAll(tasks);
}

ValueTask for Hot Paths

// Use ValueTask when result is often synchronous
public ValueTask<Patient?> GetCachedPatientAsync(Guid id)
{
    if (_cache.TryGetValue(id, out var patient))
        return ValueTask.FromResult<Patient?>(patient);

    return new ValueTask<Patient?>(LoadPatientAsync(id));
}

Channel for Producer/Consumer

public class PatientProcessor
{
    private readonly Channel<Patient> _channel =
        Channel.CreateBounded<Patient>(100);

    public async Task ProduceAsync(Patient patient, CancellationToken ct)
    {
        await _channel.Writer.WriteAsync(patient, ct);
    }

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

Result Pattern

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

    private Result(T value) => Value = value;
    private Result(string error) => 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> onSuccess,
        Func<string, TResult> onFailure)
        => IsSuccess ? onSuccess(Value!) : onFailure(Error!);
}

// Usage
public Result<Patient> CreatePatient(CreatePatientDto dto)
{
    if (string.IsNullOrEmpty(dto.Email))
        return Result<Patient>.Failure("Email is required");

    var patient = new Patient(dto.FirstName, dto.LastName, dto.Email);
    return Result<Patient>.Success(patient);
}

Extension Methods

public static class PatientExtensions
{
    public static string GetFullName(this Patient patient)
        => $"{patient.FirstName} {patient.LastName}";

    public static bool IsEligibleForDiscount(this Patient patient)
        => patient.Age > 65 || patient.Visits > 10;

    // IQueryable extension for reusable filters
    public static IQueryable<Patient> ActiveOnly(this IQueryable<Patient> query)
        => query.Where(p => p.IsActive);

    public static IQueryable<Patient> ByEmail(
        this IQueryable<Patient> query,
        string email)
        => query.Where(p => p.Email == email);
}

Performance Patterns

Span<T> for Zero-Allocation

public static int CountOccurrences(ReadOnlySpan<char> text, char target)
{
    int count = 0;
    foreach (var c in text)
    {
        if (c == target) count++;
    }
    return count;
}

// String slicing without allocation
ReadOnlySpan<char> firstName = fullName.AsSpan(0, spaceIndex);

ArrayPool for Temporary Buffers

public async Task ProcessLargeDataAsync(Stream stream)
{
    var buffer = ArrayPool<byte>.Shared.Rent(4096);
    try
    {
        int bytesRead;
        while ((bytesRead = await stream.ReadAsync(buffer)) > 0)
        {
            ProcessChunk(buffer.AsSpan(0, bytesRead));
        }
    }
    finally
    {
        ArrayPool<byte>.Shared.Return(buffer);
    }
}

StringBuilder for String Building

// Bad: String concatenation in loops
string result = "";
foreach (var item in items)
    result += item; // Creates new string each iteration

// Good: Use StringBuilder
var sb = new StringBuilder();
foreach (var item in items)
    sb.Append(item);
return sb.ToString();

Anti-Patterns to Avoid

Anti-Pattern Problem Solution
.Result / .Wait() Deadlock risk Use await
catch (Exception) Catches everything Catch specific types
String concat in loops O(n²) allocations Use StringBuilder
async void Unobserved exceptions Use async Task
Premature optimization Complexity Profile first
// Bad: Blocking on async
var result = GetPatientAsync(id).Result; // Deadlock risk!

// Good: Proper async
var result = await GetPatientAsync(id);

// Bad: async void (fire and forget)
async void ProcessPatient(Guid id) { ... }

// Good: async Task
async Task ProcessPatientAsync(Guid id) { ... }

// Bad: Catching base Exception
try { } catch (Exception ex) { }

// Good: Catch specific exceptions
try { }
catch (InvalidOperationException ex) { _logger.LogWarning(ex, "..."); }
catch (ArgumentException ex) { _logger.LogError(ex, "..."); }

LINQ Best Practices

// Avoid multiple enumeration
var patients = await _repository.GetListAsync(); // Materialize once
var count = patients.Count;
var first = patients.FirstOrDefault();

// Use AsNoTracking for read-only queries
var patients = await _context.Patients
    .AsNoTracking()
    .Where(p => p.IsActive)
    .ToListAsync();

// Prefer Any() over Count() > 0
if (await _repository.AnyAsync(p => p.Email == email)) { ... }

// Project early to reduce data transfer
var dtos = await _context.Patients
    .Where(p => p.IsActive)
    .Select(p => new PatientDto(p.Id, p.FullName, p.Email))
    .ToListAsync();

Quality Checklist

  • Use records for DTOs (immutability)
  • Use switch expressions over switch statements
  • Pass CancellationToken through async chain
  • Use ValueTask for hot paths with sync results
  • Avoid blocking calls (.Result, .Wait())
  • Use Span<T> for performance-critical parsing
  • Catch specific exception types
  • Use nullable reference types

Integration Points

This skill is used by:

  • abp-developer: Modern C# patterns in implementation
  • abp-code-reviewer: Pattern validation during reviews
  • debugger: Performance analysis and fixes