Castle Dynamic Proxy tutorial part VIII: Interface proxy without target

This is part eight of my tuto­r­ial on Cas­tle Dynamic Proxy.

After a short break let's get right back to our Dynamic Proxy work.

Last time I intro­duced dif­fer­ent kinds of prox­ies you can cre­ate with Cas­tle Dynamic Proxy. Today, we'll talk about inter­face proxy with­out tar­get. As you prob­a­bly know, inter­face by itself can not exist. It's a con­tract defin­ing what its imple­menter can do. As such you do need an imple­menter. If you cre­ate an inter­face proxy with­out tar­get how­ever you don't need to pro­vide the imple­menter. Dynamic Proxy cre­ates it for you. This is pretty powerful.

There are times where you don't want to cre­ate a new class to imple­ment the inter­face, for vari­ety of rea­sons. One such case might be using API that requires an inter­face, where a del­e­gate would be more appro­pri­ate. Back in .NET 1.0, 1.1 times hav­ing meth­ods accept­ing del­e­gates was con­sid­ered a code smell. This is under­stand­able, if you remem­ber that we didn't have lamb­das, anony­mous del­e­gates, or even gener­ics back then.

Now how­ever, it's often more con­ve­nient to pass a lambda instead of cre­at­ing man­u­ally a type. If you're work­ing with API that accepts inter­face, wish­ing you could pass a del­e­gate inside, I have a good news for you — inter­face proxy with­out tar­get may help you.

Let's con­sider a sim­ple interface:

public interface IAnsweringEngine
{
	int GetAnswer( string s );
}

and some pub­lic API that con­sumes it:

public interface IDeepThought
{
	void SetAnsweringEngine(IAnsweringEngine answeringEngine);
}

Now, con­sid­er­ing your answer­ing engine is very sim­ple one-liner, you might wish to not cre­ate a new class in code, but rather pass a lambda in. This how­ever will not work.

IDeepThought d = GetSuperComputer();
d.SetAnsweringEngine( ( string s ) => s.Length );

With what we learned how­ever so far we can make it work. All we need to do, is to build a proxy for that inter­face, with our del­e­gate pro­vid­ing the imple­men­ta­tion. See the fol­low­ing test:

[Fact]
public void Should_be_able_to_wrap_interface_with_one_method()
{
	Func<string, int> length = s => s.Length;
	var wrapped = DelegateWrapper.WrapAs<IAnsweringEngine>( length );
	Assert.NotNull( wrapped );
	var i = wrapped.GetAnswer( "Answer to Life the Universe and Everything" );
	Assert.Equal( 42, i );
}

Notice we don't have any man­u­ally cre­ated imple­menter of IAn­swerin­gEngine here. So how does it work?

When imple­ment­ing our Del­e­gateWrap­per we need to remem­ber one thing about inter­face prox­ies with­out tar­get. They are just a shells for us to fill. Dynamic Proxy does cre­ate a class that imple­ments the inter­face, but it does not know what logic to put into it.

As such it is our respon­si­bil­ity to pro­vide that logic and fill the shell via interceptors.

Our inter­cep­tors don’t have to be com­pli­cated. They just get a del­e­gate and when invoked, call the delegate.

internal class MethodInterceptor : IInterceptor
{
	private readonly Delegate _impl; 

	public MethodInterceptor( Delegate @delegate )
	{
		this._impl = @delegate;
	} 

	public void Intercept( IInvocation invocation )
	{
		var result = this._impl.DynamicInvoke( invocation.Arguments );
		invocation.ReturnValue = result;
	}
}

As you can see the inter­cep­tor imple­men­ta­tion is triv­ial. We could make it faster, but its not the goal of this tutorial.

Notice also that we do not call invocation.Proceed(). Since there's no real imple­men­ta­tion to pro­ceed to, this would be illegal.

If we did this we would get a NotIm­ple­ment­edEx­cep­tion with the fol­low­ing mes­sage: "This is a DynamicProxy2 error: the inter­cep­tor attempted to 'Pro­ceed' for a method with­out a tar­get, for exam­ple, an inter­face method or an abstract method".

Keep in mind that inter­cep­tors cre­ate a pipeline (see the pic­ture in the 2nd part of the tuto­r­ial). As such only the last inter­cep­tor must not call Pro­ceed, all oth­ers should.

Now we can move, and imple­ment Del­e­gateWrap­per. It's as triv­ial as MethodInterceptor.

public class DelegateWrapper
{
	public static T WrapAs<T>(Delegate impl) where T : class
	{
		var generator = new ProxyGenerator();
		var proxy = generator.CreateInterfaceProxyWithoutTarget( typeof( T ), new MethodInterceptor( impl ) );
		return (T) proxy;
	}
}

That's all, roughly a dozen lines of mean­ing­ful code to make the test pass. If you're scratch­ing your head won­der­ing why I put where T : class generic con­straint, it's because class in this con­text actu­ally means 'ref­er­ence type'. This may not be the best exam­ple in the world, but hope­fully by now you see the poten­tial capa­bil­i­ties inter­face prox­ies with­out tar­get give you.

Just for kicks, we might want to extend the Del­e­gateWrap­per to han­dle inter­faces with more than one method. Let's write a test for that.

