Missing libraries? No problem!

We recently released version 4.2 of our product a short while ago that introduced some pretty cool (and powerful) functionality. Well, now we're releasing some add-ons that need to be backward compatible with 4.1 and 4.1.1 as well as 4.2, and we needed to be able to take advantage of the new features available in version 4.2 when they're available.

So what happens when I have a class that has a reference to a Type that is not resolvable? Well, an exception is thrown of course! Just by having "MyClass o = new MyClass();" in a method will cause the .NET runtime to throw an exception if "MyClass" or its assembly cannot be found. Even doing a version check will fail:

if( this.Product.Version >= new Version(4,2) ) o = new MyClass();

The reason being that when the JIT compiler goes into your method, it JIT compiles everything. When it comes to your if statement, it will attempt to resolve the MyClass class and its assembly. If you're on 4.2 it'll work find because it'll find your assembly. If you're on 4.1 or 4.1.1, it will throw an exception saying it can't find your assembly. Well, the solution to this problem is just understanding how the JIT compiler works.

When the .NET runtime loads your assembly, it only loads what is needed at any given moment. So if you have a class with 10 methods (1 being the main method), it will only compile the main method then create stubs for the other 9. When one of those other methods is actually needed at runtime, the JIT compiler will see the stub there and compile the method's IL into machine code and replace the stub with the actual code. From that point on, calling into the method will always execute the actual JIT compiled code. Take that one step further to mean that the JIT compiler and the .NET runtime won't ever attempt to load an Types that aren't needed in the current method. If you have a main method that uses Types foo and bar, and another method that uses Type baz, if you never call the method that uses baz it will never be loaded into memory. If baz was an optional library that you weren't sure will be on the target machine, that means you can segregate all usage of baz into methods that you will make sure won't get called if it doesn't exist.

Class1, members:

static readonly string typeString = "MyNamespace.MyClass, MyAssembly";
static bool libraryExists = false;
private MyClass libraryClass; // exists in an optional assembly

Class1, constructors:

static Class1() {
//get whether or not this library exists on the machine
Type type = Type.GetType( typeString, false );
libraryExists = ( type != null );
}

public Class1() {
if( libraryExists ) {
CreateLibraryClass();
}
}

private void CreateLibraryClass() {
libraryClass = new MyClass();
}

Class1, methods:

public void DoSomething() {
// do some work here
if( libraryExists ) {
DoSomethingWithLibrary();
}
}

private void DoSomethingWithLibrary() {
libraryClass.DoSomeWork();
}

You should see how I have every usage of the libraryClass member segregated into its own method. The only time I call the methods that do something with it are after a check to see if my Type.GetType() method call actually returned a valid Type. Note that if you even check if libraryClass is null will attempt to JIT the MyClass Type and cause an exception. Even a typeof(MyClass) will cause an assembly resolve to happen and will throw an exception if the assembly is not on the box. As long as you have all usage of your optional assembly in methods that you never call unless that assembly can be found, then you can maintain compatibility with boxes and product versions that don't have your optional assembly.