Castle Dynamic Proxy tutorial part VI: handling non-virtual methods

This is part six of my tutorial on Castle Dynamic Proxy.

We talked about how Dynamic Proxy proxying mechanism works, but so far we only briefly spoke about its limitations. The main limitation is the fact, that it can not intercept non-virtual calls. This may be an issue of varying importance.

In case of libraries such as NHibernate, which uses DynamicProxy for lazy loading, when your member is not virtual, you can’t lazy load it. This may be a drawback for performance, but your program should be able to work nonetheless.
On the other hand, for libraries like Rhino.Mocks, or MoQ, that use DynamicProxy for creating mocks and stubs, not being able to intercept a call, means you can’t mock its behavior and the response. This is a critical issue.
Similarly in case of our sample Freezable library, if a setter is not virtual, we can’t intercept calls to it, and that means we can not warrant that the state of the object will not change.

When we talked about ProxyGenerationHook, I mentioned that it has a NonVirtualMemberNotification method, that gets called when proxy generator encounters method it can not proxy.
In case of freezable library, we took simplistic assumption that state of the object can only change via property setters. In this case if just any method we encounter is non-virtual we don’t care. Since we assume that it can’t change the state of our object anyway, we may as well let it be non-virtual and still deliver on our promise.
However, if we encounter a non-virtual property setter, we should throw an exception to indicate that we deny taking any responsibility for this objects immutability.

Enough theory – let’s write some tests to get a cleaner picture of what we want to achieve.

[Fact] 
public void Freezable_should_freeze_classes_with_nonVirtual_methods() 
{ 
    var pet = Freezable.MakeFreezable<WithNonVirtualMethod>(); 
    pet.Name = "Rex"; 
    pet.NonVirtualMethod(); 
} 
 
[Fact] 
public void Freezable_should_throw_when_trying_to_freeze_classes_with_nonVirtual_setters() 
{ 
    var exception = Assert.Throws<InvalidOperationException>( () => 
        Freezable.MakeFreezable<WithNonVirtualSetter>() ); 
    Assert.Equal( 
        "Property NonVirtualProperty is not virtual. Can't freeze classes with non-virtual properties.", 
        exception.Message ); 
} 

If we run the tests the first one will pass, the second one will fail. Is that a surprise? We don’t have any checking code in place, so when we encounter a non virtual member, we do nothing. that’s why we effectively ignored the NonVirtualMethod in the first test (which is the behavior we want), but we also ignored the non-virtual property setter in the second test, which is a no-no.

To make them pass, we need to actually implement the NonVirtualMemberNotification method, to throw an exception when it encounters a property setter. There’s no specific exception type we’re obligated to throw, so we’ll just throw InvalidOperationException as specified in our test.

Here’s the implementation:

public void NonVirtualMemberNotification(Type type, MemberInfo memberInfo) 
{ 
    var method = memberInfo as MethodInfo; 
    if(method!=null) 
    { 
        this.ValidateNotSetter( method ); 
    } 
} 
 
private void ValidateNotSetter(MethodInfo method) 
{ 
    if( method.IsSpecialName && IsSetterName( method.Name ) ) 
        throw new InvalidOperationException( 
            string.Format( 
                "Property {0} is not virtual. Can't freeze classes with non-virtual properties.", 
                method.Name.Substring( "set_".Length ) 
                ) 
            ); 
} 

Similar to ShouldInterceptMethod method MarshalByRefObject and Object classes are special cases, and by default DynamicProxy will just ignore them.

With this change, all our tests now pass.

Other than the two methods we already discussed, IProxyGenerationHook has one more method: MethodsInspected. It gets called, after all methods on proxied type have been inspected, so it is the last method to be called by proxy generator. It is useful in cases when you hold some precious resources needed by two other methods.
For example if our hook implementation asked some external service (like a WCF service or database) about what to do with non-virtual methods, MethodsInspected would be the place to close the connection and dispose of all resources that are no longer needed.

With this part we basically covered almost all of basics of Dynamic Proxy. In the next part we’ll discuss other kinds of proxies you can create with Dynamic Proxy (yes, there are a few). Then we’ll talk about more advanced scenarios, like mixins.

I’m still open for feedback, so if you feel I missed some important topic or should have expand on something let me know in the comments.

The code, as always, is here.

Technorati Tags: , ,

Comments

javier says:

Awesome Tutorial.

Matt says:

This is a great tutorial, thank you for putting it together.

I have to admit, I’m a little disappointed that DynamicProxy won’t work with non-virtual methods. And I believe I understand *why* – It appears the proxy works by generating, at runtime, a new child class to the target class. And this child class implements an override with callbacks to the Interceptors. And you can’t override a non-virtual method. You can hide it using the "new" keyword, but as we all know if you call the non virtual method "Foo" on a Child object you have to explicitly be working with said child object in a type safe manner otherwise you’ll execute "Foo" against the parent type.

Anyway, it a shame because this brings us *soooo* close to a completely decoupled solution. As it stands, if I want to be DynamicProxying any of my classes, I have to modify them with the virtual attribute. So the proxy is still affecting the code-time decisions being made in the classes. I’m sure there are plenty of use cases where a virtual method is what makes sense anyway, but we can also be sure there are plenty of use cases where virtual does not make sense.

And, the worst part, there is no explicit declaration of this coupling in the code. Unless the coder took the time to document every virtual method saying "This is virtual because we want to be able to do a DynamicProxy" then one would be left in the dark, completely and utterly, when seeing all these virtual methods and absolutely no indication as to why they are virtual.

Anyway, I’m just voicing some personal disappointment in this limitation enforced by the language. Regardless, excellent tutorial. I’m not sure I would be using dynamic proxy *at all* without it.

Matt says:

As a follow up – I imagine proxying of non-virtaul methods is something that will be possible in .NET 4.0 with the support for dynamic typing.

Matt says:

Follow up to my follow up –

Not sure about my second statement there. It wouldn’t actually solve the problem. (thinking out loud) The problem is with the compile-time type checking, and the fact that the consumer of the proxied object doesn’t (and shouldn’t) know that it’s being proxied. So, the consumer of the object expects a Pet object, and will treat it as a Pet object, instead of a PetProxy. The addition of dynamic typing doesn’t avoid this problem – I imagine if a user calls NonVirtualMethod() with the dynamic approach it will work, much like calling NonVirtualMethod() works just fine if you had consumed it as a "var" instead of a "Pet". But you still have the problem of people treating it as a "Pet", will always invoke the parent non virtual method.

Matt, strongly typed contract is still strongly typed contract. With dynamic you can add interception to any type (including sealed classes, and non-virtual methods) by wrapping them (see Type Wrapping here: using.castleproject.org/…/Dynamic+Proxy+3+des…)

You would however pay the performance and compiler check penalty due to weakly typed nature of such code.