---
id: irahardianto/awesome-agv/csharp-idioms
version: "09122a58"
license: MIT
install: manual
updated: 2026-07-17
---
# Csharp Idioms — Learn idiomatic C# patterns that leverage type safety, LINQ expressiveness, and async-first design. This skill covers modern features like nullable reference types, records, and pattern matching, plus practical guidance on error handling, dependency injection, and testing with xUnit.
Publisher: irahardianto · Stars: 150 · Updated: 2026-07-17
Install (manual): `git clone https://github.com/irahardianto/awesome-agv`
## SKILL.md
## C# Idioms and Patterns
C# rewards type safety, LINQ expressiveness, and async-first design. Modern C# (10+/.NET 6+) favors records, nullable reference types, and minimal APIs. Idiomatic C# = clean, async-aware, framework-integrated.
> Scope: C# coding idioms. Test naming: .agents/rules/testing-strategy.md. Logging: `@.agents/skills/logging-implementation/SKILL.md`.
### Modern C# Features (10+)
1. **Nullable reference types — always enabled:**
```csharp
// ✅ Explicit nullability
public Task? FindById(string id) { ... }
public Task GetById(string id) { ... } // never returns null — throws
// In .csproj: enable
```
2. **Records for immutable data:**
```csharp
public record CreateTaskRequest(string Title, Priority Priority);
public record TaskResponse(string Id, string Title, DateTime CreatedAt);
```
3. **Pattern matching:**
```csharp
return result switch
{
Success(var task) => Ok(task),
NotFound(var id) => NotFound($"Task {id} not found"),
ValidationError(var errors) => BadRequest(errors),
_ => StatusCode(500)
};
```
4. **`required` and `init` for safe construction:**
```csharp
public class AppConfig
{
public required string DatabaseUrl { get; init; }
public required string ApiKey { get; init; }
public int MaxRetries { get; init; } = 3;
}
```
### Error Handling
1. **Result pattern over exceptions for expected failures:**
```csharp
public record Result
{
public T? Value { get; init; }
public string? Error { get; init; }
public bool IsSuccess => Error is null;
public static Result Ok(T value) => new() { Value = value };
public static Result Fail(string error) => new() { Error = error };
}
```
2. **Domain exceptions for unexpected failures — never raw `Exception`.**
3. **Never `catch (Exception)` without re-throw or specific handling.**
### Async/Await
1. **Async all the way — never `.Result` or `.Wait()` on tasks:**
```csharp
// ✅ Async pipeline
public async Task GetTaskAsync(string id, CancellationToken ct)
{
return await _storage.GetByIdAsync(id, ct)
?? throw new NotFoundException("Task", id);
}
// ❌ Sync-over-async — deadlock risk
var task = _storage.GetByIdAsync(id).Result;
```
2. **Always accept `CancellationToken`** on async methods.
3. **`ConfigureAwait(false)`** in library code only.
### Dependency Injection
1. **Constructor injection — no property or method injection:**
```csharp
public class TaskService
{
private readonly ITaskStorage _storage;
private readonly ILogger _logger;
public TaskService(ITaskStorage storage, ILogger logger)
{
_storage = storage;
_logger = logger;
}
}
```
2. **Register in DI container — never `new` a service:**
```csharp
builder.Services.AddScoped();
builder.Services.AddScoped();
```
### LINQ
1. **Prefer method syntax for complex queries, query syntax for joins:**
```csharp
var active = tasks
.Where(t => t.IsActive)
.OrderByDescending(t => t.Priority)
.Select(t => new TaskSummary(t.Id, t.Title));
```
2. **Never mutate collections during LINQ iteration.**
### Naming
1. **PascalCase** for classes, methods, properties, events, namespaces.
2. **camelCase** for parameters, local variables.
3. **`_camelCase`** for private fields (prefix underscore).
4. **`I` prefix** for interfaces: `ITaskStorage`.
5. **`Async` suffix** for async methods: `GetByIdAsync`.
### Testing
1. **xUnit + FluentAssertions:**
```csharp
[Fact]
public async Task GetTask_ReturnsTask_WhenExists()
{
var result = await _service.GetTaskAsync("task-1", CancellationToken.None);
result.Should().NotBeNull();
result.Title.Should().Be("Test Task");
}
```
2. **`[Theory]` for parameterized tests:**
```csharp
[Theory]
[InlineData("low", 1)]
[InlineData("medium", 5)]
[InlineData("high", 10)]
public void PriorityScore_MapsCorrectly(string priority, int expected)
{
Priority.Score(priority).Should().Be(expected);
}
```
3. **NSubstitute or Moq for mocking.**
### Formatting and Static Analysis
| Tool | Purpose | Command |
|---|---|---|
| `dotnet format` | Canonical formatting | `dotnet format` |
| Roslyn Analyzers | Compile-time analysis | Built-in |
| `SonarAnalyzer` | Comprehensive analysis | NuGet package |
| `dotnet-outdated` | Dependency freshness | `dotnet-outdated` |
| `dotnet list package --vulnerable` | CVE scanning | Built-in (.NET 8+) |
### Related
- Code Idioms and Conventions .agents/rules/code-idioms-and-conventions.md
- Testing Strategy .agents/rules/testing-strategy.md
- Error Handling Principles .agents/rules/error-handling-principles.md
- Dependency Management Principles @.agents/rules/dependency-management-principles.md
[View on SkillFed](https://skillfed.io/irahardianto/awesome-agv/csharp-idioms) · [View on GitHub](https://github.com/irahardianto/awesome-agv)