Interfaces and Late Binding in C#

2019-07-27 01:19发布

问题:

I've done this before in C# for a plugin system which works fine, which is why I'm puzzled as to why this new, separate plugin system is not working the way I would expect.

I have my plugin assembly - let's call it "plugin.dll" and I have my main assembly - let's call it App.exe.

I have an interface called IMyObject and its implementation, MyObject and both are defined in plugin.dll. I've copied the exact code file for IMyObject (which the plugin developer has provided to me) into my main App.exe assembly.

I get an InvalidCastException when I try to cast the object I load using reflection to the interface that the object implements. I know the object implements it because

t.GetInterface(typeof(IMyObject).FullName) != null

is true. I can also browse MyObject in the object explorer in Visual Studio and I can see that it implements IMyObject.

Here's where it goes wrong:

 if (ifaceType != null)
 {
    ConstructorInfo constructor = ifaceType.GetConstructor(new Type[] { });

    if (constructor != null)
    {

       object obj = constructor.Invoke(null); // this works - obj is assigned an 
                                              // instance of MyObject

       IMyObject myObj = (IMyObject)obj;      // triggers InvalidCastException
    }               
 }

The only difference that I can see between what I'm doing now and the way I implemented this before is that the interface is defined in two separate assemblies (even though the code files are identical and they belong to the same namespace).

Is this the reason for my trouble? If so, how can I connect to my plugin without linking at compile time, and use an interface that is defined in the plugin itself?

I should add that aside from the interface, I do not have access to the source code for the plugin.

回答1:

Even though those interfaces have the same code, they are two separate interfaces in two different assemblies (you could print their AssemblyQualifiedNames and see the difference).



回答2:

Yes, the interfaces are considered different types by the runtime. They have the same name that is all.

There is no way to use the interface in the host program without reflection because you can't statically link against it. Typically, one puts interfaces in the plugin host assembly and plugins implement them, not the other way around.



回答3:

Typical skeleton of extensible application in .NET consists of these assemblies:

  • contracts assembly. This one contains contracts (just interfaces and, optionally, some data contracts, used in interfaces) for plugins, and contracts for host;
  • host assembly. This one contains host contracts implementations and depends from the first one;
  • several plugin assemblies. These assemblies contain plugin contracts implementations and depend from the first one too.

Thereby, using contracts assembly, you're achieving, that host and plugins are independent from each other (so, plugins are late-bound), but they have common contracts, known at compile-time for both sides (plugin and host).

The way you're going (define plugin contact multiple times) is a wrong way.



回答4:

As others have mentioned these are separate interfaces and the correct way to handle this is to put the definition into a third Assembly that both reference.

BUT ... there is a way to get this to work by using a library called Impromptu Interface.