بنقرة واحدة
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 |