2012-03-07 10 views
13

は私が間違っている場合同じアセンブリで循環参照が悪いのですか?

public class ParentClass : IDisposable 
{ 
    public ChildClass Child 
    { 
    get { return _child; } 
    } 
} 

public class ChildClass 
{ 
    public ParentClass Parent 
    { 
    get { return _parent; } 
    set { _parent= value; } 
    } 

    public ChildClass (ParentClass parent) 
    { 
    Parent= parent; 
    } 

} 

が私を修正して、同じアセンブリ内の以下のクラスを持っていますが、これは悪いデザインであると仮定します。これによりメモリリークやその他の予期しない問題が発生しますか?明らかに、ガベージコレクタはsuch kind of circular referencesを処理することができます。

編集

2つのクラスが他のクラスでは、このように慣れてしまう場合はどう

ParentClass objP = new ParentClass(); 
ChildClass objC =new ChildClass(objP); 
objP.Child = objC; 

考えてください....

+9

ProductGroup'クラスと 'ParentProductGroup'プロパティ)、これは私の意見では完全に有効です。 –

+2

mmm ..どのように悪いのか分かりません。これは二重リンクリストの定義です。GCはそれを世話します.. – gbianchi

+2

O/RMツールは、このようなコードを常に生成します。 – Steven

答えて

18

ガベージコレクタを心配しないでください。任意のトポロジを持つ参照グラフを簡単に処理します。不変量に違反しやすくすることによってバグを作成するのに役立つオブジェクトを書くことについて心配しています。それはGCを強調しているため

これには疑問のデザインではありません - それはしていません - それはを強制しませないというので、希望セマンティック不変:XがYの親である場合、YはしなければならないことXの子である

一貫した親子関係を維持するクラスを書くことは非常に難しいことがあります。 Roslynチームでは、実際に2つのツリーを構築しています。 「本当の」ツリーには子参照のみがあります。子供はその親を知っていない。親子関係の一貫性を強制する「ファサード」ツリーをレイヤー化します。親ノードにその子を尋ねると、実際の子の上にファサードを作成し、そのファサードオブジェクトの親を設定します真の親であること

UPDATE:Commenterブライアンは詳細を尋ねます。ここでは、子参照のみを含む「緑色」のツリー上に、子参照と親参照を持つ「赤い」ファサードを実装する方法のスケッチを示します。このシステムでは、一番下のテストコードのように、親子関係が矛盾することはありません。

