2014-01-20 13 views
5

我有两个类。第一类是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财产,刚刚从Parent二传手删除private然后去trhough所有儿童和使用该财产。但是我想让它成为只读的。类似这样的:

public Parent(string id, string name, IEnumerable<Child> children) 
{ 
    Id = id; 
    Name = name; 
    Children = children.ForEach(c => c.ParentInstance = this); 
} 
+3

您是否有*参考文献是双向的?基本上你不能有两个不可变类型的实例干净地相互引用。 (你可以在构造函数中做些东西,例如将尚未完全初始化的父对象传递给子对象,但会变得混乱。) –

+0

在孩子未出生之前没有父母! –

+0

@JonSkeet我需要两种方式。什么是最好的方法来做到这一点?可能你知道它很热,它首先在EF代码中实现。因为我记得它有一个特性,我们可以为父级声明属性并为子级声明prop。 –

回答

2

要是不可改变的包括循环引用,你需要这样的事:

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构造函数需要确保它在构造过程中不会尝试找到它的兄弟姐妹()。

+0

这不是最好的解决方案,因为我的xmlreader有'parse'方法,这些对象应该只是没有逻辑的数据对象。但是谢谢你给我的答案,并且会仔细考虑它。 –

+0

@AntonyBlazer:如果这不是最好的解决方案,那表明你认为对于你所说的问题有更好的解决方案(即对不变性和循环引用的要求)。你还表示你不需要回调。你有没有证据表明有更好的解决方案可以满足你的要求? –

+0

我想过你的解决方案,它非常好。我已经实现了你的解决方案,但是提取了解析xml来分离类助手的代码。感谢您的解释。 –

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; } 
} 

比你的父母类将whill样子

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; } 
} 
+0

感谢您的方法,但我不喜欢它,因为创建了新类并使其成为基础。它看起来真的很复杂的简单功能。而且我也发现如果我们不创建childBase并且只是在没有任何继承的情况下创建RawChildData类,并且像构造函数那样在构造函数中使用它会非常有用。 –

+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); 

     } 
    } 
+0

我也想过这个解决方案,但我想创建没有回调和任何逻辑的对象,干净的数据对象。 –

+0

现在,我明白了这一点。感谢您的回应 –

0

假设你的不变定义是“字段无法改变超出构造函数”,你所要求的是不可能的,因为为了使IEnumerable<Child> children参数存在,它必须由已构建的孩子空的父母。对儿童进行懒惰评估的序列也是不可能的,因为定义它需要参考尚不存在的父代。

解决此问题的唯一方法是让父构造函数负责构造子项。相反的(孩子构建父母)是不可能的,因为父母已经由第一个孩子构建,之后其子集合不能被修改 - 如果这是多对多的关系,那么根本就没有解。

但是如果你改变你的定义为“只有父母和孩子可以改变对方的领域”,你可以简单地在一个共同的基类,使这些广义protected领域,或限制类到装配,使他们internal领域。

相关问题