如何通过构造函数正确初始化两个类之间的父引用和子引用

时间:2014-01-20 14:09:54

标签: c# .net

我有两节课。第一类是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);
}

4 个答案:

答案 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构造函数需要确保它在施工期间尝试找到它的兄弟姐妹。

答案 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字段。