0

0

C#的record关键字如何定义不可变类型?有什么优势?

煙雲

煙雲

发布时间:2025-08-24 08:33:01

|

555人浏览过

|

来源于php中文网

原创

record关键字定义不可变类型,简化数据模型创建;其默认值语义、非破坏性修改(with表达式)和自动实现Equals/GetHashCode提升代码安全与可维护性;适用于DTO、值对象、配置等场景,确保数据不可变,避免并发bug,增强线程安全性。

c#的record关键字如何定义不可变类型?有什么优势?

C#的

record
关键字提供了一种简洁而强大的方式来定义不可变类型,其核心在于默认的非破坏性修改和值语义。这大大简化了数据模型的创建,提升了代码的清晰度和线程安全性,并且在处理数据传输对象(DTO)或领域模型时尤其有用,因为它能确保数据一旦创建就不会被意外修改,从而避免了许多潜在的bug。

public record Product(int Id, string Name, decimal Price);

上面的代码就定义了一个不可变的

Product
记录类型。
record
类型默认将其主构造函数参数对应的属性声明为
init
-only属性。这意味着这些属性只能在对象初始化时(包括通过
with
表达式创建新对象时)赋值,之后就不能再修改了。

// 创建一个Product实例
var p1 = new Product(1, "Laptop", 1200m);

// 尝试修改属性会报错,因为Name是init-only属性
// p1.Name = "Desktop"; // 编译错误:Init-only property or indexer 'Product.Name' can only be assigned in an object initializer, or on 'this' or 'base' in an instance constructor or an 'init' accessor.

// 使用with表达式进行非破坏性修改,创建一个新的record实例
var p2 = p1 with { Price = 1300m };

Console.WriteLine(p1); // Output: Product { Id = 1, Name = Laptop, Price = 1200 }
Console.WriteLine(p2); // Output: Product { Id = 1, Name = Laptop, Price = 1300 }
Console.WriteLine(ReferenceEquals(p1, p2)); // Output: False (它们是不同的对象)

通过

with
表达式,我们可以在不改变原对象的情况下,创建一个带有部分属性更新的新对象。这种“非破坏性修改”是
record
实现不可变性的关键机制之一。

为什么不可变类型在现代软件开发中如此重要?

在多线程和分布式系统日益普及的今天,可变状态是滋生bug的温床。我个人觉得,很多难以复现的并发问题,追根溯源都与某个共享的可变对象在不恰当的时机被修改有关。不可变类型从根本上消除了这种风险,因为它们一旦创建就不能被修改。这意味着你不需要担心一个对象在某个地方被意外更改,从而简化了并发编程,减少了锁的使用。

此外,不可变性极大地提升了代码的可预测性。当你在程序的任何地方传递一个不可变对象时,你都可以确信它的状态不会在不经意间发生变化。这使得调试变得容易得多,因为你不需要去追踪一个对象在不同方法调用中可能产生的各种副作用。在函数式编程范式中,不可变性更是基石,它鼓励我们编写无副作用的纯函数,让代码更易于理解、测试和维护。从长远来看,这种心智负担的减轻,远比多创建几个对象带来的微小性能开销更值得。

AI发型设计
AI发型设计

虚拟发型试穿工具和发型模拟器

下载

record
类型与传统类的不可变实现有何不同?

传统的C#类要实现不可变性,通常需要手动做很多工作。比如,你需要将所有属性设置为

get; init;
get; private set;
(然后只在构造函数中赋值),并且如果想实现值语义的相等性,还得手动重写
Equals
GetHashCode
方法。这不仅繁琐,而且容易出错。

record
类型则省去了大量这样的样板代码。它默认就为所有属性提供了
init
访问器,这意味着它们只能在对象初始化时赋值。更重要的是,
record
类型自动实现了基于值的相等性比较 (
Equals
GetHashCode
),以及一个美观的
ToString
方法。这些在类中都需要我们手动重写,而且很容易出错。尤其是当你的类型有很多属性时,手动实现这些方法简直是地狱。那个
with
表达式,更是提供了一种优雅且类型安全的方式来“修改”对象,实际上是创建了一个带有部分更改的新对象,这在处理数据流时简直是神器,让数据转换变得异常简洁。

