Suppose I have version 1 of a strong-named assembly Interfaces.dll
, which contains an IPlugin
interface:
public interface IPlugin
{
void InstallSelf(IPluginManager pluginManager);
}
A number of plugins have been implemented and everything runs smoothly.
One day, it's decided that plugins probably ought to have names so we can see which plugins we have loaded. So we produce version 2 of Interfaces.dll
containing:
public interface IPlugin
{
string PluginName { get; }
void InstallSelf(IPluginManager pluginManager);
}
Because I can't expect all the plugin implementors to immediately update their plugins, I'd like to create an adapter which adds a PluginName of "Unknown" to old plugin versions. Is there a way to implement such an adapter? I'd like it to look like this:
public sealed class PluginAdapter_v1_v2 : IPlugin[v2]
{
private readonly IPlugin[v1] _plugin;
public PluginAdapter_v1_v2(IPlugin[v1] plugin)
{
_plugin = plugin;
}
public string PluginName => "Unknown";
public void InstallSelf(IPluginManager pluginManager)
{
_plugin.InstallSelf(pluginManager);
}
}
But because they're both called IPlugin
, this isn't going to work. An alternative would be to rename IPlugin
to IPlugin_v1
and IPlugin_v2
, or do a similar renaming of its namespace, but this would require the interface (or namespace) to be renamed every time there's a new version of Interfaces.dll
, and would require implementors to change all references to the interface (or namespace) on updating to the latest assembly version. So, the question again:
Is it possible to implement an adapter of this sort, without renaming IPlugin
or its namespace?
There is a method for doing this, but it's fairly long winded. By the end of this process, we'll end up an adapter class from IPlugin
[v1] to IPlugin
[v2].
Interfaces.dll
Find the section that looks like:
<Reference Include="Interfaces">
<HintPath>path\to\library\v2\Interfaces.dll</HintPath>
</Reference>
and replace it with:
<Reference Include="Interfaces_v1">
<HintPath>path\to\library\v1\Interfaces.dll</HintPath>
<Aliases>Interfaces_v1</Aliases>
</Reference>
<Reference Include="Interfaces_v2">
<HintPath>path\to\library\v2\Interfaces.dll</HintPath>
<Aliases>Interfaces_v2</Aliases>
</Reference>
IPlugin
Adding the Aliases
elements into the project file means that types are no longer imported into the global namespace, but are instead imported into the Interfaces_v1
and Interfaces_v2
namespaces. This means that we can access the two IPlugin
versions by different names:
At the top of the adapter source file (before any using
statements), add the following:
extern alias Interfaces_v1;
extern alias Interfaces_v2;
Optionally, to neaten up the code a bit, add some type aliases too:
using IPlugin_v1 = Interfaces_v1::Interfaces.IPlugin;
using IPlugin_v2 = Interfaces_v2::Interfaces.IPlugin;
public sealed class PluginAdapter_v1_v2 : IPlugin_v2
{
private readonly IPlugin_v1 _pluginV1;
public PluginAdapter_v1_v2(IPlugin_v1 pluginV1)
{
_pluginV1 = pluginV1;
}
public string Name => _pluginV1.Name;
public string Version => "Unknown";
}