@bobnoordam

Resursively listing a directory tree using a stack object

The class offered below shows an implementation of an efficient way to scan large filesystem trees and returning the content. By not using recursion but controlling the worklist with a stack element a large part of the load on the GC is reduced. Using recursion the collection of created objects by the recusrive calls can’t be unwind until the entire process is completed, while by using a stack this control is left with the programmer. As a side result, the code becomes more linear which is usualy a good thing for readability.

The controlling application

using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
  
namespace TreeToy
{
    class Program
    {
        static void Main(string[] args)
        {
            string dirtoscan = @"C:\users\bno";
            string pattern = "*.png";
  
            // Initiate a tree scanner and get the filesystem tree
            var scanner = new TreeScanner(dirtoscan, pattern);
            List<string> filelist = scanner.GetFileTree();
  
            // Output results to console
            foreach (string s in filelist)
            {
                Console.WriteLine(s);
            }
  
            // End
            Console.Write("end-run");
            Console.ReadLine();
        }
    }
}

The treescanner class

using System;
using System.Collections.Generic;
using System.IO;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
  
namespace TreeToy
{
    class TreeScanner
    {
  
        #region "Fields"
  
        private readonly string _basedirectory;             // The directory to start scanning from
        private readonly string _pattern;                   // Optional, pattern *.jpg, etc
  
        #endregion
          
        #region "Constructors"
  
        /// <summary>
        /// Initiates a treescanner object from the given directory returning all files
        /// </summary>
        /// <param name="basedirectory">
        public TreeScanner(string basedirectory)
        {
            _basedirectory = basedirectory;
        }
  
        /// <summary>
        /// Initiates a treescanner object from the given directory returning a specific pattern
        /// </summary>
        /// <param name="basedirectory">Directory to start scanning from
        /// <param name="pattern">Pattern (*.jpg, etc..)
        public TreeScanner(string basedirectory, string pattern)
        {
            _basedirectory = basedirectory;
            _pattern = pattern;
        }
  
        #endregion
  
        #region "Methods"
  
        /// <summary>
        /// Return all files in the filesystem tree starting at the base directory
        /// </summary>
        /// <returns></returns>
        public List<string> GetFileTree()
        {
            List<string> result = new List<string>();
            var worklist = new Stack<string>();
            worklist.Push(_basedirectory);
  
            while (worklist.Count > 0)
            {
                // Get a workitem
                string workfolder = worklist.Pop();
                var dirinfo = new DirectoryInfo(workfolder);
  
                try
                {
                    // Add all subfolders in the current workdirectory to the stack of items to be processed
                    DirectoryInfo[] subfolders = dirinfo.GetDirectories();
                    foreach (DirectoryInfo subfolder in subfolders)
                    {
                        worklist.Push(subfolder.FullName);
                    }
  
                    // Add all files in the currect workdirectory to the result list
                    FileInfo[] filelist = string.IsNullOrEmpty(_pattern) ? dirinfo.GetFiles() : dirinfo.GetFiles(_pattern);
  
                    foreach (FileInfo fileInfo in filelist)
                    {
                        result.Add(fileInfo.FullName);
                    }
                }
                catch (PathTooLongException)
                {
                    // Long paths are possible, but a possible compat. nightmare, so we dont return long results. see:
                    // http://msdn.microsoft.com/en-us/library/system.io.pathtoolongexception.aspx
                    // http://blogs.msdn.com/b/bclteam/archive/2007/02/13/long-paths-in-net-part-1-of-3-kim-hamilton.aspx
                }
            }
            return result;
        }
  
        #endregion
  
    }
}