modern-csharp

Compare original and translation side by side

🇺🇸

Original

English
🇨🇳

Translation

Chinese

Modern C# (C# 14 / .NET 10)

现代C#(C# 14 / .NET 10)

Core Principles

核心原则

  1. Use the newest stable features — C# 14 is the target. Prefer language-level constructs over library workarounds.
  2. Readability over cleverness — Pattern matching and expression-bodied members improve readability when used appropriately; deeply nested patterns do not.
  3. Value types where possible — Prefer
    record struct
    ,
    Span<T>
    , and stack allocation to reduce GC pressure.
  4. Immutability by default — Use
    record
    ,
    readonly
    ,
    init
    , and
    required
    to make illegal states unrepresentable.
  1. 使用最新稳定特性 —— 目标为C# 14。优先使用语言级构造而非库的变通方案。
  2. 可读性优于技巧性 —— 模式匹配和表达式体成员在恰当使用时可提升可读性,但深度嵌套的模式则不然。
  3. 尽可能使用值类型 —— 优先选择
    record struct
    Span<T>
    和栈分配以减少GC压力。
  4. 默认不可变 —— 使用
    record
    readonly
    init
    required
    使非法状态无法被表示。

Patterns

模式

Well-Known Features Quick Reference

常用特性速查

FeatureUsageExample
Primary constructorsDI injection, eliminate field assignments
public class OrderService(IOrderRepo repo, TimeProvider clock) { }
Collection expressions
[]
for all collection types + spread
List<string> names = ["Alice", "Bob"];
/
int[] all = [..a, ..b, 99];
RecordsDTOs, value objects, immutable data
public record CreateOrderRequest(string CustomerId, List<OrderItem> Items);
readonly record struct
Small stack-allocated value types
public readonly record struct Money(decimal Amount, string Currency);
Pattern matchingSwitch expressions, list/property patterns
order switch { { Total: > 1000 } => "Premium", _ => "Standard" };
List patternsDeconstruct arrays/lists
items switch { [] => "Empty", [var x] => $"One: {x}", [var f, .., var l] => $"{f}..{l}" };
Span<T>
Zero-allocation slicing
ReadOnlySpan<char> trimmed = input.Trim(); int.TryParse(trimmed[4..], out id);
Raw string literalsMulti-line SQL, JSON, XML
var sql = """ SELECT ... """;
/ interpolated:
$$""" {"id": "{{id}}"} """;
required
members
Enforce initialization
public required string ConnectionString { get; init; }
is
pattern + extraction
Null/type/property check
if (result is { IsSuccess: true, Value: var order }) { ... }
特性(Feature)用法(Usage)示例(Example)
Primary constructors依赖注入(DI)、消除字段赋值
public class OrderService(IOrderRepo repo, TimeProvider clock) { }
Collection expressions
[]
创建所有集合类型 + 展开操作
List<string> names = ["Alice", "Bob"];
/
int[] all = [..a, ..b, 99];
Records数据传输对象(DTO)、值对象、不可变数据
public record CreateOrderRequest(string CustomerId, List<OrderItem> Items);
readonly record struct
小型栈分配值类型
public readonly record struct Money(decimal Amount, string Currency);
Pattern matching开关表达式、列表/属性模式
order switch { { Total: > 1000 } => "Premium", _ => "Standard" };
List patterns解构数组/列表
items switch { [] => "Empty", [var x] => $"One: {x}", [var f, .., var l] => $"{f}..{l}" };
Span<T>
零分配切片
ReadOnlySpan<char> trimmed = input.Trim(); int.TryParse(trimmed[4..], out id);
Raw string literals多行SQL、JSON、XML
var sql = """ SELECT ... """;
/ 插值写法:
$$""" {"id": "{{id}}"} """;
required
members
强制初始化
public required string ConnectionString { get; init; }
is
pattern + 提取
空值/类型/属性检查
if (result is { IsSuccess: true, Value: var order }) { ... }

The
field
Keyword (C# 14)

field
关键字(C# 14)

Access the auto-generated backing field in property accessors without declaring it manually.
csharp
// GOOD — field keyword for validation in auto-property
public class Product
{
    public string Name
    {
        get => field;
        set => field = value?.Trim() ?? throw new ArgumentNullException(nameof(value));
    }

    public decimal Price
    {
        get => field;
        set => field = value >= 0 ? value : throw new ArgumentOutOfRangeException(nameof(value));
    }
}
在属性访问器中访问自动生成的后备字段,无需手动声明。
csharp
// GOOD — 使用field关键字实现自动属性验证
public class Product
{
    public string Name
    {
        get => field;
        set => field = value?.Trim() ?? throw new ArgumentNullException(nameof(value));
    }

    public decimal Price
    {
        get => field;
        set => field = value >= 0 ? value : throw new ArgumentOutOfRangeException(nameof(value));
    }
}

Lazy Initialization with
field

使用
field
实现延迟初始化

csharp
public class ProductCatalog
{
    // Lazy-load on first access — no manual Lazy<T> or backing field
    public IReadOnlyList<Product> Products
    {
        get => field ??= LoadProducts();
    }

    private static List<Product> LoadProducts() => /* expensive load */;
}
csharp
public class ProductCatalog
{
    // 首次访问时延迟加载 —— 无需手动使用Lazy<T>或后备字段
    public IReadOnlyList<Product> Products
    {
        get => field ??= LoadProducts();
    }

    private static List<Product> LoadProducts() => /* 耗时加载逻辑 */;
}

Change Notification with
field

使用
field
实现变更通知

csharp
// INotifyPropertyChanged without manual backing fields
public class OrderViewModel : INotifyPropertyChanged
{
    public event PropertyChangedEventHandler? PropertyChanged;

    public string CustomerName
    {
        get => field;
        set
        {
            if (field == value) return;
            field = value;
            PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(nameof(CustomerName)));
        }
    } = "";

    public decimal Total
    {
        get => field;
        set
        {
            if (field == value) return;
            field = value;
            PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(nameof(Total)));
        }
    }
}
csharp
// 无需手动后备字段的INotifyPropertyChanged实现
public class OrderViewModel : INotifyPropertyChanged
{
    public event PropertyChangedEventHandler? PropertyChanged;

