W
WTH
I am now aware (I am primarily a C++ developer) that in C# if you reference
the same interface from the same file in two different projects the types
are actually incompatible.
I found this out because I have written a generic plugin system for my
current and future C# needs. I defined a base plugin system interface named
IPlugin (original, I know...) which contains some basic plugin infomration
all plugins of this system must expose (name, friendly name, author,
version, GUID, et cetera) and this interface is defined in the plugin
system's C# file and in its namespace (Common.Utilities.PluginSystem.) Then
I have an application, let's call it host.exe which references the plugin
system's namespace and (of course) the 'host' project references the plugin
system's file. The other project is called 'MyPlugin' and it also
references the plugin system's namespace and its project also references the
plugin system's file.
When you compile and build both projects (we haven't even defined an
interface which inherits from IPlugin yet, lol) and run the host.exe
application, the host creates an instance of the plugin manager and tells it
to load the dll produced by the MyPlugin project. No problems. The plugin
manager loads the assembly, checks to see if there are any types of the
interface type requested (host asks the plugin manager to find the interface
type 'IPlugin') and it does. It then makes sure there's a class type which
implements the same interface, and it finds that as well. So far,
everything looks good. The plugin system has found a class that implements
the 'correct' interface in the newly loaded assembly, so it creates an
instances of that type using the activator to create an instance of the type
found in the assembly which matched the criterial we specified ('a class
implementing an interface called IPlugin'.)
Here's where the problem beings. The activator succeeds and returns a type
implementing 'IPlugin' but the returned object handle cannot be case to
IPlugin in host.exe because it throws an exception stating the this is an
invalid cast. Looking at the returned object handle during debugging shows
everything I would expect, and object that implements IPlugin as I had
hoped.
After looking into this a bit I've discovered that the runtime does more
than it appears to when validating types. The assembly name is taken into
account. This means that the runtime considers the interface IPlugin, which
is of course defined in both assemblies so that each assembly can use it, to
be different in each assembly.
Apparently the 'solution' (it really seems like a hack honestly) is to put
IPlugin (and presumably the plugin manager as well while we're at it) in its
own assembly and ensure that the product/tool/whatever remembers to include
it during deployment, and ensure that every developer that wishes to write a
plugin has a copy of that assembly as well.
That's bad enough, but because this plugin system should be generic, a
developer would want to be able to define a new interface in their
tool/product/whatever that derived from IPlugin, for instance
ISuperCoolProductPlugin, and then allow some other developers (potentially
3rd party developers) to be able to create plugins of that type.
Sadly, this means YET ANOTHER assembly must be created, versioned,
supported, and deployed with the first developer's application and given to
the 3rd party to use.
Developer #1 can't put the new interface in the plugin manager's assembly
(which holds IPlugin) because it isn't his to modify, ergo, you have at
least 3 binaries to distribute to run SuperCoolProduct.exe one of which only
holds the interface ISuperCoolProductPlugin.
That seems ridiculous and very short sighted. Given the enormous
capabilities of reflection and the runtime, how come it can't map the
interfaces properly given that they have THE SAME NAMESPACE... lol. You
would think they would attribute this (or have they already?)
I'm relatively new to C# so I'm wondering if my approach to plugins is
outdated (pre 2.0) or I've missed something like generics and interface
covariance...?
This issue with types in assemblies (especially interfaces) always being
incompatible seems to be a very big issue in regards to code re-usability.
Anytime two assemblies plan to work together and reference the 'same type'
they can't.
Am I using the wrong approach (vanilla interfaces)?
WTH
P.S. BTW, please don't see this as my disrespecting C# because I'm not, it's
fantastic, but so is C++ and that hairy mother has some serious warts - C#
has a few bumps as well, lol. I really just want to write a generic,
re-usable plugin system that has as few deployment dependencies as possible.
the same interface from the same file in two different projects the types
are actually incompatible.
I found this out because I have written a generic plugin system for my
current and future C# needs. I defined a base plugin system interface named
IPlugin (original, I know...) which contains some basic plugin infomration
all plugins of this system must expose (name, friendly name, author,
version, GUID, et cetera) and this interface is defined in the plugin
system's C# file and in its namespace (Common.Utilities.PluginSystem.) Then
I have an application, let's call it host.exe which references the plugin
system's namespace and (of course) the 'host' project references the plugin
system's file. The other project is called 'MyPlugin' and it also
references the plugin system's namespace and its project also references the
plugin system's file.
When you compile and build both projects (we haven't even defined an
interface which inherits from IPlugin yet, lol) and run the host.exe
application, the host creates an instance of the plugin manager and tells it
to load the dll produced by the MyPlugin project. No problems. The plugin
manager loads the assembly, checks to see if there are any types of the
interface type requested (host asks the plugin manager to find the interface
type 'IPlugin') and it does. It then makes sure there's a class type which
implements the same interface, and it finds that as well. So far,
everything looks good. The plugin system has found a class that implements
the 'correct' interface in the newly loaded assembly, so it creates an
instances of that type using the activator to create an instance of the type
found in the assembly which matched the criterial we specified ('a class
implementing an interface called IPlugin'.)
Here's where the problem beings. The activator succeeds and returns a type
implementing 'IPlugin' but the returned object handle cannot be case to
IPlugin in host.exe because it throws an exception stating the this is an
invalid cast. Looking at the returned object handle during debugging shows
everything I would expect, and object that implements IPlugin as I had
hoped.
After looking into this a bit I've discovered that the runtime does more
than it appears to when validating types. The assembly name is taken into
account. This means that the runtime considers the interface IPlugin, which
is of course defined in both assemblies so that each assembly can use it, to
be different in each assembly.
Apparently the 'solution' (it really seems like a hack honestly) is to put
IPlugin (and presumably the plugin manager as well while we're at it) in its
own assembly and ensure that the product/tool/whatever remembers to include
it during deployment, and ensure that every developer that wishes to write a
plugin has a copy of that assembly as well.
That's bad enough, but because this plugin system should be generic, a
developer would want to be able to define a new interface in their
tool/product/whatever that derived from IPlugin, for instance
ISuperCoolProductPlugin, and then allow some other developers (potentially
3rd party developers) to be able to create plugins of that type.
Sadly, this means YET ANOTHER assembly must be created, versioned,
supported, and deployed with the first developer's application and given to
the 3rd party to use.
Developer #1 can't put the new interface in the plugin manager's assembly
(which holds IPlugin) because it isn't his to modify, ergo, you have at
least 3 binaries to distribute to run SuperCoolProduct.exe one of which only
holds the interface ISuperCoolProductPlugin.
That seems ridiculous and very short sighted. Given the enormous
capabilities of reflection and the runtime, how come it can't map the
interfaces properly given that they have THE SAME NAMESPACE... lol. You
would think they would attribute this (or have they already?)
I'm relatively new to C# so I'm wondering if my approach to plugins is
outdated (pre 2.0) or I've missed something like generics and interface
covariance...?
This issue with types in assemblies (especially interfaces) always being
incompatible seems to be a very big issue in regards to code re-usability.
Anytime two assemblies plan to work together and reference the 'same type'
they can't.
Am I using the wrong approach (vanilla interfaces)?
WTH
P.S. BTW, please don't see this as my disrespecting C# because I'm not, it's
fantastic, but so is C++ and that hairy mother has some serious warts - C#
has a few bumps as well, lol. I really just want to write a generic,
re-usable plugin system that has as few deployment dependencies as possible.