我对保持不变性的最干净方法感到困惑。
这是我到目前为止所获得的最简单,最愚蠢的方法,但是我仍然想知道是否没有一种更简单的方法来实现这一目标
class MyClass
{
public int Property1 { get; }
public string Property2 { get; }
public MyClass CopyWith(int? property1 = default, string property2 = default)
{
return new MyClass
{
Property1 = property1 ?? Property1,
Property2 = property2 ?? Property2
};
}
}
class Program
{
static void Main()
{
var myObj = new MyClass { Property1 = 123, Property2 = "123" };
var myModifiedObj = myObj.CopyWith(property2: "456");
}
}
具有泛型的扩展方法会涉及很多反思,但我仍然坚信自己考虑得太深了,也许我缺少了一个更简单的解决方案...
具有不可变的类您首先需要确保无法从外部访问属性。所以我将其更改为:
class MyClass
{
public int Property1 { get; }
public string Property2 { get; }
public MyClass(int prp1, string prp2)
{
Property1 = prp1;
Property2 = prp2;
}
public MyClass With(int? property1 = default, string property2 = default) =>
new MyClass(property1 ?? Property1, property2 ?? Property2);
}
这已经是一个很好的起点。如您所说,使用泛型可能意味着对您要解决的问题过度设计代码。
某些人喜欢对要更改的每个属性使用一种方法,如果要更改多个属性,则将调用链接起来。这可能看起来有些冗长,但通常会导致更简洁的类。
它看起来像这样:
class MyClass
{
// until here it is the same as before
public MyClass WithPrp1(int prp1) => new MyClass(prp1,Property2);
public MyClass WithPrp2(string prp2) => new MyClass(Property1,prp2);
}
class Program
{
static void Main()
{
var myObj = new MyClass(123, "123");
var myModifiedObj = myObj.WithPrp1(456).WithPrp2("789");
}
}
使属性设置器private
,然后为每个属性创建重载方法。
public class MyClass
{
public int Property1 { get; }
public string Property2 { get; }
public MyClass(int property1, string property2)
{
Property1 = property1;
Property2 = property2;
}
public MyClass CopyWith(int property1) => new MyClass(property1, Property2);
public MyClass CopyWith(string property2) => new MyClass(Property1, property2);
}
[非常不幸的是,C#在声明/更改不可变类型时非常有限-曾经有关于C#8出现不可变记录的讨论,但那没有发生。
但是您可以通过将扩展方法与委托的反射和缓存相结合进行优化,可以合理,优雅,有效地获得所需的内容,请参阅项目With。那里的代码非常密集,但是用法很简单:
class MyClass : IImmutable
{
public MyClass(int property1, string property2) { Property1 = property1; Property2 = property2; }
public int Property1 { get; }
public string Property2 { get; }
}
class Program
{
static void Main()
{
var myObj = new MyClass(123, "123");
var myModifiedObj = myObj.With(x => x.Property2, "456");
}
}
(对您问题的另一个更经典的解决方案是Builder模式,尽管结果更加冗长和笨拙)