.NET Interfaces: Compile-Time Only Enforcement

Spoiler Alert:  No code yet!

I tripped across a very interesting fact:  .NET interfaces are only enforced during compilation.


The same app I described on Friday is actually a fairly popular piece of software which serializes to intermediate XML.  Depending on the situation, these files can be deserialized back into native .NET types or XSLT transformed into HTML.  Given the popularity of this application, we have to be careful that the things we fix do not degrade the functionality of other features.  After one particularly rocky release, we vowed not to let it happen again and we built our own test harness.

Okay, I lied a little a lot.  Our test harness is actually two “runners” which have to be compiled against the dependent application whenever we reach a release candidate.  These two “runners” are held together by duct tape a pair of FileSystemWatcher that trigger another method to compare the output of each and report the differences via log file.

There are several parts of this that don’t particularly please me:

  • Each release must be compiled and archived in order to maintain the posterity we really want.
  • Watching and waiting for a slow process to run against thousands of files twice.
  • Then sewing the pieces back together and comparing them after the fact….

It doesn’t end there but our scope does so this is me metaphysically moving on.</rant>

To get to the point, I would be much happier if we did not have to compile, stage and archive against our reference assemblies in order to prepare for a single run.  This is when I started thinking,“What if we had some kind of plugin architecture?  If we had a plugin folder, I could simply throw two versions of the same assembly in that folder and click ‘Compare’.”

I haven’t completed my research, but I’m pretty close.  In so doing, I created an interface and several implementations in separate assemblies.  I even gave some of them the exact same namespace and class names to truly test how quickly .NET would fall apart.

Lessons Learned

First, AppDomain are not that fun to work with.  They ease your ability to load and unload assemblies without infecting your primary sandbox, but the benefits seem to end there.

Second, object proxies (and thus remoting objects) must extend from MarshalByRefObject so make room for them.  Tip:  Instead of using an interface for this type of thing, consider using an abstract which already uses MarshalByRefObject as its base.

Finally, and most importantly, interfaces are validated during compilation only.  I wanted to make sure that our so-called test harness could actually operate in this fashion; I wanted to ensure that I could instantiate classes factory style as long as they implement the interface (or abstract).  Here were my test steps:

  1. Create a project for my interface
  2. Create a project to be the test harness
    • Add a reference to project #1
  3. Create two projects to represent different versions of the same assembly
    • Add a reference to project #1
    • Give both projects the exact same namespace
    • Build both versions with the exact same class name(s)
    • Implement the interface
  4. Kicker
    • Unload both projects from step #3
    • Add a new method to the interface
    • Rebuild projects #1 and #2
    • Copy the assemblies from the projects in #3 into the bin of project #2.
    • Dynamically load these out-of-date assemblies and cast them down to the type of our interface.
    • Call the methods which are actually implemented.
  5. No exceptions?!

Sometimes I wish Office Depot made a “sane” button.  <sarcasm>It would be extremely handy when using SyncFusion products or in special scenarios like this.</sarcasm>

namespace PseudoProducts
    public class SaneButton : System.Windows.Forms.UserControl
        /* ... */
        public void OnClick()
            for( int i = 0; i < 1; i )
                MessageBox.Show( "That was sane!" );
Posted Sunday, September 19th, 2010 under .NET Development, Plugin Architecture.

Leave a Reply