C#的with表达式基于现有对象创建新实例,不改变原始对象,通过成员级浅拷贝实现属性修改,适用于配置对象、DTO、状态管理等场景,需注意浅拷贝共享引用和性能开销问题。

C#的
with
record
with
with
基本语法:
var newRecord = existingRecord with { Property1 = newValue1, Property2 = newValue2 };工作原理:
当你使用
with
existingRecord
newRecord
with
示例:
假设我们有一个
Person
public record Person(string FirstName, string LastName, int Age);
// 创建一个原始的Person实例
var originalPerson = new Person("张", "三", 30);
Console.WriteLine($"Original: {originalPerson}"); // Output: Original: Person { FirstName = 张, LastName = 三, Age = 30 }
// 使用with表达式修改LastName和Age
var updatedPerson = originalPerson with { LastName = "四", Age = 31 };
Console.WriteLine($"Updated: {updatedPerson}"); // Output: Updated: Person { FirstName = 张, LastName = 四, Age = 31 }
// 验证原始对象未被修改
Console.WriteLine($"Original (after update attempt): {originalPerson}"); // Output: Original (after update attempt): Person { FirstName = 张, LastName = 三, Age = 30 }
// 甚至可以只修改一个属性
var personWithNewAge = originalPerson with { Age = 32 };
Console.WriteLine($"New Age: {personWithNewAge}"); // Output: New Age: Person { FirstName = 张, LastName = 三, Age = 32 }这个例子清晰地展示了
with
originalPerson
在我看来,C#引入
with
首先,解决“样板代码”问题。在
record
WithXxx
with
其次,增强代码的可读性和可维护性。当你看一眼
someObject with { Property = newValue }再者,更好地支持不可变数据模型。不可变性是现代软件设计中的一个重要原则,它能有效减少bug,简化并发编程,并让代码更容易推理。
record
with
最后,我个人觉得,它也推动了C#向更函数式、更表达力的方向发展。虽然C#仍然是面向对象的语言,但它一直在吸收函数式编程的优秀思想。
with
在实际的项目开发中,
with
一个非常典型的场景是配置对象(Configuration Objects)。我们经常会有一个配置类,里面包含了各种设置,这些设置在应用启动后通常不应该被随意修改。但有时,你可能需要基于一个基础配置,派生出一个略有不同的配置,比如为某个特定模块提供一个微调后的配置。这时,
with
public record AppConfig(string ConnectionString, int TimeoutSeconds, bool EnableCaching);
var baseConfig = new AppConfig("server=db;", 30, true);
// 为特定服务创建一个超时时间更长的配置
var serviceConfig = baseConfig with { TimeoutSeconds = 60 };另一个很棒的场景是数据传输对象(DTOs)或领域模型(Domain Models)。在很多业务逻辑中,我们从数据库读取一个实体,然后需要对它进行一些处理,但又不想直接修改原始的数据库实体,或者说,希望在提交到数据库之前,能有一个“草稿”状态。
with
在状态管理方面,特别是对于前端框架(如Blazor)或一些采用不可变状态模式的后端服务,
with
public record TodoItem(Guid Id, string Description, bool IsCompleted);
public record TodoListState(ImmutableList<TodoItem> Items, string Filter);
var initialState = new TodoListState(ImmutableList<TodoItem>.Empty, "All");
// 添加一个Todo项
var newStateAfterAdd = initialState with
{
Items = initialState.Items.Add(new TodoItem(Guid.NewGuid(), "Learn C# records", false))
};
// 完成一个Todo项
var itemToComplete = newStateAfterAdd.Items.First();
var updatedItem = itemToComplete with { IsCompleted = true };
var newStateAfterComplete = newStateAfterAdd with
{
Items = newStateAfterAdd.Items.Replace(itemToComplete, updatedItem)
};这里,
with
尽管
with
这意味着什么呢?如果你的
record
int
string
with
record
record
List<T>
with
record
浅拷贝示例:
public record Address(string Street, string City);
public record Customer(string Name, Address HomeAddress);
var originalAddress = new Address("123 Main St", "Anytown");
var originalCustomer = new Customer("Alice", originalAddress);
// 使用with表达式修改Customer的Name
var updatedCustomer = originalCustomer with { Name = "Bob" };
Console.WriteLine($"Original Customer Address: {originalCustomer.HomeAddress}"); // Output: Original Customer Address: Address { Street = 123 Main St, City = Anytown }
Console.WriteLine($"Updated Customer Address: {updatedCustomer.HomeAddress}"); // Output: Updated Customer Address: Address { Street = 123 Main St, City = Anytown }
// 此时 originalCustomer.HomeAddress 和 updatedCustomer.HomeAddress 引用的是同一个 Address 对象
Console.WriteLine($"Are addresses the same instance? {ReferenceEquals(originalCustomer.HomeAddress, updatedCustomer.HomeAddress)}"); // Output: Are addresses the same instance? True
// 如果我们修改了 originalAddress 对象(假设Address是可变的,或者通过其他方式修改了其内部状态)
// 那么 updatedCustomer.HomeAddress 也会受到影响,因为它们指向同一个对象。
// 当然,record通常是不可变的,这里只是为了说明浅拷贝的原理。
// 如果 Address 也是一个 record,我们修改它也需要用 with,并替换整个引用。为了避免浅拷贝带来的问题,当你的
record
with
record
record
with
// 正确的深层修改方式
var newAddress = originalCustomer.HomeAddress with { Street = "456 New St" };
var customerWithNewAddress = originalCustomer with { HomeAddress = newAddress };
Console.WriteLine($"Original Customer Address: {originalCustomer.HomeAddress}"); // Output: Original Customer Address: Address { Street = 123 Main St, City = Anytown }
Console.WriteLine($"Customer with New Address: {customerWithNewAddress.HomeAddress}"); // Output: Customer with New Address: Address { Street = 456 New St, City = Anytown }
Console.WriteLine($"Are addresses the same instance now? {ReferenceEquals(originalCustomer.HomeAddress, customerWithNewAddress.HomeAddress)}"); // Output: Are addresses the same instance now? False除了浅拷贝,另一个需要考虑的是性能开销。每次使用
with
最后,对于那些习惯了传统面向对象编程中直接修改对象状态的开发者来说,
with
以上就是C#的with表达式如何修改记录类型?怎么使用?的详细内容,更多请关注php中文网其它相关文章!
每个人都需要一台速度更快、更稳定的 PC。随着时间的推移,垃圾文件、旧注册表数据和不必要的后台进程会占用资源并降低性能。幸运的是,许多工具可以让 Windows 保持平稳运行。
Copyright 2014-2025 https://www.php.cn/ All Rights Reserved | php.cn | 湘ICP备2023035733号