record
类型在实际项目中有哪些典型的应用场景?

record
类型在很多场景下都能发光发热,特别是在需要处理大量数据或状态的系统中。最常见的可能就是作为DTOs,也就是数据传输对象。当你需要从数据库读取数据,或者通过API发送数据时,一个不可变的
record
能确保数据在传输过程中不会被意外篡改,提供了一种清晰的数据契约。

在领域驱动设计(DDD)中,它非常适合定义值对象,比如一个

Address
Money
DateRange
对象,它们由其属性值来定义其相等性,并且一旦创建就不应再改变。配置对象也是一个很好的例子,一旦加载,应用程序的配置就不应该再改变,
record
可以很好地保证这一点。甚至在一些前端框架(比如Blazor)的状态管理中,
record
也能简化状态更新的逻辑,因为每次更新都产生新状态,便于追踪和调试,尤其是在实现类似Redux的单向数据流模式时,
record
的不可变特性和
with
表达式都能带来极大的便利。它让我们的数据模型更加健壮,也让代码逻辑更加清晰。

相关专题

更多
什么是分布式
什么是分布式

分布式是一种计算和数据处理的方式,将计算任务或数据分散到多个计算机或节点中进行处理。本专题为大家提供分布式相关的文章、下载、课程内容,供大家免费下载体验。

325

2023.08.11

分布式和微服务的区别
分布式和微服务的区别

分布式和微服务的区别在定义和概念、设计思想、粒度和复杂性、服务边界和自治性、技术栈和部署方式等。本专题为大家提供分布式和微服务相关的文章、下载、课程内容,供大家免费下载体验。

232

2023.10.07

string转int
string转int

在编程中,我们经常会遇到需要将字符串(str)转换为整数(int)的情况。这可能是因为我们需要对字符串进行数值计算,或者需要将用户输入的字符串转换为整数进行处理。php中文网给大家带来了相关的教程以及文章,欢迎大家前来学习阅读。

318

2023.08.02

string转int
string转int

在编程中,我们经常会遇到需要将字符串(str)转换为整数(int)的情况。这可能是因为我们需要对字符串进行数值计算,或者需要将用户输入的字符串转换为整数进行处理。php中文网给大家带来了相关的教程以及文章,欢迎大家前来学习阅读。

318

2023.08.02

int占多少字节
int占多少字节

int占4个字节,意味着一个int变量可以存储范围在-2,147,483,648到2,147,483,647之间的整数值,在某些情况下也可能是2个字节或8个字节,int是一种常用的数据类型,用于表示整数,需要根据具体情况选择合适的数据类型,以确保程序的正确性和性能。本专题为大家提供相关的文章、下载、课程内容,供大家免费下载体验。

538

2024.08.29

c++怎么把double转成int
c++怎么把double转成int

本专题整合了 c++ double相关教程,阅读专题下面的文章了解更多详细内容。

52

2025.08.29

C++中int的含义
C++中int的含义

本专题整合了C++中int相关内容,阅读专题下面的文章了解更多详细内容。

197

2025.08.29

线程和进程的区别
线程和进程的区别

线程和进程的区别:线程是进程的一部分,用于实现并发和并行操作,而线程共享进程的资源,通信更方便快捷,切换开销较小。本专题为大家提供线程和进程区别相关的各种文章、以及下载和课程。

481

2023.08.10

高德地图升级方法汇总
高德地图升级方法汇总

本专题整合了高德地图升级相关教程,阅读专题下面的文章了解更多详细内容。

43

2026.01.16

热门下载

更多
网站特效
/
网站源码
/
网站素材
/
前端模板

精品课程

更多
相关推荐
/
热门推荐
/
最新课程
SQL 教程
SQL 教程

共61课时 | 3.5万人学习

C# 教程
C# 教程

共94课时 | 6.9万人学习

C 教程
C 教程

共75课时 | 4.1万人学习

关于我们 免责申明 举报中心 意见反馈 讲师合作 广告合作 最新更新
php中文网:公益在线php培训,帮助PHP学习者快速成长!
关注服务号 技术交流群
PHP中文网订阅号
每天精选资源文章推送

Copyright 2014-2026 https://www.php.cn/ All Rights Reserved | php.cn | 湘ICP备2023035733号