he .NET platform supports a number of seemingly esoteric programming techniques, such as reflection, dynamic loading, late binding, and custom attributes. At first glance these topics may appear to be of little more than academic interest, having little use in the "real world." However, this is most certainly not the case, as these very technologies allow you to build extremely flexible software. Before diving into a full-blown example of these topics at work, here's how I'm using the terminology:
- Reflection: The ability to discover the composition of a type (e.g., class, interface, structure, enumeration, or delegate) at runtime.
- Dynamic Loading: The act of loading an assembly into memory programmatically at runtime.
- Late Binding: The ability to create types and invoke their members at runtime without prior compile time knowledge. In contrast, early binding demands that the code base has compile time knowledge of the type you wish to create.
- Attributes: Attributes provide the ability for developers to annotate their code base with bits of custom metadata. Attributes by themselves are completely useless until another piece of software discovers its presence via reflection.
Of the preceding four terms, reflection is listed first because it's the foundation for dynamic loading, late binding, and the usefulness of attributes (in addition to numerous other .NET technologies). Again, despite what you might be thinking, these topics are not limited to the construction of object browsers, compilers, or code generation utilities.
Defining Extensible Applications
Assume that you are on a programming team that is building an application with the following requirement:
- The product must be extensible through the use of third-party tools.
So, what exactly does extensible
mean? Consider Visual Studio 2005. When Microsoft developed it, the company inserted various "hooks" to allow other software vendors to snap custom modules into the IDE. Obviously, the Visual Studio 2005 team had no way to set references to external .NET assemblies for which no code was yet available (thus, no early binding), so how exactly did they provide the required hooks? Here is one possible approach.
- First, an extensible application must provide some input vehicle (such as a dialog box or command-line argument) to allow users to specify the module they want to plug in. This feature requires dynamic loading.
- Second, an extensible application must be able to determine if the module supports the correct functionality (usually a set of predefined interfaces) to plug into its environment. This feature requires reflection.
- Finally, an extensible application must be able to obtain access to the required infrastructure (e.g., the interface types) and invoke their members to trigger the underlying functionality. This feature requires late binding.
Simply put, if an extensible application has been preprogrammed to query for specific interfaces, it will then be able to determine at runtime if it can use the types in an external assembly (such types typically go by the term 'snap-ins'). This is the exact approach taken by the Visual Studio 2005 team, and despite what you may be thinking, is not at all difficult.