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;
}
}
这种写法有两个问题:
-
需要手写 backing field(
_name) -
需要手写赋值逻辑(
= 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;
}
}
解决方案 :使用 @field 或 this.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)
💬 欢迎点赞、收藏、转发,你的支持是我持续创作的动力!