【问题标题】:C# immutable object setter understandingC#不可变对象setter理解
【发布时间】:2015-09-26 16:23:43
【问题描述】:

我有不可变的 F1 类。我想改变它的一个领域。在 set 方法中,我必须返回 F1 类的新实例并进行更改。我不明白我怎么能做到这一点。

public class F1
{
    public readonly int k1;
    public readonly ImmutableList<int> k2;

    public F1(int k)
    {
        ...
    }

    public int GetItem(int pos)
    {
        return k2[pos];
    }

    public F1 SetItem(int pos, int val)
    {
        return new F1() // How i can create new instance with changes in pos
    }
}

String.cs 中有 Replace 方法。 String 是 C# 中的不可变类(或者我认为是这样)。替换方法定义如下:

[SecuritySafeCritical]
[MethodImpl(MethodImplOptions.InternalCall)]
private string ReplaceInternal(char oldChar, char newChar);

[__DynamicallyInvokable]
public string Replace(char oldChar, char newChar)
{
  return this.ReplaceInternal(oldChar, newChar);
}

所以我不知道 ReplaceInternal 是如何工作的,然后找不到我的问题的答案。

【问题讨论】:

  • k2 是如何构造的?您需要创建一个完整的 F1 新实例,并在构建时设置所有字段。
  • F1 f = new F1(){k2 = this.k2 , k1 = this.k1}; 然后在f 中进行更改。并返回它

标签: c# immutability immutable-collections


【解决方案1】:

很难说出您在构造函数中究竟想做什么,但您可以添加另一个接受 ImmutableList 的构造函数,如 Kryzsztof 所示,并像这样修改 SetItem 方法:

public F1 SetItem(int pos, int val)
{
    return new F1(k1, k2.SetItem(pos, val));
}

全面实施:

public class F1
{
    public readonly int k1;
    public readonly ImmutableList<int> k2;

    public F1(int k)
    {
        ...
    }

    private F1(int k1, ImmutableList<int> k2)
    {
        this.k1 = k1;
        this.k2 = k2;
    }

    public int GetItem(int pos)
    {
        return k2[pos];
    }

    public F1 SetItem(int pos, int val)
    {
        return new F1(k1, k2.SetItem(pos, val));
    }
}

请注意,我将新构造函数设为私有,假设您不想将此构造函数公开用于此目的以外的任何其他目的。

编辑: 我还应该注意 ImmutableList 的语义是这样的:使用典型列表方法对列表进行的任何修改都会产生一个新列表,例如对 SetItem 的调用:

k2.SetItem(pos, val)

【讨论】:

    【解决方案2】:

    假设您的构造函数如下所示:

    public F1(int k1, ImmutableList<int> k2)
    {
        this.k1 = k1;
        this.k2 = k2;
    }
    

    您可以通过创建和返回具有更改属性的新对象而不是改变当前对象来创建修改属性的方法。

    public F1 SetK1(int newk1)
    {
        return new F1(newk1, this.k2);
    }
    
    public F1 SetK2(ImmutableList<int> newK2)
    {
        return new F1(this.k1, newK2);
    }
    

    【讨论】:

      【解决方案3】:

      您的解决方案基于 String 的 Replace 方法,这可能不是最好的主意。 StackOverflow 的历史表明,人们,尤其是 .NET 框架的新手,通常会误解 String.Replace 的语义,因为它的语法并不意味着不变性,而且你不得不依赖外部文档或先验知识。

      我将创建一个名为“GetModifiedCopy”的方法,而不是创建实际上不设置值的 setter/Set 方法,它显式返回具有修改后值的新副本。这个

      public class F1
      {
          public readonly int k1;
      
          public F1(int k1)
          {
            ...
          }
      
          public F1 GetModifiedCopy(int newVal)
          {
              return new F1(newVal);
          }
      }
      

      现在,您的情况有点复杂,因为您不只是用单个值实例化一个新实例,而是复制整个现有列表并修改一个值。不过解决方法是一样的——创建一个私有构造函数,接收原始列表和新值,在构造函数中修改列表,返回新实例。

      private F1(ImmutableList<int> baseList, int pos, int value)
      {
          var tempList = baseList.ToList(); // create mutable list.
          tempList[pos] = value; // modify list.
          this.k2 = new ImmutableList<int>(tempList); // immutablize!
      }
      public F1 GetModifiedCopy(int pos, int value)
      {
          return new F1(this.k2, pos, value);
      }
      

      【讨论】:

        猜你喜欢
        • 2021-09-12
        • 1970-01-01
        • 1970-01-01
        • 2021-10-11
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 2011-02-23
        • 1970-01-01
        相关资源
        最近更新 更多