Nerdy tidbits from my life as a software engineer

Tuesday, December 16, 2008

Overriding Dispose With Reflection.Emit

I ran into a bizarre problem today with my mock framework.  I was trying to create a mock on an object that implemented IDisposable, but I kept getting this strange error when I hit the CreateType method:

System.TypeLoadException: Declaration referenced in a method implementation cannot be a final method.

This didn’t make any sense to me, since I was in no way trying to override a method that was sealed – the Dispose method wasn’t declared virtual, but it wasn’t declared sealed either (besides, a method that implements an interface is virtual by definition).  Perplexed, I hit a breakpoint in the debugger and looked at the properties dialog to see if there was some obvious flag on Dispose’s MethodInfo that I should look out for in order to avoid this problem:

image

There’s nothing here that popped out at me.  The IsVirtual and IsAbstract values are true, and IsFinal is false.  What could possibly be wrong? So I started searching and came across this article on Microsoft support, which said something very interesting:

If this is done, no compile errors are raised. However, when the derived class is loaded at runtime, you receive a runtime error message similar to the following:

An unhandled exception of type 'System.TypeLoadException' occurred in system.windows.forms.dll.

Additional information: Declaration referenced in a method implementation can not be a final method. Type: ClassLibrary1.UserControl1. Assembly: Dispose.

Although I’m still a bit perplexed as to what’s going on here, it appears to me as though the compiler is automagically making Dispose sealed by default on the implementer.  I figured this out be checking the MethodInfo on the class that implements the interface, not the interface itself (sure enough, it was final despite not being labeled as such in the code).  I’m not entirely sure why it’s doing this, but it is.  What I don’t understand is why this needs to be a runtime error.  Can’t this be caught at compile time?

We do a lot of funkiness with the Dispose method that I don’t particularly like.  I get the sense that this is another optimization having to do with IDisposable.  I hate making special cases for specific interfaces and classes.  I don’t have this problem mocking objects that implement other interfaces.  Why should IDisposable be any different?

0 comments: