Solved problem of getting virtual method from constructors in DynamicProxy

time to read 6 min | 1099 words

I encountered a problem in Dynamic Proxy, it threw a null reference exception when I tried to run the following code:

proxy = generator.CreateClassProxy(typeof(ArrayList),new StandardInterceptor(), new int[]{1,2,3});

The reason was that ArrayList::ctor(ICollection ) calls a virtual method, and since Dynamic Proxy intercept all dynamic calls, it recieved the call, but before it had time to initialize itself, so it tried to reference a null pointer.

I was quite lost as to what to do, this is something that users of Rhino Mocks would likely want to do, and it's a certainly a general problem. I thought that there must be a way to solve that:

public class Base
{
  protected virtual void Init() {}
  public Base() { Init(); }
}
public class Derived : Base
{
  string str = "{0}.{1}";
  protected override void Init() { str = string.Format(str,1,2); }
  public Derived() { /*do more work*/ }

public class Derived2 : Base
{
  string str;
  protected override void Init() { str = string.Format(str,1,2); }
  public Derived2() { str = "{0}.{1}"

Calling new Derived() will succeed, but calling new Derived2() will fail with null reference exception. Here are the IL for Derived2's constructor:

.method public hidebysig specialname rtspecialname instance void .ctor() cil managed
{
   // Code Size: 18 byte(s)
  .maxstack 2
  L_0000: ldarg.0
  L_0001: call instance void Base::.ctor()
  L_0006: ldarg.0
  L_0007: ldstr "{0}.{1}"
  L_000c: stfld string Derived2::str
  L_0011: ret
}

The constructor for Base is called, which will call init, which will reference str, which is a null, since we set it only after we call Base' constructor. Here is the IL for Derived's constructor:

.method public hidebysig specialname rtspecialname instance void .ctor() cil managed
{
  // Code Size: 18 byte(s)
  .maxstack 2
  L_0000: ldarg.0
  L_0001: ldstr "{0}.{1}"
  L_0006: stfld string Derived::str
  L_000b: ldarg.0
  L_000c: call instance void Base::.ctor()
  L_0011: ret
}

Here we can see the difference, we first set str, and only then we will call Base' constructor, so this version works.

When I figured out what the difference was (and it took some time, I'm not used to reading IL) I approached Dynamic Proxy with dread, I know just about zilch about Reflection.Emit, and Dynamic Proxy has another layer on top of that. However, in the end it took less than an hour, to grok source that I saw for the first time, and then make the changes (moving four lines, that was it). I sent the patch to Castle Project, and I hope they will accept it. 

On that note, I'm getting more and more curious about CastleProject's other products, especially Aspect# MonoRail & Windsor.