简体   繁体   中英

c# - Different types when loading from assembly using reflection

I have a solution with 3 projects: 1) A GUI executable 2) A Class library with an containing a public API and a public interface 3) A Class library of a class that implements the above interface

I'm trying to implement a resource loader in the API, so that when the GUI calls method API.Foo() I go over every assembly inside a specific folder (found at: .\\resources) which contains a copy of the assemblies I compiled (#3). Then I want to add the resource to a list and use this list to call a function that is a part of the interface (which every resource implements)

So what I've done is:

private List<IResource> m_resources;

public void Foo()
{
    string resourceDir = Directory.GetCurrentDirectory() + @"\Resources";
    m_resources= new List<IResource>();
    foreach (var dllFile in  Directory.EnumerateFiles(resourceDir))
    {
        IResource dllInstance;
        if (TryLoadingDLL(Path.Combine(resourceDir, dllFile), out dllInstance))
        {
            resources.Add(dllInstance);
        }
    }
}

private static bool TryLoadingDLL(string dllFullPath, out  IResource instanceOfDll)
{
    instanceOfDll = null;
    try
    {
        Assembly assembly = Assembly.LoadFrom(dllFullPath);
        Assembly IResourceAssambly = Assembly.LoadFrom(@"C:\MyProject\MyAPI.dll");
        Type[] types = assembly.GetTypes();
        foreach (Type type in types)
        {
            var interfaces = type.GetInterfaces();
            var typeOfIResource = IResourceAssambly.GetType("MyProject.IResource");
            if (interfaces.Any())
            {
                var interfaceType = interfaces[0]; //In debuger they have the same GUID
                if (interfaceType.IsEquivalentTo(typeOfIResource)) //also tried ==
                {
                    instanceOfDll = Activator.CreateInstance(type) as IResource;
                    return true;
                }
            }
        }
    }
    catch (Exception e)
    {
        Console.Error.WriteLine("Failed to load dll {0}, exception: {1}",dllFullPath, e.Message);
        return false;
    }
    return false;
}

Iv'e actually used this at first which gave the same result:

List<Type> derivedTypesList = typses.Where(type => type.GetInterfaces().Contains(IWNAssambly.GetType("MyProject.IResource"))).ToList();
if (derivedTypesList.Count > 0)
{
    instanceOfDll = (IResource)Activator.CreateInstance(derivedTypesList[0]);
    return true;
}

but then I broke it down so I can debug it easily.

When I run any of these snippets, I Indeed find 1 type that implements the interface, but when I try to cast it I get null via the as operator and an exception when casting with (IResource) . The exception is:

{System.InvalidCastException: Unable to cast object of type 'MyProject.MyFirstResource' to type 'MyProject.IResource'.

at ...

The problem looked like it was coming from the types so I tried replacing

var typeOfIResource = IResourceAssambly.GetType("MyProject.IResource");

with

var typeOfIResource = typeof(MyProject.IResource);

And the result was that now it didn't find anything at all, ie the interfaceType.IsEquivalentTo(typeOfIResource) is always false. When I looked with the debugger on these types they looked exactly the same so I don't know what's the problem.

First, is this a good practice? I want other developers to supply me with their assemblies and if they implement the IResource interface then use reflection to create an instance and invoke the wanted method.

Second and more important at this time, what is the problem and how can I solve it?

Thanks!!!

This brings back memories; I ran into the same problem in the very first .NET program I ever wrote, which must have been fifteen years ago now.

The problem is that .NET has different "binding contexts" in which a type can be loaded, and "Load" and "LoadFrom" load into different contexts. The "same" type in two different contexts will be treated as different by the runtime, and you cannot cast between them.

This is a fairly frequently asked question on Stack Overflow; if you do a search for those terms you should find some examples of explanations and possible solutions. For example, this one:

Create object from dynamically load assembly and cast it to interface (.NET 2.0)

Also, this blog article from the early days of .NET might help explain the design

http://blogs.msdn.com/b/suzcook/archive/2003/09/19/loadfile-vs-loadfrom.aspx

Finally, the other answer is correct; if you are looking to build a plugin system, I recommend against doing it from scratch. Use MEF or MAF or some other system designed specifically to solve your problem. Loading assemblies may be the least of your worries; suppose you must live in a world where third-party plugins might be hostile? Solving that security problem is difficult, so let someone else do it for you.

Look system.component, since .NET 4 the framework MEF have been integrated. MEF

This framework permits you to flag by an attribute [Export(typeof(interface))] on every implementation of the interface regardless the dll and to load them using a catalog system. (There is one for the folder DirectoryCatalog

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM