To get things rolling, let's create an abstract class called CommProtocol. I'll define this class in its own DLL so that it can be shared across multiple DLLs that want to derive from it. (Note that the command-line parameters are embedded in the code's comments.) -
// CommProtocol.cs // Build with the following command line switches // csc /t:library commprotocol.cs public abstract class CommProtocol { public static string DLLMask = "CommProtocol*.dll"; public abstract void DisplayName(); }
Now, I'll create two separate DLLs, each representing a communications protocol and containing a class derived from the abstract class CommProtocol. Note that both need to reference the CommProtocol.dll when compiled. Here's the IGN DLL: -
// CommProtocolIGN.cs // Build with the following command line switches // csc /t:library CommProtocolIGN.cs /r:CommProtocol.dll using System; public class CommProtocolIGN : CommProtocol { public override void DisplayName() { Console.WriteLine("This is the IBM Global Network"); } }
And here's the TCP/IP DLL: -
// CommProtocolTcpIp.cs // Build with the following command line switches // csc /t:library CommProtocolTcpIp.cs /r:CommProtocol.dll using System; public class CommProtocolTcpIp : CommProtocol { public override void DisplayName() { Console.WriteLine("This is the TCP/IP protocol"); } }
Let's look at how easy it is to dynamically load an assembly, search for a type, instantiate that type, and call one of its methods.
using System; using System.Reflection; using System.IO; class LateBindingApp { public static void Main() { string[] fileNames = Directory.GetFiles (Environment.CurrentDirectory, CommProtocol.DLLMask); foreach(string fileName in fileNames) { Console.WriteLine("Loading DLL '{0}'", fileName); Assembly a = Assembly.LoadFrom(fileName); Type[] types = a.GetTypes(); foreach(Type t in types) { if (t.IsSubclassOf(typeof(CommProtocol))) { object o = Activator.CreateInstance(t); MethodInfo mi = t.GetMethod("DisplayName"); Console.Write("\t"); mi.Invoke(o, null); } else { Console.WriteLine("\tThis DLL does not have " + "CommProtocol-derived class defined"); } } } } }
First I use the System.IO.Directory class to find all the DLLs in the current folder with a mask of CommProtocol*.dll. The Directory.GetFiles method will return an array of objects of type string that represents the filenames of files that match the search criteria. I can then use a foreach loop to iterate through the array, calling the Assembly.LoadFrom method that you learned about earlier in this chapter. Once an assembly is created for a given DLL, I then iterate through all the assembly's types, calling the Type.SubClassOf method to determine whether the assembly has a type that is derived from CommProtocol. I'm assuming that if I find one of these I have a valid DLL to work with. When I do find an assembly that has a type derived from CommProtocol, I instantiate an Activator object and pass to its constructor the type object. As you can probably guess from its name, the Activator class is used to dynamically create, or activate, a type.
I then use the Type.GetMethod method to create a MethodInfo object, specifying the method name DisplayName. Once I've done that, I can use the MethodInfo object's Invoke method, passing to it the activated type, and-voila!-the DLL's DisplayName method is called.