2011-03-20 2 views
1

다음은 복합 패턴을 사용하기위한 첫 번째 시도입니다.복합 반복 실패 (.net)

필자는 임의로 중첩 할 수 있고 Duration 속성에 대한 정확한 결과를 얻을 수 있다는 점에서 효과적입니다. 하지만 점에서 코딩 문제가 출력 복합의 toString()를 필요로하는 아동에 걸쳐 반복 실패

System.InvalidOperationException : Collection was modified; enumeration operation may not execute. 

은을 피하기 위해 스택을 사용하는 것을 포함하여이 posting에서 GetDescendents에 대한 몇 가지 확장 방법이다 재귀 비용과 중첩 반복기.

그래도 난 더 나은 먼저 패턴을 이해하고자하는, 그래서 여기에 몇 가지 질문이 있습니다

  • 가 어떻게이 오류를 방지하기 위해 기존의 반복 코드를 변경할 수 있습니까? 나는 그것을 Linq 동등한 것으로 변환하는 방법을 알고 있지만 내가 그것으로 잘못 무엇을 이해하기 전까지는 루프로두고 싶습니다.
  • 일반적으로 Composite에서 Count 속성을 제공하거나 반복 후에 카운트를 캐시합니까?
  • 일반적으로 전문 컬렉션이 필요하지 않은 경우 일반적으로 Children 속성을 IEnumerable, IList 또는 List로 설정 하시겠습니까?

작동하는 (예 : trival) .net 코드에 대한 좋은 링크도 많은 도움이 될 것입니다.

건배,
Berryl

public interface IComponent { 
    void Adopt(IComponent node); 
    void Orphan(IComponent node); 

    TimeSpan Duration { get; } 
    IEnumerable<IComponent> Children { get; } 
} 

public class Allocation : Entity, IAllocationNode { 

    public void Adopt(IAllocationNode node) { throw new InvalidOperationException(_getExceptionMessage("Adopt", this, node)); } 
    public void Orphan(IAllocationNode node) { throw new InvalidOperationException(_getExceptionMessage("Orphan", this, node)); } 

    public IEnumerable<IAllocationNode> Allocations { get { return Enumerable.Empty<IAllocationNode>(); } } 

    public virtual TimeSpan Duration { get; set; } 
} 


class MyCompositeClass : IAllocationNode { 
     public MyCompositeClass() { _children = new List<IAllocationNode>(); } 

     public void Adopt(IAllocationNode node) { _children.Add(node); } 
     public void Orphan(IAllocationNode node) { _children.Remove(node); } 

     public TimeSpan Duration { 
      get { 
       return _children.Aggregate(TimeSpan.Zero, (current, child) => current + child.Duration); 
      } 
     } 

     public IEnumerable<IAllocationNode> Children { 
      get { 
       var result = _children; 
       foreach (var child in _children) { 
        var childOnes = child.Children; 
        foreach (var node in childOnes) { 
         result.Add(node); 
        } 
       } 
       return result; 
      } 
     } 
     private readonly IList<IAllocationNode> _children; 

     #endregion 

     public override string ToString() { 
      var count = Children.Count(); 
      var hours = Duration.TotalHours.ToString("F2"); 
      return string.Format("{0} allocations for {1} hours", count, hours); 
     } 
    } 

답변

3

가 어떻게이 오류를 방지하기 위해 기존 반복 코드를 변경할 수 CODE? Children 속성의 게터의 코드가 그 위에 컬렉션을 순회하면서 수정되기 때문에

예외가 발생된다.

당신은 코드

var result = _children; 

_children 필드에 의해 언급 된 목록의 사본을 작성하는 인상 될 것으로 보인다. 그것은 단지 참조을 변수에 복사합니다 (필드 값이 나타내는 것임).

를 통해 목록을 복사하는 쉬운 수정 대신하는 것입니다 :

var result = _children.ToList(); 

가 나는 Linq에 동등한로 변환하는 방법을 알고있다.

게으른 방식으로 작동합니다 현재 코드의 LINQ 해당하는,이다 :

return _children.Concat(_children.SelectMany(child => child.Children)); 

편집 : 나는 코드가 순회 깊이를 제한 한 인상을 원래이었다 두 가지 수준 (자녀 및 손자), 그러나 지금은 그렇지 않은 것을 볼 수는 : 실제로 재산Children에 재귀 호출이 아닌 필드_children의 단지 가치가있다. 속성과 'backing'필드가 완전히 다른 것을 나타 내기 때문에이 이름 지정은 매우 혼란 스럽습니다. Descendants과 같이 좀 더 의미있는 것으로 속성의 이름을 바꾸는 것이 좋습니다.

+0

@Ari. 그래, 그게 해결 됐어. 나는 통과의 깊이를 어디에서 제한하고 있는지 보지 못하고 있습니다 - 모든 진영을 얻기 위해 어떻게 수정합니까? – Berryl

+0

철자가 틀린 이름입니다. 죄송합니다. 내가 통과의 깊이를 제한하고있는 곳을 보지 않고 있습니다. 모든 진부한 것들을 얻기 위해 어떻게 수정합니까? 건배 – Berryl

+0

@ Berryl : 네 말이 맞았다. 나는 편집을했다. – Ani

관련 문제