🎯

csharp-advanced-patterns

🎯Skill

from thapaliyabikendra/ai-artifacts

VibeIndex|
What it does

csharp-advanced-patterns skill from thapaliyabikendra/ai-artifacts

csharp-advanced-patterns

Installation

Install skill:
npx skills add https://github.com/thapaliyabikendra/ai-artifacts --skill csharp-advanced-patterns
3
Last UpdatedJan 23, 2026

Skill Details

SKILL.md

"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#."

Overview

# 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/Memory
  • Refactoring legacy code to modern C#
  • Creating immutable DTOs with records

Modern C# Features (.NET 10)

Records for DTOs

```csharp

// 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

```csharp

// 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

```csharp

// Class with primary constructor

public class PatientService(

IRepository repository,

ILogger logger)

{

public async Task GetAsync(Guid id)

{

logger.LogInformation("Getting patient {Id}", id);

return await repository.GetAsync(id);

}

}

```

Collection Expressions

```csharp

// Modern collection initialization

int[] numbers = [1, 2, 3, 4, 5];

List names = ["Alice", "Bob", "Charlie"];

Span span = [1, 2, 3];

// Spread operator

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

```

Async/Await Patterns

Proper Async with Cancellation

```csharp

public async Task GetPatientAsync(

Guid id,

CancellationToken cancellationToken = default)

{

var patient = await _repository

.GetAsync(id, cancellationToken);

return ObjectMapper.Map(patient);

}

```

Parallel Processing with SemaphoreSlim

```csharp

public async Task ProcessPatientsAsync(

IEnumerable 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

```csharp

// Use ValueTask when result is often synchronous

public ValueTask GetCachedPatientAsync(Guid id)

{

if (_cache.TryGetValue(id, out var patient))

return ValueTask.FromResult(patient);

return new ValueTask(LoadPatientAsync(id));

}

```

Channel for Producer/Consumer

```csharp

public class PatientProcessor

{

private readonly Channel _channel =

Channel.CreateBounded(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

```csharp

public readonly record struct Result

{

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 Success(T value) => new(value);

public static Result Failure(string error) => new(error);

public TResult Match(

Func onSuccess,

Func onFailure)

=> IsSuccess ? onSuccess(Value!) : onFailure(Error!);

}

// Usage

public Result CreatePatient(CreatePatientDto dto)

{

if (string.IsNullOrEmpty(dto.Email))

return Result.Failure("Email is required");

var patient = new Patient(dto.FirstName, dto.LastName, dto.Email);

return Result.Success(patient);

}

```

Extension Methods

```csharp

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 ActiveOnly(this IQueryable query)

=> query.Where(p => p.IsActive);

public static IQueryable ByEmail(

this IQueryable query,

string email)

=> query.Where(p => p.Email == email);

}

```

Performance Patterns

Span<T> for Zero-Allocation

```csharp

public static int CountOccurrences(ReadOnlySpan text, char target)

{

int count = 0;

foreach (var c in text)

{

if (c == target) count++;

}

return count;

}

// String slicing without allocation

ReadOnlySpan firstName = fullName.AsSpan(0, spaceIndex);

```

ArrayPool for Temporary Buffers

```csharp

public async Task ProcessLargeDataAsync(Stream stream)

{

var buffer = ArrayPool.Shared.Rent(4096);

try

{

int bytesRead;

while ((bytesRead = await stream.ReadAsync(buffer)) > 0)

{

ProcessChunk(buffer.AsSpan(0, bytesRead));

}

}

finally

{

ArrayPool.Shared.Return(buffer);

}

}

```

StringBuilder for String Building

```csharp

// 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 |

```csharp

// 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

```csharp

// 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 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