【问题标题】:Dealing with optional dependencies (C#)处理可选依赖项 (C#)
【发布时间】:2010-11-28 03:55:01
【问题描述】:

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

我该怎么办?

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

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

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

1 对我来说似乎很冒险,另一方面 2b 似乎有些过头了——我基本上是在构建一个插件系统。

肯定有更简单的方法。

【问题讨论】:

  • “TFS”在这种情况下是“Team Foundation System”,对吧?
  • 是的,但我认为这个问题不是关于 TFS 而是更多关于引用某些用户计算机上可能不存在的程序集的最佳方式。
  • D'oh,我没有仔细阅读第一句话,所以我没有意识到“可选”的意思是“可能不在用户的机器上”。

标签: c# dependencies


【解决方案1】:

“插件”概念可能是要走的路,如果需要,它还可以让您(以后)扩展您的应用程序以与 TFS 以外的其他产品一起使用。选项 2a 与选项 1 一样“有风险”(链接文件丢失时失败)。

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

事实上,如果你让这些类型继承自 MarshalByRef,你甚至可以将它们加载到另一个 AppDomain 中,从而彻底分离你的插件,并使它们可卸载。

【讨论】:

  • 我其实是在尝试实现一个插件系统。但是,有时我想使用在另一个程序集中引入的类型。使用这种类型的代码将被一个“if”语句保护,该语句将保证它仅在库可用和加载时使用。有没有办法在 C# 中做到这一点?
  • @SergiyByelozyorov,是的,这可以做到,但只能通过后期绑定(例如反射或动态)。如果该类型可以实现一些始终可用的接口,则使用起来会更方便。
【解决方案2】:

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

制作一个抽象您对 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 构造函数);您的其余代码可以调用 TfsFactory.instance 的 ITfs 方法,而无需知道是否安装了 TFS。


在下面回答最近的cmets:

根据我的测试(我不知道这是否是“定义的行为”),当您(一旦)调用依赖于缺少程序集的方法时,就会引发异常。因此,将依赖于缺失程序集的代码封装在程序集中的至少一个单独的方法(或单独的类)中非常重要。

例如,如果缺少 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>

【讨论】:

  • 但是你说如果 Tfs 类的实现存在于同一个程序集中就可以了吗?
  • 使用传统的 Windows API,如果您链​​接到最终用户计算机上不存在的 DLL,那么您的可执行文件将根本不会加载:因此,您需要使用显式加载库。我不确定我是否已经对此进行了测试,但我认为 dotNet 对最终用户机器上不存在的程序集的引用不再是真的……因此在您的计算机中包含这些引用是安全的程序集(只有当您尝试调用不存在的程序集时才会出现运行时异常)。
  • 这将使 TfsFactory 依赖于包含 Tfs 类的程序集。由于在应用程序中使用了 TfsFactory,它仍然需要使用“可选”Tfs dll 分发软件。 :-)
  • @SergiyByelozyorov 我相信(如果我错了,请纠正我)你需要 DLL 才能构建,但你可以在没有它的情况下发布/部署......如果不是存在于目标机器上,程序集仍将加载,并且所有内容(TfsFactory 方法除外)都将正常运行。
  • @SergiyByelozyorov 将依赖于 Talk 的代码移动到单独的方法中,例如像这样: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() + "!"); } }
【解决方案3】:

你可以看看Managed Extensibility Framework (MEF)。

【讨论】:

  • 这看起来确实很有趣,尽管我目前正在做的事情可能有点过头了!不过我以后可能会看看。
猜你喜欢
  • 1970-01-01
  • 2018-04-21
  • 1970-01-01
  • 1970-01-01
  • 2018-06-19
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多