
Pro CSharp 2008 And The .NET 3.5 Platform [eng]
.pdf

CHAPTER 16 ■ TYPE REFLECTION, LATE BINDING, AND ATTRIBUTE-BASED PROGRAMMING |
553 |
■Source Code The VehicleDescriptionAttributeReaderLateBinding project is included under the Chapter 16 subdirectory.
Putting Reflection, Late Binding, and Custom
Attributes in Perspective
Even though you have seen numerous examples of these techniques in action, you may still be wondering when to make use of reflection, dynamic loading, late binding, and custom attributes in your programs. To be sure, these topics can seem a bit on the academic side of programming (which may or may not be a bad thing, depending on your point of view). To help map these topics to a real-world situation, you need a solid example. Assume for the moment that you are on a programming team that is building an application with the following requirement:
• The product must be extendable by the use of additional third-party tools.
So, what exactly is meant by extendable? Consider the Visual Studio 2008 IDE. When this application was developed, various “hooks” were inserted to allow other software vendors to snap custom modules into the IDE. Obviously, the Visual Studio 2008 development team had no way to set references to external .NET assemblies it had not developed yet (thus, no early binding), so how exactly would an application provide the required hooks? Here is one possible way to solve this problem:
•First, an extendable application must provide some input vehicle to allow the user to specify the module to plug in (such as a dialog box or command-line flag). This requires dynamic loading.
•Second, an extendable application must be able to determine whether the module supports the correct functionality (such as a set of required interfaces) in order to be plugged into the environment. This requires reflection.
•Finally, an extendable application must obtain a reference to the required infrastructure (such as a set of interface types) and invoke the members to trigger the underlying functionality. This may require late binding.
Simply put, if the extendable application has been preprogrammed to query for specific interfaces, it is able to determine at runtime whether the type can be activated. Once this verification test has been passed, the type in question may support additional interfaces that provide a polymorphic fabric to their functionality. This is the exact approach taken by the Visual Studio 2008 team, and despite what you may be thinking, is not at all difficult.
Building an Extendable Application
In the sections that follow, I will take you through a complete example that illustrates the process of building an extendable Windows Forms application that can be augmented by the functionality of external assemblies. What I will not do at this point is comment on the process of programming Windows Forms applications (see Chapter 27 for an overview of the Windows Forms API). So, if you are not familiar with the process of building Windows Forms applications, feel free to simply open up the supplied sample code and follow along. To serve as a road map, our extendable application entails the following assemblies:

554CHAPTER 16 ■ TYPE REFLECTION, LATE BINDING, AND ATTRIBUTE-BASED PROGRAMMING
•CommonSnappableTypes.dll: This assembly contains type definitions that will be used by each snap-in object and will be directly referenced by the Windows Forms application.
•CSharpSnapIn.dll: A snap-in written in C#, which leverages the types of
CommonSnappableTypes.dll.
•VbNetSnapIn.dll: A snap-in written in Visual Basic, which leverages the types of
CommonSnappableTypes.dll.
•MyExtendableApp.exe: This Windows Forms application will be the entity that may be extended by the functionality of each snap-in.
Again, this application will make use of dynamic loading, reflection, and late binding to dynamically gain the functionality of assemblies it has no prior knowledge of.
Building CommonSnappableTypes.dll
The first order of business is to create an assembly that contains the types that a given snap-in must leverage to be plugged into the expandable Windows Forms application. The CommonSnappableTypes Class Library project defines two types:
namespace CommonSnappableTypes
{
public interface IAppFunctionality
{
void DoIt();
}
[AttributeUsage(AttributeTargets.Class)]
public sealed class CompanyInfoAttribute : System.Attribute
{
private string companyName; private string companyUrl; public CompanyInfoAttribute(){}
public string Name
{
get { return companyName; } set { companyName = value; }
}
public string Url
{
get { return companyUrl; } set { companyUrl = value; }
}
}
}
The IAppFunctionality interface provides a polymorphic interface for all snap-ins that can be consumed by the extendable Windows Forms application. Given that this example is purely illustrative, you supply a single method named DoIt(). A more realistic interface (or a set of interfaces) might allow the object to generate scripting code, render an image onto the application’s toolbox, or integrate into the main menu of the hosting application.
The CompanyInfoAttribute type is a custom attribute that will be applied on any class type that wishes to be snapped in to the container. As you can tell by the definition of this class, [CompanyInfo] allows the developer of the snap-in to provide some basic details about the component’s point of origin.

