0

I have a C# VS2017 project that uses third-party assembly Foo.dll version 2.0.0.0. Now Foo.dll 3.0.0.0 has been released and I would like to allow the user to switch between them at runtime because version 3 is not backwards compatible.

This is what I have so far:

Type V2 = Assembly.Load("foo, Version=2.0.0.0, Culture=neutral, PublicKeyToken=7f709c5b713576e1, processorArchitecture=MSIL");
Type V3 = Assembly.Load("foo, Version=3.0.0.0, Culture=neutral, PublicKeyToken=7f709c5b713576e1, processorArchitecture=MSIL");

Type BarV2Type = V2.GetType('Foo.Baz.Bar');
Type BarV3Type = V3.GetType('Foo.Baz.Bar');

var BarV2 = Activator.CreateInstance(BarV2Type);
var BarV3 = Activator.CreateInstance(BarV3Type);

I now need to call the function CreateEngine() on BarV2 or BarV3, depending on what the user chooses. However BarV2/BarV3 are object type. It seems to cast them to the actual type of 'Bar' I need to add a reference to the assemblies, but I can't do that because they have the same name.

The class Bar is not derived from any interface or class in either assembly.

I then tried adding a reference to Foo.dll V2 and changing its Alias to 'foov2' so I could add a reference to Foo.dll V3 but VS2017 stopped me from adding it, complaining 'Foo' was already added to the project.

Is what I want to do possible? I am thinking that I need to get the CreateEngine() function using reflection and call it via that, but am I just going to create endless problems for myself with this general approach?

Heretic Monkey
  • 11,687
  • 7
  • 53
  • 122
Andy
  • 938
  • 1
  • 10
  • 21
  • 1
    "version 3 is not backwards compatible." -- This is where different namespaces for different and incompatible API versions would be useful. V2 namespaces, V3 namespaces, side by side is no longer a problem. (At least until conflicting dependencies kick in.) – Dave S Jul 25 '23 at 21:45
  • 1
    As long as you solved loading them at the same time (clearly you've solved that as you'd be asking it first :) ) I don't see any problem with using strongly typed references - likely you'd need to have two assembly of your own to refer to each version, but that is somewhat given... (answer by Dan Bryant suggests just that) – Alexei Levenkov Jul 25 '23 at 21:48
  • https://stackoverflow.com/questions/2347260/when-must-we-use-extern-alias-keyword-in-c – Hans Passant Jul 25 '23 at 22:23
  • @HansPassant As I wrote, I tried that and VS complained about the assembly already being added. – Andy Jul 25 '23 at 22:27

2 Answers2

2

If this library wasn't designed with this backwards compatibility in mind, you can get around this by creating interfaces that map to the classes in the two different implementations. You can then either create two different wrapper dlls that implement shims of the interfaces to pass through to the specific implementations or you can get fancy and do some dynamic code gen with IL to create these wrappers.

In the explicitly implemented case you would have a WrapperV2 that directly links BarV2 and a WrapperV3 that directly links BarV3. Both of them implement interfaces defined in WrapperAPI. Now your main app will reference WrapperAPI directly and then dynamically load one of either WrapperV2 or WrapperV3, using the interfaces to isolate itself from the specific library. It's a lot of glue code, but one advantage to this shim approach is that you can also figure out how to account for feature differences by, for example, emulating certain features that were missing in V2 or at least providing some fallback implementation.

Dan Bryant
  • 27,329
  • 4
  • 56
  • 102
  • Oooh... I like this idea! It also solves a related problem regarding lists that I have with managing these two libraries. – Andy Jul 25 '23 at 21:51
1

Using reflection, you can access methods, properties, and fields of types without needing to have a direct reference to the assembly at compile-time. Here's how you can call the CreateEngine() function on BarV2 or BarV3 using reflection:

// Assuming you have already loaded V2 and V3 assemblies as you mentioned in your code

// Assuming 'CreateEngine' method returns an object, you can change the return type accordingly
MethodInfo createEngineMethodV2 = BarV2Type.GetMethod("CreateEngine");
MethodInfo createEngineMethodV3 = BarV3Type.GetMethod("CreateEngine");

// Call CreateEngine() on BarV2
object engineV2 = createEngineMethodV2.Invoke(BarV2, null);

// Call CreateEngine() on BarV3
object engineV3 = createEngineMethodV3.Invoke(BarV3, null);

By using reflection, you can invoke the method on both BarV2 and BarV3 objects without knowing their types at compile-time.

However, keep in mind that working with reflection can be more error-prone and less performant than using direct references to types and methods. If possible, consider creating an interface or base class for Bar in both versions of Foo.dll and let BarV2 and BarV3 implement or derive from it.

Saeed Gholamzadeh
  • 932
  • 1
  • 6
  • 22
  • Thanks! I forgot to mention that Foo.dll is a third-party assembly I can't change. I have updated my question to reflect that – Andy Jul 25 '23 at 21:44