我已将不可变类型存储在临时CQRS读取存储(查询/读取方面,实际上由具有抽象访问层的简单List实现,此时我不想使用完整的文档数据库)。这些读取存储包含以下项目:
xxx.com/blog
现在我要更改名称,并在第二个命令中更改说明。 这些更改应该保持当前状态,这意味着上面的示例:
yyy.com/
如果您有多个属性,这看起来很容易让我感觉...您必须管理保持当前状态。我可以为每种类型添加类似Clone()的东西,但我认为/希望有更好的东西表现良好且易于使用,我不想写很多重复代码(懒惰的程序员)。有什么建议如何改进上面的代码? SomeItem类需要保持不变(通过几个不同的线程传输)。
答案 0 :(得分:4)
可悲的是,在C#中没有简单的方法。 F#有with
关键字,你可以看一下镜头,但在C#中有点单调乏味。我能给你的最好的就是这样:
class SomeItem
{
private readonly string name;
private readonly string description;
public SomeItem(string name, string description)
{
this.name = name;
this.description = description;
}
public SomeItem With
(
Option<string> name = null,
Option<string> description = null
)
{
return new SomeItem
(
name.GetValueOrDefault(this.name),
description.GetValueOrDefault(this.description)
);
}
}
这允许您进行
等更新var newItem = oldItem.With(name: "My name!");
我已经将此方法与扩展方法和T4一起使用效果很好,但即使您手动编写代码,它也相当可靠 - 如果添加新字段,则必须将其添加到With
中好吧,所以效果很好。
如果您愿意容忍运行时代码生成并降低类型安全性,那么还有一些方法,但这有点违背谷物IMO。
答案 1 :(得分:2)
在 C#9 中,我们获得了用于此目的的 with 运算符。
public record Car
{
public string Brand { get; init; }
public string Color { get; init; }
}
var car = new Car{ Brand = "BMW", Color = "Red" };
var anotherCar = car with { Brand = "Tesla"};
<块引用>
With-expressions 在处理不可变数据时,一个常见的模式是 从现有值中创建新值以表示新状态。为了 例如,如果我们的人要更改他们的姓氏,我们会 将它表示为一个新对象,它是旧对象的副本,除了 一个不同的姓氏。这种技术通常被称为 非破坏性突变。而不是代表这个人 时间,该记录代表该人在给定时间的状态。到 帮助这种编程风格,记录允许一种新的 表达; with 表达式:
注意 仅记录支持 with 运算符。
<块引用>记录 经典面向对象编程的核心思想是对象具有强身份并封装可变状态 随着时间的推移而发展。 C# 在这方面一直很有效,但是 有时你想要的恰恰相反,这里是 C# 默认值往往会妨碍工作,使事情变得非常费力。
答案 2 :(得分:1)
您正在寻找的通常称为 with operator :
// returns a new immutable object with just the single property changed
someItem = { someItem with Name = "newName" };
不幸的是,与F#不同,C#没有这样的运营商(但是?)。
其他C#开发人员也缺少此功能,这就是someone wrote a Fody extension to do exactly that:
的原因这是另一种方法,它手动实现UpdateWith
方法但需要Option<T>
辅助类。 Luaan's answer更详细地描述了这种方法:
答案 3 :(得分:-2)
如果您想要做的是(如您所评论的那样)更新现有对象的名称,则readonly属性可能是错误的设计。 否则,如果它真的是你想要创建的新对象,你可能希望你的类用'Dispose'方法实现一些接口。