1

Is there any way I could tell Visual Studio to build me a .dll that loads another referenced (managed) library only if it exists but still have it as a reference?

Basically I want to be able to inherit from a class that is inside the external lib and only if it the external class exists I will initialize it.

Longer explanation:

  • I've made a game with a modding api (via Assembly.LoadFrom)
  • I want mods to be moddable. Lets say i have 2 mods Mod A and Mod B. Mod A offers a static registry for adding additonal functionality. So in Mod B I am currently checking if Mod A is loaded, and if yes, use Mod As static registry to register my addtions.
  • The problem is, in order to do so without compilation errors I have to add Mod A as a reference in the Visual Studio project for Mod B - which causes that Mod B fails to load if Mod A is not present. But it should be an optional reference :/

Something like "Lazy Library Referencing" would be perfect - where a lib reference is only loaded once it is actually needed.

Tyron
  • 1,938
  • 11
  • 30

2 Answers2

2

You should have a read about how MEF works (Managed Extensibility Framework) as it was designed with this kind of plugin scenario in mind.

This is also where you should think about how you orchastrate your abstractions around interfaces rather than inheritance. I'm sure you could probably do something clever with dynamic types and reflection but I also think that would most likely become complex and therefore error prone at the same time.

You can also make use of Lazy<T> to ensure things only get loaded when needed through lazy instantiation of the external dependencies. You also don't have to add static references to these plugin dlls. You can scan directories for libraries and types that implement your abstracted interface definitions.

I'm also a little concerned about why you would make one abstraction dependent on another abstraction (Mod B checking for Mod A) that sounds problematic to some degree - a "leaky" abstraction so to speak.

If you really do have to have a way of making these plugins cooperate together you could make use of a custom attribute approach so a mod can declare a dependency.

public class ModDependency : Attribute
{
    public string DependsOn { get; set; }
}

But ultimately you will have some co-ordination to do in your main application code to then provide instance references to these plugins at run-time.

Jammer
  • 9,969
  • 11
  • 68
  • 115
  • Yep i would like to avoid using reflection / dynamic types. Thanks I'll read into MEF – Tyron Nov 20 '16 at 09:03
  • Just as a note ``TypeBuilder`` mentioned above is all about reflection so that option is off the table. I would investigate altering your current approach to enable the use of an ``interface`` that can be implemented in these external dependencies. – Jammer Nov 20 '16 at 09:08
  • Thanks for the additional info - I don't think Lazy will work because it seems the CLR will try to load the referenced libary without even creating an instance of the class in question (also not accessing any static vars). Looks like as soon as there is any class making use of the referenced library it will try to load the referenced lib, even when the class is not used at all. – Tyron Nov 20 '16 at 09:19
  • Each mod already can declare must-have dependencies and optional dependencies which ensures that they are loaded in the correct order. – Tyron Nov 20 '16 at 09:21
  • Sure, that is how .NET works in general though, once a DLL is loaded it cannot be unloaded but Lazy will defer the actual initialisation of types. – Jammer Nov 20 '16 at 09:31
  • But the type is nowhere initialized in the first place. It still tries to load the references library. Anyhow, I'll check out MEF, thank you – Tyron Nov 20 '16 at 09:54
0

"Assemblies in .Net are loaded on demand by the CLR. Typically an assembly load won't be attempted until a method is JIT'd which uses a type from that assembly." (from a different SO Answer)

I knew it! Apparently I do can apply my kind of approach of optional dependencies!

I discovered the problem arises due to the way I was loading those mods. I was iterating over all the assembly types checking for any class that implements IMod, which obviously caused the class in question to be loaded and that required the CLR to load the dependency.

Instead of calling assembly.GetTypes() I now call assembly.GetExportedTypes() and declare the class in question as internal. Now it is never loaded if the dependency does not exist.

Community
  • 1
  • 1
Tyron
  • 1,938
  • 11
  • 30
  • No, it doesn't "load the class" (you can't "load" classes) it loads the DLL into the current ``AppDomain`` in order to retrieve the list of classes within the DLL via metadata. The only time an object will be created is when the ``new`` keyword is used. Once a DLL is loaded into an ``AppDomain`` it cannot be unloaded. The only way to do this *and* be able to unload a DLL is to do all of this in a separate ``AppDomain``. – Jammer Nov 20 '16 at 17:24
  • yes but it only seems to attempt loading the referenced library once the class in question is being references the first time. So as long as nobody touches that class it never loads the referenced lib – Tyron Nov 20 '16 at 20:59