CHAPTER 16 ■ TYPE REFLECTION, LATE BINDING, AND ATTRIBUTE-BASED PROGRAMMING |
555 |
Building the C# Snap-In
Next up, you need to create a type that implements the IAppFunctionality interface. Again, to focus on the overall design of an extendable application, a trivial type is in order. Assume a new C# Class Library project named CSharpSnapIn defines a class type named CSharpModule. Given that this class must make use of the types defined in CommonSnappableTypes, be sure to set a reference to this binary (as well as System.Windows.Forms.dll to display a noteworthy message). This being said, here is the code:
using System;
using CommonSnappableTypes; using System.Windows.Forms;
namespace CSharpSnapIn
{
[CompanyInfo(Name = "Intertech Training", Url = "www.intertech.com")]
public class CSharpModule : IAppFunctionality
{
void IAppFunctionality.DoIt()
{
MessageBox.Show("You have just used the C# snap in!");
}
}
}
Notice that I choose to make use of explicit interface implementation when supporting the IAppFunctionality interface. This is not required; however, the idea is that the only part of the system that needs to directly interact with this interface type is the hosting Windows application. By explicitly implementing this interface, the DoIt() method is not directly exposed from the
CSharpModule type.
Building the Visual Basic Snap-In
Now, to simulate the role of a third-party vendor who prefers Visual Basic over C#, create a new Visual Basic code library (VbNetSnapIn) that references the same external assemblies as the previous CSharpSnapIn project. The code is (again) intentionally simple:
Imports System.Windows.Forms
Imports CommonSnappableTypes
<CompanyInfo(Name:="Chucky's Software", Url:="www.ChuckySoft.com")> _
Public Class VbNetSnapIn
Implements IAppFunctionality
Public Sub DoIt() Implements CommonSnappableTypes.IAppFunctionality.DoIt
MessageBox.Show("You have just used the VB .NET snap in!")
End Sub
End Class
Notice that applying attributes in the syntax of Visual Basic requires angle brackets (< >) rather than square brackets ([ ]). Also notice that the Implements keyword is used to implement interface types on a given class or structure.

556 CHAPTER 16 ■ TYPE REFLECTION, LATE BINDING, AND ATTRIBUTE-BASED PROGRAMMING
Building an Extendable Windows Forms Application
The final step is to create a new Windows Forms application (MyExtendableApp) that allows the user to select a snap-in using a standard Windows Open dialog box. Next, set a reference to the
CommonSnappableTypes.dll assembly, but not the CSharpSnapIn.dll or VbNetSnapIn.dll code libraries. Remember that the whole goal of this application is to make use of late binding and reflection to determine the “snapability” of independent binaries created by third-party vendors.
Again, I won’t bother to examine all the details of Windows Forms development at this point in the text. However, assuming you have placed a MenuStrip component onto the forms designer, define a topmost menu item named File that provides a single submenu named Snap In Module. As well, the main window will contain a ListBox type (which I renamed as lstLoadedSnapIns) that will be used to display the names of each snap-in loaded by the user. Figure 16-11 shows the final GUI.
Figure 16-11. GUI for MyExtendableApp
The code that handles the Click event for the File Snap In Module menu item (which may be created simply by double-clicking the menu item from the design-time editor) displays a File Open dialog box and extracts the path to the selected file. Assuming the user did not select the CommonSnappableTypes.dll assembly (as this is purely infrastructure), the path is then sent into a helper function named LoadExternalModule() for processing. This method will return false when it is unable to find a class implementing IAppFunctionality:
private void snapInModuleToolStripMenuItem_Click(object sender, EventArgs e)
{
// Allow user to select an assembly to load.
OpenFileDialog dlg = new OpenFileDialog();
if (dlg.ShowDialog() == DialogResult.OK)
{
if(dlg.FileName.Contains("CommonSnappableTypes")) MessageBox.Show("CommonSnappableTypes has no snap-ins!");
else if(!LoadExternalModule(dlg.FileName)) MessageBox.Show("Nothing implements IAppFunctionality!");
}
}

