Proxy Delegates
By Marc Clifton

Introduction

A requirement of the Application Automation Layer (http://www.codeproject.com/useritems/AAL-1.asp[^]) technology that I’m developing for C# is runtime component plug-ins. One of the issues I am facing is that the components need to register publicly available services with a Component Manager. Since the Component Manager loads the component, it seems reasonable to pass an instance of the Component Manager class to the plug in so that it can use a public “register service” method. This is easily done in C++, and the plug-ins only need a definition of the Component Manager class.

Unfortunately, this implementation is not as easily achieved in C#. An instance of the Component Manager class cannot be passed to the plug in without having the Component Manager referenced in the plug-in’s assembly, and this defeats one of the purposes of component development—being able to eliminate build dependencies between components.

The Proxy Class

A better implementation, even in C++, is to use a proxy class that internally knows how to interface to the Component Manager. This allows changes to the Component Manager class without requiring recompilation of the plug-ins.

A simple proxy can similarly be implemented in C# using a delegate, which is actually a type of class. The proxy allows the plug-in to invoke back without requiring specific knowledge of the target interface. The following diagram illustrates the design.



Note that the only pre-requisite is that the plug-in have a defined entry point that the loader can use to initialize the application/plug-in interface.

The Proxy Delegate Implementation

The implementation for the proxy delegate is ridiculously simple:
using System;

namespace Proxy
{
	public delegate void Callback(string s);
}
This implementation must be placed in a DLL that is referenced in the application and plug-in projects.

The Plug In Implementation

This implementation is for demonstration purposes only:
using System;

namespace PlugIn
{
	public class PlugIn
	{
		public static void Test(Proxy.Callback cbck)
		{
			cbck("Success!");
		}
	}
}
This is a very simple demonstration, in which the plug-in invokes a method in the application using a delegate.

Loading The Plug-In

The plug-in is loaded using Reflection. I have a fairly generic method that loads a DLL and gets the specified method (I’ve removed try-catch blocks from this for simplicitly):
public static MethodInfo GetMethodInfo(string reflection)
{
	MethodInfo mi=null;
	string[] info=reflection.Split(new char[] {'/'});
	Assembly mainAssembly=Assembly.LoadFrom(info[0]);
	Type type=mainAssembly.GetType(info[1]);
	mi=type.GetMethod(info[2]);
	return mi;
}
The plug-in is loaded and the Test method is acquired by calling the GetMethodInfo function:
mi=GetMethodInfo("PlugIn.dll/PlugIn.PlugIn/Test");
Note how the three components of an assembly are parsed, using ‘/’ as a separator.

The first component of an assembly is the file name;
The second component of an assembly is the namespace and class name;
The third component of an assembly is the method name.

The Callback Function

The callback function is a simple test function that sets the text of an edit box to the string passed in by the plug-in:
private void CallbackTest(string s)
{
	textBox1.Text=s;
}

Creating The Delegate

A delegate is instantiated with one line:
	Proxy.Callback cbck=new Proxy.Callback(CallbackTest);

Invoking The Plug-In’s Test Method

The Test method of the plug-in expects the delegate as a parameter. This is established by creating a parameter list:
object[] parms=new object[] {cbck};
The plug-in function is called using the reflection Invoke method:
mi.Invoke(null, BindingFlags.Public | BindingFlags.NonPublic | BindingFlags.InvokeMethod | BindingFlags.Static, null, parms, null);

The Demo Program

I have put together a very simple demonstration program.



Click on the “Load Plug In” button to load the PlugIn.dll assembly.
Click on the “Invoke” button to call into the plug-in using reflection and to have the plug-in call back to the executable using the proxy delegate.

For simplicity, compile only in Debug mode. The projects are set up to place all the DLL’s in the ProxyDemo/bin/Debug directory.

Notes About This Implementation

In this implementation, the plug-in’s method must be static. An instance of an object cannot be used because this would necessitate referencing the plug-in assembly, which we are trying to avoid. Therefore, the first parameter of the Invoke method is null, since there is no object on which to invoke the method (and in fact, this parameter is ignored for static method invocations). However, as demonstrated by this code, the delegate can reference a method associated with a specific instance.

Why Use This?

Unlike marshalling, which is intended to operate with unmanaged memory and COM objects, this approach is instead very simple when you have complete control over the plug-in interface. It promotes modular implementations and allows components to be individually unit tested. For example, a test jig could be created separate from the application for the purposes of component testing.

References

Component-Based Development with Visual C#, by Ted Faison, M&T Books
C# In A Nutshell, by Pter Drayton, Ben Albabari and Ted Neward, O’Reilly Press
http://www.ondotnet.com/pub/a/dotnet/2002/11/04/delegates.htm[^]