跳至页脚内容
Iron Academy Logo
C# 和 .NET 中的新内容

在 10 分钟或更短时间内实现 C# 10 和 .NET 6 中的常数插值

Tim Corey
6 分 14 秒

随着每个新版本的发布,C# 在功能和优雅性方面不断进步,为开发人员提供更简洁、更高效的代码编写方式。 C# 10 中引入的其中一项新增功能是常量插值字符串——该功能允许开发人员在常量中使用字符串插值。

在此功能出现之前,如果您尝试使用插值创建常量字符串(例如,$"{companyName} Products"),编译器会抛出错误。 你只能将普通的字符串字面量赋值给常量值,这在构建常量消息或属性字符串时常常会导致代码重复或笨拙。

现在,从 C# 10 和 .NET 6 开始,您可以在编译时直接使用字符串插值将常量组合在一起。这项改进不仅使您的代码更简洁,而且还能提高效率,因为所有值都会在程序运行之前进行求值。

为了更清晰地理解其工作原理,Tim Corey 在他的视频" 10 分钟内掌握 C# 10 和 .NET 6 中的常量插值"中,一步步地讲解了这一概念。Tim 以他一贯简洁实用的教学风格,解释了这项新特性的作用、它与传统字符串连接的区别,以及它在哪些情况下特别有用——尤其是在属性中。

那么让我们一起来看看 Tim 的解释,了解 C# 中的常量插值字符串究竟是如何工作的,以及为什么它们对开发人员来说是一个有用的补充。

简介

在这段视频中,Tim 解释了插值字符串(即使用熟悉的美元符号 ($) 语法的字符串)现在可以在常量表达式中使用,这是以前无法实现的。 这一小小的改变使得持续的弦线制作更加容易、干净、易于维护。

探索 .NET 6 中的常量

Tim 打开了一个 .NET 6 控制台应用程序,并删除了样板代码,以便完全专注于常量定义。

他从最基本的常量字符串声明开始:

const string companyName = "Acme";
const string companyName = "Acme";

这是一个简单的字符串字面量,被赋值为一个常量字符串。 像这样的常量是在编译时求值的,这意味着它们的值是固定的,并嵌入到编译后的程序中。

但 Tim 很快就提出了一个核心问题:如果我们想将常量字符串合并在一起,或者使用插值将值直接嵌入到其他字符串中,该怎么办?

在 C# 9 及更早版本中,你无法这样做:

const string productName = $"{companyName} Anvils"; // Not allowed before C# 10
const string productName = $"{companyName} Anvils"; // Not allowed before C# 10

这行代码会引发编译时错误,因为字符串插值不支持常量表达式。

C# 10 中的常量插值字符串

正如 Tim 所演示的那样,在 C# 10 中,编译器现在支持常量插值字符串——只要其中的所有插值表达式本身都是常量即可。

所以下面的例子现在可以完美运行了:

const string productName = $"{companyName} Anvils";
const string productName = $"{companyName} Anvils";

这是一个常量插值字符串,这意味着编译器会在编译时而不是运行时计算该插值字符串。程序运行时不会进行额外的字符串连接或字符串格式化——编译器会生成一个类似"Acme Anvils"的单个常量字符串字面量。

Tim 解释说,如果我们把 companyName 的值从"Acme"改为"ABC",编译器会自动为 productName 生成"ABC Anvils"。 这是编译时字符串构造,而不是运行时插值。

这项改进使得合并常量字符串变得更加容易,无需使用 + 连接或手动重复值。

嵌套常量插值

蒂姆更进一步,给出了另一个恒定的定义:

const string productDescription = $"{productName} are the best way to crush unsuspecting roadrunners.";
const string productDescription = $"{productName} are the best way to crush unsuspecting roadrunners.";

这是一个嵌套插值的例子,其中一个常量插值字符串(productName)被用在另一个常量插值字符串中。

编译器将所有这些都视为常量表达式,在编译时生成一个单一的、不可变的字符串表示形式。

当 Tim 运行该程序时,输出结果为:

Acme Anvils are the best way to crush unsuspecting roadrunners.
Acme Anvils are the best way to crush unsuspecting roadrunners.

这证实了即使跨越多个常量,常量插值也能无缝运行。

常数为何重要

这时,蒂姆停下来解释了为什么常量(以及现在的常量插值字符串)是有益的。

他指出,常量非常节省内存,因为它们的值直接存储在编译后的代码中,而不是作为单独的实例存储在内存中。

