In this post, we will look at several common usage scenarios for System.Reflection.
Late Binding
Late binding is simply runtime binding. You may not have access to the assembly at compile time. You can likely expect it to appear a certain way. You have to know something about the assembly, after all. Late binding is common in systems that may have interchangeable components. For example, a plug-in system. Your application may support plug-ins that conform to a specific standard such as the plug-in must implement a specific interface which allows your application to instantiate the plug-in. The plug-in may be anything. you have no way of knowing anything about it while you are creating the application but as long as it has the Start method, you can instantiate it.
Listing 1.1 – Simple Plugin System
internal void Load() { string pluginDirectory = Path.Combine(AppDomain.CurrentDomain.BaseDirectory, "Plugins"); DirectoryInfo pluginFolder = new DirectoryInfo(pluginDirectory); FileInfo[] plugins = pluginFolder.GetFiles("*.dll"); foreach (FileInfo plugin in plugins) { Assembly assembly = Assembly.LoadFile(plugin.FullName); Type[] types = assembly.GetTypes(); foreach (Type t in types) { try { IPlugin ip = (IPlugin)Activator.CreateInstance(t); ip.Start("Sample message from main system"); } catch(MissingMethodException) { Console.WriteLine(t.FullName + " is not a plugin."); } } } }
In this short example, Reflection is used to load the assembly and then interrogate the assembly to get the Types the assembly contains. Then, we attempt to call the Start method on each Type. For this example, I created an interface named IPlugin and created two different plugins and then moved those assemblies to the bin\Plugins directory along with the assembly containing the IPlugin interface. Below is the output from this code.
Listing 1.2 – Plugin System Output
Press any key to load plugins... Hello from Plugin1: Sample message from main system Hello from Plugin2: Sample message from main system PluginUtilities.IPlugin is not a plugin.
Property Inspection
Another scenario that is very common for Reflection and one that most .NET developers will encounter at some point is property inspection. System.Reflection gives developers the ability to interrogate unknown assemblies, classes, etc and find out most of what there is to know about them at runtime.
The below code builds upon what is in Listing 1.1 to load each assembly in the plugins folder, loop through them, get all the different types in the assembly, loop through those, print out their full name, and then, finally, loop through all the methods on the type and print out a list of those as well.
Listing 1.3 – Property Inspection
internal void Load() { string pluginDirectory = Path.Combine(AppDomain.CurrentDomain.BaseDirectory, "Plugins"); DirectoryInfo pluginFolder = new DirectoryInfo(pluginDirectory); FileInfo[] plugins = pluginFolder.GetFiles("*.dll"); foreach (FileInfo plugin in plugins) { foreach (Type t in Assembly.LoadFile(plugin.FullName).GetTypes().ToList()) { Print(t); } } } private void Print(Type t) { Console.WriteLine($"Full Name: {t.FullName}"); foreach(MethodInfo method in t.GetMethods().ToList()) { Console.WriteLine($"Method: {method.Name}"); } }
Listing 1.4 – Property Inspection Output
Full Name: PlugIn1.Tool Method: Start Method: ToString Method: Equals Method: GetHashCode Method: GetType Full Name: Plugin2.Tool Method: Start Method: ToString Method: Equals Method: GetHashCode Method: GetType Full Name: PluginUtilities.IPlugin Method: Start
Notice that the default object methods are included such as ToString, Equals, GetHashCode, and GetType. There are similar methods on the Type object for Constructors, Members, Interfaces, Custom Attributes, Fields, Events, and so on. Using this approach, you can find out almost everything there is to know about an assembly.
Conclusion
I hope this post has been informative. If you work with .NET long enough, you will be required to do this at some level. It is just too useful to pass by without using.