编写 .NET 程序的时候,我们经常会在项目的输出目录下发现一大堆的文件。除了我们项目自己生成的程序集之外,还能找到这个项目所依赖的一大堆依赖程序集。有没有什么方法可以把这些依赖和我们的程序集合并到一起呢?

本文介绍四种将程序集和依赖打包合并到一起的方法,每一种方法都有其不同的原理和优缺点。我将介绍这些方法的原理并帮助你决定哪种方法最适合你想要使用的场景。


本文内容

  • 四种方法
    • 使用 .NET Core 3.0 自带的 PublishSingleFile 属性合并依赖
    • 使用 Fody
    • 使用 SourceYard 源代码包
    • 使用 ILMerge 或者 ILRepack 等工具
      • ILMerge
      • ILRepack
      • ILMerge-GUI 工具(已过时,但适合新手随便玩玩)
    • 其他方法
      • 使用 Microsoft.DotNet.ILCompiler
      • 使用 dnSpy
      • 使用 Warp
  • 各种方法的原理和使用场景比较
    • 原理
    • 适用范围
  • 开源社区

四种方法

目前我已知的将 .NET 程序集与依赖合并到一起的方法有下面四种:

  1. 使用 .NET Core 3.0 自带的 PublishSingleFile 属性合并依赖
  2. 使用 Fody
  3. 使用 SourceYard 源代码包
  4. 使用 ILMerge(微软所写)或者 ILRepack(基于 Mono.Ceil)
  5. 其他方法

如果你还知道有其他的方法,欢迎评论指出,非常感谢!

上面的第五种方法我也会做一些介绍,要么是因为无法真正完成任务或者适用场景非常有限,要么是其原理我还不理解,因此只进行简单介绍。

使用 .NET Core 3.0 自带的 PublishSingleFile 属性合并依赖

.NET Core 3.0 自 Preview 5 开始,增加了发布成单一 exe 文件的功能。

在你的项目文件中增加下面的两行可以开启此功能:

    <Project Sdk="Microsoft.NET.Sdk"><PropertyGroup><OutputType>Exe</OutputType><TargetFramework>netcoreapp3.0</TargetFramework>
++      <RuntimeIdentifier>win10-x64</RuntimeIdentifier>
++      <PublishSingleFile>true</PublishSingleFile></PropertyGroup></Project>

第一行 RuntimeIdentifier 一定需要指定,因为发布的单一文件是特定于架构的。这里,我们指定了 win10-x64,你也可以指定为其他的值。可以使用的值你可以在这篇文章中查询到:

  • .NET Core Runtime IDentifier (RID) catalog - Microsoft Docs

第二行 PublishSingleFile 即开启发布时单一文件的功能。这样,你在发布你的程序的时候可以得到一个单一的可执行程序。发布一个 .NET Core 项目的方法是在命令行中输入:

dotnet publish

当然,如果你没有更改任何你的项目文件(没有增加上面的那两行),那么你在使用发布命令的时候就需要把这两个属性再增加上。因此完整的发布命令是下面这样的:

dotnet publish -r win10-x64 /p:PublishSingleFile=true

这里的 -r 就等同于在项目中指定 RuntimeIdentifier 持续。这里的 /p 是在项目中增加一个属性,而增加的属性名是 PublishSingleFile,增加的属性值是 true

使用 .NET Core 3.0 这种自带的发布单一 exe 的方法会将你的程序的全部文件(包括所有依赖文件,包括非托管程序集,包括各种资源文件)全部打包到一个 exe 中。当运行这个 exe 的时候,会首先将所有这些文件生成到本地计算机中一个临时目录下。只有第一次运行这个 exe 的时候才会生成这个目录和其中的文件,之后的运行是不会再次生成的。

下面说一些 .NET Core 3.0 发布程序集的一点扩展——.NET Core 3.0 中对于发布程序集的三种处理方式可以放在一起使用:

  • 裁剪程序集(Assembly Trimmer)
  • 提前编译(Ahead-of-Time compilation,通过 crossgen)后面马上会说到 Microsoft.DotNet.ILCompiler
  • 单一文件打包(Single File Bundling)本小节

关于 .NET Core 3.0 中发布仅一个 exe 的方法、原理和实践,可以参见林德熙的博客:

  • dotnet core 发布只有一个 exe 的方法

.NET Core 在 GitHub 上开源:

  • .NET Foundation

使用 Fody

在你的项目中安装一个 NuGet 包 Costura.Fody。一般来说,安装完之后,你编译的时候就会生成仅有一个 exe 的程序集了。

