我在C#中以函数式编写代码.我的许多类都是不可变的,用于返回实例的修改副本的方法.
例如:
sealed class A { readonly X x; readonly Y y; public class A(X x,Y y) { this.x = x; this.y = y; } public A SetX(X nextX) { return new A(nextX,y); } public A SetY(Y nextY) { return new A(x,nextY); } }
这是一个微不足道的例子,但想象一个更大的类,有更多的成员.
问题是构建这些修改后的副本非常冗长.大多数方法只更改一个值,但我必须将所有未更改的值传递给构造函数.
在使用修饰符方法构造不可变类时,是否存在避免所有这种样板的模式或技术?
注意:我不想使用reasons discussed elsewhere on this site的结构.
更新:我发现这在F#中称为“复制和更新记录表达式”.
解决方法
对于较大的类型,我将构建一个With函数,该函数具有如果未提供则默认为null的参数:
public sealed class A { public readonly X X; public readonly Y Y; public A(X x,Y y) { X = x; Y = y; } public A With(X X = null,Y Y = null) => new A( X ?? this.X,Y ?? this.Y ); }
然后使用C#的命名参数功能:
val = val.With(X: x); val = val.With(Y: y); val = val.With(X: x,Y: y);
我发现int比许多setter方法更具吸引力.它确实意味着null变为不可用的值,但是如果你正在使用功能路由,那么我假设你也试图避免使用null并使用选项.
如果你有值类型/结构作为成员,那么在With中使它们为Nullable,例如:
public sealed class A { public readonly int X; public readonly int Y; public A(int x,int y) { X = x; Y = y; } public A With(int? X = null,int? Y = null) => new A( X ?? this.X,Y ?? this.Y ); }
但请注意,这不是免费的,每次调用时都有N个空比较操作,其中N是参数的数量.我个人觉得这个便利是值得花费的(最终可以忽略不计),但是如果你有任何特别敏感的东西,那么你应该回到定制的setter方法.