可以同时使用两个同名的类型吗?
本文关键字:两个 类型 | 更新日期: 2023-09-27 18:08:17
假设我有一个强命名程序集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。有没有实现这样一个适配器的方法?我希望它看起来像这样:
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_v1
和IPlugin_v2
,或者对其名称空间进行类似的重命名,但这将要求每次出现新版本的Interfaces.dll
时都重命名接口(或名称空间),并且要求实现者在更新到最新的程序集版本时更改对接口(或名称空间)的所有引用。那么,问题又来了:
是否有可能实现这种类型的适配器,而不重命名IPlugin
或其名称空间?
有一种方法可以做到这一点,但它相当冗长。在此过程结束时,我们将结束从IPlugin
[v1]到IPlugin
[v2]的适配器类。
参考Interfaces.dll
的两个版本
- 创建一个项目来放置适配器类。
- 在Visual Studio中正常添加对最新版本的引用。
- 右键单击项目,选择"卸载项目" 再次右键单击项目,选择"Edit [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>
- 保存并关闭项目文件
- 右键单击项目,选择"Reload project"
参考IPlugin
的两个版本
将Aliases
元素添加到项目文件中意味着类型不再导入到全局命名空间中,而是导入到Interfaces_v1
和Interfaces_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";
}
你的要求没有意义。将PluginName
添加到接口将破坏实现该接口的所有现有代码。不给新接口指定新名称或新名称空间的原因是希望自动使用新接口。但是你不想破坏现有的代码。鱼和熊掌不可兼得。
最简单的工作方法是使用
public interface IPlugin
{
void InstallSelf(IPluginManager pluginManager);
}
public interface IPlugin2 : IPlugin
{
string PluginName { get; }
}
在某些时候,如果你发布了一个新的版本,对现有的插件进行了突破性的修改,那么你可能会选择合并接口。
当DLL的新版本准备好了新的接口定义时,它也包括所有旧的接口版本。所有现有的代码将继续编译和运行。您可以动态检测插件实现是否只实现了IPlugin
,还是也实现了IPlugin2
。