我有两节课。第一类是Parent
,它有一个对象列表(Child
)。 Child
中的每一个都引用了他的Parent
类。问题是如何通过构造函数实现此引用。
public sealed class Child
{
public Child(string id, string name, Parent parent)
{
Id = id;
Name = name;
Parent = parent;
}
public Parent ParentInstance { get; private set; }
public string Id { get; private set; }
public string Name { get; private set; }
}
public sealed class Parent
{
public Parent(string id, string name, IEnumerable<Child> children)
{
Id = id;
Name = name;
Children = children;
}
public string Id { get; private set; }
public string Name { get; private set; }
public IEnumerable<Child> Children { get; private set; }
}
问题是我有一个代码可以解析一些XML代码并创建Parent
个对象的列表。这是一个例子:
internal Parent ParseParent(XElement parentXElement)
{
return new Parent(parentXElement.Attribute("id").Value, parentXElement.Attribute("name").Value, parentXElement.Descendants("child").Select(ParseChild));
}
当然,我可以在Parent
构造函数中初始化Parent
属性,只需从private
setter中移除Parent
,然后对所有孩子进行操作并使用此属性。但我想把它变为只读。像这样:
public Parent(string id, string name, IEnumerable<Child> children)
{
Id = id;
Name = name;
Children = children.ForEach(c => c.ParentInstance = this);
}
答案 0 :(得分:3)
您可以尝试另外一种方法......
class Program
{
public sealed class Child
{
private readonly Func<Parent> _getParent;
public Child(string id, string name, Func<Parent> getParent)
{
Id = id;
Name = name;
_getParent = getParent;
}
public Parent ParentInstance { get { return _getParent(); } }
public string Id { get; private set; }
public string Name { get; private set; }
}
public sealed class Parent
{
public Parent(string id, string name, IEnumerable<Child> children)
{
Id = id;
Name = name;
Children = children;
}
public string Id { get; private set; }
public string Name { get; private set; }
public IEnumerable<Child> Children { get; private set; }
}
static void Main(string[] args)
{
Parent parent = null;
Func<Parent> getParent = () => { return parent; };
parent = new Parent("0", "Parent", new[] {new Child("0", "Child1", getParent), new Child("1", "Child1", getParent)});
Console.WriteLine(parent.Children.First().ParentInstance.Name);
}
}
答案 1 :(得分:3)
要成为不可变的和包含循环引用,你需要这样的东西:
public sealed class Parent
{
private readonly IEnumerable<Child> children;
private readonly string name; // Just for example
public Parent(XElement element)
{
name = (string) element.Attribute("name");
children = element.Elements("Child")
.Select(x => new Child(x, this))
.ToImmutableList(); // Or whatever collection you want
}
}
public sealed class Child
{
private readonly Parent parent;
private readonly string name; // Just for example
public Child(XElement element, Parent parent)
{
this.name = (string) element.Attribute("name");
// Better not ask the parent for its children yet - they won't be
// initialized!
this.parent = parent;
}
}
Child
构造函数中的注释应该让你高兴起来。即使Parent
是不可变的,我们在完成初始化之前就会泄漏this
...所以Child
构造函数需要确保它不1} em>在施工期间尝试找到它的兄弟姐妹。
答案 2 :(得分:0)
如果您的Child类具有基本calss,除了父级之外的其他所有内容,它会不会为您提供。
public sealed class ChildBase
{
public Child(string id, string name)
{
Id = id;
Name = name;
}
public string Id { get; private set; }
public string Name { get; private set; }
}
和你的孩子班级
public sealed class Child : ChildBase
{
public Child(string id, string name, Parent parent): base(Id, name)
{
Parent = parent;
}
public Child(ChildBase stereotype, Parent parent): base(stereotype.Id, stereotype.name)
{
Parent = parent;
}
public Parent ParentInstance { get; private set; }
}
比你的父类看起来像
public sealed class Parent
{
public Parent(string id, string name, IEnumerable<ChildBase> children)
{
Id = id;
Name = name;
//Children = children.Select(c => new Child(c.id, c.Name, this));
Children = children.Select(c => new Child(c, this));
}
public string Id { get; private set; }
public string Name { get; private set; }
public IEnumerable<Child> Children { get; private set; }
}
答案 3 :(得分:0)
假设您对 immutable 的定义是“字段不能在构造函数之外更改”,那么您要求的是不可能的,因为为了使IEnumerable<Child> children
参数存在,它必须由已经构建的父母为空的孩子组成。懒惰地评估儿童序列也是不可能的,因为定义它将需要引用尚不存在的父母。
解决这个问题的唯一方法是让父构造函数负责构建子代。相反(构建父母的孩子)是不可能的,因为父母已经由第一个孩子构建,之后其子集合无法修改 - 如果这是一个多对多的关系,那么根本就没有溶液
但是,如果您将定义更改为“只有父级和子级才能更改彼此的字段”,则可以在公共基类中简单地创建这些通用protected
字段,或者将类限制为程序集并将其设置为internal
字段。