2

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

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

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

有一天,我们决定插件可能应该有名字,这样我们才能看到我们加载了哪些插件。因此,我们制作了包含以下内容的版本 2 Interfaces.dll

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

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

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,所以这是行不通的。另一种方法是重命名IPluginIPlugin_v1and IPlugin_v2,或对其命名空间进行类似的重命名,但这将要求每次有新版本时重新命名接口(或命名空间)Interfaces.dll,并且需要实现者更改对接口的所有引用(或命名空间)更新到最新的程序集版本。那么,问题又来了:

IPlugin是否可以在不重命名或其命名空间的情况下实现这种适配器?

4

2 回答 2

1

有一种方法可以做到这一点,但它相当冗长。在这个过程结束时,我们将最终得到一个从IPlugin[v1] 到IPlugin[v2] 的适配器类。

参考两个版本Interfaces.dll

  • 创建一个项目来放置适配器类。
  • 照常在 Visual Studio 中添加对最新版本的引用。
  • 右键单击项目并选择“卸载项目”
  • 再次右键单击项目并选择“编辑[项目名称] .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>
    
  • 保存并关闭项目文件
  • 右键单击项目并选择“重新加载项目”

参考两个版本IPlugin

Aliases元素添加到项目文件中意味着类型不再导入到全局命名空间中,而是导入到Interfaces_v1andInterfaces_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";
    }
于 2016-09-22T07:24:51.600 回答
0

你所要求的没有意义。添加PluginName到接口会破坏实现接口的所有现有代码。您不给新接口一个新名称或新命名空间的原因是您希望自动使用该新接口。然而,您不想破坏现有代码。你不能两全其美。

最简单的工作方法是

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

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

在某些时候,如果您制作了一个新版本,其中包含不再支持现有插件的重大更改,那么您可以选择合并接口。

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

于 2016-09-21T11:49:25.667 回答