[ Team LiB ] Previous Section Next Section

13.6 Late Binding

Reflection can also perform late binding, in which the application dynamically loads, instantiates, and uses a type at runtime. This provides greater flexibility at the expense of invocation overhead.

In this section, we create an example that uses very late binding, dynamically discovers new types at runtime, and uses them as well.

In the next example, one or more assemblies are loaded by name (as specified on the command line). The example then iterates through the types in the assembly looking for subtypes of the Greeting abstract base class. When one is found, the type is instantiated and its SayHello( ) method invoked, which displays an appropriate greeting.

To perform the runtime discovery of types, we use an abstract base class that's compiled into an assembly as follows (see the source comment for filename and compilation information):

// Greeting.cs - compile with /t:library
public abstract class Greeting { 
  public abstract void SayHello( );
}

Compiling this code produces a file named Greeting.dll, which the other parts of the sample can use.

We now create a new assembly containing two concrete subtypes of the abstract type Greeting, as follows (see the source comment for filename and compilation information):

// English.cs - compile with /t:library /r:Greeting.dll
using System;
public class AmericanGreeting : Greeting {
  private string msg = "Hey, dude. Wassup!";
  public override void SayHello( ) {
    Console.WriteLine(msg);
  }
}
public class BritishGreeting : Greeting {
  private string msg = "Good morning, old chap!";
  public override void SayHello( ) {
    Console.WriteLine(msg);
  }
}

Compiling the source file English.cs produces a file named English.dll, which the main program can now dynamically reflect over and use.

Now we create the main sample, as follows (see the source comment for filename and compilation information):

// SayHello.cs - compile with /r:Greeting.dll
// Run with SayHello.exe <dllname1> <dllname2> ... <dllnameN>
using System;
using System.Reflection;
class Test {
  static void Main (string[ ] args) {
  
    // Iterate over the cmd-line options,
    // trying to load each assembly
    foreach (string s in args) {
      Assembly a = Assembly.LoadFrom(s);
      
      // Pick through all the public types, looking for
      // subtypes of the abstract base class Greeting
      foreach (Type t in a.GetTypes( ))
        if (t.IsSubclassOf(typeof(Greeting))) {
  
          // Having found an appropriate subtype, create it
          object o = Activator.CreateInstance(t);
  
          // Retrieve the SayHello MethodInfo & invoke it
          MethodInfo mi = t.GetMethod("SayHello");
          mi.Invoke(o, null);
        }
    }
  }
}

Running the sample now with SayHello English.dll produces the following output:

Hey, dude. Wassup!
Good morning, old chap!

The interesting aspect of the preceding sample is that it's completely late-bound; i.e., long after the SayHello program is shipped you can create a new type and have SayHello automatically take advantage of it by simply specifying it on the command line. This is one of the key benefits of late binding via reflection. You could use this pattern to develop an application that supports third-party plug-ins. Each plug-in would have to implement the same interface and be stored in its own assembly. Your application could then use Assembly.LoadFrom( ) and Activator.CreateInstance( ) to activate the plug-in.

13.6.1 Activation

In the previous examples, you loaded an assembly by hand and used the System.Activator class to create a new instance based on a type. There are many overrides of the CreateInstance( ) method that provide a wide range of creation options, including the ability to short-circuit the process and create a type directly:

object o = Activator.CreateInstance("Assem1.dll",              
                                    "Friendly.Greeting");

Other capabilities of the Activator type include creating types on remote machines, creating types in specific AppDomains (sandboxes), and creating types by invoking a specific constructor (rather than using the default constructor as these examples show).

    [ Team LiB ] Previous Section Next Section