2

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?

Philip C
  • 1,819
  • 28
  • 50

2 Answers2

1

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].

Reference both versions of Interfaces.dll

  • Create a project in which to put the adapter class.
  • Add a reference to the latest version in Visual Studio as normal.
  • Right click on the project and select "Unload Project"
  • Right click on the project again and select "Edit [projectname].csproj"
  • 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>
    
  • Save and close the project file
  • Right click on the project and select "Reload Project"

Reference both versions of 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;     
    

Implement the adapter

    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";
    }
Philip C
  • 1,819
  • 28
  • 50
0

What you're asking for doesn't make sense. Adding PluginName to the interface would break all existing code that implements the interface. Your reason for not giving the new interface a new name or a new namespace is that you want to automatically use that new interface. Yet you don't want to break existing code. You can't have it both ways.

The simplest working approach is to have

public interface IPlugin
{
    void InstallSelf(IPluginManager pluginManager);
}

public interface IPlugin2 : IPlugin
{
    string PluginName { get; }
}

At some point, if you make a new release with breaking changes where existing plugins won't be supported any longer, you may then choose to merge the interfaces.

When a new version of your DLL is ready with a new interface definition, it includes all the older interface versions as well. All existing code would continue to compile and run. You can detect dynamically whether a plugin implementation implements only IPlugin, or also IPlugin2.

  • And this lends itself to defining a `PluginAdapter` which adapts v1 plugins to v2 plugins with the name "Unknown" – Jamiec Sep 21 '16 at 11:54
  • As @Jamiec says, my plan is to search for all implementations of both versions of `IPlugin`, then adapt the v1 plugins to behave like v2 plugins and use the v2 plugins in the main plugin system. Defining the adapter is the bit I'm struggling with. – Philip C Sep 21 '16 at 12:25
  • 1
    @PhilipC Since in my answer, the interfaces have different names, there's no problem making `PluginAdapter_v1_v2` implement `IPlugin2` and have a private field of type `IPlugin`. –  Sep 21 '16 at 12:46
  • But my question was: can you make such an adapter **without** having to rename the interface? I mentioned giving the interfaces different names in the question, and stated the reasons I'd rather not do it. – Philip C Sep 21 '16 at 12:50
  • @PhilipC And I explained in my answer why that's not going to achieve what you want anyway. –  Sep 21 '16 at 12:59
  • My reason for not renaming the interface is so that when a plugin implementor releases a new version of their plugin, they can change their dependency from v1 to v2 without having to change the code (except for adding a new `PluginName` property). The original plugin ships with `Interfaces.dll` v1 - it knows nothing about what assemblies the application is using. When the application is updated to include `Interfaces.dll` v2 (in addition to v1), the plugin continues to use the v1 it was shipped with - no breakages. The only missing link is getting the application to use the v1 plugin. – Philip C Sep 21 '16 at 13:09
  • "without having to change the code (except for adding a new PluginName property)" -- So... by changing the code. Anyway, it's clear what you want. It's also clear that that's not possible. You'll have to accept that and find some other way. If there's a better way than what I answered, go for it, but it won't be what you have in mind right now. –  Sep 21 '16 at 13:13