如果你继续留意,可以发现项目中多了一个 Fody 的专属配置文件 FodyWeavers.xml,内容如下:

<?xml version="1.0" encoding="utf-8" ?>
<Weavers><Costura/>
</Weavers>

仅仅到此为止你已经足够利用 Fody 完成程序集的合并了。

但是,如果希望对 Fody 进行更精细化的配置,可以阅读叶洪的博客:

  • .NET 合并程序集(将 dll 合并到 exe 中) - Iron 的博客 - CSDN博客

Fody 在 GitHub 上开源:

  • Fody/Fody: Extensible tool for weaving .net assemblies

使用 SourceYard 源代码包

SourceYard 源代码包在程序集合并上是另辟蹊径的一种合并方式。它不能帮助你将所有的依赖全部合并,但足以让你在发布一些简单应用的时候不至于引入大量的依赖。

例如,你可以考虑新建一个项目,然后安装下面的 NuGet 包:

  • lindexi.src.MacAddress.Source

安装完成之后,你就可以在你的项目中使用到此 NuGet 包为你带来的获取 MAC 地址的工具类了。

using System;
using lindexi.src;namespace Walterlv.Demo
{internal static class Program{static void Main(){var macList = MacAddress.GetActiveMacAddress();foreach (var mac in macList){Console.WriteLine(mac);}}}
}

编译完你的项目,你会发现你的项目没有携带任何依赖。你安装的 NuGet 包并没有成为你的依赖,反而成为你正在编译的程序集的一部分。

如果你要制作一个像上面那样的源代码包,只需要在你要制作 NuGet 包的项目安装上 dotnetCampus.SourceYard,在你打包成 NuGet 包的时候,就会生成一个普通的 NuGet 包以及一个 *.Source.nupkg 的源代码包。将源代码包上传到 nuget.org 上,其他人便可以安装你制作的源代码包了。

关于如何使用 SourceYard 制作一个源代码包的方法可以阅读林德熙的博客:

  • SourceYard 制作源代码包

关于能够做出源代码包的原理,可以阅读我的博客:

  • 入门篇:将 .NET Core 项目打一个最简单的 NuGet 源码包,安装此包就像直接把源码放进项目一样
  • 进阶篇:从零开始制作 NuGet 源代码包(全面支持 .NET Core / .NET Framework / WPF 项目)

SourceYard 在 GitHub 上开源:

  • dotnet-campus/SourceYard: Add a NuGet package only for dll reference? By using dotnetCampus.SourceYard, you can pack a NuGet package with source code. By installing the new source code package, all source codes behaviors just like it is in your project.

使用 ILMerge 或者 ILRepack 等工具

ILMerge 和 ILRepack 的合并就更加富有技术含量——当然坑也更多。

这两个都是工具,因此,你需要将工具下载下来使用。你有很多种方法下载到工具使用,因此我会推荐不同的人群使用不同的工具。

ILMerge

ILMerge 命令行工具是微软官方出品,下载地址:

  • Download ILMerge from Official Microsoft Download Center

其使用方法请参见我的博客:

  • .NET 使用 ILMerge 合并多个程序集,避免引入额外的依赖 - walterlv

ILRepack

ILRepack 基于 Mono.Ceil 来进行 IL 合并,其使用方法可以参见我的博客:

  • .NET 使用 ILRepack 合并多个程序集(替代 ILMerge),避免引入额外的依赖 - walterlv

ILMerge-GUI 工具(已过时,但适合新手随便玩玩)

你可以在以下网址中找到 ILMerge-GUI 的下载链接:

  • wvd-vegt / ilmergegui / Downloads — Bitbucket

ILMerge-GUI 工具在 Bitbucket 上开源:

  • wvd-vegt / ilmergegui — Bitbucket

其他方法

使用 Microsoft.DotNet.ILCompiler

可以将 .NET Core 编译为单个无依赖的 Native 程序。

你需要先安装一个预览版的 NuGet 包 Microsoft.DotNet.ILCompiler

关于 Microsoft.DotNet.ILCompiler 的使用,你可以阅读林德熙的博客:

  • dotnet core 使用 CoreRT 将程序编译为 Native 程序

使用 dnSpy

dnSpy 支持添加一个模块到程序集,也可以创建模块,还可以将程序集转换为模块。因此,一个程序集可以包含多个模块的功能就可以被充分利用起来。

使用 Warp

