Thursday, October 20, 2005

Breadth-first directory traversal without recursion


Update 2011-05-01: This should probably be rewritten to use the new IEnumerable-returning directory listing method.

public const uint DefaultMaxDirectoryDepth = 32;

public delegate void DirectoryVisitedDelegate(string path, IFileSystem fileSystem);

public static void Traverse
(
    string rootPath,
    DirectoryVisitedDelegate visitedDelegate,
    IFileSystem fileSystem
)
{
    Traverse(rootPath, visitedDelegate, fileSystem, DefaultMaxDirectoryDepth);
}

public static void Traverse
(
    string rootPath,
    DirectoryVisitedDelegate visitedDelegate,
    IFileSystem fileSystem,
    uint maxDepth
)
{
    char[] directorySplitChars = new char[] { Path.DirectorySeparatorChar };
    Queue<string> directoryQueue = new Queue<string>();

    directoryQueue.Enqueue(rootPath);

    while (directoryQueue.Count > 0)
    {
        string path = directoryQueue.Dequeue();

        try
        {
            foreach (string subDirectoryPath in fileSystem.GetDirectories(path))
            {
                if (subDirectoryPath.Split(directorySplitChars).Length < maxDepth)
                {
                    directoryQueue.Enqueue(subDirectoryPath);
                }
            }
        }
        catch (UnauthorizedAccessException)
        {
            // We can't look in that directory. Never mind.
        }
        catch (FileNotFoundException)
        {
            // The directory has gone away. Never mind.
        }

        visitedDelegate.Invoke(path, fileSystem);
    }
}

Wednesday, August 31, 2005

Boolean parameters

When I first started developing software, one thing that annoyed me immediately was when people used boolean parameters where their meaning wasn’t immediately obvious.

This is ok:
void setUserIsDeadFlag(bool)

This is not:
User findUserByName(string, bool)

In the documentation, the parameters will have names and possibly further info pertaining to their use, but when I come to write code, I’m forced to add a comment explaining what the parameter is, so that I can read my own code later:
User bob = findUserByName("bob", false /* Don’t include dead users */);

I can’t even trim my comment down, because the parameter name is something like include_dead_users. If I simply used the name in my comment, I’d make it look like I was passing false because I wanted to include dead users, which would be confusing:
User bob = findUserByName("bob", false /* include_dead_users */);

The best solution to this problem (in all the languages I know) is to use enumerations, or at least constants. Here’s C++:
enum DeadUserInclusionPolicy { Include, Exclude };

Before we start rushing off though, what’s wrong with the above? Let’s have a look at a method signature and a call:
void findUserByName(string name, DeadUserInclusionPolicy deadUserInclusionPolicy)
User bob = findUserByName("bob", Exclude);
The enumeration is still no help, because ‘Exclude’ means nothing to the reader of our code. Let’s try again:
namespace DeadUsers { enum InclusionPolicy { Include, Exclude } };
void findUserByName(string name, DeadUsers::InclusionPolicy policy)
User bob = findUserByName("bob", DeadUsers::Exclude);

Finally we have readable ‘user’ code. In VB.NET and C# you don’t need to bother with a namespace and creative naming, because when you pass a value from an enumeration, you have to prefix it with the name of the enumeration, e.g. DeadUsersInclusionPolicy.Exclude

A large proportion of code that I come across has the boolean parameter problem. When I’m using C++ or C#, I add a comment to remind myself what I’m passing - and try to remember to update the comment if I change the value I pass. When I’m using VB.NET, I get to use a feature of the language to make things explicit: named parameters.

IO.Directory.Delete("c:\", Recursive:=True)

That’s better! Not only does this help where I have to call a method with boolean parameters, it also allows me to call methods with multiple parameters with less worry that I’ll get them mixed up:
Dim Bob As New User(Name:="Bob", Surname:="Holness", Whereabouts:="Unknown")

Thursday, August 18, 2005

Adding a doctype to System.Xml.XmlDocument


Doc.AppendChild _
( _
Doc.CreateDocumentType _
( _
name:="html", _
publicID:="-//W3C//DTD XHTML 1.0 Strict//EN", _
systemID:="http://www.w3.org/TR/xhtml1/DTD/xhtml1-strict.dtd", _
internalSubset:=Nothing _
) _
)

Wednesday, August 17, 2005

Allowing anonymous access to an IIS virtual directory through code (programmatically)

var entry = new System.DirectoryServices.DirectoryEntry(@"IIS:\\127.0.0.1\W3SVC\1\Root\" + VirtualDirectoryName);
entry.Properties["AuthAnonymous"][0] = true;
entry.CommitChanges();

Tuesday, February 1, 2005

An asynchronous testing framework for .NET

AsyncTestFramework is a simple test framework for .NET which allows testing of asynchronous code, i.e. code which uses callbacks. In .NET, there are two main types of callback systems: those based on AsyncCallback and those based on Events. This framework supports both.

Thanks to Jeremy Hopkin for suggestions, insight and asking me difficult questions.