具有循环依赖的不可变数据

发布于 2025-01-10 12:24:01 字数 1213 浏览 1 评论 0原文

我有一堆应该代表数据的类。这些类是嵌套的,因此它们形成一棵树(或者,在简单的情况下,形成一条链)。由于数据应该只创建一次,然后只读取,因此我使用具有仅限初始化属性的类:

public class Foo { public Bar Bar { get; init; } }
public class Bar { public Baz Baz { get; init; } }
public class Baz { public int Value { get; init; } }

为了不仅从上到下导航这样的树/链,我需要一个 Owner (或 Parent) 每个类的属性。所以让我们假设我有这样的:

public interface IData { }

public class Data<OwnerType> : IData where OwnerType : IData
{ public OwnerType Owner { get; init; } }

public class Foo : IData { public Bar Bar { get; init; } }
public class Bar : Data<Foo> { public Baz Baz { get; init; } }
public class Baz : Data<Bar> { public int Value { get; init; } }

但现在我遇到了循环依赖的问题:为了创建一个非空的 Foo,我必须设置它的 Foo.Bar施工期间的财产。但是该 Bar 对象需要一个 Foo 对象作为构造期间设置的 Bar.Owner 属性,该属性不能是 Foo目前正在创建中(因为它还没有被创建),尽管它应该是这样的。

var foo = new Foo { Bar = new Bar { Owner = foo, ... } }; // this does not work but this is what I want

如何解决这个问题?

我可以让Owner属性有一个(私有)setter。但我不喜欢这个,因为 private 没有说只能设置一次

I have a bunch of classes which are supposed to represent data. These classes are nested, so they form a tree (or, in simple cases, a chain). Since the data should just be created once and then only read, I use classes with init-only properties:

public class Foo { public Bar Bar { get; init; } }
public class Bar { public Baz Baz { get; init; } }
public class Baz { public int Value { get; init; } }

In order to navigate such a tree/chain not just from top to bottom, I need an Owner (or Parent) property for each class. So let's assume I have it like this:

public interface IData { }

public class Data<OwnerType> : IData where OwnerType : IData
{ public OwnerType Owner { get; init; } }

public class Foo : IData { public Bar Bar { get; init; } }
public class Bar : Data<Foo> { public Baz Baz { get; init; } }
public class Baz : Data<Bar> { public int Value { get; init; } }

But now I have a problem with circular dependencies: In order to create a non-empty Foo, I must set its Foo.Bar property during construction. But that Bar object needs a Foo object as Bar.Owner property set during construction, which cannot be the Foo currently in creation (because it hasn't been created yet), although that's the one it should be.

var foo = new Foo { Bar = new Bar { Owner = foo, ... } }; // this does not work but this is what I want

How can I solve this problem?

I could make the Owner property have a (private) setter. But I don't like this because private doesn't say this can be set only once.

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(1

泪冰清 2025-01-17 12:24:01

“A”解决方案是将Data的setter公开,并在类构造函数中设置它。

注意:恕我直言 Data 是一个坏名字,所以我将其重命名:

public interface IsOwner { }

public class OwnedBy<OwnerType> where OwnerType : IsOwner
{ public OwnerType Owner { get; set; } }

public class Foo : IsOwner
{
    public Bar _bar { get; init; }
    public Foo(Bar bar)
    {
        bar.Owner = this;
        _bar = bar;
    }
}
public class Bar : OwnedBy<Foo>, IsOwner
{ 
    public Baz _baz { get; init; }

    public Bar(Baz baz)
    {
        baz.Owner = this;
        _baz = baz;
    }
}
public class Baz : OwnedBy<Bar>
{
    public int _value { get; init; }

    public Baz(int value)
    {
        _value = value;
    }
}

static class Program
{
    static void Main()
    {
        var foo = new Foo(new Bar(new Baz(1)));
    }
}

编辑:我错过了您问题中的 set-once 项目。
您确实可以使用私有字段来支持 Owner,确保它被设置一次

public class OwnedBy<OwnerType> where OwnerType : IsOwner
{
    private OwnerType owner;

    public OwnerType Owner { get => owner; set => owner ??= value; }
}

,甚至抛出异常被设置两次。

public OwnerType Owner
{
    get => owner; set {
        if (owner is not null) throw new InvalidOperationException(
           $"{nameof(Owner)} can only be set once");
        owner = value;
    }
}

"A" solution would be to make the setter of Data public, and set it in the class constructor.

Note: IMHO Data is a bad name, So I renamed it:

public interface IsOwner { }

public class OwnedBy<OwnerType> where OwnerType : IsOwner
{ public OwnerType Owner { get; set; } }

public class Foo : IsOwner
{
    public Bar _bar { get; init; }
    public Foo(Bar bar)
    {
        bar.Owner = this;
        _bar = bar;
    }
}
public class Bar : OwnedBy<Foo>, IsOwner
{ 
    public Baz _baz { get; init; }

    public Bar(Baz baz)
    {
        baz.Owner = this;
        _baz = baz;
    }
}
public class Baz : OwnedBy<Bar>
{
    public int _value { get; init; }

    public Baz(int value)
    {
        _value = value;
    }
}

static class Program
{
    static void Main()
    {
        var foo = new Foo(new Bar(new Baz(1)));
    }
}

edit: I missed the set-once item in your question.
You can indeed back the Owner up with a private field, ensuring it's set once

public class OwnedBy<OwnerType> where OwnerType : IsOwner
{
    private OwnerType owner;

    public OwnerType Owner { get => owner; set => owner ??= value; }
}

Or even throw an exception is set twice.

public OwnerType Owner
{
    get => owner; set {
        if (owner is not null) throw new InvalidOperationException(
           
quot;{nameof(Owner)} can only be set once");
        owner = value;
    }
}
~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文