如何实例化不可变的相互递归对象?

R. *_*des 20 c# immutability

我有一个不可变的递归类型:

public sealed class Foo
{
    private readonly object something;
    private readonly Foo other; // might be null

    public Foo(object something, Foo other)
    {
        this.something = something;
        this.other = other;
    }
    public object Something { get { return something; } }
    public Foo Other { get { return other; } }
}
Run Code Online (Sandbox Code Playgroud)

我需要实例化这种相互引用的两个对象,即a.Other == b && b.Other == a.

我不想放弃不变性不变量,因为Foo它旨在用作flyweight.我可以(并且我认为必须)放弃readonly这些字段,并保持"胆量"可变,但公共接口必须是不可变的.

冰棍不变性来完成这件事的唯一途径?

我正在尝试为一系列类型建模.每种类型都有一个名称和几个属性.每个属性都有一个名称和一个类型.有一些相互递归的类型,这就是出现这个问题的地方.

Eri*_*ert 11

还有其他方法可以完成它,但它们可能没有您想要的属性.

假设您想要表示一个不可变的值树,从树叶开始构建.这非常简单.您可能有一个节点构造函数,它接受一个值和一个子节点列表.这使得用古树建造新树非常简单,并且它们保证是非循环的.

现在假设您想要表示值的不可变有向图.现在你遇到节点可以有周期的问题; 可能没有"叶子"来构建图表.解决方案是放弃节点知道其邻居是什么的原则.您可以通过创建一组不可变的节点和一个不可变的边缘列表来表示不可变图形.要将节点添加到不可变图形,您需要构建一个新图形,并将该节点添加到节点集中.同样地增加边缘; 你构造一个新的边列表.现在,图形拓扑中存在周期的事实是无关紧要的; 没有一个对象在它引用的对象中有一个循环.

如果不了解您的实际问题空间,很难说不可变数据结构对您的应用程序有用.你能告诉我们更多关于你想要做什么的事吗?

我正在尝试为一系列类型建模.每种类型都有一个名称和几个属性.每个属性都有一个名称和一个类型.有一些相互递归的类型,这就是出现这个问题的地方.

那么geez,你应该首先这样说.如果我知道一件事,那就是对类型的分析.显然,编译器需要能够处理各种疯狂类型的情况,包括具有循环基类的类型,涉及内部类型的循环,类型参数,类型约束等.

在C#编译器中,我们主要通过使对象在其不变性中"暂存"来解决这些问题.也就是说,当我们首先创建一组类型时,每个类型对象都知道它的名称及其在源代码(或元数据)中的位置.然后名称变得不可变.然后我们解析基类型并检查它们的周期; 然后基类型变为不可变的.然后我们检查类型约束...然后我们解析属性......等等,直到所有内容都被分析.

我考虑过其他方法.例如,我们可能使用我刚才为图表建议的相同技术:创建一个名为"compilation"的不可变对象,您可以向其添加类型,从而生成新的不可变编译.编译可以跟踪不可变哈希映射中类型与其基类型之间的"边",然后可以检查结果图的周期.然后,一个类型不知道它的基本类型; 你必须要求编译类型的基本类型是什么.

你可以在这里做同样的事情.您可以拥有一个类"排版",其中包含一组不可变类型,以及从类型到一组不可变属性的多映射.您可以根据需要构建一组类型和属性集; 改变的是地图,而不是类型.

不利的一面是你不再要求类型的属性; 你问排版类型的属性.如果您需要独立于任何排版集传递类型,那么这可能无法满足您的需求.

  • @Daniel:整洁的方法! (2认同)

And*_*rey 6

使用一次写入不变性绝对不可能.让我解释一下原因.您只能在构造函数中设置字段的值.所以,如果你想a引用b你必须通过引用ba的构造.但是b已经被冻结了.所以唯一的选择是ba构造函数中实例化.但这是不可能的,因为你无法传递引用a,因为this构造函数内部无效.

从这一点来看,冰棒不变性是最简单,最优雅的解决方案.另一种方法是创建静态方法Foo.CreatePair,该方法将实例化两个对象,设置交叉引用并返回冻结对象.

public sealed class Foo
{
    private readonly object something;
    private Foo other; // might be null

    public Foo(object something, Foo other)
    {
        this.something = something;
        this.other = other;
    }
    public object Something { get { return something; } }
    public Foo Other { get { return other; } private set { other = value; } }

    public static CreatePair(object sa, object sb)
    {
        Foo a = new Foo(sa, null);
        Foo b = new Foo(sb, a);
        a.Other = b;
        return a;
    }
}
Run Code Online (Sandbox Code Playgroud)