2012-03-07 4 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을 처리 할 수 ​​있습니다.

편집

어떤 두 클래스가 다른 클래스에서 다음과 같이 사용 치울 경우?

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 팀에서하는 것은 실제로 두 개의 나무를 만드는 것입니다. "실제"트리에는 하위 참조 만 있습니다. 어떤 부모도 그 부모를 안다.우리는 부모 - 자식 관계의 일관성을 강화하는 것의 위에 "정면"나무를 겹쳐 쌓습니다 : 부모 노드에 자식을 요청하면 실제 자식 위에 정면을 만들고 그 정면 객체의 부모를 설정합니다 진정한 부모가되는 것.

업데이트 : 댓글 작성자 Brian이 더 자세한 정보를 묻습니다. 다음은 자식 참조 만 포함하는 "녹색"트리를 통해 자식 참조와 부모 참조로 "빨간색"파사드를 구현하는 방법에 대한 스케치입니다. 이 시스템에서는 하단의 테스트 코드에서 볼 수 있듯이 일관성없는 부모 - 자식 관계를 만들 수 없습니다.

은 (화이트 보드에 데이터 구조를 그릴 때, 사람들은 우리가 사용하는 마커 색상 있었기 때문에 우리는이 "빨간색"및 "녹색"나무를 호출합니다.) 계층 적 트리 데이터 구조에서

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

이 내가 생각 확인하고 당신이 양방향의 관계를 탐색 할 수 있도록해야 할 경우 유용합니다.

GC에서 처리 할 수 ​​있다고 지적 했으므로 문제가되는 이유는 무엇입니까? 필요한 기능을 제공하는 경우 문제가되지 않습니다.

Google 코드에서 문제가 될 것이라는 가정하에 속성이 동기화되지 않는 문제에주의해야합니다.

+0

감사합니다. 내 질문이 업데이트되었습니다. 그래서 내가 처리하는 방법을 보여 주었다고해서 문제가되지 않아야합니까? – user20358

+0

아니요. 예를 보여주는 방법보다는 ChildClass 생성자에서 상위 클래스의 하위 속성을 설정하고 관계가 동기화 상태를 유지하도록 sdome 검사 코드를 추가하는 것이 좋습니다 ... –

+0

어떤 종류의 문제가 발생할 수 있습니까? 속성이 동기화되지 않으면 어떻게됩니까? 어떻게 동기화되지 않을까요? – user20358

3

아주 미세한 IMO입니다.
예를 들어 MS는 대부분의 UI 컨트롤/요소에서이를 수행합니다. Form에는 Controls 컬렉션이 있고 컬렉션의 각 컨트롤에는 ParentForm 속성이 있습니다.

+0

"심지어 MS가하는 일"이 항상 그렇게해야한다는 것을 의미하지는 않습니다. 이는 Framework의 이전 부분 중 일부에 특히 적용됩니다. – svick

+0

그게 사실입니다, 그냥 "큰 규모로"사용되는 예제를주고 싶었어요 ... – ChrFin

+0

그래서 이들이 부모 자식 관계가 없었던 두 클래스라면 어떤 종류의 코드 냄새를 의미 할 것입니다 지금 그럴거야? :) – 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

속성이 동기화되지 않은 경우 어떤 종류의 문제가 발생할 수 있습니까? 어떻게 동기화되지 않을까요? 두 클래스 사이에 부모 자식 관계가 없다면 순환 참조에 영향을 미칠 수 있습니까? 이 두 클래스가 다른 일을하고 있으며 한 클래스가 특정 유틸리티 작업을 수행하기 위해 다른 클래스를 사용했다고 가정합니다. – user20358