C# Dynamic Linking of Different Libraries and External Classes

cclass-designdependency-managementlibraries

tl;dr

Is it possible to include an external library (and create instances of objects represented within that library) based on some condition? This must be done at compile-time, so separate builds?

Background information

Warning: Wall of text.

If I have some code that requires a link to an external class:

using myExternalLibrary;
namespace myNamespace
{
  public class Engine
  {
     public Engine()
     {
       // external class is contained
       // within myExternalLibrary
       public externalClass instanceOfExternalClass
              = new externalClass();
     }
  }
}

// external class
namespace myExternalLibrary
{
  public class externalClass
  {
     public externalClass()
     {
       // constructor logic
     }
  }
}

By providing references to myExternalLibrary in the project that contains myClass and providing a using statement in a relevant place, I can create an instance of externalClass as and when I need it within myClass. This I am fine with. If I have some very common code that requires several different external libraries, I can do the same thing but with each of the external libraries that are required (i.e add a reference and a using statement for each one of the external libraries).

However, due to the design of the software I am working on, I am required to use an external library only when certain conditions are met. I am also not to include references to those external libraries if they are not required.

Class Detail

I can't go into specifics about the class design, but this is basically what I have: I have a class (this is the Engine class) that is required to act as a bridge between the GUI and some hardware. The Engine class requires external libraries to allow it to communicate with external hardware.

I have two external libraries for two separate types of hardware:

  • InternallyDevelopedHardwareControlLibrary (our internally developed hardware library for controlling our internally developed hardware)
  • LicensedHardwareControlLibrary (a library that we have licensed for controlling some hardware we have bought in).

By default, the Engine will communicate via InternallyDevelopedHardwareControlLibrary to our internally developed hardware. However, Engine also needs to be able to communicate via LicensedHardwareControlLibrary to the licensed hardware, if (and only if) the end-user has licensed our software to run along side the externally developed hardware.

LicensedHardwareControlLibrary will be licensed to the end-user with our software to some of our end-users. In this instance, LicensedHardwareControlLibrary is required to be shipped with our software. However, if the end-user has is not licensed to use LicensedHardwareControlLibrary, we are not permitted to ship that library with out software.

Since Engine is taking the place of communicating with the external hardware via InternallyDevelopedHardwareControlLibrary by default, it requires a reference to it. However if a user is licensed to utilise LicensedHardwareControlLibrary, I need to add a reference to that, but only if the user is licensed to use that library.

Question

Since Engine is required to be generic, I need to figure out a way of conditionally including this second library.

My initial thought was to move all of the elements of Engine to an abstract class, and to have two separate Engines (one for each of the libraries) that extend the abstract Engine class. Something like:

public class Engine
{
  // all common code for each Engine
  // type in here
}

public class StandardEngine : Engine
{
  // the engine that will utilise
  // InternallyDevelopedHardwareControlLibrary
}

public class LicensedEngine : Engine
{
  // the engine that will utilise
  // LicensedHardwareControlLibrary
}

The StandardEngine code will make use of an instance of the objects represented in InternallyDevelopedHardwareControlLibrary, and LicensedEngine will make use of an instance of the objects represented in LicensedHardwareControlLibrary.

Or more simply put: Each of the less-abstract versions of Engine require to operate on a representation of an object that is contained within either InternallyDevelopedHardwareControlLibrary (StandardEngine) or LicensedHardwareControlLibrary (LicensedEngine).

The standard build will only contain the StandardEngine class, however for some users we will need to run a separate build which also contains the LicensedEngine class.

However, I feel that this will not be optimal because LicensedEngine will need to be external from the main project/library because it cannot be shipped in our standard build. What are the ways in which I can achieve this?

Best Answer

Make a big decision first: compile time or runtime?

If you do want to use assembly references, they are compile-time only. Then you can use #if directives in your code and you can use MSBuild tricks (edit the csproj file outside Visual Studio) to conditionally have the assembly reference. MSBuild is a very powerful tool, and if you are careful, it won't mess too much with Visual Studio. Or you can just have multiple csproj files, living in the same directory, using (almost) the same source files, and different references. This all happens at compile time.

A completely different approach is doing the reference loading during runtime, that's why they invented reflection. You are lucky if the external reference implements a common interface because once you get the type with reflection, you can cast it to your interface and use it normally. Otherwise, you are stuck with Invoke()-ing MethodInfo-s. Or, if your requirements allow it, you can create a very small adapter layer assembly between the external library and yours, as a 3rd assembly. If you need to use the external library, you load your adapter assembly dynamically, cast it to an interface, and use the external library through that layer, no Invoke() anywhere.

Related Topic