원클릭으로
modern-csharp
Use when adopting C# 12/13/14 features — primary constructors, collection expressions, or field keyword.
Codex 또는 Claude로 설치 이 Prompt를 복사해 Codex, Claude 또는 다른 어시스턴트에 붙여 넣으면 Skill 페이지를 검토하고 설치를 진행할 수 있습니다.
메뉴
Use when adopting C# 12/13/14 features — primary constructors, collection expressions, or field keyword.
Codex 또는 Claude로 설치 이 Prompt를 복사해 Codex, Claude 또는 다른 어시스턴트에 붙여 넣으면 Skill 페이지를 검토하고 설치를 진행할 수 있습니다.
SOC 직업 분류 기준
Use when adding caching to .NET APIs or optimizing response times with distributed cache, output cache, or ETags.
Use when configuring API response formats, custom formatters, or Accept header handling.
Use when building controller-based REST APIs with action results, model binding, or MediatR integration.
Use when creating RESTful API controllers with MediatR dispatch and ProblemDetails error responses.
Use when designing gRPC services, proto files, or adding gRPC-Web or JSON transcoding.
Use when building minimal API endpoints with route groups, filters, or TypedResults.
| name | modern-csharp |
| description | Use when adopting C# 12/13/14 features — primary constructors, collection expressions, or field keyword. |
| metadata | {"category":"core","agent":"dotnet-architect","alwaysApply":true} |
[..] over explicit collection initializersFeature C# Version Min .NET
--------------------------------------------------
File-scoped namespaces C# 10 .NET 6
Global usings C# 10 .NET 6
Required members C# 11 .NET 7
Raw string literals C# 11 .NET 7
List patterns [a, .., z] C# 11 .NET 7
Primary constructors C# 12 .NET 8
Collection expressions [..] C# 12 .NET 8
Inline arrays C# 12 .NET 8
using aliases for generics C# 12 .NET 8
params collections C# 13 .NET 9
Lock object C# 13 .NET 9
field keyword C# 14 .NET 10
Extension types C# 14 .NET 10
// Services -- capture DI parameters directly
public sealed class OrderService(
IOrderRepository repository,
ILogger<OrderService> logger)
{
public async Task<Order?> GetAsync(Guid id, CancellationToken ct)
{
logger.LogInformation("Fetching order {OrderId}", id);
return await repository.FindAsync(id, ct);
}
}
// Records already had primary constructors -- still the best fit for DTOs
public sealed record OrderResponse(Guid Id, string CustomerName, decimal Total);
// Structs
public readonly record struct Money(decimal Amount, string Currency);
// Array, List, Span -- all use [..]
int[] numbers = [1, 2, 3];
List<string> names = ["Alice", "Bob"];
ReadOnlySpan<byte> bytes = [0x00, 0xFF];
// Spread operator
List<string> merged = [..existing, ..additional, "extra"];
// Empty collection (replaces Array.Empty<T>())
int[] empty = [];
// Command message
public sealed record Create{Entity}Command(
string Name,
string Description) : IRequest<Result<Guid>>;
// Value object
public sealed record Address(
string Street,
string City,
string PostalCode,
string Country);
// Record struct for small value types (no heap allocation)
public readonly record struct Coordinate(double Latitude, double Longitude);
// Switch expression with property pattern
string description = order switch
{
{ Status: OrderStatus.Pending, Total: > 1000 } => "Requires approval",
{ Status: OrderStatus.Pending } => "Awaiting processing",
{ Status: OrderStatus.Shipped } => "In transit",
{ Status: OrderStatus.Delivered } => "Complete",
_ => "Unknown"
};
// List patterns (C# 11)
var result = args switch
{
[] => "No arguments",
[var single] => $"One argument: {single}",
[var first, ..] => $"Multiple arguments, first: {first}"
};
// Relational and logical patterns
bool IsValidAge(int age) => age is >= 0 and <= 150;
// Multi-line SQL or JSON without escaping
var sql = """
SELECT o.Id, o.CustomerName, o.Total
FROM Orders o
WHERE o.Status = @Status
ORDER BY o.CreatedAt DESC
""";
// Interpolated raw strings
var json = $$"""
{
"name": "{{name}}",
"total": {{total}}
}
""";
public sealed class {Company}.{Domain}.Models.ProductOptions
{
public required string Name { get; init; }
public required decimal Price { get; init; }
public string? Description { get; init; }
}
// Caller must set required properties
var options = new ProductOptions
{
Name = "Widget", // required
Price = 9.99m // required
// Description is optional
};
// Auto-property with custom validation -- no backing field declaration needed
public string Name
{
get => field;
set => field = value?.Trim() ?? throw new ArgumentNullException(nameof(value));
}
public int Quantity
{
get => field;
set => field = value >= 0 ? value : throw new ArgumentOutOfRangeException(nameof(value));
}
using OrderList = System.Collections.Generic.List<{Company}.{Domain}.Models.Order>;
using OrderDict = System.Collections.Generic.Dictionary<System.Guid, {Company}.{Domain}.Models.Order>;
// Then use the alias
OrderList orders = [new Order(), new Order()];
// PREFER: file-scoped (saves one level of indentation)
namespace {Company}.{Domain}.Features.Orders;
public sealed class OrderService { }
// AVOID: block-scoped
namespace {Company}.{Domain}.Features.Orders
{
public sealed class OrderService { }
}
| Anti-Pattern | Problem | Correct Approach |
|---|---|---|
Using new List<int> { 1, 2, 3 } on C# 12+ | Verbose, ignores collection expressions | [1, 2, 3] |
| Mutable class for DTO | Unintended mutation | sealed record |
| Long if/else chains for type checks | Hard to read, error-prone | Switch expression with patterns |
string.Format() or $"" for multi-line SQL | Escaping issues, poor readability | Raw string literals """ |
field keyword on .NET 8/9 projects | Compilation error | Check <LangVersion> first |
Primary constructors storing to private readonly | Redundant -- captured params are already fields | Use captured parameter directly |
| Block-scoped namespaces | Wastes indentation | File-scoped namespace X; |
== null / != null comparisons | Operator overloading can break intent | is null / is not null |
<LangVersion> in .csproj or Directory.Build.props (preview, latest, 12, 13, 14)<TargetFramework> for net8.0, net9.0, net10.0class Foo( or struct Foo(record keyword usage in model/DTO files= [ in existing coderequired modifier on propertiesDirectory.Build.props or .csproj for <LangVersion>sealed record[..] syntax on C# 12+ projectsif/switch blocks with switch expressionsdotnet formatfield keyword or extension types on .NET 10+ projects| Scenario | Recommendation |
|---|---|
| .NET 8 project | C# 12: primary constructors, collection expressions, records |
| .NET 9 project | C# 13: all above + params collections, lock object |
| .NET 10+ project | C# 14: all above + field keyword, extension types |
| Shared library targeting multiple TFMs | Use lowest common C# version across targets |
| New greenfield project | Use <LangVersion>latest</LangVersion> and latest TFM |