【.NET新特性·第11篇】C# 14 字段属性:告别手写 backing field

field 关键字让你在属性访问器中直接使用编译器合成的 backing field,告别手写样板代码

版本定位

适用版本:.NET 10 | C# 14 前置知识:C# 13 基础、属性和 backing field 概念

背景

在 C# 中,如果你需要在属性的 get/set 访问器中添加逻辑,通常需要手写 backing field:

复制代码
// C# 13 及之前
public class Person
{
    private string _name;

    public string Name
    {
        get => _name;
        set => _name = value?.Trim() ?? string.Empty;
    }
}

这种写法有两个问题:

  1. 需要手写 backing field(_name

  2. 需要手写赋值逻辑(= value

C# 14 引入了 field 关键字,让编译器自动合成 backing field。

field 关键字详解

基本用法

复制代码
// C# 14
public class Person
{
    public string Name
    {
        get => field;
        set => field = value?.Trim() ?? string.Empty;
    }
}

field 是什么?

  • 编译器自动合成的 backing field

  • 在 get 访问器中,field 是当前值

  • 在 set 访问器中,field 是要设置的新值

只在 get 中使用

复制代码
public class Product
{
    public decimal Price { get; set; }

    public decimal PriceWithTax
    {
        get => field == 0 ? Price * 1.1m : field;
        set => field = value;
    }
}

只在 set 中使用

复制代码
public class User
{
    public string Email
    {
        get => field;
        set
        {
            if (string.IsNullOrEmpty(value))
                throw new ArgumentException("Email cannot be empty");
            field = value.ToLower();
        }
    }
}

与验证逻辑配合

复制代码
public class Age
{
    public int Value
    {
        get => field;
        set
        {
            if (value < 0 || value > 150)
                throw new ArgumentOutOfRangeException(nameof(value));
            field = value;
        }
    }
}

与旧写法对比

场景 1:简单的值转换

复制代码
// C# 13 及之前
private string _name;
public string Name
{
    get => _name;
    set => _name = value?.Trim();
}

// C# 14
public string Name
{
    get => field;
    set => field = value?.Trim();
}

场景 2:验证 + 赋值

复制代码
// C# 13 及之前
private int _age;
public int Age
{
    get => _age;
    set
    {
        if (value < 0)
            throw new ArgumentOutOfRangeException(nameof(value));
        _age = value;
    }
}

// C# 14
public int Age
{
    get => field;
    set
    {
        if (value < 0)
            throw new ArgumentOutOfRangeException(nameof(value));
        field = value;
    }
}

场景 3:计算属性

复制代码
// C# 13 及之前
private DateTime _createdTime;
public DateTime CreatedTime
{
    get => _createdTime;
    set => _createdTime = value.ToUniversalTime();
}

public DateTime CreatedTimeLocal => _createdTime.ToLocalTime();

// C# 14
public DateTime CreatedTime
{
    get => field;
    set => field = value.ToUniversalTime();
}

public DateTime CreatedTimeLocal => CreatedTime.ToLocalTime();

注意事项

1. 字段名冲突

如果类中已经有一个名为 field 的成员,会发生冲突:

复制代码
public class Example
{
    private int field;  // 字段

    public int Value
    {
        get => field;  // 这里是字段,不是 keyword
        set => field = value;
    }
}

解决方案 :使用 @fieldthis.field 来消歧义

复制代码
public class Example
{
    private int field;

    public int Value
    {
        get => @field;  // 使用 @ 消歧义
        set => @field = value;
    }
}

2. 不能用于自动属性

复制代码
// 编译错误
public int Value { get => field; set => field = value; }

// 正确:自动属性
public int Value { get; set; }

// 正确:使用 field 的属性
public int Value
{
    get => field;
    set => field = value;
}

3. 不能用于表达式-bodied 属性

复制代码
// 编译错误
public int Value => field;

// 正确
public int Value
{
    get => field;
}

实战场景

DTO 类

复制代码
public class UserDto
{
    public string Name
    {
        get => field;
        set => field = value?.Trim() ?? string.Empty;
    }

    public string Email
    {
        get => field;
        set => field = value?.ToLower().Trim();
    }

    public int Age
    {
        get => field;
        set
        {
            if (value < 0 || value > 150)
                throw new ArgumentOutOfRangeException(nameof(value));
            field = value;
        }
    }
}

配置类

复制代码
public class AppConfig
{
    public string ConnectionString
    {
        get => field;
        set
        {
            if (string.IsNullOrEmpty(value))
                throw new ArgumentException("Connection string cannot be empty");
            field = value;
        }
    }

    public int MaxRetries
    {
        get => field;
        set => field = Math.Max(0, Math.Min(10, value));
    }

    public TimeSpan Timeout
    {
        get => field;
        set => field = value > TimeSpan.Zero ? value : TimeSpan.FromSeconds(30);
    }
}

迁移建议

自动迁移

field 关键字是向后兼容的,可以直接使用:

复制代码
<PropertyGroup>
    <LangVersion>14</LangVersion>
</PropertyGroup>

逐步迁移

复制代码
// 旧代码
private string _name;
public string Name
{
    get => _name;
    set => _name = value?.Trim();
}

// 新代码
public string Name
{
    get => field;
    set => field = value?.Trim();
}

// 删除 _name 字段

一句话总结

field 关键字让你告别手写 backing field,用更简洁的语法实现属性的 get/set 逻辑。


官方文档


📦 示例代码:.NET 新特性巡礼全系列配套示例代码(含 dotnet 8/9/10)

💬 欢迎点赞、收藏、转发,你的支持是我持续创作的动力!

相关推荐
论迹复利8 分钟前
FreeRTOS 在 RISC-V 上是如何“点火“的 —— 从 main() 到第一个任务的完整链路
java·开发语言·risc-v
rockey62720 分钟前
AScript之编译递归函数
c#·.net·script·动态脚本
张宇Joaquin28 分钟前
鲲鹏统一并行加速库KUPL--众核并行能力介绍
java·开发语言·网络
宸津-代码粉碎机1 小时前
AI攻防战升级!基于Spring AI构建Java应用自动免疫安全体系
java·大数据·开发语言·人工智能·python·安全·spring
2601_963749102 小时前
越华环保集团数字化污水治理:端边云采集架构与平台对接实现
java·大数据·架构
xcLeigh2 小时前
Go入门:整数类型的溢出与安全边界
java·安全·golang
源码宝3 小时前
SpringBoot+Vue2 诊所门诊管理系统,完整前后端源码,可直接部署上线
java·源码·his系统·门诊系统·程序代码·诊所系统·门诊his
yychen_java3 小时前
九:Text-to-SQL 智能数据查询与 Human-in-the-Loop 人机协作
java·人工智能·架构
lhldsg4 小时前
智慧场馆解决方案小程序开发实战:从架构设计到部署指南
java·小程序·uni-app
Freak嵌入式4 小时前
Pico UART 数据收发实战:硬件配置、MicroPython 编程
java·开发语言·单片机·嵌入式硬件·生活