本文也叫跟着 Stephen Toub 大佬学性能优化系列,这是我从 Stephen Toub 大佬给 WPF 框架做性能优化学到的知识,通过让委托无捕获来进行性能优化
在 .NET 应用中,咱可以通过委托将某个方法传入到某个模块里面,让这个模块在合适的时候,执行传入的方法。如果传入的是实例的方法,那在编译器生成委托时,将会自动加上捕获包,本质上的捕获包是一个对象,也就是每次调用都需要分配对象
如以下代码
咱需要将 Foo 方法调用 AddFoo 方法,将 Foo 方法作为委托传入到 AddFoo 方法。此时咱有多个不同的方法来实现,为了让大家看到效果,咱来新建一个空白的 WPF 项目,在界面上放两个按钮
点击第一个按钮,此时咱使用简单的方法
大部分的逻辑都是采用上面的方法传入的
但 Stephen Toub 大佬在性能优化上,是使用了传入对象自身,减少委托捕获的方法来优化性能。在点击 民与名扬 按钮时,触发以下代码
以上的逻辑有点绕,大概就是在 AddFoo 方法在设计上允许传入最后一个参数,最后的一个参数将会自动作为参数传入给委托,也就是代码的 s 变量的值就是 this 的值。此时的优化在于调用了 AddFoo 方法加入的委托不需要对 this 有任何的引用,因此就可以让 编译器 进行缓存,不需要每次都创建新的委托对象
咱来运行代码对比一下性能,运行代码,在看到 WPF 应用打开时,点击内存的获取快照,然后点击第一个按钮,再点击内存的获取快照,可以看到下图
可以看到加了 100 多个对象
然后点击 民与名扬 按钮,点击内存的获取快照,可以看到下图
可以看到内存几乎没有添加任何对象。再多点击 民与名扬 按钮几次,点击内存的获取快照,可以看到几乎没有对象的分配。但是如果点击第一个按钮,点击内存的获取快照,可以看到内存加了很多对象
在性能优化时,可以考虑减少委托的捕获,如在传入实例的方法,也就是非静态的方法时,将会让委托捕获了 this 变量,需要创建委托。而如果 this 的变量是通过参数重新传入给委托的,此时可以做到不需要创建新的委托
这就是为什么有一些方法设计了委托传入,同时支持再传入一个 object 对象的原因。如 WPF 的 BeginInvoke 方法就是这样设计的,虽然咱现在推荐更多使用的是 InvokeAsync 方法
可以通过如下方式获取本文的源代码,先创建一个空文件夹,接着使用命令行 cd 命令进入此空文件夹,在命令行里面输入以下代码,即可获取到本文的代码
以上使用的是 gitee 的源,如果 gitee 不能访问,请替换为 github 的源
获取代码之后,进入 NewhearfarairchaylucoLerhejuche 文件夹
本作品采用 知识共享署名-非商业性使用-相同方式共享 4.0 国际许可协议 进行许可。 欢迎转载、使用、重新发布,但务必保留文章署名 林德熙 (包含链接: https://blog.lindexi.com ),不得用于商业目的,基于本文修改后的作品务必以相同的许可发布。如有任何疑问,请与我 联系。