Warp 在 GitHub 上开源:

  • dgiagio/warp: Create self-contained single binary applications

其使用可以参见林德熙的博客:

  • dotnet core 发布只有一个 exe 的方法

各种方法的原理和使用场景比较

原理

使用 .NET Core 3.0 自带的 PublishSingleFile 属性合并依赖,其原理是生成一个启动器容器程序。最终没有对程序进行任何修改,只是单纯的打包而已。

使用 Fody,是将程序集依赖放到了资源里面。当要加载程序集的时候,会直接将资源中的程序集流加载到内存中。

使用 SourceYard 源代码包,是直接将源代码合并到了目标项目里面。

使用 ILMerge / ILRepack,是在 IL 级别对程序集进行了合并。

我们可以通过下面一张图来感受一下后三种原理上的不同。

这是一个分别通过 Fody、SourceYard 和 ILMerge / ILRepack 生成的程序集的反编译图。可以看到,对于 ILRepack / ILMerge 和 SourceYard,反编译后看到的源代码都在目标程序集中,而对于 Fody,依赖仅仅出现在资源中。

适用范围

由于其原理不同,所以其适用范围和造成的副作用也不同。

如果你基于 .NET Core 3.0 开发,并且也不在意在目标计算机上生成的临时文件夹,那么可以考虑使用 PublishSingleFile 属性合并依赖。

如果你不在乎启动性能以及内存消耗,那么可以考虑 Fody(这意味着小型程序比较适合采用)。

如果你的程序非常在乎启动性能,那么就需要考虑 SourceYard、ILMerge / ILRepack 了。

对于 ILMerge / ILRepack 和 SourceYard 的比较,可以看下面这张表格:

方案 ILRepack / ILMerge SourceYard
适用于 任意 .NET 程序集 通过 SourceYard 发布的 NuGet 包
WPF ILRepack 支持,ILMerge 不支持 支持
调试(支持) 仅支持一般方法的调试 支持一般程序集支持的所有调试方法
调试(不支持) 不支持异步方法调试,不支持显示局部变量 没有不支持的
隐藏 API internal 的类型和成员可以隐藏 必须是 private 类型和成员才可隐藏

可以发现,如果我们能够充分将我们需要的包通过 SourceYard 发布成 NuGet,那么我们将可以获得比 ILRepack / ILMerge 更好的编写和调试体验。

表格之外还有一些特别需要说明的:

  1. ILRepack 额外支持修改 WPF 编译生成的 Baml 文件,将资源的引用路径修改成新程序集的路径。
  2. SourceYard 的类型需要写成 private 才可以隐藏,但是只有内部类才可以写 private,因此如果特别需要隐藏,请首先写一个内部类。(因此,你可能会发现有一个类型有很多个分部类,每一个分部类中都是一个私有的内部类)

开源社区

最后说一下,以上所说的所有方法全部是开源的,有问题欢迎在社区讨论一起解决:

  • .NET Foundation
  • Fody/Fody
  • dotnet-campus/SourceYard
  • dotnet/ILMerge
  • gluck/il-repack
  • 0xd4d/dnSpy
  • dgiagio/warp
  • corert/pkg/Microsoft.DotNet.ILCompiler

我的博客会首发于 https://blog.walterlv.com/,而 CSDN 会从其中精选发布,但是一旦发布了就很少更新。

如果在博客看到有任何不懂的内容,欢迎交流。我搭建了 dotnet 职业技术学院 欢迎大家加入。

