具有循环依赖的不可变数据
immutable data with circular dependencies
我有一堆 classes 应该代表数据。这些 class 是嵌套的,因此它们形成了一棵树(或者,在简单的情况下,形成一条链)。由于数据应该只创建一次然后只读,我使用 classes 和 init-only 属性:
public class Foo { public Bar Bar { get; init; } }
public class Bar { public Baz Baz { get; init; } }
public class Baz { public int Value { get; init; } }
为了不只是从上到下导航这样的 tree/chain,我需要每个 [=37= 的 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
没有说 this can be set only once
.
"A" 解决方案是将 Data
的 setter 设为 public,并将其设置在 class构造函数。
注意:恕我直言 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;
}
}
我有一堆 classes 应该代表数据。这些 class 是嵌套的,因此它们形成了一棵树(或者,在简单的情况下,形成一条链)。由于数据应该只创建一次然后只读,我使用 classes 和 init-only 属性:
public class Foo { public Bar Bar { get; init; } }
public class Bar { public Baz Baz { get; init; } }
public class Baz { public int Value { get; init; } }
为了不只是从上到下导航这样的 tree/chain,我需要每个 [=37= 的 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
没有说 this can be set only once
.
"A" 解决方案是将 Data
的 setter 设为 public,并将其设置在 class构造函数。
注意:恕我直言 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;
}
}