CHAPTER 16 ■ TYPE REFLECTION, LATE BINDING, AND ATTRIBUTE-BASED PROGRAMMING |
557 |
The LoadExternalModule() method performs the following tasks:
•Dynamically loads the selected assembly into memory
•Determines whether the assembly contains any types implementing IAppFunctionality
•Creates the type using late binding
If a type implementing IAppFunctionality is found, the DoIt() method is called, and the fully qualified name of the type is added to the ListBox (note that the foreach loop will iterate over all types in the assembly to account for the possibility that a single assembly has multiple snap-ins). Finally, notice that we are making use of a LINQ query to obtain IAppFunctionality-compatible class types.
private bool LoadExternalModule(string path)
{
bool foundSnapIn = false; Assembly theSnapInAsm = null;
try
{
// Dynamically load the selected assembly. theSnapInAsm = Assembly.LoadFrom(path);
}
catch(Exception ex)
{
MessageBox.Show(ex.Message); return foundSnapIn;
}
// Get all IAppFunctionality compatible classes in assembly. var theClassTypes = from t in theSnapInAsm.GetTypes()
where t.IsClass && (t.GetInterface("IAppFunctionality") != null) select t;
// Now, create the object and call DoIt() method. foreach (Type t in theClassTypes)
{
foundSnapIn = true;
// Use late binding to create the type.
IAppFunctionality itfApp = (IAppFunctionality)theSnapInAsm.CreateInstance(t.FullName, true);
itfApp.DoIt();
lstLoadedSnapIns.Items.Add(t.FullName);
}
return foundSnapIn;
}
At this point, you can run your application. When you select the CSharpSnapIn.dll or VbNetSnapIn.dll assemblies, you should see the correct message displayed. The final task is to display the metadata provided by the [CompanyInfo] attribute. To do so, update LoadExternalModule() to call a new helper function named DisplayCompanyData() before exiting the foreach scope. Notice this method takes a single System.Type parameter.
private bool LoadExternalModule(string path)
{
...
foreach (Type t in theClassTypes)

558 CHAPTER 16 ■ TYPE REFLECTION, LATE BINDING, AND ATTRIBUTE-BASED PROGRAMMING
{
...
// Show company info.
DisplayCompanyData(t);
}
return foundSnapIn;
}
Using the incoming type, simply reflect over the [CompanyInfo] attribute:
private void DisplayCompanyData(Type t)
{
// Get [CompanyInfo] data.
var compInfo = from ci in t.GetCustomAttributes(false) where (ci.GetType() == typeof(CompanyInfoAttribute)) select ci;
// Show data.
foreach (CompanyInfoAttribute c in compInfo)
{
MessageBox.Show(c.Url,
string.Format("More info about {0} can be found at", c.Name));
}
}
Figure 16-12 shows one possible run.
Figure 16-12. Snapping in external assemblies
Excellent! That wraps up the example application. I hope at this point you can see that the topics presented in this chapter can be quite helpful in the real world and are not limited to the tool builders of the world.
■Source Code The CommonSnappableTypes, CSharpSnapIn, VbNetSnapIn, and MyExtendableApp projects are included under the Chapter 16 subdirectory.

CHAPTER 16 ■ TYPE REFLECTION, LATE BINDING, AND ATTRIBUTE-BASED PROGRAMMING |
559 |
Summary
Reflection is a very interesting aspect of a robust OO environment. In the world of .NET, the keys to reflection services revolve around the System.Type class and the System.Reflection namespace. As you have seen, reflection is the process of placing a type under the magnifying glass at runtime to understand the who, what, where, when, why, and how of a given item.
Late binding is the process of creating a type and invoking its members without prior knowledge of the specific names of said members. Late binding is often a direct result of dynamic loading, which allows you to load a .NET assembly into memory programmatically. As shown during this chapter’s extendable application example, this is a very powerful technique used by tool builders as well as tool consumers. This chapter also examined the role of attribute-based programming. When you adorn your types with attributes, the result is the augmentation of the underlying assembly metadata.

