I have occasionally had cause to write collection class adapters, i.e. create an adapter for a class that implements IList<T>
proxying its methods, whist adding some extra functionality. The IList
interface has numerous methods / properties, I was wondering whether the straight-through proxy methods could be implemented dynamically? I had a look at DynamicObject
, but could only find a few simple examples that proxy DTO classes, i.e. proxying a class that just has properties.
Is the proxying of IList<T>
possible?
e.g.
public class ListProxy : IList<T>
{
private IList<T> _adaptee;
public ListProxy(IList<T> adaptee)
{
_adaptee = adaptee
}
// dynamically implement straight-through IList methods / properties
}
Something like this?
using System;
using System.Collections.Generic;
using System.Reflection;
using System.Runtime.Remoting.Messaging;
using System.Runtime.Remoting.Proxies;
class Program
{
static void Main(string[] args)
{
IList<string> listProxy = MyProxyGenerator.Create<IList<string>>(new ListProxy<string>(new List<string>() { "aa","bb" }));
bool b1 = listProxy.Contains("aa");
bool b2 = listProxy.Contains("cc");
int count = listProxy.Count;
string s = listProxy[1];
}
public class ListProxy<T>
{
private IList<T> _adaptee;
//Only method needed by proxy generator
object Adaptee
{
get { return _adaptee; }
}
public ListProxy(IList<T> adaptee)
{
_adaptee = adaptee;
}
}
class MyProxyGenerator : RealProxy
{
Type _Type;
object _Instance;
public static T Create<T>(object instance)
{
return (T)new MyProxyGenerator(typeof(T),instance).GetTransparentProxy();
}
MyProxyGenerator(Type type,object instance) : base(type)
{
_Type = type;
_Instance = instance.GetType().InvokeMember("get_Adaptee", BindingFlags.NonPublic | BindingFlags.Instance | BindingFlags.InvokeMethod, null, instance, null);
}
public override IMessage Invoke(IMessage msg)
{
IMethodCallMessage methodMessage = new MethodCallMessageWrapper((IMethodCallMessage)msg);
string method = (string)msg.Properties["__MethodName"];
object[] args = (object[])msg.Properties["__Args"];
object retObj = _Instance.GetType().InvokeMember(method, BindingFlags.Public | BindingFlags.Instance | BindingFlags.InvokeMethod,null,_Instance,args);
return new ReturnMessage(retObj,methodMessage.Args,methodMessage.ArgCount,methodMessage.LogicalCallContext,methodMessage);
}
}
}