    public string CustomerName
    {
        get => field;
        set
        {
            if (field == value) return;
            field = value;
            PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(nameof(CustomerName)));
        }
    } = "";

    public decimal Total
    {
        get => field;
        set
        {
            if (field == value) return;
            field = value;
            PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(nameof(Total)));
        }
    }
}

Extension Members (C# 14)

扩展成员(C# 14)

C# 14 adds
extension
blocks inside static classes. Unlike classic extension methods, they support extension properties and static extension members — the receiver is declared once for the whole block.
csharp
// GOOD — extension block (shipped C# 14 syntax)
public static class OrderExtensions
{
    extension(Order order)
    {
        public decimal TotalWithTax => order.Total * 1.2m;

        public bool IsHighValue => order.Total > 1000m;

        public string ToSummary() =>
            $"Order #{order.Id}: {order.Total:C} ({order.Items.Count} items)";
    }

    // Static extension members use the type (no receiver instance)
    extension(Order)
    {
        public static Order Empty => Order.Create("none", [], DateTimeOffset.MinValue);
    }
}

// Callers see them as if declared on Order
if (order.IsHighValue) { /* ... */ }
Classic
this
-parameter extension methods still work and coexist — use extension blocks when you need properties or several members on the same receiver.
C# 14在静态类中新增了
extension
块。与传统扩展方法不同,它支持扩展属性静态扩展成员——接收者为整个块声明一次。
csharp
// GOOD — 扩展块(C# 14正式语法)
public static class OrderExtensions
{
    extension(Order order)
    {
        public decimal TotalWithTax => order.Total * 1.2m;

        public bool IsHighValue => order.Total > 1000m;

        public string ToSummary() =>
            $"Order #{order.Id}: {order.Total:C} ({order.Items.Count} items)";
    }

    // 静态扩展成员使用类型(无需接收者实例)
    extension(Order)
    {
        public static Order Empty => Order.Create("none", [], DateTimeOffset.MinValue);
    }
}

// 调用者可像调用Order自身成员一样使用它们
if (order.IsHighValue) { /* ... */ }
传统的带
this
参数的扩展方法仍然有效并可共存——当需要为同一接收者添加多个成员或属性时,使用扩展块。

Anti-patterns

反模式

Don't Use Obsolete Patterns When Modern Alternatives Exist

当有现代替代方案时,不要使用过时模式

csharp
// BAD — manual backing field when field keyword works
private string _name;
public string Name
{
    get => _name;
    set => _name = value ?? throw new ArgumentNullException();
}

// BAD — old-style collection initialization
var list = new List<int>() { 1, 2, 3 };

// BAD — Tuple instead of record for domain types
(string Name, decimal Price) product = ("Widget", 9.99m);
// GOOD — record
public record Product(string Name, decimal Price);
csharp
// BAD — 当field关键字可用时,仍手动声明后备字段
private string _name;
public string Name
{
    get => _name;
    set => _name = value ?? throw new ArgumentNullException();
}

// BAD — 旧式集合初始化方式
var list = new List<int>() { 1, 2, 3 };

// BAD — 领域类型使用Tuple而非record
(string Name, decimal Price) product = ("Widget", 9.99m);
// GOOD — 使用record
public record Product(string Name, decimal Price);

Don't Over-pattern-match

不要过度使用模式匹配

csharp
// BAD — deeply nested pattern that's hard to read
if (order is { Customer: { Address: { Country: { Code: "US" } } } })

// GOOD — extract to a clear method or use sequential checks
if (order.Customer.Address.Country.Code == "US")
csharp
// BAD — 深度嵌套的模式难以阅读
if (order is { Customer: { Address: { Country: { Code: "US" } } } })

// GOOD — 提取为清晰的方法或使用顺序检查
if (order.Customer.Address.Country.Code == "US")

Don't Use
var
When the Type Is Not Obvious

当类型不明显时,不要使用
var

csharp
// BAD — what type is this?
var result = Process(order);

// GOOD — explicit type when not obvious
Result<Order> result = Process(order);
// Also GOOD — var is fine when type is apparent
var orders = new List<Order>();
csharp
// BAD — 无法明确此变量类型
var result = Process(order);

// GOOD — 类型不明确时使用显式类型
Result<Order> result = Process(order);
// 同样GOOD — 类型明显时使用var没问题
var orders = new List<Order>();

Decision Guide

决策指南

ScenarioRecommendation
DTO / API contract
record
(reference type)
Small value object (2-3 fields)
readonly record struct
Service with DIPrimary constructor
Collection creationCollection expression
[]
Property with validation
field
keyword
Multi-line string (SQL, JSON)Raw string literal
"""
Slicing strings/arrays
Span<T>
Type checking + extractionPattern matching with
is
/
switch
Enforced initialization
required
modifier
Adding methods to external typesExtension members
场景(Scenario)推荐方案(Recommendation)
DTO / API契约
record
(引用类型)
小型值对象(2-3个字段)
readonly record struct
带依赖注入的服务Primary constructor
集合创建集合表达式
[]
带验证的属性
field
关键字
多行字符串(SQL、JSON)原始字符串字面量
"""
字符串/数组切片
Span<T>
类型检查 + 提取使用
is
/
switch
的模式匹配
强制初始化
required
修饰符
为外部类型添加方法扩展成员