【.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)

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

相关推荐
白狐_79840 分钟前
408数据结构第8章:排序②——性质对比秒杀、场景选择与外部排序
java·数据结构·算法
zander2581 小时前
LeetCode 84:柱状图中的最大矩形——单调栈如何确定左右边界
java·数据结构·算法
码匠许师傅1 小时前
【C++ 面试真题】聊聊 C++ 的拷贝构造与拷贝赋值
java·c++·面试
arbboter3 小时前
【网络工具】NetProxy网络代理用户手册
开发语言·网络·c#·网络代理·网络异常·代理工具
极创信息3 小时前
系统安全隐患全方位排查方案:标准化渗透测试全流程
java·opencv·struts·数据挖掘·eclipse·语音识别·hibernate
微小冷3 小时前
海康威视相机C#二次开发环境配置
数码相机·c#·.net·海康威视·相机开发
汉字萌萌哒4 小时前
2019CCF-CSP入门级C++试题解析
java·开发语言·c++
喜欢的名字被抢了4 小时前
程序出问题怎么查,以及如何让它不掉线
java·运维·数据库
数据知道4 小时前
反序列化漏洞:Java、PHP、Python 三条线各讲透
java·网络·python·安全·网络安全·php
2601_949950635 小时前
Java 面试准备,用练题簿把“背过八股”变成“真正会答”
java·开发语言·面试·刷题·小程序推荐