Tuesday, October 9. 2007Implementing a Single Interface with Multiple ClassesThis article will provide you information about how you can distribute the functionality of a single interface into multiple classes. Here are some of the places you can use this approach:
Let me start with a quick example:
interface ITest
{
void Method1 ();
void Method2 ();
void Method3 ();
}
I don't want to implement this interface in a single class. I would like to use the MethodOne() from my existing class A, and I would like to use MethodTwo from my existing class B. And also, I don't want to implement Method3 at all.
class A
{
public void MethodOne ()
{
Console.WriteLine("MethodOne");
}
}
class B
{
public void MethodTwo ()
{
Console.WriteLine("MethodTwo");
}
}
Here is how we can do it:
// Create our classes
A a = new A();
B b = new B();
// Create our proxy
InterfaceProxy<ITest> iTestProxy = new InterfaceProxy<ITest>();
// Method1 comes from class A
iTestProxy.Add("Method1", a, "MethodOne");
// Method2 comes from class B
iTestProxy.Add("Method2", b, "MethodTwo");
// Get my implemented interface
ITest iTest = iTestProxy.GetObject();
// Let's use our methods
iTest.Test1();
iTest.Test2();
Here is the code for our InterfaceProxy class:
//
// (c) 2007 by A. Onur Cinar
// http://www.zdo.com
//
// Provided under LGPL license.
//
using System;
using System.Collections.Generic;
using System.Reflection;
using System.Runtime.Remoting.Messaging;
using System.Runtime.Remoting.Proxies;
using System.Text;
namespace RealProxyTest
{
public class InterfaceProxy<T> : RealProxy
{
#region Fields
Dictionary<string, Invoker> invokers;
#endregion
#region Constructor
public InterfaceProxy ()
: base(typeof(T))
{
invokers = new Dictionary<string, Invoker>();
}
#endregion
#region Methods
public override IMessage Invoke (IMessage msg)
{
// We only want method calls
IMethodCallMessage methodCall = msg as IMethodCallMessage;
if (msg == null)
return null;
// Create method key
MethodBase methodBase = methodCall.MethodBase;
string key = CreateKey(methodBase.Name, methodBase.GetParameters());
// Check for invoker
if (!invokers.ContainsKey(key))
throw new NullReferenceException(methodBase.Name);
// Invoke
object result = invokers[key].Invoke(methodCall.Args);
return new ReturnMessage(result, methodCall.Args, methodCall.ArgCount,
methodCall.LogicalCallContext, methodCall);
}
public void Add (string iMethodName, object target, string methodName)
{
// Get best matching method
MethodInfo methodInfo = GetMatchingMethod(typeof(T), iMethodName, target, methodName);
if (methodInfo == null)
throw new ArgumentException("methodName");
// Add to directory
string key = CreateKey(iMethodName, methodInfo.GetParameters());
Invoker invoker = new Invoker(target, methodInfo);
invokers.Add(key, invoker);
}
public T GetObject ()
{
return (T) GetTransparentProxy();
}
#endregion
#region Private helpers
static string CreateKey (string name, ParameterInfo[] parameters)
{
const char SEPARATOR = ';';
StringBuilder builder = new StringBuilder();
builder.Append(name);
foreach (ParameterInfo parameter in parameters)
{
builder.Append(SEPARATOR);
builder.Append(parameter.ParameterType.FullName);
}
return builder.ToString();
}
static MethodInfo GetMatchingMethod (Type type, string iMethodName, object target, string methodName)
{
MethodInfo[] iMethodInfos = type.GetMethods();
MethodInfo[] methodInfos = target.GetType().GetMethods();
foreach (MethodInfo iMethodInfo in iMethodInfos)
{
if (!iMethodInfo.Name.Equals(iMethodName))
continue;
ParameterInfo[] iParameterInfos = iMethodInfo.GetParameters();
foreach (MethodInfo methodInfo in methodInfos)
{
if (!methodInfo.Name.Equals(methodName))
continue;
if (IsParameterInfosEqual(iParameterInfos, methodInfo.GetParameters()))
return methodInfo;
}
}
return null;
}
static bool IsParameterInfosEqual (ParameterInfo[] p1, ParameterInfo[] p2)
{
if (p1 == p2)
return true;
if (p1 == null || p2 == null)
return false;
if (p1.Length != p2.Length)
return false;
for (int i = 0; i < p1.Length; i++)
{
if (!p1[i].ParameterType.FullName.Equals(p2[i].ParameterType.FullName))
return false;
}
return true;
}
#endregion
}
}
Also you will need the code for Invoker class:
//
// (c) 2007 by A. Onur Cinar
// http://www.zdo.com
//
// Provided under LGPL license.
//
using System;
using System.Reflection;
namespace RealProxyTest
{
public class Invoker
{
#region Fields
object target;
MethodInfo methodInfo;
#endregion
#region Constructor
public Invoker (object target, MethodInfo methodInfo)
{
this.target = target;
this.methodInfo = methodInfo;
}
#endregion
#region Methods
public object Invoke (object[] parameters)
{
return methodInfo.Invoke(target, parameters);
}
public override string ToString ()
{
return String.Format("{0};{1}", target.ToString(), methodInfo.ToString());
}
#endregion
}
}
If you found this post helpful, please "Kick" it so others can find it too: Trackbacks
Trackback specific URI for this entry
No Trackbacks
|