IntroductionWhen 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 assembliesThe 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.aspxBut 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 classNow 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.aspxobjectFromOtherDll 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 InterfacesA 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
--------------------------------------------------
SummaryIn 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.