如何有效地 "change" 不可变对象?
How to "change" immutable objects effectively?
"modification" 不可变对象是否有任何聪明的设计模式或某种通用方法?
背景:
让我们有一组不同的(没有共同的 base
)不可变对象,每个对象都有不同的一组 {get; private set;}
属性和一个 public 构造函数(接受一组 属性 值)。这些对象是并且应该保持不变,但有时,在某种特殊模式下,它们的值需要 "change".
这种修改意味着只创建与原始对象具有相同值的新对象,除了更新的属性(如 o = new c(o.a, updateB, updateC, o.d, ...)
)。
我可以想象就地调用构造函数,或者定义一个(例如扩展)方法返回新实例,接受一些参数来识别更新的 property/ies 并通过反射修改 it/them,但是一切对于 "any" 不可变,在系统范围内使用似乎非常具体且不优雅。我喜欢 linq
处理的方式,例如IEnumerable
s 和您可以生成的链并完全重新转换输入集合。有什么想法吗?
据我所知,类似的一个例子是 Roslyn,Microsoft 当前的 C# 编译器,它广泛使用不可变数据结构。语法树 类 为每个 属性 提供了方便的方法,用于返回一个新实例,仅更改了一个 属性,例如
var cu = Syntax.CompilationUnit()
.AddMembers(
Syntax.NamespaceDeclaration(Syntax.IdentifierName("ACO"))
.AddMembers(
Syntax.ClassDeclaration("MainForm")
.AddBaseListTypes(Syntax.ParseTypeName("System.Windows.Forms.Form"))
.WithModifiers(Syntax.Token(SyntaxKind.PublicKeyword))
.AddMembers(
Syntax.PropertyDeclaration(Syntax.ParseTypeName("System.Windows.Forms.Timer"), "Ticker")
.AddAccessorListAccessors(
Syntax.AccessorDeclaration(SyntaxKind.GetAccessorDeclaration).WithSemicolonToken(Syntax.Token(SyntaxKind.SemicolonToken)),
Syntax.AccessorDeclaration(SyntaxKind.SetAccessorDeclaration).WithSemicolonToken(Syntax.Token(SyntaxKind.SemicolonToken))),
Syntax.MethodDeclaration(Syntax.ParseTypeName("void"), "Main")
.AddModifiers(Syntax.Token(SyntaxKind.PublicKeyword))
.AddAttributes(Syntax.AttributeDeclaration().AddAttributes(Syntax.Attribute(Syntax.IdentifierName("STAThread"))))
.WithBody(Syntax.Block())
)
)
);
或者您的情况:
o = o.WithB(updateB).WithC(updateC);
我认为这对于预期的用例来说读起来非常好(仅更新几个属性,同时保持其他所有属性不变)。当有 许多 个属性时,它尤其胜过 »总是必须调用 ctor« 方法。
我认为在 Roslyn 中,这些都是自动生成的。如果有必要,您可以对 T4 做类似的事情。
"modification" 不可变对象是否有任何聪明的设计模式或某种通用方法?
背景:
让我们有一组不同的(没有共同的 base
)不可变对象,每个对象都有不同的一组 {get; private set;}
属性和一个 public 构造函数(接受一组 属性 值)。这些对象是并且应该保持不变,但有时,在某种特殊模式下,它们的值需要 "change".
这种修改意味着只创建与原始对象具有相同值的新对象,除了更新的属性(如 o = new c(o.a, updateB, updateC, o.d, ...)
)。
我可以想象就地调用构造函数,或者定义一个(例如扩展)方法返回新实例,接受一些参数来识别更新的 property/ies 并通过反射修改 it/them,但是一切对于 "any" 不可变,在系统范围内使用似乎非常具体且不优雅。我喜欢 linq
处理的方式,例如IEnumerable
s 和您可以生成的链并完全重新转换输入集合。有什么想法吗?
据我所知,类似的一个例子是 Roslyn,Microsoft 当前的 C# 编译器,它广泛使用不可变数据结构。语法树 类 为每个 属性 提供了方便的方法,用于返回一个新实例,仅更改了一个 属性,例如
var cu = Syntax.CompilationUnit()
.AddMembers(
Syntax.NamespaceDeclaration(Syntax.IdentifierName("ACO"))
.AddMembers(
Syntax.ClassDeclaration("MainForm")
.AddBaseListTypes(Syntax.ParseTypeName("System.Windows.Forms.Form"))
.WithModifiers(Syntax.Token(SyntaxKind.PublicKeyword))
.AddMembers(
Syntax.PropertyDeclaration(Syntax.ParseTypeName("System.Windows.Forms.Timer"), "Ticker")
.AddAccessorListAccessors(
Syntax.AccessorDeclaration(SyntaxKind.GetAccessorDeclaration).WithSemicolonToken(Syntax.Token(SyntaxKind.SemicolonToken)),
Syntax.AccessorDeclaration(SyntaxKind.SetAccessorDeclaration).WithSemicolonToken(Syntax.Token(SyntaxKind.SemicolonToken))),
Syntax.MethodDeclaration(Syntax.ParseTypeName("void"), "Main")
.AddModifiers(Syntax.Token(SyntaxKind.PublicKeyword))
.AddAttributes(Syntax.AttributeDeclaration().AddAttributes(Syntax.Attribute(Syntax.IdentifierName("STAThread"))))
.WithBody(Syntax.Block())
)
)
);
或者您的情况:
o = o.WithB(updateB).WithC(updateC);
我认为这对于预期的用例来说读起来非常好(仅更新几个属性,同时保持其他所有属性不变)。当有 许多 个属性时,它尤其胜过 »总是必须调用 ctor« 方法。
我认为在 Roslyn 中,这些都是自动生成的。如果有必要,您可以对 T4 做类似的事情。