Jamie Dixon
Jamie Dixon

Reputation: 54011

Last iteration of recursive method check

I have a recursive method that builds a tree-like structure of a resource and its related resources.

For each resource that I work with I add it to a class member list which I check on each iteration to make sure that we don't loop infinately on resources that are inter dependent.

Each time I call this recursive method for the first time, I need the class member list to be clear.

At the moment I have a seperate method to do this which I can call between calls to the recursive method.

I'd like to get rid of this method call and reset the list automatically each time.

At the moment I can see two options to solve this problem:

  1. Test whether the calling method is the same as the currently executing method and if not, reset the list
  2. Get rid of the recursion and queue items instead, dequeueing and enqueueing as we go. At the end of the method call I can reset the list.

How would you go about solving this problem? What approach would you take?

Here's how my code currently looks:

public class GetAllRelatedResourcesByParentGuidQuery : IGetAllRelatedResourcesByParentGuidQuery
    {
        private readonly IList<Guid> _itemsCheckedForRelations = new List<Guid>();

        public IEnumerable<IDependency> Invoke(Guid parentCiId, 
                                                  IResoucesByIdQuery getResources)
        {
            if (!_itemsCheckedForRelations.Contains(parentCiId))
            {
                var relatedResources = getResources.Invoke(parentCiId);

                _itemsCheckedForRelations.Add(parentCiId);

                if (relatedResources.Count() > 0)
                {
                    foreach (var relatedResource in relatedResources)
                    {
                        relatedResource.Resource.DependentResources = Invoke(
                                                         relatedResource.Resource.Id, 
                                                         getResources);

                        yield return relatedResource;
                    }
                }
            }
        }

        public void ResetCheckedItemsCollection()
        {
            _itemsCheckedForRelations.Clear();
        }
    }

Upvotes: 2

Views: 2779

Answers (4)

Enigmativity
Enigmativity

Reputation: 117174

You could try using a recursive lambda expression:

public class GetAllRelatedResourcesByParentGuidQuery :
                    IGetAllRelatedResourcesByParentGuidQuery
{
    public IEnumerable<IDependency> Invoke(
            Guid parentCiId,
            IResoucesByIdQuery getResources)
    {
        var checkedItems = new List<Guid>();
        Func<Guid, IResoucesByIdQuery, IEnumerable<IDependency>> invoke = null;
        invoke = (pcid, gr) =>
        {
            if (!checkedItems.Contains(pcid))
            {
                checkedItems.Add(pcid);
                var drs = gr.Invoke(pcid).ToArray();
                foreach (var relatedResource in drs)
                {
                    relatedResource
                        .Resource
                        .DependentResources =
                            invoke(relatedResource.Resource.Id, gr);
                }
                return drs;
            }
            return Enumerable.Empty<IDependency>();
        };
    }
}

With this approach multiple concurrent calls can be made without any special logic to clear the list.

Upvotes: 0

Yahia
Yahia

Reputation: 70379

It seems to me that the List shouldn't be a class member but a parameter for the method you call... any direct call happens with null for this parameter (could even be a default!)... the method allocates the List in that case and on the recursive calls it just passes the allocated List...

Upvotes: 1

Polity
Polity

Reputation: 15130

Just make a method calling an inner method like this:

public class GetAllRelatedResourcesByParentGuidQuery : IGetAllRelatedResourcesByParentGuidQuery 
{ 
        private readonly IList<Guid> _itemsCheckedForRelations = new List<Guid>(); 

        public IEnumerable<IDependency> Invoke(Guid parentCiId,  
                                                  IResoucesByIdQuery getResources) 
        {
            Reset();
            return InternalInvoke(parentCiID, getResources);
        }

        private IEnumerable<IDependency> InternalInvoke(Guid parentCiId,  
                                                  IResoucesByIdQuery getResources) 
        {
             //actual implementation, when going recursive, call this internal method
        }
}

Upvotes: 1

Jon Skeet
Jon Skeet

Reputation: 1503290

I would make a public method which performs the creation, but make the recursive method not care, and take it as a parameter.

public List<string> DoSomething(int input)
{
    List<string> results = new List<string>();
    DoSomethingImpl(input, results);
    return results;
}

private void DoSomethingImpl(int input, List<T> results)
{
    // For example...
    if (input == 0)
    {
        return results;
    }
    results.Add("Foo");
    DoSomethingImpl(input - 1, results);        
}

Upvotes: 4

Related Questions