改变属性值而不破坏不变性

问题描述 投票:3回答:3

我对保持不变性的最干净方法感到困惑。

这是我到目前为止所获得的最简单,最愚蠢的方法,但是我仍然想知道是否没有一种更简单的方法来实现这一目标

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");
    }
}

具有泛型的扩展方法会涉及很多反思,但我仍然坚信自己考虑得太深了,也许我缺少了一个更简单的解决方案...

c# immutability
3个回答
3
投票

具有不可变的类您首先需要确保无法从外部访问属性。所以我将其更改为:

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");
    }
}

0
投票

使属性设置器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);

}

0
投票

[非常不幸的是,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模式,尽管结果更加冗长和笨拙)

© www.soinside.com 2019 - 2024. All rights reserved.