IRONSOFTWAREHOME

C# 14中的新field关键字

[[academy-video-youtube({'vid': '_Z551_SKHA4', 'start_time': '0', 'title': 'C# 14 新 field 关键字', 'creator': 'Tim Corey', 'length': '10m 35s'})]]

C# 中的自动属性简洁,但当您需要在 setter 中进行验证或转换逻辑时,您一直不得不完全抛弃它们并编写一个带有手动基础字段的完整属性。 从一行跳到七行是为了添加一个单一保护条款付出的巨大代价。 C# 14 引入了field关键字来弥合这一差距,让您可以自定义getter或setter,同时编译器仍然为您管理后备字段。

在他的视频《C# 14 新 field 关键字》中,Tim Corey 演示了这个功能解决的问题,演练了 setter 验证的实际例子,并介绍了升级前您应该了解的命名冲突。 我们将详细跟随每一步,以便您可以自信地在自己的属性中开始使用field

设置:一个简单的 Person 模型

[0:12 - 1:07] Tim 从在.NET 10和Visual Studio 2026上运行的控制台应用程序开始演示。演示的中心是一个具有几个属性的Person类:

public required string FirstName { get; set; }
public required string LastName { get; set; }
public int Age { get; set; }
C#

还有一个由私有字段支持的Demo属性,当命名冲突出现时变得相关。 在Program.cs中,Tim 使用LastName = "Corey"创建了一个实例,然后打印姓氏、年龄和演示值。 一切如预期输出:"Corey", 0(默认整数)和"test"。

问题:自动属性接受错误数据

[1:23 - 2:49] 问题在Tim在构建后将LastName时出现:

p.LastName = null;
C#

尽管required并被类型化为不可为null的字符串,但分配仍可编译。 required修饰符仅强制在对象初始化时提供值; 它不会阻止后来将属性设置为null。 结果是在运行时没有抛出错误而是留下一个空白的姓氏。

这是数据完整性的一个真正缺口。 类型系统用一个 可空 引用警告您,但这只是编译时提示,而不是运行时保护。 如果您的应用程序依赖于LastName始终包含有效的字符串,自动属性本身无法强制执行该约定。

旧版修复:带手动基础字段的完整属性

[2:58 - 4:19] 在 C# 14 之前,标准解决方案是将自动属性转换为带有显式 基础字段 的完整属性:

private string _lastName;
public required string LastName
{
    get => _lastName;
    set => _lastName = value ?? throw new ArgumentNullException(nameof(LastName));
}
C#

Tim 运行此程序并确认异常正确触发:"值不能为空。 参数名称:LastName。" 这种方法有效,但需要声明一个私有字段,连接 getter 和 setter,并在多个行中重复属性名称。 对于一个单一验证规则,这是一种很大的仪式。

此情况下的 getter 并没有什么特别; 它不变地返回字段。 然而,由于语法要求,一旦您离开自动属性领域,您仍然需要显式地编写它。 Tim 将这种繁琐视为新功能背后的动机。

C# 14解决方案:field关键字

[4:23 - 5:47] C# 14 引入了一个中间立场。 与其声明一个私有后备字段,不如在getter或setter中使用上下文关键字field直接引用编译器生成的后备字段:

public required string LastName
{
    get;
    set => field = value ?? throw new ArgumentNullException(nameof(LastName));
}
C#

getter 仍然是自动实现的get;,无需主体。 setter 使用value。 编译器在幕后创建并管理基础字段,就像标准自动属性一样。

运行演示在分配null时产生相同的ArgumentNullException。 这种行为与手动支持的版本相同,从七行压缩到仅关注需要自定义的部分。 您保留自动属性 getter,仅为 setter 添加逻辑,完全跳过手动字段声明。

这为一个简单的自动属性(一行,无验证)和一个完整属性(七行或更多行,完整控制)之间提供了有用的中间步骤。 当您的逻辑只触及 setter 时,您不再需要为重写 getter 支付语法成本。

使用 Setter Guard 验证年龄

[6:16 - 7:39] 为了显示field不限于null检查,Tim 在Age 属性中添加了范围验证:

public int Age
{
    get;
    set
    {
        if (value > 0 && value < 120)
            field = value;
    }
}
C#

在这里,setter 默默忽略不在合理范围内的值。 分配field从未写入。 Tim 指出您可以抛出异常,但无声的方法表明setter体可以包含您需要的任何逻辑,同时仍然依赖field进行存储。

该模式广泛适用:限定数值范围,修剪字符串空格,规范化大小写,或者您希望在每次设置属性时应用的任何转换。

与现有 field 变量的命名冲突

[7:39 - 9:43] Tim 介绍了一个故意的边缘案例。 演示类有一个私有成员字面上命名为field

private string field = "test";
C#

一旦C# 14激活,编译器将field视为属性访问器中的关键字而非变量。 这意味着引用field的属性默默地从属性后面的隐藏存储(空的)读取,而不是包含"test"的字符串成员。 输出变为空白,没有编译错误,只有警告。

存在两种解决方法。添加this.field前缀告诉编译器您指的是类级成员而不是关键字。 或者,@field转义以相同的方式工作:

// Both refer to the instance variable, not the keyword
string demo => this.field;
string demo => @field;
C#

Tim 强烈建议在升级到C# 14时重命名任何名为field的变量。在您的IDE中快速"重命名全部"可以永久消除歧义。 冲突仅在属性访问器内出现; 构造函数和方法将field解析为变量名称,因为这些上下文没有隐式后备存储。

总结:减少模板代码,保持相同的控制

[10:04 - 10:28] field关键字在日常C#代码中填补了一个实际空白。 需要一个保护条款或转换的属性不再需要通过手动设置字段进行全面重写。 您只定制需要逻辑的访问器并将其他部分保留为标准自动实现。

结论

[10:28 - 10:35] 总结:C# 14 的field关键字让您可以直接访问任何属性访问器内的隐式后备存储。 在不放弃不需要自定义的部分的自动属性语法的情况下,使用它添加 setter 验证、getter 转换或两者。

在升级之前,搜索您的代码库中任何名为field的变量并重命名它们。 这一预防措施避免了该功能引入的唯一真正陷阱。 除此之外,这是一种自然适合到大多数开发人员已经设置模型的途径的模板代码的干净减少。

示例提示:如果您只需验证setter,请将getter保留为无主体的简单get;。 编译器将其处理为自动属性 getter,避免编写不添加任何内容的透传返回语句。

观看他的 YouTube 视频以获取关于 C# 语言特性的更多见解。

Earn More by Sharing What You Love

Do you create content for developers working with .NET, C#, Java, Python, or Node.js? Turn your expertise into extra income!

Let's Stay in Touch!

Join our newsletter, you’ll get exclusive access on article updates. We value your privacy

Key in blue circle

立即获取免费的 30 天试用版密钥

Your trial license will be sent to your email address

无任何限制。100% 解锁。无需信用卡。

bullet_checked无需信用卡或创建账户无任何限制。100% 解锁。无需信用卡。
  • Logo Aetna
  • Logo NASA
  • Logo GE
  • Logo Porsche
  • Logo USDA
  • Logo Qatar
Join Millions of Engineers who’ve tried IronPDF
获取您的无义务咨询
填写下面的表格或通过sales@ironsoftware.com
您的资料将始终保密。
深受全球数百万工程师信赖
Iron Software 的客户徽标
立即获取您的免费30 天试用密钥
无需信用卡或创建账户