Castle Dynamic Proxy tutorial part VII: Kinds of proxy objects

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

So far all we have been doing with our sim­ple freez­able library is cre­at­ing a prox­ies for classes. Not only that, we also intro­duced one arti­fi­cial lim­i­ta­tion – prox­ied classes had to have default, para­me­ter­less con­struc­tor. You can see it by writ­ing the fol­low­ing test:

[Fact]
public void Freezable_should_be_able_to_call_nonDefault_constructor()
{
    var dog = Freezable.MakeFreezable<Dog>("Rex");
    Assert.Equal("Rex", dog.Name);
}

public class Dog
{
    public Dog(string name)
    {
        Name = name;
    }

    public virtual string Name { get; set; }
}

This test how­ever will not compile.

dptutorial_7_no_default_constructor_error

If we tried to proxy a class with no such con­struc­tor, we would get a com­pi­la­tion error.

The com­pile time error would be a result of the new() generic type con­straint we put in Make­Freez­able method. It is impor­tant to under­stand that it has noth­ing to do with Cas­tle Dynamic Proxy. If we removed the con­straint we would be able to com­pile the code, and then get the excep­tion from Dynamic Proxy at run­time: “System.MissingMethodException : Con­struc­tor on type 'DogProxyc319d54e871741668ed468410d1b1013' not found”.

There is how­ever an over­load of Cre­ate­ClassProxy method that allows you to pass para­me­ters to prox­ied type con­struc­tor. All we need to do, is to refac­tor the Make­Freez­able method.

public static TFreezable MakeFreezable<TFreezable>(params object[] ctorArguments) where TFreezable : class
{
    var freezableInterceptor = new FreezableInterceptor();
    var options = new ProxyGenerationOptions(new FreezableProxyGenerationHook()) { Selector = _selector };
    var proxy = _generator.CreateClassProxy(typeof(TFreezable), new Type[0], options, ctorArguments,
                                            new CallLoggingInterceptor(), freezableInterceptor);
    return proxy as TFreezable;
}

We remove the new() con­straint, since it obvi­ously is not needed. We also add an array para­me­ter to the method that we can use to pass argu­ments to the prox­ied type con­struc­tor. We can use params C# key­word to make the call­ing of the method a lit­tle bit more convenient.

Note that, since we’re pass­ing the argu­ments as untyped array of objects, you can still get a run­time error if you pass incor­rect argu­ments. This solu­tion is also not refac­tor­ing friendly, so If you refac­tor your con­struc­tor and for exam­ple reorder its para­me­ters, the para­me­ters passed to Make­Freez­able will not be reordered. You might want to change this by using lambda expres­sions as I described here a while ago. With that you get refac­tor­ing sup­port and strong typing.

Other than class prox­ies, Proxy gen­er­a­tor class is able to cre­ate three dif­fer­ent kinds of inter­face proxies:

dptutorial_7_proxy_kinds

  • Proxy with tar­get. This one is very easy to explain. We want to proxy an inter­face. Since inter­face can’t exist on its own, we need a class that imple­ments it. The instance of this class is the tar­get. It’s very sim­i­lar to the class proxy we’ve been talk­ing about.
  • Proxy with­out tar­get. This one is tricky. You don’t sup­ply tar­get imple­men­ta­tion for the inter­face. Dynamic Proxy gen­er­ates it for you at run­time, using inter­cep­tors to pro­vide behav­ior for its methods.
  • Proxy with tar­get inter­face. This one is quite sim­i­lar to proxy with tar­get. You do have to pro­vide a tar­get imple­men­ta­tion for the inter­face. The dif­fer­ence is, you can swap it later, and make the method be called on some other object.

We will cover each kind of inter­face proxy in forth­com­ing parts of the tuto­r­ial, so that hope­fully this will all become crys­tal clear.

If you have any sug­ges­tions or ques­tions, feel free to leave a comment.

Tech­no­rati Tags: ,

Comments are closed.