Thadeux
Thadeux

Reputation: 144

Two Dlls Loaded with Assembly.LoadFrom Define the Same Class and Work Fine?

This is freaking me out, and I'm guessing it's because I'm severely misunderstanding something basic about how assemblies get loaded. I was not expecting this to work, can someone explain why it does?

Projects:

Lib1 and Lib2 share a code file via symlink:

namespace Shared
{
    public class SharedClass
    {
        public static string Key { get; set; }
    }
}

Lib1 Plugin:

namespace Lib1
{
    public class Lib1Plugin : Plugin
    {
        public override void Load()
        {
            SharedClass.Key = "Lib1 Key";

            Console.WriteLine(SharedClass.Key);
        }

        public override void Run()
        {
            Console.WriteLine(SharedClass.Key);
        }
    }
}

Lib2 Plugin:

namespace Lib2
{
    public class Lib2Plugin : Plugin
    {
        public override void Load()
        {
            SharedClass.Key = "Lib2 Key";

            Console.WriteLine(SharedClass.Key);
        }

        public override void Run()
        {
            Console.WriteLine(SharedClass.Key);
        }
    }
}

Console:

    static class Functions
    {
        public static IEnumerable<Type> FindDerivied(Assembly asm, Type baseType)
        {
            try
            {
                return asm.GetTypes().Where(t => baseType.IsAssignableFrom(t) && t != baseType);
            }
            catch (Exception e)
            {
                return new List<Type>();
            }
        }
    }
    class Program
    {
        static void Main(string[] args)
        {
            var di = new DirectoryInfo("Plugins");

            var bos = new List<Plugin>();
            if (di.Exists)
            {
                var dlls = di.EnumerateFiles();
                foreach (var dll in dlls)
                {
                    var asm = Assembly.LoadFrom(dll.FullName);

                    var builders = Functions.FindDerivied(asm, typeof(Plugin));
                    foreach (var builder in builders)
                    {
                        var bo = (Plugin)Activator.CreateInstance(builder);

                        bo.Load();
                        bos.Add(bo);
                    }
                }

                foreach (var bo in bos)
                {
                    bo.Run();
                }

                var asms = AppDomain.CurrentDomain.GetAssemblies();
                foreach (var asm in asms)
                {
                    var exports =
                        asm.GetExportedTypes().Where(type => type.Name == "SharedClass")
                        .ToList();

                    foreach (var export in exports)
                    {
                        Console.WriteLine(export.FullName);
                    }
                }
            }
        }
    }

Output:

Lib1 Key
Lib2 Key
Lib1 Key
Lib2 Key
Shared.SharedClass
Shared.SharedClass

How does it know the difference!?

Upvotes: 0

Views: 446

Answers (2)

Damien_The_Unbeliever
Damien_The_Unbeliever

Reputation: 239636

There is nothing preventing two assemblies from declaring types with identical fully-qualified names. Whether those types are similar or completely different (or here, are actually defined in the same source file) is irrelevant.

Although the page discussing extern alias uses "two versions of the same assembly" as it's motivating example, it's describing the general mechanism that would allow any consuming application to consume two (or more) libraries that declare types with identical fully-qualified type names.

You might have to reference two versions of assemblies that have the same fully-qualified type names. For example, you might have to use two or more versions of an assembly in the same application. By using an external assembly alias, the namespaces from each assembly can be wrapped inside root-level namespaces named by the alias, which enables them to be used in the same file.

And further, what this comes down to is that a fully-qualified type name, by itself, does not uniquely identify a specific type. A type's identity includes not just its name but also its assembly.

Upvotes: 2

Bernhard Hiller
Bernhard Hiller

Reputation: 2397

You did not share compiled code, but the file SharedClass.cs file. So the libraries don't know about each other's SharedClass, and hence there is nothing which should "know the difference". At compile time, each plugin gets linked to the SharedClass contained in the same assembly, and at runtime there are two SharedClasses which do not know anything about each other.

Upvotes: 0

Related Questions