你能同时使用两个同名的类型吗?

时间:2016-09-21 11:37:54

标签: c# api-versioning

假设我有一个强名称程序集Interfaces.dll的版本1,其中包含IPlugin接口:

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

已经实施了许多插件,一切运行顺利。

有一天,决定插件可能应该有名字,以便我们可以看到我们加载了哪些插件。因此,我们生成Interfaces.dll的版本2,其中包含:

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

因为我不能指望所有插件实现者立即更新他们的插件,所以我想创建一个适配器,为旧插件版本添加一个PluginName为“Unknown”。有没有办法实现这样的适配器?我希望它看起来像这样:

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);
    }
}

但是因为它们都被称为IPlugin,所以这不起作用。另一种方法是将IPlugin重命名为IPlugin_v1IPlugin_v2,或者对其命名空间进行类似的重命名,但这需要在每次重命名时重命名接口(或命名空间)新版本的Interfaces.dll,并且要求实现者在更新到最新的程序集版本时更改对接口(或命名空间)的所有引用。那么,问题又来了:

是否可以实现此类适配器,而无需重命名IPlugin或其命名空间?

2 个答案:

答案 0 :(得分:1)

有一种方法可以做到这一点,但它已经相当长时间了。在此过程结束时,我们将从IPlugin [v1]到IPlugin [v2]结束适配器类。

引用Interfaces.dll

的两个版本
  • 创建一个放置适配器类的项目。
  • 正常在Visual Studio中添加对最新版本的引用。
  • 右键单击该项目,然后选择"卸载项目"
  • 再次右键单击该项目并选择"编辑 [projectname] .csproj"
  • 找到如下所示的部分:

    <Reference Include="Interfaces">
      <HintPath>path\to\library\v2\Interfaces.dll</HintPath>
    </Reference>
    

    并将其替换为:

    <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>
    
  • 保存并关闭项目文件
  • 右键单击该项目,然后选择&#34; Reload Project&#34;

引用IPlugin

的两个版本

Aliases元素添加到项目文件中意味着不再将类型导入到全局命名空间中,而是将其导入Interfaces_v1Interfaces_v2命名空间。这意味着我们可以通过不同的名称访问两个IPlugin版本:

  • 在适配器源文件的顶部(在任何using语句之前),添加以下内容:

    extern alias Interfaces_v1;
    extern alias Interfaces_v2;
    
  • 可选地,为了稍微加密代码,也添加一些类型别名:

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

答案 1 :(得分:0)

你所要求的是没有意义的。将PluginName添加到接口将破坏实现该接口的所有现有代码。您没有为新接口提供新名称或新名称空间的原因是您希望自动使用该新接口。但是你不想破坏现有的代码。你无法双管齐下。

最简单的工作方法是

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

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

在某些时候,如果您使用不再支持现有插件的重大更改创建新版本,则可以选择合并接口。

当新版本的DLL准备好使用新的接口定义时,它还包括所有旧版本的接口。所有现有代码将继续编译和运行。您可以动态检测插件实现是仅实现IPlugin还是IPlugin2