I have a plugin system in c# originated from stackoverflow. The key parts are Assembly.LoadFile, Type.IsAssignableFrom and Activator.CreateInstance. It works, however I don't fully understand how IsAssignableFrom can identify the types load from outer assemblies. Say, I have
public interface PluginInterface
{
int calculateSomething();
}
I've compiled it into PluginInterface.dll.
PluginInterface.dll is a reference in an example plugin, eg.:
public class CalculateOnePlugin : PluginInterface
{
public int calculateSomething() { return 1; }
}
It is compiled into CalculateOnePlugin.dll.
Now, create an application with reference to PluginInterface.dll and try to load and use CalculateOnePlugin.dll:
var pluginAssembly = Assembly.LoadFile("CalculateOnePlugin.dll");
Type interfaceType = typeof(PluginInterface);
Type[] typesInLoadedAssembly = pluginAssembly.GetTypes();
Type pluginType = null;
foreach (var typeInLoadedAssembly in typesInLoadedAssembly)
if (interfaceType.IsAssignableFrom(typeInLoadedAssembly)) // ???
{
pluginType = typeInLoadedAssembly;
break;
}
if (pluginType!=null)
{
PluginInterface pi = (PluginInterface)Activator.CreateInstance(pluginType);
// and do what you want with that plugin object
}
My question is how IsAssignableFrom identifies and matches the types loaded from two external assembly? What assembly or class properties are taken into consideration by IsAssignableFrom?
If I copy the source of PluginInterface and compile it into (eg.) AnotherPluginInterface.dll and use this reference when building CalculateOnePlugin.dll then it won't work. So type matching not just cares about method signatures, for example, it knows somehow the origin of the assembly and the type.
When I use two different version of PluginInterface for the application and the plugin (let's say a method was added to the latter) it won't work again, so the origin of the two dlls is not enough, it must have some version stuff inside it.
But simply recompiling PluginInterface between the two usage (app and plugin) does not make mess, it works.
Is the GUID of PluginInterface.dll included into references of CalculateOnePlugin.dll and this is how IsAssignableFrom knows that they're the same? Or a hash-like value is used for types? Or both of them?
There's a complex assembly resolution process going on under the hood where referenced types either get mapped to an already loaded assembly or result in loading the necessary assembly on the fly. When you load plugins, this can get a bit tricky, especially if you have more than one location where the referenced assembly is present. This can result in weird conditions like two copies of the same assembly being loaded (one from your main folder and another from your plugin's folder), which then causes runtime errors due to two different incompatible copies of all those Types being loaded.
The actual matching process normally uses the Fully Qualified Name mentioned by the answer from George Vovos, though there are things like binding redirects that can complicate the picture.
There are ways you can customize the assembly resolution process using the AppDomain.AssemblyResolve event. If you're creating a plugin system, it's well worth doing some research into the assembly resolution process, as there are some gotchas, especially if you do things like reloading updated versions of plugins 'live'.
My question is how IsAssignableFrom identifies and matches the types loaded from two external assembly? What assembly or class properties are taken into consideration by IsAssignableFrom?
Essentially there is a equivalence comparison made.
A Type descends from a class called RunTimeType.
RunTimeType has a Equality operator which performs a check like this:
return RuntimeTypeHandle.IsEquivalentTo(this, otherRtType);
Unfortunately RuntimeTypeHandle.IsEquivalentTo is marked as part of the CLR core and cannot be reflected, so I can't tell you exactly what it looks like or what it does from the code.
However, according to this article entitled "Anatomy of a .NET Assembly",
Type values are stored using a string representing the type name and fully qualified assembly name (for example,
MyNs.MyType, MyAssembly, Version=1.0.0.0, Culture=neutral, PublicKeyToken=0123456789abcdef). If the type is in the current assembly or mscorlib then just the type name can be used.
So the assembly name, culture, version, namespace, and public key token would all appear to be matched in order to uniquely identify a type.
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With