代码之家  ›  专栏  ›  技术社区  ›  Daniel Pryden

我应该如何“选择性地”引用另一个.NET项目中的类库项目?

  •  1
  • Daniel Pryden  · 技术社区  · 15 年前

    好的,我有一个使用插件的.NET项目。插件实现为类库(DLL)项目,每个项目构建在各自的文件夹中。主项目不需要插件DLL来运行,但是如果插件DLL可用,它们将用于各种可选功能。插件中的类由 Type.GetType() .

    然而,出于我自己的目的,在测试软件时,我希望能够同时开发插件和主应用程序。我已经创建了一个引用所有项目的“主”解决方案文件,因此我可以设置断点并跨程序集边界执行步骤。然而,问题是插件构建到它们自己的目录中,因此我必须以某种方式获取插件DLL文件 Type.GetType() 我能找到他们。

    编辑 :为了澄清,我的代码已枚举与.exe相同目录中的DLL文件,并查找与给定接口匹配的类(使用 Assembly.LoadFile() , Assembly.GetExportedTypes() Type.IsAssignableFrom() )。然后用户选择要启用的插件,我使用 Type.AssemblyQualifiedName . 最后,当我需要使用插件提供的功能时,我使用 Type.GetType() 加载插件类。所有这些都完全符合预期。只是当我构建应用程序时,我得到了一个没有任何插件的.exe副本。我正在寻找一种方法来构造项目和解决方案文件,这样我就可以将主应用程序和插件项目分开,同时仍然能够在VisualStudio中一起调试它们。这有意义吗?

    到目前为止,我的想法如下:

    • 从主项目添加对插件项目的引用 .
      这样做的好处是,它在我的调试环境中始终可以按预期工作,但当我的应用程序在没有插件的情况下部署时,这会导致问题吗?我使用Dependency Walker进行了检查,似乎没有任何由项目引用创建的直接DLL依赖关系。这里有什么隐藏的问题需要了解吗?

    • .
      这似乎工作得很好,但它也会弄乱我的构建树,并且会使只开发一个项目而不签出其他项目变得更加困难(现在它们都是Subversion中的兄弟文件夹)。

    • 添加生成后脚本以将插件复制到另一个目录 .

    • 想办法 在其他目录中搜索DLL
      这与我使用的方法相似 LD_LIBRARY_PATH

    有趣的是,在 this tutorial 关于这个问题,它说:

    这对我来说似乎不太理想。有更好的办法吗?

    4 回复  |  直到 15 年前
        1
  •  1
  •   Mike J    15 年前

    对于一个插件模型,我会看看 Activator.CreateInstance Activator.CreateInstanceFrom 方法,甚至可以看一看 Assembly.LoadFrom . 这些方法允许您从“传统”输出目录以外的目录中的程序集加载类型( bin, debug, release ).

    强迫 Type.GetType 定位“插件”程序集实际上取决于您如何修改加载程序搜索程序集的策略。例如,您可以设置 AppDomain.PrivateBinPath 属性更改加载程序将查找的子目录 参考

    关于第一点“添加对插件项目的引用”。好吧,这将使一个处于一个困难的境地,阻止将来的可扩展性。如果…怎么办 想要制作一个新的插件吗?哎哟,现在你没有引用它,所以我想我的插件不会工作了?这就是为什么需要另一种方法。我们将“探测”插件程序集。

    笔记
    在一个插件模型中,所有插件都应该针对一个公共接口(或抽象类)工作,这是非常有用的 您的实现正在您加载的任何新插件中寻找。例如:

    private void LoadAllPlugins(string path)
    {
        foreach(string file in Directory.GetFiles(path, "*.dll"))
        {
            Assembly a = Assembly.LoadFrom(file);
            foreach(Type t in a.GetTypes())
            {
                if (t.GetInterface("IMyPluginInterface", true))
                {
                   // we now have a potential plugin type that implements required functionality
                }
            }
        }
    }
    

    最后,.NET3.5支持插件模型(System.AddIn),还可以查看Microsoft发布的托管可扩展性框架,该框架允许开发人员构建简单的 组合的 http://www.codeplex.com/MEF )

        2
  •  1
  •   ShuggyCoUk    15 年前

    只需在您的解决方案中包含插件项目,但只需要从插件到“应用程序”项目的依赖关系。通过保留一个重复的解决方案(但只包含应用程序项目)并确保构建干净来实现这一点。您可能还想考虑一个生成后的步骤,它尝试单独构建每个插件项目而不需要其他的构建。

    我赞成使用 MEF 在编译时您的插件实际上是已知的,只是不知道您计划加载哪个特定版本的dll。

        3
  •  1
  •   Paul Farry    15 年前

    设置项目,以便将可执行文件编译到1个位置,并将“插件”编译到相同的位置。您可以使用交叉点使“bin”成为通往最终位置的路径。

    此外,在插件项目中,将可执行文件设置为调试主机。

    如果您还没有这样做,请确保您的接口没有在每次编译Exe时重新编译(如果它是强名称的),因为这将迫使您在每次重新编译主机Exe时都必须重新编译每个插件。

        4
  •  1
  •   Jorge Córdoba    15 年前

    plugin system 不久前使用反射。这种机制类似于drupal中使用的机制,我发现它非常灵活。

    Sistema de plugins con C#. Parte I. Conceptos Sistema de plugins con C#. Parte II. El código explicado 但我还没有时间翻译它们(所以它们是西班牙语的)。尽管如此,代码还是用英语注释的(如果我没记错的话)。

    基本上,您有通常定义的插件,实现简单接口的类。此接口描述加载、卸载、安装、卸载和配置插件的方法。

    首先,插件可以通过实现公共(已知)接口并使用“ServiceAttribute”将自身标记为服务,从而向其他类提供服务。插件系统将注册该服务,并将其公开给任何有兴趣使用它的人。例如:

    ICrypt crypto = Plugins.Service["Crypt"] as ICrypt;
    if (crypto != null)
      crypto.Crypt(data, key, etc)
    

    另一方面,你有钩子。钩子是插件事件。您有两个属性HookAttribute和HookableAttribute。

    当您将一个方法声明为可挂接时,您是在说您希望它被其他插件拦截。例如

    [Hookable]
    public event TextChanged OnTextChanged;
    //.....
    if (OnTextChanged != null)
      OnTextChanged(ref myText);
    

    在其他插件中

    [Hooks("OnTextChanged")]
    public bool MyTextChanged(ref myText)
    {
      myText = "Hello from the plugin";
      return true;
    }
    

    只要签名匹配,钩子签名(委托)就可以是未知的。同样,插件系统会处理匹配项并将其放到一起。

    顺便说一下,我使用了一个插件子文件夹来放置插件。只要插件项目是我正在开发的解决方案的一部分,我就可以从VS调试它们。我不需要引用它们或其他任何东西,如果插件是解决方案的一部分,那么我就能够调试它。