C# 如何使用MEF允许插件覆盖现有功能?
我使用MEF允许用户扩展我的C#库。到目前为止,它工作得很好,但现在我正试图以一种我以前从未见过的方式使用它 到目前为止,我看到的MEF的主要用例是:C# 如何使用MEF允许插件覆盖现有功能?,c#,mef,C#,Mef,我使用MEF允许用户扩展我的C#库。到目前为止,它工作得很好,但现在我正试图以一种我以前从未见过的方式使用它 到目前为止,我看到的MEF的主要用例是: 应用程序公开基本接口(IPerson) 外部库使用MEF和基本接口来扩展主库的功能(例如,IPoliceman:IPerson,添加功能) 应用程序然后使用ImportMany搜索正确的IPerson,具体取决于它必须执行的操作 但我需要这样的东西:假设我有一个税收计算器,它接受一系列参数,并根据这些参数返回估计的税收。我希望用户能够使用ME
- 应用程序公开基本接口(
)IPerson
- 外部库使用MEF和基本接口来扩展主库的功能(例如,
,添加功能)IPoliceman:IPerson
- 应用程序然后使用
搜索正确的ImportMany
,具体取决于它必须执行的操作IPerson
所以基本上,我的问题归结为:通常MEF允许添加类和方法的实现。我如何使用它来允许用户替换一个实现?您所说的实际上只是以不同的方式看待同一个问题。答案比听起来要简单——对于您希望客户端能够覆盖的任何行为,只需将该行为放入插件中即可
没有任何东西说你不能仅仅因为你是应用程序的作者就写插件。将TaxCalculator类放入插件中,并公开一个允许用户编写自己的税务计算器的接口。在运行时,如果加载了多个,请选择不属于您的。开箱即用,您将使用您的税务计算器插件,因此它将完全按照您期望的方式工作。如果用户创建了自己的税务计算器插件并将其放到正确的目录中,您就可以使用它,从而有效地让他们“覆盖”您的原始功能。我不确定这会有多大意义,但让我试试 我将创建一个
TaxCalculatorManager
类。该类可以从MEF加载所有ITaxCalculator
实现。从那里,您可以在Export
属性中获得一些内容,以允许对实现进行排序。然后,当您需要计算税款时,您将调用TaxCalculatorManager.calculate
,它将对ITaxCalculator
实现进行排序,并调用calculate
对获胜者进行排序
如果您需要我澄清任何要点,请告诉我。通常,当您尝试覆盖应用程序中已存在的导出时,您将获得
[Import(typeof(IFoo)]
的基数异常,因为MEF希望正好有一个匹配的导出可用
但是,您可以将插件放在单独的导出提供程序中,并赋予其优先级。我在这里为应用程序文件夹中的“插件”子文件夹执行此操作:
Assembly executingAssembly = Assembly.GetExecutingAssembly();
string exeLocation = Path.GetDirectoryName(executingAssembly.Location);
string pluginPath = Path.Combine(exeLocation, "plugins");
var pluginCatalog = new DirectoryCatalog(pluginPath);
var pluginExportProvider = new CatalogExportProvider(pluginCatalog);
var appCatalog = new DirectoryCatalog(exeLocation,"*");
var appExportProvider = new CatalogExportProvider(appCatalog);
var container = new CompositionContainer(
pluginExportProvider, appExportProvider);
pluginExportProvider.SourceProvider = container;
appExportProvider.SourceProvider = container;
传递到合成容器的导出提供程序的顺序决定了优先级:如果导出同时由插件和应用程序部件提供,则插件将获得优先级。这似乎非常简单。只需允许加载一个插件,如果没有可用插件,则使用默认实现。然后,您可以使用用户在您的配置中选择。这很有意义,谢谢!如果加载两个插件试图覆盖同一个实现,会发生什么情况。我应该让每个插件覆盖彼此的更改,还是应该引发异常。如果我确实引发异常,我如何首先检测冲突?您希望如何处理它在您的业务案例中添加例外。有时例外是个好主意,有时您只想选择一个例外。加载程序集时可以检测到冲突。如果有多个程序集导出您的ITaxCollector接口(即使在从列表中删除您的程序集后),您知道存在冲突。好的,谢谢。我必须考虑一下异常与选择异常之间的关系。谢谢您的帮助!我想您是说使用
[ImportMany]
无处不在,以及优先选择非默认部分的代码。或者,您建议使用可选的导入和AllowDefault=true
,如果导入为null
,则返回默认值。无论哪种方式,这对我来说都有点麻烦。您可以使用[import]
并在容器设置中构建插件的首选项,如我自己的回答所示。正如Fuji所指出的,如果存在多个实现,您可以允许用户选择获胜者。感谢您的回答。我喜欢按重要性对实现进行排序的想法……我会研究它。