1
votes

I have a problem with dynamically loaded assemblys and casting it to interface. Where is my mistake?

Main app (load plugins):

namespace Console_IFce_Test
{
    class Program
    {
        static void Main(string[] args)
        {
            Console.WriteLine("Press any key to find IPlugin library...");
            Console.ReadKey();            

            string[] files = Directory.GetFiles(Directory.GetCurrentDirectory(), "*.dll");
            Console.WriteLine("Loading assembly: {0}", Path.GetFileName(files[0]));

            Assembly asm = Assembly.LoadFrom(files[0]);
            //Trying this, but still have problems
            //Assembly asm = Assembly.Load(File.ReadAllBytes(files[0])); 

            foreach (Type t in asm.GetTypes())
            {
                Console.WriteLine("Searching in type {0}... ", t.FullName);

                foreach (Type iface in t.GetInterfaces())
                {
                    Console.WriteLine("Interface found: {0}", iface.FullName);
                }

                if (t is IPlugin)
                {
                    Console.WriteLine("1 - IPlugin found!");
                    IPlugin plugin = (IPlugin)Activator.CreateInstance(t);
                    return;
                }

                if (typeof(IPlugin).IsAssignableFrom(t))
                {
                    Console.WriteLine("2 - IPlugin found!");
                    IPlugin plugin = (IPlugin)Activator.CreateInstance(t);
                    return;
                }
            }

            Console.WriteLine("All operations done! Press any key to exit...");
            Console.ReadKey();
        }
    }
}

Interface:

namespace Console_IFce_Test
{
    interface IPlugin
    {
        int GetZero();
    }
}

And plugin:

namespace Library
{
    public class Plugin : Console_IFce_Test.IPlugin
    {
        public int GetZero()
        {
            return 0;
        }
    }
}

In directory with .exe - only 1 .dll (plugin). So, it's output:

Press any key to find IPlugin library... 
Loading assembly: Library.dll 
Searching in type Console_IFce_Test.IPlugin... 
Searching in type Library.Plugin... 
Interface found: Console_IFce_Test.IPlugin 
All operations done! Press any key to exit...

You see, that program found IPlugin interface in assembly, but when i trying to compare it with interface (two conditional statements) - they return false. And if i trying to manually cast it - it returns exception "Can't cast".

I found similar problem: Two Types not equal that should be , and author of answer write:

The same class / type loaded by different app domains [.NET] or class loaders [Java] will not compare equal and are not assignable to/from each other directly.

But i can't understand what should i do? And how?

2
Best way to avoid this trap is create a 3rd assembly with just the interface types. Referenced by both the host and the plugin. Which ensures you won't have the same type in more than one assembly.Hans Passant
I try. No result. I'm feeling, that i making very fool mistake, but i can't understand - where?DeniDoman

2 Answers

2
votes

You need to somehow make .NET to load the assemblies to the same context to be able to do a cast between the types in those assemblies.

1- Put the assembly in the application path or GAC so that the Load (not the LoadFrom) function will find it.

2- Create an app domain and do all of the work there. You can fully control where the assemblies are being searched for an app domain.

3- Use the add-in model.

Read this article to learn more about your options: Best Practices for Assembly Loading

2
votes

In .Net types are distinguished by context as well as by name...

And LoadFrom loads types in a different context than any types loaded normally into your program

So you have two distinct types IPlugin, one loaded in the default context and one in the LoadFrom context. They won't compare the same and you can't cast between them.

You have to make sure that types referenced by assemblies loaded with LoadFrom can't be resolved in the LoadFrom context if you want them to be the same as types loaded in the default context.