c#不可变对象设置器的理解

本文关键字:设置 不可变 对象 | 更新日期: 2023-09-27 18:14:35

我有一个不可变类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方法。字符串是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,然后找不到我的问题的答案

c#不可变对象设置器的理解

很难告诉你在构造函数中到底要做什么,但你可以添加另一个接受ImmutableList的构造函数,就像Kryzsztof所示,并修改settitem方法,像这样:

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

请注意,我将这个新的构造函数设置为私有的,这是假定您不希望将这个构造函数公开用于其他目的。

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

的调用
k2.SetItem(pos, val)

假设你有这样的构造函数:

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

您的解决方案基于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);
}