【问题标题】:Allow our app to call a customer-provided code that calls our code允许我们的应用调用调用我们代码的客户提供的代码
【发布时间】:2016-12-19 17:11:08
【问题描述】:

我正在寻找最简单的解决方案,以允许我们的 .Net EXE 调用由第 3 方提供的 DLL。这就是问题所在 - DLL 在我们的代码中调用对象。

.Net 中的一个优点是 EXE 可以用作其他项目中的程序集,就像它是一个 DLL。我最初的想法是让他们将我们的 EXE 导入到他们的项目中,然后在其中调用函数。

我模拟了它,它运行良好,但客户不希望它以这种方式运行,他们希望运行 我们的应用程序并让它调用 他们的代码,最好在 DLL 中提供。

现在,如果他们的代码不调用我们的代码,这将是微不足道的,但在我尝试制作一个模拟它的项目时,我总是遇到循环引用问题。我知道我可以通过运行时支持避免所有这些,但也许我错过了一个更简单的解决方案?

【问题讨论】:

  • 好吧。您可以将客户需要从他们的代码中调用的部分提取到另一个库项目(dll)中。此 dll 公开您的客户可以用来交互的接口。您的 exe 链接相同的 dll。所以你有:你的 exe 引用接口库,客户代码引用接口库。例如,您的接口库可以有一个用于直接与您的应用程序交互的单例。另一方面,您可以编写一个简单的服务,从工作目录中实现接口的所有 dll 加载所有类。
  • 所以基本上是一个三角形,我们的大部分代码在一个 DLL 中,它们的代码在另一个 DLL 中,并且两者都有一个包装器?
  • 不,你不需要包装两者。这可能太长了,无法发表评论。我会尝试用答案重新解释:-)

标签: .net dll circular-reference


【解决方案1】:

您可以通过使用反射轻松克服这种情况,并避免您的应用程序和第三方开发之间的编译时引用:

  1. 定义应放置第三方程序集的位置。
  2. 在 #1 中定义的位置查找程序集并在运行时加载它们。
  3. 您可以定义一些应该实现这些第三方程序集的接口来设置程序集入口点。
  4. 找到#3中定义的接口的实现并调用入口点方法(例如:impl.Main())。
  5. 完成!整个第三方程序集已加载到您的应用程序中,它将能够与您注入到 #3 接口实现中的任何内容进行交互。

代码示例:

IService service = new ServiceImpl();

IEnumerable<IAddin> addIns = Directory.GetFiles("<path to third-party assemblies>")
         .SelectMany(file => Assembly.Load(file).GetTypes())
         .Where(type => typeof(IAddin).IsAssignableFrom(type))
         .Select(type => (IAddIn)Activator.CreateInstance(type, new object[] { serviceImpl });

foreach(IAddin addIn in addIns)
{
     addIn.Main();
}

有些插件可能如下所示:

public interface IAddIn
{
    void Main();
}

public class SomeAddIn : IAddIn
{
    public SomeAddIn(IService service)
    {
         Service = service;
    }

    private IService Service { get; }

    public void Main()
    {
        // Service will contain whatever you need to interact with the 
        // main app
    }
}

不要重新发明轮子

请注意,我为您提供了一个代码示例,让您了解该方法,尽管我不会从头开始。

我会期待:

【讨论】:

    【解决方案2】:

    将客户需要与之交互的组件提取到他们自己的程序集中。这甚至可能只是一个接口(为了简洁起见,我们称之为IPlugin,虽然我不知道你的客户是否真的写插件)。

    public interface IPlugin
    {
      void Register();
      void Execute();
    }
    

    现在您的客户在他们的 DLL 中使用一个类来实现这个接口,并将它复制到您的应用程序的目录中(在最简单的情况下)。

    您的应用程序可能会在启动时执行以下操作。

    • 在指定路径中加载程序集(使用Assemblyclass)。
    • 在实现您的接口的程序集中获取类型
    • 实例化并在需要时调用Register-方法

    在注册时,插件可以订阅您应用程序上的事件,或者您的应用程序可以在需要时调用它们的执行方法。

    这可能需要一些额外的工作甚至返工,但它易于维护且简单。 根据您想要做什么,您可以在调用接口上的方法时将其他类/接口传递给插件。例如,想象一个类Application,它实现了一个接口IApplication,该接口定义在interface-dll 中。 现在插件可以调用应用程序中的方法,存储对它的引用,并在接口定义中执行您允许它们执行的任何操作。

    我希望这比我的第一条评论更容易理解:-)

    干杯

    【讨论】:

    • 我认为这里的关键是程序集的加载。我打算尽量避免这种情况,但后来遇到了循环依赖问题。我有一些圣诞节时间来试验......
    【解决方案3】:

    我对 .Net 了解不多,但在我看来,您的循环引用是一个架构问题,而不是编码问题。我确信 .Net 中有几个方法可以实现依赖倒置或注入。

    您需要设计一个中间“类”来封装它们的功能并完全隐藏它们的内部实现。他们应该在启动时为您提供该类的实例,或者在调用您的代码时作为参数提供。这就像我确信您在 iOS/MacOS/Swift 中使用的“委托”方法。

    如果有许多使用上下文,甚至可能有几个不同的委托类。

    您应该负责设计类的方法和属性(其签名),他们应该负责实例化对象并实现其背后的代码。

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 2021-03-30
      • 2016-05-13
      • 1970-01-01
      • 2015-05-07
      • 2014-10-18
      • 1970-01-01
      • 2018-08-18
      • 1970-01-01
      相关资源
      最近更新 更多