【问题标题】:Is it a bad practice to reference an exe file in C# project在 C# 项目中引用 exe 文件是一种不好的做法吗
【发布时间】:2020-01-29 02:06:11
【问题描述】:

我有一个简单的问题。

我知道我可以在我的 C# 项目中引用一个 .net 可执行文件。

我不想为了调用一些 dll 而使用“输出类型:Windows 应用程序”制作不必要的项目。

我只是想知道引用 exe 文件是否可以,还是一种不好的做法?

【问题讨论】:

  • 即使你引用了一个,你如何处理那个 exec 文件?
  • 使用“输出类型:Windows 应用程序”制作不必要的项目是什么意思?您是否将 exe 文件作为项目的一部分包含在内,并在编译项目时将其输出?
  • 我认为他的意思是,他正在引用 exe 项目类型来访问某些功能。他可以将它重构为一个单独的类库,但有必要吗?
  • 嗨。我有一个编译成 exe 文件的项目,因为我需要它是一个应用程序。我有一些类,我想在其他项目中使用,所以我想引用它。
  • @NikhilAgrawal 如果类型是公共的,则不需要反射,它可以像在类库中一样使用,否则需要反射。

标签: c# .net


【解决方案1】:

是的,这可以被视为一种不好的做法,原因如下:

  • 糟糕的项目架构
    如果您需要从 .exe 调用某些逻辑,则该逻辑被错误地放置在那里。相反,您应该将它放在一个单独的 dll 中,并从您当前引用的可执行文件和引用该可执行文件的应用程序中引用相同的 .dll。 正如comments below 中所建议的,将逻辑提取到库中可以帮助您避免一些 CPU 架构限制,我将在下一点中对此进行说明,因为库可以针对任何 CPU 构建。

  • 架构限制
    referenced 可执行文件可能已构建为最适合 32 位或 64 位机器,甚至特定 CPU(如 Itanium)。可以在没有这些规范的情况下构建库1,以便跨 CPU 兼容,以便以后任何项目都可以引用。如果您引用具有特定体系结构设置的可执行文件,则应使用与引用项目兼容的设置。我认为这是一个限制,因为您无法将最终产品分发到某些平台。

  • 让单元测试变得困难
    正如 Abel in the comments 所暗示的那样,您的单元测试将进入它们自己的 DLL,并且它们也需要引用可执行文件。如果您不使用 InternalsVisibleTo 属性公开一些内部方法/字段,或者使用反射(这是较慢的替代方法)来检查和断言对象的某些非公开可见状态,则可能很难对其进行测试。可执行文件可能不是使用 InternalsVisibleTo 属性集构建的,如果您回退到反射,您可能会遇到阻止您反射可执行文件成员的 .NET 安全问题(因为测试套件是在更严格的设置中执行的,例如实例)。

    您还会遇到上述架构限制,这将导致您的单元测试使用相同的架构。如果您的测试套件作为自动构建的一部分在远程机器上执行(例如在 TravisCI、Bamboo、TeamCity 等中),则可能会出现问题。然后 CI 代理必须遵守可执行文件和测试套件的 CPU 架构。如果没有合适的代理,则无法运行测试。此外,如果您使用公共 CI 平台来构建您的应用程序并执行测试,则这可以算作法律意义上的可执行文件的分发。您很可能会违反可执行文件的许可 - 请参阅下一节了解更多详细信息。

  • 潜在的许可问题
    您应该小心地分发您的应用程序。如果引用的可执行文件需要额外的许可或费用才能使用,您必须强制用户在您的应用程序旁边接受该可执行文件的许可(并在需要时付费),否则您有进行非法分发的风险它与您的软件。这也意味着您有权首先引用可执行文件。

  • 未知后果
    可执行文件将被复制到 bin 文件夹中,并与您的应用程序一起安装。如果有人浏览 bin 文件夹并执行它,不知道会发生什么。这样做有几个问题:

    • 可执行文件因输入不当而崩溃或行为异常。如果它没有任何 GUI,通常会发生这种情况(例如,如果用户双击命令行程序,它不会以命令行参数的形式获得任何输入,因此会崩溃或行为不端)。

    • 该可执行文件不打算由您的程序所有者使用,因为这会在法律上或逻辑上与您的软件的功能相矛盾。

然而,在某些情况下引用可执行文件是合理的,但这种情况非常罕见:

  • 可执行文件来自第三方,不存在具有相同功能的库,也没有其他方法可以链接到该功能。这也可能是您的雇主或客户对您的项目的明确要求。
  • 可执行文件用另一种语言编写,您需要通过互操作与它进行通信。

只要后者不适用于您,特别是如果您开发自己引用的可执行文件,我肯定会建议将所需的逻辑提取到单独的库中。


