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+

Creating Objects with Assembly.CreateInstance

Late binding is a useful technique that permits the type of an object to be determined at run-time, rather than being set at compile time via early binding. One way to achieve late binding is through the use of the Assembly type's CreateInstance method.

Late Binding

In the eighteenth part of the Reflection tutorial I described how you can achieve late binding using the Activator class. Most C# code uses early binding. This is where you define the types of variables, fields, properties and other items directly within your source code. The type is set during compilation and the code is checked to ensure that all of the methods and properties used are defined. This is why you cannot build code that calls a member of a class or structure if that member does not exist.

Late binding doesn't set the types of objects until run-time. For most scenarios the benefits of early binding make it the more appropriate option. However, there are situations where you do not want to set specific types until the software is executing. For example, you might develop a program that includes a plug-in system. You could specify that a particular element of the software must implement a particular interface and allow the user to provide a type that meets this condition at run time. You might do this by including the name of the type in a configuration file, for example.

In the tutorial article I described late binding using the CreateInstance method that is provided by the Activator class. This method includes many overloaded versions, as it is very flexible. When you do not need such flexibility, you can use the simpler Assembly.CreateInstance method.

Assembly.CreateInstance

The Assembly class's CreateInstance method is used to generate an object from a known Assembly instance. You can obtain this prerequisite object using reflection. The method returns an object of the System.Object type, which you can convert to a more usable type by casting or using the as operator. The type that you wish to generate is determined by a string parameter. You should use the string to provide the fully qualified name of the target type.

To demonstrate, create a console application named, "AssemblyCreateInstanceDemo". Add the simple interface and the two classes shown below to the project. The classes are those that we will late bind to at run-time.

public interface IMessageProducer
{
    void ShowMessage();
}

public class HelloProducer : IMessageProducer
{
    public void ShowMessage()
    {
        Console.WriteLine("Hello, world!");
    }
}

public class GoodbyeProducer : IMessageProducer
{
    public void ShowMessage()
    {
        Console.WriteLine("Goodbye, cruel world!");
    }
}

Switch to the class containing the Main method to complete the example. As we are using types from the System.Reflection namespace, add the following using directive to the top of the source code file.

using System.Reflection;

Now add the following code to the Main method. The program requests a keypress from the user to determine whether a "Hello" or "Goodbye" message is required. If the user presses the "Y" key, an instance of the HelloProducer type is created and assigned to a variable of the IMessageProducer interface. If any other key is pressed, it is a GoodbyeProducer object that is instantiated. Once the producer variable is set, its ShowMessage method is used to display a message in the console window.

Console.WriteLine("Hello?");
string desiredType = Console.ReadKey(true).Key == ConsoleKey.Y
    ? "AssemblyCreateInstanceDemo.HelloProducer"
    : "AssemblyCreateInstanceDemo.GoodbyeProducer";

Assembly assembly = Assembly.GetExecutingAssembly();
IMessageProducer producer = assembly.CreateInstance(desiredType) as IMessageProducer;
producer.ShowMessage();

Run the program and try the two options to see how a different class is used according to the input. You can see that this would be useful to allow the behaviour of the program to change without needing to rebuild the solution. This technique would be especially useful when allowing changes via a configuration file.

NB: The names for the types must be correctly qualified. Specifically, they should match the value returned by the FullName property when reflecting their types.

Case Sensitivity

When the above overloaded version of CreateInstance tries to locate the desired type within an assembly, it performs a case-sensitive search. If you mistakenly substitute lower case characters for capital letters, the search will fail and the method will return null. In our example Main method this would throw a NullReferenceException when the ShowMessage method is called.

You can see this by updating the code as shown below. Here the names of the two types have been provided in lower case. Running the program causes the exception.

Console.WriteLine("Hello?");
string desiredType = Console.ReadKey(true).Key == ConsoleKey.Y
    ? "assemblycreateinstancedemo.helloproducer"
    : "assemblycreateinstancedemo.goodbyeproducer";

Assembly assembly = Assembly.GetExecutingAssembly();
IMessageProducer producer = assembly.CreateInstance(desiredType) as IMessageProducer;
producer.ShowMessage();

Case-sensitivity is not ideal if you are allowing users to define types as strings in a configuration file. It would be too easy for a user to make a simple typing error and be unable to identify the problem. To simplify such situations you can elect to ignore the case of the namespace and type when finding a class to instantiate. To do so, you add a Boolean argument to the CreateInstance call. The default value of false indicates a case-sensitive search. By setting the value to true, you specify that the search should disregard capitalisation.

The following code adds the Boolean argument. Run it to see that the lower case text is successfully translated into objects of the HelloProducer or GoodbyeProducer classes.

Console.WriteLine("Hello?");
string desiredType = Console.ReadKey(true).Key == ConsoleKey.Y
    ? "assemblycreateinstancedemo.helloproducer"
    : "assemblycreateinstancedemo.goodbyeproducer";

Assembly assembly = Assembly.GetExecutingAssembly();
IMessageProducer producer = assembly.CreateInstance(desiredType, true) as IMessageProducer;
producer.ShowMessage();
13 September 2013