(ホワイトボード上のデータ構造を描画するとき、それらは我々が使用されるマーカーの色だったので、私たちは、これらの「赤」と「緑」の木を呼ぶ。)は、階層ツリーデータ構造(例えば、 `で

using System; 

interface IValue { string Value { get; } } 
interface IParent : IValue { IChild Child { get; } } 
interface IChild : IValue { IParent Parent { get; } } 

abstract class HasValue : IValue 
{ 
    private string value; 
    public HasValue(string value) 
    { 
     this.value = value; 
    } 
    public string Value { get { return value; } } 
} 

sealed class GreenChild : HasValue 
{ 
    public GreenChild(string value) : base(value) {} 
} 

sealed class GreenParent : HasValue 
{ 
    private GreenChild child; 
    public GreenChild Child { get { return child; } } 
    public GreenParent(string value, GreenChild child) : base(value) 
    { 
     this.child = child; 
    } 

    public IParent MakeFacade() { return new RedParent(this); } 

    private sealed class RedParent : IParent 
    { 
     private GreenParent greenParent; 
     private RedChild redChild; 
     public RedParent(GreenParent parent) 
     { 
      this.greenParent = parent; 
      this.redChild = new RedChild(this); 
     } 
     public IChild Child { get { return redChild; } } 
     public string Value { get { return greenParent.Value; } } 

     private sealed class RedChild : IChild 
     { 
      private RedParent redParent; 
      public RedChild(RedParent redParent) 
      { 
       this.redParent = redParent; 
      } 
      public IParent Parent { get { return redParent; } } 
      public string Value 
      { 
       get 
       { 
        return redParent.greenParent.Child.Value; 
       } 
      } 
     } 
    } 
} 
class P 
{ 
    public static void Main() 
    { 
     var greenChild1 = new GreenChild("child1"); 
     var greenParent1 = new GreenParent("parent1", greenChild1); 
     var greenParent2 = new GreenParent("parent2", greenChild1); 

     var redParent1 = greenParent1.MakeFacade(); 
     var redParent2 = greenParent2.MakeFacade(); 

     Console.WriteLine(redParent1.Value); // parent1 
     Console.WriteLine(redParent1.Child.Parent.Value); // parent1 ! 
     Console.WriteLine(redParent2.Value); // parent2 
     Console.WriteLine(redParent2.Child.Parent.Value); // parent2 ! 

     // See how that goes? RedParent1 and RedParent2 disagree on what the 
     // parent of greenChild1 is, **but they are self-consistent**. They 
     // always report that the parent of their child is themselves. 
    } 
} 
+0

"ファサード"ツリーの使用方法/生成方法について詳しく知りたいのですか? – Brian

+2

@ブライアン:確かに、ここに行く。 –

+0

これは非常に興味深いモデルです。実際のオブジェクトを参照する「オカレンス」クラスを使用して、これまでと同様のことをやっていますが、「ファサード」の考えは考えていませんでした。この関係モデリングスタイルには名前がありますか、それ以上の参考資料は知っていますか? –

2

これは私が考えてOKですし、両方向に関係をナビゲートできるようにする必要がある場合に便利です。

GCがそれを処理できることを指摘したので、なぜそれが問題になるのでしょうか?あなたが必要とする機能を提供するなら、それはうまくいくはずです。

これは私たちのコードの問題であると仮定して、プロパティが同期しなくなる問題に注意する必要があります。

+0

ありがとう..私の質問を更新しました。だから私はそれが処理されていることを実証している方法でさえ、それは問題ではないでしょうか? – user20358

+0

いいえ、私はChildClassのコンストラクタの親クラスにchildプロパティを設定していますが、どのように表示されているかは分かりませんが、おそらくsdomeチェックコードを追加して関係が同期していることを確認します。 –

+0

プロパティが同期しなくなった場合に発生しますか?彼らはどうやって同期が外れますか? – user20358

3

これは非常に細かいIMOです。例えば、MSはほとんどのUIコントロール/要素でこれを行う。 FormControlsコレクションを持ち、コレクション内の各コントロールにはParentFormプロパティがあります。

+0

「MSでもこれはあります」という意味ではありません。これは特に、フレームワークの古い部分のいくつかに当てはまります。 – svick

+0

これは確かに真実ですが、これが "より大きい規模で使用される"例を挙げたかったのです... – ChrFin

+0

これらが親子関係を持たない2つのクラスであれば、ある種のコードの匂いを意味します今だろうか? :) – user20358

11

特に悪い設計でも技術的な観点からも問題ではありません。クラスインスタンス(オブジェクト)は参照型です。つまり、_parentおよび_childは、オブジェクト自体ではなく、それぞれのオブジェクトへの参照のみを保持します。だから、あなたは無限のデータ構造を引き起こさない。

自分自身を投稿したとき、ガベージコレクタは循環参照を処理できます。これは、主に参照カウントを使用しないためです。

この種の構造の唯一の「問題」は、通常、関係の両端を同期させたい場合が多いということです。与えられた子cと親pはp.Child == c if and only if c.Parent == pです。あなたはこれを最善の方法で処理する方法を決める必要があります。たとえば、Parent.AddChild(Child c)メソッドがある場合、これはParent.Childをcに設定するだけでなく、親に対してChild.Parentを設定することもできます。しかし、Child.Parentが直接割り当てられている場合はどうなりますか?それはあなたが対処しなければならないかもしれない疑問です。

+0

返信いただきありがとうございます。私は更新された投稿でそれを実証しましたが、それは問題ではないでしょうか? – user20358

+0

いいえ、私が書いたように、関係の両端が同期されていることを確認したい場合があります。 –

+0

プロパティが同期しなくなるとどのような問題が発生する可能性がありますか?彼らはどうやって同期が外れますか? 2つのクラスの間に親子関係がなければ、この種の循環参照に影響はありますか?これらの2つのクラスが異なることをしていて、あるクラスが特定のユーティリティタスクを実行するために他のクラスを使用しているとします。 – user20358