1 事实上,正如Dominic Kexel's comment 所述,您还可以构建一个针对任何CPU 的可执行文件。反之亦然 - 为特定 CPU 构建库,但不太常见,因为可执行文件通常是为硬件量身定制的。 因此,为了澄清我的观点,我想到的是引用第 3 方可执行文件,或者由于其他原因无法重建的可执行文件,并且该可执行文件已经针对某些特定架构进行了优化。如果您可以重建并更改可执行文件的目标 CPU,那么您绝对可以将所需的逻辑提取到 dll 中。

【讨论】:

  • 可执行文件可能被构建为最适合 32 位或 64 位架构。没有这些规范也可以构建库 您也可以使用AnyCPU 构建可执行文件。如果你的意思是别的,你能否详细说明如何构建一个可执行文件以优化 32 位或 64 位架构?
  • @DominicKexel,是的。但是,这是如果您可以控制 所引用的可执行文件的构建方式。如果是这样,您还可以将逻辑提取到 dll 中,并完全避免引用可执行文件。我将编辑我的帖子以澄清这个问题,因为它现在可能看起来模棱两可。
  • 您应该在 架构限制 部分添加另一个示例:也许您的可执行文件 必须是 32 位或 64 位,因为该可执行文件使用本机库,并且您不希望您的第二个可执行文件是特定于平台的(您希望它AnyCpu),那么您必须从您的第一个可执行文件中提取相关代码到一个单独的库中。跨度>
  • @DominicKexel,是的,这也是有效的观点。我已经编辑过,但将其添加到我的第一点。
  • 很好的答案,但我认为您错过了几乎每个项目都会遇到的一个非常重要的用例:单元测试。通常,单元测试进入它们自己的 DLL。为可执行文件创建单元测试的自然方法是链接到该可执行文件并在单元测试 DLL 中为(public/protected/internal-with-internalsVisibleTo)方法、属性等编写测试。
【解决方案2】:

如果您在项目中包含一个可执行文件作为资源,那么我想如果它解决了您的问题并且可以正常工作,这没什么大不了的(尽管从理论上讲,将通用逻辑提取到单独的 @ 中似乎更正确987654321@ 可用于多个项目)。

但是:您可能希望将 .exe 包含为 嵌入式资源,以便它在输出中不直接可见构建项目时的目录:

右击项目节点,选择Add > Existing Item,找到.exe文件。现在在解决方案资源管理器中右键单击它,选择properties 并将Build Action 设置为Embedded Resource

该文件将被“烘焙”到您自己的 .dll 或 .exe 或您正在构建的任何文件中,而不是简单地复制到您的输出目录中。

【讨论】:

  • +1。我喜欢嵌入式资源的想法。这确实可以解决部署和不需要的用户操作(如果用户运行该可执行文件)的许多潜在问题。代价将是一些用于获取可执行文件逻辑的样板代码——而不是像使用 dll 那样进行引用,因此需要一些反射内容,以及用于提取 exe 的临时目录空间。
  • @IvayloSlavov - 你有这方面的工作例子吗?
  • @FrenkyB,事实上,与可执行文件无关。我尝试将其他 dll(程序集)加载到另一个程序集中,但目前我无法使用该代码。如果你有兴趣,我可以为你做一个要点。
【解决方案3】:

.NET DLL 或 EXE,两者都是程序集,您可以通过引用它们来使用 exe 或 dll。 将 exe 与您的代码一起发送没有问题,除非您不想单独执行此 exe。

【讨论】:

  • 是否可以从另一个项目(引用exe)调用这个exe中的方法?
  • @FrenkyB 可以从另一个项目调用 exe 中的方法(我只是尝试从生成 DLL 的 xUnit 项目调用 EXE 中的静态方法)。但是,您必须小心,因为如果假设 Main() 方法已执行,某些代码可能会失败(如果您引用 EXE 并调用任何您想要的公共方法,则不会)。
【解决方案4】:

我对这个问题的回答远非意见。
能够将 .NET 可执行程序集作为库引用是 .NET 和 CLI 设计的特点之一。此功能使您不必创建一个单独的项目,然后在那里移动和重新处理您的逻辑。
如果您担心项目在交付时的外观,那么ILMerge 是您的朋友。此外,如果您引用的是严格许可的第 3 方库,那么您可以告诉 ILMerge 在合并过程中跳过它。
但是正如@Ivaylo Slavov 指出的那样,如果您的项目依赖于架构(x86/x64),那么您必须走上艰难的道路。

【讨论】:

    【解决方案5】:

    没关系,如果它可以帮助您及时发货。

    从长远来看是不行的。所以可能只是给自己留个便条,以便在您有必要的时间时尽快修改它更好

    但首先,如果您现在没有足够的时间,没关系:让它工作并发货。

    【讨论】:

      猜你喜欢
      • 2015-05-31
      • 1970-01-01
      • 2012-08-04
      • 1970-01-01
      • 1970-01-01
      • 2017-12-26
      • 2016-02-09
      • 2019-01-24
      • 1970-01-01
      相关资源
      最近更新 更多