Iterating over a .NET Assembly
This thread gave me an idea: how do I write code that can iterate over every class within an assembly and do something interesting with them in a nice, general, reusable way?
In .NET you have a fair amount of control in terms of being able to explore classes and members of classes. While tedious, the process is at least straight forward and the task really becomes one of filtration more than anything else. In this article, I'll show you how you can iterate over all the ImageCommands in dotImage.
The first thing you need is an Assembly object. The easiest way to get one is to ask for it by using one known type within the assembly:
Assembly:
Assembly dotImage = Assembly.GetAssembly(typeof(AtalaImage));
Alternately, you can manually load the assembly:
Assembly dotImage = Assembly.Load("Atalasoft.dotImage");With this done, you pull out all of the types exported by the assembly:
Type[] allTypes = dotImage.GetExportedTypes();
At this point you loop through each of the types and find the ones that meet your needs. You can compare any entry in allTypes to a known type, or better yet you can ask if the type is a subclass of a given type. For my needs, I wanted to get every command that is a subclass of ImageCommand:
if (t.IsSubclassOf(typeof(ImageCommand)))
Here is a complete implementation of some utility methods for iterating over ImageCommands. The first thing is a delegate to define a procedure to call on an ImageCommand:
public delegate void ImageCommandProc(ImageCommand command);
public class ImageCommandForEach
{
public static void ForEach(Type[] CommandsToIgnore, ImageCommandProc proc)
{
Assembly dotImage = Assembly.GetAssembly(typeof(AtalaImage));
Type[] allTypes = dotImage.GetExportedTypes();
Type[] emptyList = new Type[0];
foreach (Type t in allTypes)
{
if (!t.IsSubclassOf(typeof(ImageCommand)))
continue;
if (CommandsToIgnore != null &&
CommandsToIgnore.Length > 0 &&
Array.IndexOf(CommandsToIgnore, t) >= 0)
continue;
ConstructorInfo ctor = t.GetConstructor(emptyList);
if (ctor != null)
{
object obj = ctor.Invoke(null);
ImageCommand command = (ImageCommand)obj;
proc(command);
}
}
}
public static void ForEach(ImageCommandProc proc)
{
ImageCommandForEach.ForEach(null, proc);
}
}
Notice that the first implementation also includes an array of types to ignore. This is nice, but not vital. The filtration could just as easily happen further down the stream, but isn't it nice to be able to filter out a class before you go to the trouble of constructing it? I like filtering this work out upstream rather than downstream.
Here is a chunk of code to exercise the iterator. This prints out the names of all ImageCommands:
static void NamePrinter(ImageCommand command)
{
Console.WriteLine(command.GetType().Name);
}
[STAThread]
static void Main(string[] args)
{
ImageCommandForEach.ForEach(new ImageCommandProc(NamePrinter));
Console.ReadLine();
}