处理可选的依赖项(C#)

我们有一个可选择与TFS集成的应用程序,但由于集成是可选的,我显然不希望所有机器都需要TFS程序集作为要求

我该怎么办?

  1. 我可以在我的主程序集中引用TFS库,并确保在使用TFS集成时仅引用TFS相关对象。
  2. 或者,更安全的选择是在一些单独的“TFSWrapper”程序集中引用TFS库:

    一个。 那么我可以直接引用该程序集(只要我小心我所说的)

    湾 我应该为我的TFSWrapper程序集公开一组接口来实现,然后在需要时使用reflection实例化这些对象。

1对我来说似乎有风险,另一方面2b似乎过于顶级 – 我基本上是在建立一个插件系统。

当然必须有一个更简单的方法。

最安全的方法(即在应用程序中不出错的最简单方法)可能如下所示。

创建一个抽象使用TFS的接口,例如:

interface ITfs { bool checkout(string filename); } 

编写一个使用TFS实现此接口的类:

 class Tfs : ITfs { public bool checkout(string filename) { ... code here which uses the TFS assembly ... } } 

编写另一个实现此接口的类,而不使用TFS:

 class NoTfs : ITfs { public bool checkout(string filename) { //TFS not installed so checking out is impossible return false; } } 

在某个地方有一个单身人士:

 static class TfsFactory { public static ITfs instance; static TfsFactory() { ... code here to set the instance either to an instance of the Tfs class or to an instance of the NoTfs class ... } } 

现在只有一个地方需要小心(即TfsFactory构造函数); 其余的代码可以在不知道是否安装了TFS的情况下调用TfsFactory.instance的ITfs方法。


回答下面的评论:

根据我的测试(我不知道这是否是’已定义的行为’),当您(一旦)调用依赖于缺少的程序集的方法时,会抛出exception。 因此,在程序集中至少在一个单独的方法(或单独的类)中封装代码 – 这取决于缺少的程序集是很重要的。

例如,如果缺少Talk程序集,则不会加载以下内容:

 using System; using OptionalLibrary; namespace TestReferences { class MainClass { public static void Main(string[] args) { if (args.Length > 0 && args[0] == "1") { Talk talk = new Talk(); Console.WriteLine(talk.sayHello() + " " + talk.sayWorld() + "!"); } else { Console.WriteLine("2 Hello World!"); } } } } 

以下将加载:

 using System; using OptionalLibrary; namespace TestReferences { class MainClass { public static void Main(string[] args) { if (args.Length > 0 && args[0] == "1") { foo(); } else { Console.WriteLine("2 Hello World!"); } } static void foo() { Talk talk = new Talk(); Console.WriteLine(talk.sayHello() + " " + talk.sayWorld() + "!"); } } } 

这些是测试结果(在Windows上使用MSVC#2010和.NET):

 C:\github\TestReferences\TestReferences\TestReferences\bin\Debug>TestReferences.exe 2 Hello World! C:\github\TestReferences\TestReferences\TestReferences\bin\Debug>TestReferences.exe 1 Unhandled Exception: System.IO.FileNotFoundException: Could not load file or assembly 'OptionalLibrary, Version=1.0.0.0, Culture=neutral, PublicKeyToken=null' or one of its dependencies. The system cannot find the file specified. at TestReferences.MainClass.foo() at TestReferences.MainClass.Main(String[] args) in C:\github\TestReferences\TestReferences\TestReferences\Program.cs: line 11 C:\github\TestReferences\TestReferences\TestReferences\bin\Debug> 

您可以查看Managed Extensibility Framework (MEF)。

“插件”概念可能是最佳选择,如果需要,它还可以允许您(稍后)扩展您的应用程序以使用除TFS之外的其他产品。 选项2a将与“风险”一样(当链接文件丢失时失败)作为选项1。

您可以根据特定目的使用所需的接口进行组装,并从您的应用程序和“TFS插件”中引用此程序集。 然后,后者提供接口的实现,并使用TFS来执行操作。 该应用程序可以动态加载程序集并创建所需插件类型的实例(通过Activator等)并将这些实例转换为您的接口。

事实上,如果你让这些类型inheritance自MarshalByRef ,你甚至可以将它们加载到另一个AppDomain中 ,从而清楚地分离你的插件,并使它们无法加载。