本作品采用知识共享署名-非商业性使用-相同方式共享 4.0 国际许可协议进行许可。欢迎转载、使用、重新发布,但务必保留文章署名吕毅(包含链接:https://walterlv.blog.csdn.net/),不得用于商业目的,基于本文修改后的作品务必以相同的许可发布。如有任何疑问,请与我联系。

.NET 将多个程序集合并成单一程序集的 4+3 种方法相关推荐

  1. PPT转成Word文档的四种方法

    注: 实际使用中,方法四比较好使. 如果想要提取图片,可以先转换成pdf,再转换成word. 方法一:利用"大纲"视图       打开PPT演示文稿,单击"大纲&quo ...

  2. matlab 图像转视频教程,Matlab制作视频并转换成gif动态图的两种方法

    一.第一个方法比较简单,就是使用movie(f)直接取生成AVI视频文件. %% f(t)-->f(4*t+12) 并且验证%% function Signal_change() tic%记录程 ...

  3. 【汇编】16进制转换成10进制(三种方法)

    汇编语言程序:16进制转换成10进制(三种方法) 1 ;天水浪客(Gouki Jiang) , 16进制to10进制例题 2007.5.18 2 STACK SEGMENT PARA STACK 3 ...

  4. 多个vue项目合并成一个_多个Excel表格合并成一个表,最简单的方法在这里

      经常跟Excel表格打交道的小伙伴,对于合并表格肯定是不陌生的,又是突然有个任务,需要把几百个表格文件合并成一个,这时候用复制粘贴就很不明智了,要想在规定时间内完成是万万不可能的.这时候,我们就需 ...

  5. 图片怎么转换成PDF格式?这两种方法都可以实现转换

    怎么把图片转换成PDF格式呢?大家在日常中也会经常使用到图片,不管是出门游玩还是办公学习,图片都会给我们带来极大的便利.但是一旦图片的数量多了起来,我们又不能删除,那么这些图片的存放就是一个关键的问题 ...

  6. 把图片转换成gif动图的几种方法介绍

    怎么把图片转换成gif动图?在今天的数字化时代,gif动图越来越受到人们的欢迎和重视,因为它可以将多张图片合成一个动态的图像,增强信息展示的吸引力和表现力.将图片转换成gif动图在生活和工作中有着重要 ...

  7. 电脑word在哪_怎么将图片转换成Word?学会这3种方法,轻松将图片转文字!

    怎么将图片转换成Word文字?相信很多朋友都有这样的困惑,大多数人都会选择将图片里面的内容一个字一个字的录入到Word里面,这样的话会浪费我们大量的时间,这里笔者给大家介绍几种方法,不管哪一种都能够轻 ...

  8. ​CAD图纸怎么转换成PDF格式?这两种方法快速转换

    怎么把CAD图纸转换成PDF格式呢?大家在使用CAD绘图工具制作CAD图纸的时候,在将图纸绘制好后为了能够方便发送和浏览,会选择把图纸转换成PDF文件格式来保存发送,这样就不会出现文件打不开的现象.但 ...

  9. 图片怎么转换成PDF格式?这两种方法赶紧记下

    怎么把图片转换成PDF文件格式呢?大家在整理很多张图片的时候,对于一些比较粗心的小伙伴来说非常困难,因为太容易出现错误了,不是照片丢失就是排序出现了问题.针对这种问题,有一个很好的解决方法就是将这些图 ...

最新文章

  1. 文档过期打不开怎么办_网络上下载文档,常见的3个问题,一招教你快速解决!...
  2. idea与mysql连接时,遇见的小问题
  3. JavaScript 笔记 ( Prototype )
  4. 互联网金融2.0 这是最好的时代
  5. Uploadify3.2中文提示
  6. Jquery插件之ajaxForm
  7. MapReduce编程实践之自定义数据类型
  8. Bootstrap(二)——Grid栅格布局
  9. python在线朗读-简单的python代码实现语音朗读
  10. 如何为编程爱好者设计一款好玩的智能硬件(十)——无线2.4G通信模块研究·一篇说完...
  11. conda pip 安装NumPy速度不佳解决方案
  12. python基础之包、模块、命名空间和作用域
  13. 成功解决3dmax中,旋转时透视图可以看穿物体
  14. 新年新气象,90行代码菜鸟用Python制作小姐姐新年日历天气卡,送人太有面子啦!...
  15. 超市选址c语言程序,谈谈超市选址的重要性
  16. Java输出PPT文件(三) - 饼图数据替换
  17. NX二次开发-UFUN读取属性的值UF_ATTR_read_value
  18. 【区块链108将】微数链林道坤:区块链有助于更好的发挥大数据价值
  19. 突发!又一风云人物,被警方采取刑事强制措施
  20. 转凯文·凯利斯坦福演讲-预言未来20年科技潮流

热门文章

  1. 会计专业为什么要学python-御丽诗妃:为什么我建议你一定要学Python?
  2. xgboost 怎么读_金融本科毕业5年应该读MBA吗?
  3. 具有在线外参校准的多激光雷达系统的里程计和地图绘制系统
  4. 讲讲项目中你是如何处理高并发的?
  5. SUBTOTAL 函数的用法及应用实例
  6. Android 横竖屏切换小结
  7. web前端基础仏学习css精灵css sprite技术解析
  8. matlab 凸包质心算法,求多边形凸包(线性算法)--陈氏凸包算法--Computing the convex hull of a simple polygon(源码)...
  9. 【CKA考试笔记】二十、升级k8s
  10. SRAM和DRAM的联系与区别