Jeffrey Richter's Blog

Building a component that works with different versions of a library

It’s common to want to build a .NET component that works with different versions of a particular library. It is also common for newer versions of the library to introduce new methods that your component might want to call. However, if you build your component against the oldest version of the library you support, then your component cannot access the new methods unless you add code to your component that uses reflection to look for the new methods at runtime. In this blog post, I’d like to share a technique for using C# 4.0’s new dynamic type and C# 3.0’s extension methods feature to simplify the building of components that leverage newer library features if the library is available at runtime and gracefully falls back to some default implementation if the newer library is not available at runtime.

Let’s starts by looking at Version 1.0 of some library class:

    public sealed class VersioningType {    // v1.0
      // Other methods not shown
   }

And, let’s say that version 2.0 of the same library class looks like this:

   public sealed class VersioningType {    // v2.0
      public String NewInstanceMethod(String arg) { // New method introduced in v2.0
         return "Real instance method: " + arg;
      }
      // Other methods not shown
   }

Now, we want to write some code in a component that calls VersioningType’s NewInstanceMethod if it exists at runtime. To do this, I would add the following VersioningTypeExtensions class into my component code:

   internal static class VersioningTypeExtensions {
    private static Boolean s_NewInstanceMethodFound = true;   // Assume the method exists
      /// <summary>The stub for VersioningType's instance NewInstanceMethod method</summary>
public static String NewInstanceMethod(this VersioningType vt, String arg) {
if (s_NewInstanceMethodFound) {
// We think the method exists, try to invoke it
try {
return ((dynamic)vt).NewInstanceMethod(arg);
}
catch (RuntimeBinderException) {
// The method doesn't exist.
            // In the future, avoid the dynamic/exception perf hit
            s_NewInstanceMethodFound = false;
}
}
       // This is the default implementation we want if the method doesn't exist
     return "Stub instance method: " + arg;
}
}

This code introduces a C# extension method called NewInstanceMethod that matches the same signature as the NewInstanceMethod introduced in v2.0 of the library. Now, in my component’s code, I can call this extension method and I get IntelliSense support and compile-time type safety at all the call sites. Inside this stub method, I cast the vt variable to C# 4.0’s new dynamic type and then I try to call NewInstanceMethod. Casting vt to dynamic lets the code compile and, at runtime, reflection will be used to determine if the VersioningType actually offers a NewInstanceMethod. If our component is running against v2.0 of the library, then its NewInstanceMethod will be invoked. But, if our component is running against v1.0 of the library, then attempting to call NewInstanceMethod will throw a RuntimeBinderException. My stub method catches this exception, sets the static Boolean field to false and then my code that simulates the default behavior of this method executes. In this case, I return a string indicating that my stub method handled the call.

The static s_NewInstanceMethodFound field exists to improve performance. Once the stub determines that VersioningType doesn’t offer the desired method, this field is set to false and future invocations of the stub method avoid the performance hit of entering/leaving a try block, attempting to dynamically invoke the NewInstanceMethod and the performance hit of processing the thrown RuntimeBinderException.

There is an additional benefit that comes from writing the code this way. Let’s say that in the future, we decide that our component no longer needs to work with v1.0 of the library. If we build our component code against v2.0 of the library (instead of against v1.0), then the C# compiler will see that VersioningType offers a NewInstanceMethod method and the compiler will bind all of the calls to this method. The compiler will simply ignore the stub method entirely. This improves performance. Of course, it would also be good to just delete the stub method now since it is serving no purpose anymore.

The technique I just described for version works great for instance methods. However, it does not work for static methods that are added to later versions of a library. This is because there is no object (so you can’t cast to dynamic) and extension methods only work for instance methods too. However, I do have a variation of this technique that will work for static methods. I’ll share the static-method technique in a future blog posting.

On Jul 9 2010 10:14 AMBy jeffreyr .NET FrameworkWith 7 Comments

Comments (7)

  1. Interesting post! I've been collecting different use cases for dynamic, and this is a great one!

    I also enjoyed how you used extension methods to automatically choose the more efficient solution if the client does compile against a later version of the library.

    I wrote a blog entry a couple months ago regarding dynamic binding to static methods; the example I used was DivRem. It'll call DivRem defined by a type if it exists (e.g., BigInteger) but falls back to Math.DivRem if the type doesn't provide its own static DivRem:

    http://nitoprograms.blogspot.com/2010/04/dynamically-binding-to-static-class.html

  2. Nikos Baxevanis

    I like the idea! This could be very useful when doing customization on a product via COM and want to deal with different versions of the API.

  3. Excelent post !
    One note...By removing casting to 'dynamic' (e.g.:
    return vt.NewInstanceMethod(arg); ), the code still compiles and executes but crashes due to a StackOverflowException caused by calling the same method in an infinite loop .

Leave a Comment