BlackWaspTM

This web site uses cookies. By using the site you accept the cookie policy.This message is for compliance with the UK ICO law.

Reflection
.NET 2.0+

Late Binding with Reflection

The eighteenth part of the Reflection tutorial provides a change in direction to the previous articles, which concentrated on the extraction of information about code using reflection. This article is the first that considers late binding techniques.

What is Late Binding?

When you are developing software using C#, or any other strongly-typed compiled language, you specify the types of all of your variables, return values and method parameters in the code. The compiler checks all of the types when you build your software and ensures that you haven't inadvertently tried to use members that don't exist or assigned an incorrect value to a type. This is known as early binding.

The alternative to early binding is late binding. Types that are late bound are checked only when they are used at run time. Late binding is ideal for configurable software, such as that which has a plug-in model. In such software you might allow the user to specify the names of types and methods to be used in a configuration file. The software would use this configuration data to load assemblies at runtime, with no prior knowledge of the plug-ins when writing the main application.

Late binding has several downsides. As the compiler cannot verify the late bound types, it is possible that the type that you want to use will not exist at run time. If the type does exist, it may not include the members that you expect. When you try to load a missing type or invoke an invalid method or property, you will encounter an exception. Another problem with late binding is that performance can suffer. You should always test the speed of your programs to ensure that they perform sufficiently well. If not, you should consider alternative, early-bound approaches.

In this article we'll introduce late binding with reflection by loading types from assemblies using their names. We've already seen in the "Reflecting Assembly Information" how assemblies can be loaded dynamically at run time, so we won't repeat that information here. In future articles in this tutorial we will look at how to use the members of the late bound objects.

Well use the following types in the example code, each of which can be used to generate a string using an overridden ToString method. Add this to a class library project named, "MessageGenerators", and compile the DLL. Take note of the path to the compiled assembly and substitute that path in the samples in the later sections of the article. The samples use the path "c:\test\MessageGenerators.dll".

public class HelloMessageGenerator
{
    public override string ToString()
    {
        return "Hello, world!";
    }
}

class GoodbyeMessageGenerator
{
    private GoodbyeMessageGenerator() { }

    public override string ToString()
    {
        return "Goodbye, world!";
    }
}

class CustomMessageGenerator
{
    string _message;

    public CustomMessageGenerator(string message)
    {
        _message = message;
    }

    private CustomMessageGenerator(string message1, string message2)
    {
        _message = message1 + " " + message2;
    }

    public override string ToString()
    {
        return _message;
    }
}

We'll be creating our examples in a separate console application project. In that console application we'll be using types from the System.Reflection namespace so create the project and add the following using directive to the Program class:

using System.Reflection;

Activator Class

To create types using late binding we'll be using the Activator class. This allows you to create instances of objects locally or remotely and can be used to obtain references to existing remote objects. We'll be using it locally only. The static method we will use is CreateInstance, which creates type instances and returns them as System.Objects.

There are a large number of overloaded versions of Activator.CreateInstance. We will look at four that work with System.Type objects that have been obtained from assemblies, as these cover most situations. Other overloads that we won't examine allow types to be instantiated without adding the steps for loading the assembly first. Some of the overloads are available in the .NET framework version 1.1. Others were introduced in later versions.

Simple Instance Activation

The simplest way of creating a late-bound object from a reflected type requires only that you provide a Type object representing the class to be instantiated. This creates a new object using the class's default constructor. We can demonstrate this using the HelloMessageGenerator type from our test DLL. The code below first opens the assembly using reflection. The second line obtains a Type instance for the desired class. The third line uses Activator.CreateInstance to generate a new object. The value returned from the ToString method is then outputted.

Assembly asm = Assembly.LoadFile(@"c:\test\MessageGenerators.dll");
Type type = asm.GetType("MessageGenerators.HelloMessageGenerator");
object lateBound = Activator.CreateInstance(type);
Console.WriteLine(lateBound.ToString());

/* OUTPUT

Hello, world!

*/
19 July 2012