Search code examples
c#api-versioning

Can you simultaneously use two identically named types?


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?


Solution

  • 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";
        }