[Fact]
public void Should_be_able_to_write_interface_with_two_methods()
{
	Func<string, string, bool> compare = ( s1, s2 ) => s1.Length.Equals( s2.Length );
	Func<string, int> getHashCode = s => s.Length.GetHashCode();
	var comparer = DelegateWrapper.WrapAs<IEqualityComparer<string>>( compare, getHashCode );
	var stringByLength = new Dictionary<string, string>( comparer )
	{
		{ "four", "some string" },
		{ "five!", "some other string" }
	};
	Assert.Equal( 2, stringByLength.Count );
	var atFive = stringByLength["12345"];
	Assert.Equal( "some other string", atFive );
}

We now need some way of decid­ing which del­e­gate should be bound to which method. Inter­cep­torS­e­lec­tor to the rescue!

public static TInterface WrapAs<TInterface>(Delegate d1, Delegate d2)
{
	var generator = new ProxyGenerator();
	var options = new ProxyGenerationOptions { Selector = new DelegateSelector() };
	var proxy = generator.CreateInterfaceProxyWithoutTarget(
		typeof( TInterface ),
		new Type[0],
		options,
		new MethodInterceptor( d1 ),
		new MethodInterceptor( d2 ) );
	return (TInterface) proxy;
}

Here's one very sim­ple imple­men­ta­tion of such selector:

internal class DelegateSelector : IInterceptorSelector
{
  public IInterceptor[] SelectInterceptors(Type type, MethodInfo method, IInterceptor[] interceptors)
  {
	  foreach (var interceptor in interceptors)
	  {
		  var methodInterceptor = interceptor as MethodInterceptor;
		  if (methodInterceptor == null)
			  continue;
		  var d = methodInterceptor.Delegate;
		  if (IsEquivalent(d, method))
			  return new[] { interceptor };
	  }
	  throw new ArgumentException();
  } 

  private static bool IsEquivalent(Delegate d, MethodInfo method)
  {
	  var dm = d.Method;
	  if (!method.ReturnType.IsAssignableFrom(dm.ReturnType))
		  return false;
	  var parameters = method.GetParameters();
	  var dp = dm.GetParameters();
	  if (parameters.Length != dp.Length)
		  return false;
	  for (int i = 0; i < parameters.Length; i++)
	  {
		  //BUG: does not take into account modifiers (like out, ref...)
		  if (!parameters[i].ParameterType.IsAssignableFrom(dp[i].ParameterType))
			  return false;
	  }
	  return true;
  }
}

Notice that I also extended Method­In­ter­cep­tor class to expose its del­e­gate as a property.

On a side note, if you still don't see how pow­er­ful inter­face prox­ies with­out tar­get are, think about WCF prox­ies — that is exactly what they are.

Tech­no­rati Tags: , ,
  • arnaud

    hey Krzysztof,
    i was look­ing for info about VS2008 and i found your blog, i m not good at code but it seems you know very well your stuff and like it!
    if i can help you with french trans­la­tion do not hes­i­tate i may help (well, i m french)
    Also, are you actu­ally fully busy or still inter­est­ing in side project oppor­tu­ni­ties? I sum­ma­rize it: i spent last years at Orange as a pro­jet man­ager then quit last Octo­ber for a real estate web­site project for east­ern europe, we've (I and the friends i work with on it) every required skills so far but lack a really skilled coder. I m going to see friends in krakow (tam stu­diowalem) in may, if you re just curi­ous maybe we can share some piwo and i ll tell you few words about it?
    let me know, i ll be glad!
    Arnaud

  • Michael Har­ring­ton

    It's inter­est­ing that you sug­gest DP as a solu­tion to have an API that expects an inter­face where you would pre­fer using a del­e­gate, because DP itself is an API that expects an inter­face where I would pre­fer using a del­e­gate. :P

    I'm con­sid­er­ing sim­pli­fy­ing a sys­tem I'm work­ing on by using DP, but the prob­lem is that it is performance-sensitive. I'm con­cerned about the over­head of the inter­cep­tor chain.

    Rather than DP inject­ing its hooks into the the proxy and fun­nel­ing the data back and forth, I'd like to just give DP a dic­tio­nary of strings to del­e­gates to use as the imple­men­ta­tion and skip DP's inter­cep­tor chain alto­gether. Then, in the­ory, the only added cost to a call on a proxy would be one level of indirection.

    Is there a tech­ni­cal rea­son this couldn't be done? Is there a rea­son this could be a bad idea?

  • http://kozmic.pl/Default.aspx Krzysztof Koźmic

    @Michael

    So you want to do the fol­low­ing:
    given an inter­face with some meth­ods, and dic­tio­nary that maps names of these meth­ods to del­e­gates pro­vid­ing imple­men­ta­tion cre­ate a proxy for that inter­face that uses the del­e­gates as imple­men­ta­tion. Is that correct?

    I would sug­gest doing it the way I shown in the post, and see if the Dynam­icIn­voke on the del­e­gate has sat­is­fy­ing per­for­mance. The DP itself with sin­gle inter­cep­tor adds really very minor over­head so I wouldn't worry about that.

    If you find the per­for­mance unac­cept­able you will have to extend the DP itself to bind directly to del­e­gates in a strongly typed man­ner. You will need DP 2.2 beta for that. How­ever I would strongly sug­gest doing it the sim­pler way first.