相比之下,以前开发人员需要类似功能时,通常使用只读字段:

readonly string companyName = "Acme";
readonly string companyName = "Acme";

但 Tim 指出,只读字段与常量字段不同——它们在运行时进行求值,这会消耗更多内存并阻止编译时优化。

借助常量插值字符串,我们现在可以编写富有表现力且可重用的格式化字符串,这些字符串在编译时保持常量,从而提高清晰度和性能。

实际示例——在属性中使用常量

蒂姆随后介绍了一个现实世界的场景,在这个场景中,这项新功能——属性——大放异彩。

他在 Main() 函数内部定义了一个简单的局部方法:

void SayHi() { }
void SayHi() { }

然后他尝试使用引用某个变量的字符串消息来应用 [Obsolete] 属性:

string myCompany = "Tim's Company";
[Obsolete($"This is no longer used for {myCompany}")]
string myCompany = "Tim's Company";
[Obsolete($"This is no longer used for {myCompany}")]

这样做会失败,因为属性只能接受常量表达式作为参数。 由于 myCompany 是一个变量,而不是常量,因此编译器会报错。

Tim 解释说,该属性的消息必须是编译时常量——它不能依赖于运行时值或实例变量。

然而,得益于 C# 10 中的常量插值字符串,我们现在可以安全地做到这一点:

const string productName = $"{companyName} Anvils";
[Obsolete($"This is no longer used for {productName}")]
const string productName = $"{companyName} Anvils";
[Obsolete($"This is no longer used for {productName}")]

在这里,编译器识别出 companyName 和 productName 都是常量,因此整个插值字符串是一个常量表达式。

编译器在编译时生成格式化字符串,使其在属性中有效。

这个例子完美地说明了为什么常量插值不仅仅是语法糖——它实现了新的场景,例如直接在属性或元数据中使用格式化的编译时字符串。

幕后揭秘——编译器如何处理

Tim 在视频中没有深入讲解编译器内部的底层原理,但这个概念与 C# 10 中插值字符串处理程序的工作方式密切相关。

一般来说,当编译器遇到插值字符串时,它会创建类似于格式化字符串操作的代码,在后台生成类似 AppendLiteral() 和 AppendFormatted() 的调用。

但是,在处理常量插值字符串时,编译器会在编译时评估所有内容——生成的 IL 代码中不会发出任何插值字符串处理程序或方法调用。

这意味着生成的值与任何字符串字面量的行为完全相同,但您仍然可以使用来自其他常量的嵌入式表达式来组合它。

它巧妙地平衡了表现力和效率——编译器静态地处理字符串构造,确保零运行时成本。

何时使用常量插值字符串

蒂姆承认,并非所有人每天都会使用此功能。 很少定义常量或编写属性的开发人员可能不会立即受益。

但是,对于那些创建大量编译时定义、常量消息或属性元数据的人来说,此功能简化了代码并防止了字符串连接造成的混乱。

它也更安全——因为常量是不可变的,并且由编译器检查,所以可以消除动态连接字符串或管理不当的变量带来的错误。

这能使你的代码更健壮、更易读、更易于维护。

结论

视频结尾,蒂姆邀请开发者们思考一下,他们是否会在项目中使用常量插值字符串。 有些人可能认为它们对于更清晰的编译时格式化字符串至关重要,而另一些人则可能认为它们只是一种小小的便利。

无论如何,Tim 的演示准确地展示了如何有效地实现和应用此功能。

总之:

  • 常量插值字符串允许在常量内使用插值表达式。

  • 它们在编译时进行求值,生成高效的字符串字面量。

它们用更简洁的语法取代了重复的字符串连接。

  • 它们在属性、基于常量的配置和元数据消息中特别有用。

通过将可读性与编译时安全性相结合,C# 10 的常量插值是朝着富有表现力和高效的 C# 编程迈出的又一步——正如 Tim Corey 的例子清楚地表明的那样。

Hero Worlddot related to 在 10 分钟或更短时间内实现 C# 10 和 .NET 6 中的常数插值
Hero Affiliate related to 在 10 分钟或更短时间内实现 C# 10 和 .NET 6 中的常数插值

分享您的所爱,赚取更多收入

您为使用 .NET、C#、Java、Python 或 Node.js 的开发人员创建内容吗?将您的专业知识转化为额外收入!

钢铁支援团队

我们每周 5 天,每天 24 小时在线。
聊天
电子邮件
打电话给我