Google
WWW Yariv Hammer's Code Site

Monday, August 28, 2006

Loading a class dynamically

Introduction
When you write software that can be extended, you have to allow othe rprogrammers to write their own classes, and then load those classes to your application and use them.
For example, let's assume you program a graphical tool, and you prvide square, circle and triangle shapes. You want other users to provide any other shapes, so you let them program the shapes in their own class library and then you load the shapes into your application.

Loading classes from different assemblies
The line of code needed to load a class that you don't know is:
---------------------------------------------------
object objectFromOtherDll = Activator.CreateInstance ("assemblyName", "Namespace.DllClass").Unwrap();

--------------------------------------------------
assemblyName - The name of the class library where the class is stored (without '.dll').
Namespace - The full name of the namespace.
DllClass - The name of the class you want to load.

Note - A file with the name assemblyName.dll must be placed in the same folder as the executable.
Activator is a class in the System namespace. CreateInstance has other overloads. You might want to look in http://msdn2.microsoft.com/en-us/library/system.activator.createinstance.aspx

But how can we know which assembly did the user provide, and what class name to use? As you saw those are strings that you need to put in your code, so it's definitely an issue. The best way is to place them in the App.Config file (or other configuration file). The programmer who supply the extension should put the names of the classes, with their namespaces and assembly names in the configuration file that you provide for your application. You will need to read the configuration file (should be easy with the System.Configuration namespace), and load the classes that were provided in the configuration.

Invoking a method of the unknown class
Now we have an object that we know nothing about. We want to call a method of the object. We must know the name of the method. In the example of Shape, we have the Draw method.
We can use reflection to invoke the method. First we need a using directive to the System.Reflection namespace.
Then we need to invoke the method like this:
--------------------------------------------------
objectFromOtherDll.GetType().InvokeMember("methodName", BindingFlags.DeclaredOnly BindingFlags.Public BindingFlags.NonPublic BindingFlags.Instance BindingFlags.InvokeMethod, null, objectFromOtherDll, null);
------------------------------------------------
More information about InvokeMember can be found here: http://msdn2.microsoft.com/en-us/library/de3dhzwy.aspx

objectFromOtherDll is the object we created with Activator. It appears twice in the statement (once as a parameter) so pay attention.
methodName is a string containing the name of the method to invoke.

How do we know the method name? We could use a configuration file.
How do we know that there is a method called methodName? For example, the programmer might not have implemented the "Draw" method of the Shape class. We could know that by catching exception, or by using reflection to find a method called "Draw".

Using Interfaces
A better way, in my opinion, is to use interfaces instead of reflection. The idea is that you supply a class library with all the interface that you require the programmer to supply to you. The programmer must implement the interface if he wants his object to be loaded in your framework.

For example, in a class library called Interfaces, I add an interface called IShape.
--------------------------------------------
public interface IShape
{
void Draw();
}
-------------------------------------------

The programmer need to refernce to the Interface.dll you gave him, and each Shape must implement the IShape interface:
-----------------------------------------
public class Ellipse:IShape
{
public void Draw()
{
//Do Whatever
}
}
---------------------------------------

The programmer will put in your configuration file the class Ellipse, and place his assembly in the folder of your application.

You will load his class in the following way:
---------------------------------------------------
IShape shape = Activator.CreateInstance("assemblyName", "CustomShapes.Ellipse").Unwrap() as IShape;

if (IShape == null) throw new Exception("Illegal class");
shape .Draw(); //No reflection needed
--------------------------------------------------

Summary
In this article I showed how to use Activator to load classes that you don't know about in advance.
I showed how to use reflection in order to invoke a member.
I showed how to use interfaces to help the programmer supply the right code for you.

0 Comments:

Post a Comment

<< Home

Feel free to use everything here. Add links to my site if you wish.

Do not copy anything to other sites without adding link to here.

All the contents of the site belong to Yariv Hammer.