<?xml version="1.0" encoding="UTF-8"?><?xml-stylesheet href="/rss/feed.xsl" type="text/xsl"?><rss version="2.0" xmlns:content="http://purl.org/rss/1.0/modules/content/"><channel><title>天天的糖果屋</title><description>Walk softly through the world.</description><link>https://blog.rqwq.link</link><item><title>自定义关键词示例</title><link>https://blog.rqwq.link/post/custom-keywords</link><guid isPermaLink="false">custom-keywords</guid><description>展示如何设置文章关键词。</description><pubDate>Sun, 05 Apr 2026 10:43:11 GMT</pubDate><content:encoded>&lt;p&gt;本文展示如何设置文章关键词。&lt;/p&gt;
&lt;h2&gt;frontmatter 关键词设置&lt;a href=&quot;#frontmatter-关键词设置&quot;&gt;&lt;span&gt;&lt;/span&gt;&lt;/a&gt;&lt;/h2&gt;
&lt;p&gt;在文章的 frontmatter 中添加 keywords 字段即可设置关键词：&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;&lt;span&gt;&lt;span&gt;---&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;title: 我的文章&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;date: 2026-01-01&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;keywords:&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;  -&lt;/span&gt;&lt;span&gt; 示例关键词一&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;  -&lt;/span&gt;&lt;span&gt; 示例关键词二&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;---&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;这是篇带有关键词的文章。&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;然后生成的页面会包含以下 meta 标签：&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;&lt;span&gt;&lt;span&gt;&amp;lt;&lt;/span&gt;&lt;span&gt;meta&lt;/span&gt;&lt;span&gt; name&lt;/span&gt;&lt;span&gt;=&lt;/span&gt;&lt;span&gt;&quot;keywords&quot;&lt;/span&gt;&lt;span&gt; content&lt;/span&gt;&lt;span&gt;=&lt;/span&gt;&lt;span&gt;&quot;示例关键词一, 示例关键词二, ... site.keywords&quot;&lt;/span&gt;&lt;span&gt;&amp;gt;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;</content:encoded><category>category:笔记</category><category>tag:教程</category></item><item><title>不是哥们这类型到底是个啥——深入理解 C# 泛型</title><link>https://blog.rqwq.link/post/note/dotnet/knowledge-generics</link><guid isPermaLink="false">note/dotnet/knowledge-generics</guid><description>先让我们来看个代码：
T? Cast&amp;lt;T&amp;gt;(object? obj) {
    return (T?)obj;
}
这段代码某些情况下会在 return 语句这一行产生一个空引用异常（NullReferenceException，常简称为 NRE），来猜猜为什么呢？</description><pubDate>Mon, 02 Mar 2026 11:26:45 GMT</pubDate><content:encoded>&lt;p&gt;先让我们来看个代码：&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;&lt;span&gt;&lt;span&gt;T&lt;/span&gt;&lt;span&gt;? &lt;/span&gt;&lt;span&gt;Cast&lt;/span&gt;&lt;span&gt;&amp;lt;&lt;/span&gt;&lt;span&gt;T&lt;/span&gt;&lt;span&gt;&amp;gt;(&lt;/span&gt;&lt;span&gt;object&lt;/span&gt;&lt;span&gt;? &lt;/span&gt;&lt;span&gt;obj&lt;/span&gt;&lt;span&gt;) {&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;    return&lt;/span&gt;&lt;span&gt; (&lt;/span&gt;&lt;span&gt;T&lt;/span&gt;&lt;span&gt;?)obj;&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;}&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;这段代码某些情况下会在 return 语句这一行产生一个空引用异常（&lt;code&gt;NullReferenceException&lt;/code&gt;，常简称为 NRE），来猜猜为什么呢？&lt;/p&gt;
&lt;p&gt;是的，写这篇文章的起因是 C# 这个东西在&lt;strong&gt;把空引用转换到值类型&lt;/strong&gt;的时候会产生 NRE 然后什么额外信息都不报，于是让我 debug 了一下午才找出来这个破烂 NRE 到底在哪。&lt;/p&gt;
&lt;p&gt;看到这里你可能会心一笑，那说明你大概率不是这篇文章的受众；你也可能半懵半醒着，那就对了，这篇文章就要把广泛隐藏在 C# 泛型和类型系统里面那些不干人事的妙妙小特性从头到尾翻个遍。&lt;/p&gt;
&lt;h2&gt;C# 强大的类型系统&lt;a href=&quot;#c-强大的类型系统&quot;&gt;&lt;span&gt;&lt;/span&gt;&lt;/a&gt;&lt;/h2&gt;
&lt;p&gt;让我们从 C# 这门语言的诞生讲起。&lt;/p&gt;
&lt;p&gt;20 世纪 90 年代，微软发布了 Microsoft Visual J，在 Java 的基础上添加了很多自己的特性，使其可以更方便地调用 Win32 API 和 ActiveX 等。然而好景不长，1997 年，Sun 以违反合约为理由（实际上就是想捞钱）起诉微软并索赔，迫使微软遵守 Java 的跨平台和生态共享原则。然后呢，微软肯定不能任 Sun 无理取闹，直接撂一边不干了，开始在自己的生态中逐步淘汰 JVM，并最终在 2004 年结束了 J 的支持。&lt;/p&gt;
&lt;p&gt;但 JVM 开始被淘汰，Windows 却又迫切需要一个具有 Java 相似优势的软件开发平台。于是，在 2000 年，微软在开发者大会上发布了自己的编程语言——C#。C# 出自同样是 J++ 创始人的 Anders Hejlsberg 之手（没错这人还是 Turbo Pascal 的创始人和 TypeScript 的首席开发者，一辈子专注于造语言改变世界，顶级大佬了属于是），极大程度上吸收了同年代的 Java、C++、VB 等等语言的各种特点，尤其是 Java。因此，C# 常被戏称为 Microsoft Java，就是因为它跟 Java 在很多方面极其相似。&lt;/p&gt;
&lt;p&gt;讲了这么多有什么用呢？你一定注意到了，C# 这个语言与 Java 有着千丝万缕的联系，也正因此，C# 的很多特性就是直接从 Java 抄过来的：像是类、对象、引用、继承、接口等等，全是跟 Java 几乎一模一样的特性。同时，它又要保持跟 Java 的相似性以接纳由于逐步淘汰 JVM 而转向 .NET 平台的开发者，让他们上手难度尽可能低。这些原因融合在一起，就决定了 C# 一定会在很大程度上保留 Java 那套特性，但微软又要追求自己的高性能和各种新奇特性，就导致这门语言变成了很多神奇的东西跑在一起的样子。这里要讲的类型系统，也正是因此，才变得如此复杂。&lt;/p&gt;
&lt;p&gt;好了步入正题。&lt;/p&gt;
&lt;p&gt;&lt;em&gt;未完待续&lt;/em&gt;&lt;/p&gt;</content:encoded><category>category:笔记</category><category>category:.NET</category><category>tag:C#</category><category>tag:.NET</category><category>tag:CLR</category><category>tag:泛型</category></item><item><title>macOS 26 初体验</title><link>https://blog.rqwq.link/post/life/daily/macos-26-first-use</link><guid isPermaLink="false">life/daily/macos-26-first-use</guid><description>前不久抱着试试看的心态更新了 macOS 26。
从最初的惊艳到后来发现的各处细节问题，再到从各处角落发现一些不错的改进，感觉这个升级并不像很多人说的一样一无是处，它对于大多数用户其实还是一个不错的体验进化，而且进化了不少（前提是硬件带得动，但实际上只要是 Apple Silicon</description><pubDate>Mon, 02 Mar 2026 03:35:59 GMT</pubDate><content:encoded>&lt;p&gt;前不久抱着试试看的心态更新了 macOS 26。&lt;/p&gt;
&lt;p&gt;从最初的惊艳到后来发现的各处细节问题，再到从各处角落发现一些不错的改进，感觉这个升级并不像很多人说的一样一无是处，它对于大多数用户其实还是一个不错的体验进化，而且进化了不少（前提是硬件带得动，但实际上只要是 Apple Silicon 现在应该都带得动了）。&lt;/p&gt;
&lt;p&gt;于是写个文章来总结一下我发现的 macOS 26 的改进与问题，希望能给像我一样迟疑是否应该升级系统的伙伴一点参考。&lt;/p&gt;
&lt;div&gt;
&lt;p&gt;本文的所有信息均基于写作时的 macOS Tahoe 最新正式版 26.3，如果后续系统更新中出现不一致的行为，本文大概率不会更正或补充，请自行注意甄别本文所含信息的时效性。&lt;/p&gt;
&lt;/div&gt;
&lt;h2&gt;启动台与全局搜索&lt;a href=&quot;#启动台与全局搜索&quot;&gt;&lt;span&gt;&lt;/span&gt;&lt;/a&gt;&lt;/h2&gt;
&lt;h3&gt;启动台&lt;a href=&quot;#启动台&quot;&gt;&lt;span&gt;&lt;/span&gt;&lt;/a&gt;&lt;/h3&gt;
&lt;p&gt;这是这次更新中可能最影响体验的一项更改：启动台（Launchpad）被砍了！&lt;/p&gt;
&lt;p&gt;如果你十分在意启动台，而对新版本系统的一些新特性（下面会讲）不在乎，千万不要更新，它就是直接 R.I.P. 了，而非隐藏起来或是给了切换选项。取而代之的，苹果整了个功能很简单的 Application 面板，简单到连文件夹都不支持。&lt;/p&gt;
&lt;figure&gt;&lt;img src=&quot;https://s3-blaze.rqwq.link/vscode/d7861985e5cd3a8939e999dde5c53cee.png&quot; alt=&quot;Application 面板&quot; loading=&quot;lazy&quot; /&gt;&lt;figcaption&gt;Application 面板&lt;/figcaption&gt;&lt;/figure&gt;
&lt;p&gt;面板上方有一些采用类似 iOS 侧边抽屉分类方式的选项，可以用于快速查找相应类别的应用，但我体验下来的感受就是——废物，是的没错就是废物，一点效率提升都没有。不过好在我已经习惯了 Spotlight 启动应用，所以这个更改对我影响不算大，最多有点不顺眼。&lt;/p&gt;
&lt;p&gt;这个面板目前还有个容易触发强迫症问题，就是每次打开的时候都会加载一遍图标。它的缓存似乎没有很丝滑，曾经的启动台图标是几乎不会让用户看到加载过程的，但现在的效果就是，总有那么几次，打开会发现应用图标由一块透明区域稀里哗啦的冒出来，而不是本来就在那。&lt;/p&gt;
&lt;h3&gt;聚焦搜索（Spotlight）&lt;a href=&quot;#聚焦搜索spotlight&quot;&gt;&lt;span&gt;&lt;/span&gt;&lt;/a&gt;&lt;/h3&gt;
&lt;p&gt;苹果对 Spotlight 进行了一波改进，原先单调的一个框现在变成了一个框冒出来 4 个按钮。&lt;/p&gt;
&lt;p&gt;为什么要强调冒出来呢？因为它就是冒出来的。&lt;/p&gt;
&lt;div&gt;&lt;/div&gt;
&lt;p&gt;实际上就是给 Spotlight 加了个分类功能，同时让它能搜索更多东西了。现在可以在 Spotlight 中搜索本机文件、App Store、网络内容等等很多东西，这个改进还是比较有实用性的。&lt;/p&gt;
&lt;h2&gt;系统界面&lt;a href=&quot;#系统界面&quot;&gt;&lt;span&gt;&lt;/span&gt;&lt;/a&gt;&lt;/h2&gt;
&lt;p&gt;新的系统界面给我的第一感受就是变圆了，到处都变得更圆了。&lt;/p&gt;
&lt;p&gt;这点变化不知道是否是由新设计引入的，但它连鼠标指针都变圆了就很让人难绷……&lt;/p&gt;
&lt;figure&gt;&lt;img src=&quot;https://s3-blaze.rqwq.link/vscode/0726129611e355d2aae81d5eaa9afc76.png&quot; alt=&quot;新的鼠标指针形状&quot; loading=&quot;lazy&quot; /&gt;&lt;figcaption&gt;新的鼠标指针形状&lt;/figcaption&gt;&lt;/figure&gt;
&lt;p&gt;当然指针的默认颜色实际上没有改动，仍然是黑的，截图中的指针是我自己调整的颜色。&lt;/p&gt;
&lt;h3&gt;液态玻璃（Liquid Glass）&lt;a href=&quot;#液态玻璃liquid-glass&quot;&gt;&lt;span&gt;&lt;/span&gt;&lt;/a&gt;&lt;/h3&gt;
&lt;p&gt;本次更新最重磅的更改就是苹果的全新设计风格液态玻璃了，从锁屏界面开始到各种各样的组件，整个系统各处都被换成了液态玻璃的材质与设计。&lt;/p&gt;
&lt;figure&gt;&lt;img src=&quot;https://s3-blaze.rqwq.link/vscode/986b0552309370cf22f2b46e12960dd8.png&quot; alt=&quot;整体界面&quot; loading=&quot;lazy&quot; /&gt;&lt;figcaption&gt;整体界面&lt;/figcaption&gt;&lt;/figure&gt;
&lt;p&gt;之前对这个新设计也没有很反感，反而觉得它做好了其实挺好看的，就是有点吃配置，而且确实在一定程度上影响部分 UI 元素的可读性。但实测下来，现在的液态玻璃性能已经不算差了，也并不会对待机时长造成什么影响，我的体验就是感受不到跟更新前有什么区别。&lt;/p&gt;
&lt;p&gt;像 iOS 26 一样，macOS 26 也在设置中提供了一个选项用于切换液态玻璃的两种不同模式，一种是默认的高透效果，而另一种为了照顾可读性牺牲一些视觉效果，为玻璃区域添加背景色。&lt;/p&gt;
&lt;figure&gt;&lt;img src=&quot;https://s3-blaze.rqwq.link/vscode/650f03283494b8b9dcfe45132c997865.png&quot; alt=&quot;液态玻璃选项&quot; loading=&quot;lazy&quot; /&gt;&lt;figcaption&gt;液态玻璃选项&lt;/figcaption&gt;&lt;/figure&gt;
&lt;h3&gt;窗口动画&lt;a href=&quot;#窗口动画&quot;&gt;&lt;span&gt;&lt;/span&gt;&lt;/a&gt;&lt;/h3&gt;
&lt;p&gt;现在窗口打开和关闭在大部分情况下会有一个变形动画，感觉还是很灵动的，这可能算是个巨大的改进，因为苹果在过去很久都没给窗口加过任何动画。&lt;/p&gt;
&lt;div&gt;&lt;/div&gt;
&lt;p&gt;至于为什么说是大部分情况，有些时候这个动画会被无端吃掉，原因未知。&lt;/p&gt;
&lt;h3&gt;亮度/音量控件&lt;a href=&quot;#亮度音量控件&quot;&gt;&lt;span&gt;&lt;/span&gt;&lt;/a&gt;&lt;/h3&gt;
&lt;p&gt;这两个小控件现在被移到了屏幕上边缘，而且由原先的清晰刻度变成了一条没有直接刻度的线。&lt;/p&gt;
&lt;h3&gt;小组件（Widget）&lt;a href=&quot;#小组件widget&quot;&gt;&lt;span&gt;&lt;/span&gt;&lt;/a&gt;&lt;/h3&gt;
&lt;p&gt;现在 Widgets 可以被直接放到桌面上了，因此喜欢挂各种参数监控但受限于顶栏空间的友友们现在可以发挥创意装饰自己没用到的桌面空间了。但这改进对我无感，我还是喜欢把这些组件放在通知中心，因为我的桌面时常会被各种各样的文件塞满。&lt;/p&gt;
&lt;h3&gt;顶栏&lt;a href=&quot;#顶栏&quot;&gt;&lt;span&gt;&lt;/span&gt;&lt;/a&gt;&lt;/h3&gt;
&lt;p&gt;顶栏现在被改成了透明的，当然也可以在设置中改回原来的样子，但我觉得透明的可能更好看一些。&lt;/p&gt;
&lt;p&gt;具体效果可以参考 &lt;a href=&quot;#%E6%B6%B2%E6%80%81%E7%8E%BB%E7%92%83&quot;&gt;液态玻璃&lt;/a&gt; 一节的截图。&lt;/p&gt;
&lt;h3&gt;通知中心&lt;a href=&quot;#通知中心&quot;&gt;&lt;span&gt;&lt;/span&gt;&lt;/a&gt;&lt;/h3&gt;
&lt;p&gt;现在的通知中心相比之前，大量要求点两下的操作都改成了悬浮等待 + 点一下，这样在不乏防误触的情况下又做到了简化操作，是一个不错的改进。&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;这个特性由于目前没什么通知可以截图，就不截了。&lt;/p&gt;
&lt;/blockquote&gt;
&lt;h2&gt;应用和辅助工具&lt;a href=&quot;#应用和辅助工具&quot;&gt;&lt;span&gt;&lt;/span&gt;&lt;/a&gt;&lt;/h2&gt;
&lt;h3&gt;截图工具&lt;a href=&quot;#截图工具&quot;&gt;&lt;span&gt;&lt;/span&gt;&lt;/a&gt;&lt;/h3&gt;
&lt;p&gt;系统自带的截图/录屏工具现在支持直接选择窗口录制了，而且有范围选择同款的周围内容暗化效果，同时也支持在录制时随意拖动窗口而录制内容不动，太先进了！隔壁 Windows 什么时候学学果子给你那丑死的 Windows API 录制内容外框改一下&lt;/p&gt;
&lt;h3&gt;Apple Intelligence&lt;a href=&quot;#apple-intelligence&quot;&gt;&lt;span&gt;&lt;/span&gt;&lt;/a&gt;&lt;/h3&gt;
&lt;p&gt;现在 Apple Intelligence 在我的设备上终于正常支持了为多条通知生成总结的功能，虽然生成的内容……一眼难尽，但是多少还有点参考价值。&lt;/p&gt;
&lt;figure&gt;&lt;img src=&quot;https://s3-blaze.rqwq.link/vscode/b5109de3310fa9f3a88648ccde565f81.png&quot; alt=&quot;Apple Intelligence 通知总结&quot; loading=&quot;lazy&quot; /&gt;&lt;figcaption&gt;Apple Intelligence 通知总结&lt;/figcaption&gt;&lt;/figure&gt;
&lt;h3&gt;iPhone 互联&lt;a href=&quot;#iphone-互联&quot;&gt;&lt;span&gt;&lt;/span&gt;&lt;/a&gt;&lt;/h3&gt;
&lt;p&gt;别的不说了，多了一个很抢眼的功能，直接上图：&lt;/p&gt;
&lt;figure&gt;&lt;img src=&quot;https://s3-blaze.rqwq.link/vscode/064bcc847d3a86e9a9916df5ffe5e551.png&quot; alt=&quot;iPhone Mirroring Live Activity&quot; loading=&quot;lazy&quot; /&gt;&lt;figcaption&gt;iPhone Mirroring Live Activity&lt;/figcaption&gt;&lt;/figure&gt;
&lt;p&gt;现在手机上的 Live Activity 通知会直接显示在顶栏里，又是一个相当不错的功能了。妈妈再也不用担心我看电脑看得错过火车了（12306 应用有动态的行程通知）&lt;/p&gt;
&lt;p&gt;不过啊果果，你都把 iPhone 的特性全丢 Mac 上面了，什么时候能把 iPhone 丢掉只用 Mac？&lt;/p&gt;
&lt;h2&gt;结语&lt;a href=&quot;#结语&quot;&gt;&lt;span&gt;&lt;/span&gt;&lt;/a&gt;&lt;/h2&gt;
&lt;p&gt;本次更新大量的明显改动都在视觉设计上，功能性的更改并不多，但也聊胜于无，相比隔壁 Windows 11 完全堆史山负优化的行为已经算是很不错的体验升级了。&lt;/p&gt;
&lt;p&gt;很多人比较担心的性能问题，在一段时间的体验之后，我觉得并不是什么问题。性能方面的优化已经很极致了，之前许多内测时候的卡顿和耗电问题，在目前的版本来看都完全不存在。可能这就是苹果要么不做要么极致的偏执，单从用户体验来看，这种偏执倒是给用户创造了不少体验进步。&lt;/p&gt;
&lt;p&gt;目前没发现有什么很明显的改进了，所以就这样吧，本篇就到此结束了。&lt;/p&gt;</content:encoded><category>category:随笔</category><category>category:日常</category><category>tag:macOS</category><category>tag:Apple</category></item><item><title>Roslyn 源生成器与预处理编程（一）</title><link>https://blog.rqwq.link/post/note/dotnet/knowledge-sourcegeneration-1</link><guid isPermaLink="false">note/dotnet/knowledge-sourcegeneration-1</guid><description>前言
昨晚与 Roslyn 源生成器大战三百回合，做了一个基于元注解的源生成器 IoC（inversion of control, 控制反转）实现。在做的过程中基本把 Roslyn</description><pubDate>Mon, 09 Feb 2026 12:03:40 GMT</pubDate><content:encoded>&lt;h2&gt;前言&lt;a href=&quot;#前言&quot;&gt;&lt;span&gt;&lt;/span&gt;&lt;/a&gt;&lt;/h2&gt;
&lt;p&gt;昨晚与 Roslyn 源生成器大战三百回合，做了一个基于元注解的源生成器 IoC（inversion of control, 控制反转）实现。在做的过程中基本把 Roslyn 源生成器的大部分特性和坑踩了一遍，遂决定写一系列文章系统介绍一下源生成器的基本用法，预处理编程的思想，以及如何运用它们解决项目的实际问题。&lt;/p&gt;
&lt;p&gt;这一篇是这一系列的第一篇文章，简单介绍一下什么是源生成器以及为什么我们要用它。&lt;/p&gt;
&lt;h2&gt;何为源生成器&lt;a href=&quot;#何为源生成器&quot;&gt;&lt;span&gt;&lt;/span&gt;&lt;/a&gt;&lt;/h2&gt;
&lt;p&gt;源生成器，何意味，能帮我写代码吗？&lt;/p&gt;
&lt;p&gt;正如它的名字 source generator（源代码生成器），这是一个在已有源代码的基础上根据各种定义好的规则生成源代码的东西。它并不能完全帮你写代码（怎么可能呢，连 AI 都代替不了程序员），但是可以减少你的一些重复、无聊的工作，转而让注意力集中在更核心、更重要的功能实现上。&lt;/p&gt;

一个简单的源生成器
&lt;div&gt;
&lt;pre&gt;&lt;code&gt;&lt;span&gt;&lt;span&gt;using&lt;/span&gt;&lt;span&gt; System&lt;/span&gt;&lt;span&gt;;&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;// ...&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;// 此处省略亿点 using&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;namespace&lt;/span&gt;&lt;span&gt; PCL&lt;/span&gt;&lt;span&gt;.&lt;/span&gt;&lt;span&gt;Core&lt;/span&gt;&lt;span&gt;.&lt;/span&gt;&lt;span&gt;SourceGenerators&lt;/span&gt;&lt;span&gt;;&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;[&lt;/span&gt;&lt;span&gt;Generator&lt;/span&gt;&lt;span&gt;(LanguageNames.CSharp)]&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;public&lt;/span&gt;&lt;span&gt; class&lt;/span&gt;&lt;span&gt; EnvironmentInteropGenerator&lt;/span&gt;&lt;span&gt; : &lt;/span&gt;&lt;span&gt;IIncrementalGenerator&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;{&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;    public&lt;/span&gt;&lt;span&gt; void&lt;/span&gt;&lt;span&gt; Initialize&lt;/span&gt;&lt;span&gt;(&lt;/span&gt;&lt;span&gt;IncrementalGeneratorInitializationContext&lt;/span&gt;&lt;span&gt; context&lt;/span&gt;&lt;span&gt;)&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;    {&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;        var&lt;/span&gt;&lt;span&gt; secretProvider&lt;/span&gt;&lt;span&gt; =&lt;/span&gt;&lt;span&gt; context.CompilationProvider.&lt;/span&gt;&lt;span&gt;Select&lt;/span&gt;&lt;span&gt;(&lt;/span&gt;&lt;span&gt;static&lt;/span&gt;&lt;span&gt; (&lt;/span&gt;&lt;span&gt;_&lt;/span&gt;&lt;span&gt;, &lt;/span&gt;&lt;span&gt;_&lt;/span&gt;&lt;span&gt;) &lt;/span&gt;&lt;span&gt;=&amp;gt;&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;        {&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;#&lt;/span&gt;&lt;span&gt;pragma&lt;/span&gt;&lt;span&gt; warning&lt;/span&gt;&lt;span&gt; disable&lt;/span&gt;&lt;span&gt; RS1035&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;            var&lt;/span&gt;&lt;span&gt; envs&lt;/span&gt;&lt;span&gt; =&lt;/span&gt;&lt;span&gt; Environment.&lt;/span&gt;&lt;span&gt;GetEnvironmentVariables&lt;/span&gt;&lt;span&gt;();&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;#&lt;/span&gt;&lt;span&gt;pragma&lt;/span&gt;&lt;span&gt; warning&lt;/span&gt;&lt;span&gt; restore&lt;/span&gt;&lt;span&gt; RS1035&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;            var&lt;/span&gt;&lt;span&gt; secretPairs&lt;/span&gt;&lt;span&gt; =&lt;/span&gt;&lt;span&gt; envs.&lt;/span&gt;&lt;span&gt;Contains&lt;/span&gt;&lt;span&gt;(&lt;/span&gt;&lt;span&gt;&quot;PCL_WRITE_SECRET&quot;&lt;/span&gt;&lt;span&gt;) &lt;/span&gt;&lt;span&gt;?&lt;/span&gt;&lt;span&gt; (&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;                from&lt;/span&gt;&lt;span&gt; key&lt;/span&gt;&lt;span&gt; in&lt;/span&gt;&lt;span&gt; (&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;                    from&lt;/span&gt;&lt;span&gt; key&lt;/span&gt;&lt;span&gt; in&lt;/span&gt;&lt;span&gt; envs.Keys.&lt;/span&gt;&lt;span&gt;Cast&lt;/span&gt;&lt;span&gt;&amp;lt;&lt;/span&gt;&lt;span&gt;string&lt;/span&gt;&lt;span&gt;&amp;gt;()&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;                    where&lt;/span&gt;&lt;span&gt; !&lt;/span&gt;&lt;span&gt;string&lt;/span&gt;&lt;span&gt;.&lt;/span&gt;&lt;span&gt;IsNullOrWhiteSpace&lt;/span&gt;&lt;span&gt;(key) &lt;/span&gt;&lt;span&gt;&amp;amp;&amp;amp;&lt;/span&gt;&lt;span&gt; key.&lt;/span&gt;&lt;span&gt;StartsWith&lt;/span&gt;&lt;span&gt;(&lt;/span&gt;&lt;span&gt;&quot;PCL_&quot;&lt;/span&gt;&lt;span&gt;) &lt;/span&gt;&lt;span&gt;&amp;amp;&amp;amp;&lt;/span&gt;&lt;span&gt; key &lt;/span&gt;&lt;span&gt;!=&lt;/span&gt;&lt;span&gt; &quot;PCL_WRITE_SECRET&quot;&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;                    select&lt;/span&gt;&lt;span&gt; key&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;                )&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;                let&lt;/span&gt;&lt;span&gt; value&lt;/span&gt;&lt;span&gt; =&lt;/span&gt;&lt;span&gt; envs[key]&lt;/span&gt;&lt;span&gt;?&lt;/span&gt;&lt;span&gt;.&lt;/span&gt;&lt;span&gt;ToString&lt;/span&gt;&lt;span&gt;()&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;                where&lt;/span&gt;&lt;span&gt; !&lt;/span&gt;&lt;span&gt;string&lt;/span&gt;&lt;span&gt;.&lt;/span&gt;&lt;span&gt;IsNullOrWhiteSpace&lt;/span&gt;&lt;span&gt;(value)&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;                select&lt;/span&gt;&lt;span&gt; (key.&lt;/span&gt;&lt;span&gt;Substring&lt;/span&gt;&lt;span&gt;(&lt;/span&gt;&lt;span&gt;4&lt;/span&gt;&lt;span&gt;), value)&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;            ) &lt;/span&gt;&lt;span&gt;:&lt;/span&gt;&lt;span&gt; [];&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;            return&lt;/span&gt;&lt;span&gt; secretPairs;&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;        });&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;        // 注册源代码输出&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;        context.&lt;/span&gt;&lt;span&gt;RegisterSourceOutput&lt;/span&gt;&lt;span&gt;(secretProvider, _Execute);&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;    }&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;    private&lt;/span&gt;&lt;span&gt; static&lt;/span&gt;&lt;span&gt; void&lt;/span&gt;&lt;span&gt; _Execute&lt;/span&gt;&lt;span&gt;(&lt;/span&gt;&lt;span&gt;SourceProductionContext&lt;/span&gt;&lt;span&gt; context&lt;/span&gt;&lt;span&gt;, &lt;/span&gt;&lt;span&gt;IEnumerable&lt;/span&gt;&lt;span&gt;&amp;lt;(&lt;/span&gt;&lt;span&gt;string&lt;/span&gt;&lt;span&gt;, &lt;/span&gt;&lt;span&gt;string&lt;/span&gt;&lt;span&gt;)&amp;gt; &lt;/span&gt;&lt;span&gt;secretPairs&lt;/span&gt;&lt;span&gt;)&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;    {&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;        var&lt;/span&gt;&lt;span&gt; sb&lt;/span&gt;&lt;span&gt; =&lt;/span&gt;&lt;span&gt; new&lt;/span&gt;&lt;span&gt; StringBuilder&lt;/span&gt;&lt;span&gt;();&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;        sb.&lt;/span&gt;&lt;span&gt;AppendLine&lt;/span&gt;&lt;span&gt;(&lt;/span&gt;&lt;span&gt;&quot;// &amp;lt;auto-generated /&amp;gt;&quot;&lt;/span&gt;&lt;span&gt;);&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;        sb.&lt;/span&gt;&lt;span&gt;AppendLine&lt;/span&gt;&lt;span&gt;(&lt;/span&gt;&lt;span&gt;&quot;// 此文件由 Source Generator 自动生成，请勿手动修改&quot;&lt;/span&gt;&lt;span&gt;);&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;        sb.&lt;/span&gt;&lt;span&gt;AppendLine&lt;/span&gt;&lt;span&gt;();&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;        sb.&lt;/span&gt;&lt;span&gt;AppendLine&lt;/span&gt;&lt;span&gt;(&lt;/span&gt;&lt;span&gt;&quot;#nullable enable&quot;&lt;/span&gt;&lt;span&gt;);&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;        sb.&lt;/span&gt;&lt;span&gt;AppendLine&lt;/span&gt;&lt;span&gt;();&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;        sb.&lt;/span&gt;&lt;span&gt;AppendLine&lt;/span&gt;&lt;span&gt;(&lt;/span&gt;&lt;span&gt;&quot;namespace PCL.Core.Utils.OS;&quot;&lt;/span&gt;&lt;span&gt;);&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;        sb.&lt;/span&gt;&lt;span&gt;AppendLine&lt;/span&gt;&lt;span&gt;();&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;        sb.&lt;/span&gt;&lt;span&gt;AppendLine&lt;/span&gt;&lt;span&gt;(&lt;/span&gt;&lt;span&gt;&quot;partial class EnvironmentInterop&quot;&lt;/span&gt;&lt;span&gt;);&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;        sb.&lt;/span&gt;&lt;span&gt;AppendLine&lt;/span&gt;&lt;span&gt;(&lt;/span&gt;&lt;span&gt;&quot;{&quot;&lt;/span&gt;&lt;span&gt;);&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;        sb.&lt;/span&gt;&lt;span&gt;AppendLine&lt;/span&gt;&lt;span&gt;(&lt;/span&gt;&lt;span&gt;&quot;    private static readonly System.Collections.Generic.Dictionary&amp;lt;string, string?&amp;gt; SecretDictionary = new()&quot;&lt;/span&gt;&lt;span&gt;);&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;        sb.&lt;/span&gt;&lt;span&gt;AppendLine&lt;/span&gt;&lt;span&gt;(&lt;/span&gt;&lt;span&gt;&quot;    {&quot;&lt;/span&gt;&lt;span&gt;);&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;        foreach&lt;/span&gt;&lt;span&gt; (&lt;/span&gt;&lt;span&gt;var&lt;/span&gt;&lt;span&gt; (&lt;/span&gt;&lt;span&gt;key&lt;/span&gt;&lt;span&gt;, &lt;/span&gt;&lt;span&gt;value&lt;/span&gt;&lt;span&gt;) &lt;/span&gt;&lt;span&gt;in&lt;/span&gt;&lt;span&gt; secretPairs)&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;            sb.&lt;/span&gt;&lt;span&gt;AppendLine&lt;/span&gt;&lt;span&gt;(&lt;/span&gt;&lt;span&gt;$&quot;        [&lt;/span&gt;&lt;span&gt;\&quot;&lt;/span&gt;&lt;span&gt;{&lt;/span&gt;&lt;span&gt;key&lt;/span&gt;&lt;span&gt;}&lt;/span&gt;&lt;span&gt;\&quot;&lt;/span&gt;&lt;span&gt;] = &lt;/span&gt;&lt;span&gt;{&lt;/span&gt;&lt;span&gt;_ToVerbatimString&lt;/span&gt;&lt;span&gt;(&lt;/span&gt;&lt;span&gt;value&lt;/span&gt;&lt;span&gt;)}&lt;/span&gt;&lt;span&gt;,&quot;&lt;/span&gt;&lt;span&gt;);&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;        sb.&lt;/span&gt;&lt;span&gt;AppendLine&lt;/span&gt;&lt;span&gt;(&lt;/span&gt;&lt;span&gt;&quot;    };&quot;&lt;/span&gt;&lt;span&gt;);&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;        sb.&lt;/span&gt;&lt;span&gt;AppendLine&lt;/span&gt;&lt;span&gt;(&lt;/span&gt;&lt;span&gt;&quot;}&quot;&lt;/span&gt;&lt;span&gt;);&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;        context.&lt;/span&gt;&lt;span&gt;AddSource&lt;/span&gt;&lt;span&gt;(&lt;/span&gt;&lt;span&gt;&quot;EnvironmentInterop.g.cs&quot;&lt;/span&gt;&lt;span&gt;, sb.&lt;/span&gt;&lt;span&gt;ToString&lt;/span&gt;&lt;span&gt;());&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;    }&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;    private&lt;/span&gt;&lt;span&gt; static&lt;/span&gt;&lt;span&gt; string&lt;/span&gt;&lt;span&gt; _ToVerbatimString&lt;/span&gt;&lt;span&gt;(&lt;/span&gt;&lt;span&gt;string&lt;/span&gt;&lt;span&gt; text&lt;/span&gt;&lt;span&gt;)&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;    {&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;        return&lt;/span&gt;&lt;span&gt; &quot;@&lt;/span&gt;&lt;span&gt;\&quot;&lt;/span&gt;&lt;span&gt;&quot;&lt;/span&gt;&lt;span&gt; +&lt;/span&gt;&lt;span&gt; text.&lt;/span&gt;&lt;span&gt;Replace&lt;/span&gt;&lt;span&gt;(&lt;/span&gt;&lt;span&gt;&quot;&lt;/span&gt;&lt;span&gt;\&quot;&lt;/span&gt;&lt;span&gt;&quot;&lt;/span&gt;&lt;span&gt;, &lt;/span&gt;&lt;span&gt;&quot;&lt;/span&gt;&lt;span&gt;\&quot;\&quot;&lt;/span&gt;&lt;span&gt;&quot;&lt;/span&gt;&lt;span&gt;) &lt;/span&gt;&lt;span&gt;+&lt;/span&gt;&lt;span&gt; &quot;&lt;/span&gt;&lt;span&gt;\&quot;&lt;/span&gt;&lt;span&gt;&quot;&lt;/span&gt;&lt;span&gt;;&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;    }&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;}&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;/div&gt;

&lt;p&gt;这段代码来自 &lt;a href=&quot;https://github.com/PCL-Community/PCL2-CE/blob/dev/PCL.Core.SourceGenerators&quot;&gt;PCL.Core.SourceGenerators&lt;/a&gt;，作用是读取环境变量中的 secrets 并生成一个字典在代码中存放它们。这个源生成器为 CI 添加了一个方便快捷的内嵌 secrets 途径，省去了原本写在 bash 脚本中大量重复的替换逻辑。&lt;/p&gt;
&lt;p&gt;不要被这段看似很复杂的 &lt;code&gt;Initialize()&lt;/code&gt; 逻辑和成吨的 &lt;code&gt;StringBuilder&lt;/code&gt; 调用吓到，有关源生成器的用法会在这一系列之后的文章中具体讲述。&lt;/p&gt;
&lt;p&gt;稍观察下代码不难发现，&lt;strong&gt;源生成器实际上就是一组跑在 Roslyn Analyzer 中的程序&lt;/strong&gt;（有关 Roslyn 是什么下文会提到），它会从项目源代码或是任何它能触及的环境中收集必要的信息，然后根据这些信息生成新的代码，最后新的代码会参与到整个项目的编译中，与项目原本的代码共同成为程序集的一部分。&lt;/p&gt;
&lt;h2&gt;“提前处理”的思想&lt;a href=&quot;#提前处理的思想&quot;&gt;&lt;span&gt;&lt;/span&gt;&lt;/a&gt;&lt;/h2&gt;
&lt;h3&gt;预处理编程&lt;a href=&quot;#预处理编程&quot;&gt;&lt;span&gt;&lt;/span&gt;&lt;/a&gt;&lt;/h3&gt;
&lt;p&gt;事实上，这类由编译器或构建系统相关组件来事先对代码动点手脚的编程思想，很多人应该并不陌生。&lt;/p&gt;
&lt;p&gt;早在 C 语言中，就存在像是宏定义、宏展开这样迷倒众人挥之不去的蜜汁特性，这实际上就是&lt;strong&gt;预处理编程&lt;/strong&gt;，或者另一个可能更多人熟悉的名字：&lt;strong&gt;宏编程&lt;/strong&gt;。&lt;/p&gt;
&lt;p&gt;预处理编程即尽可能将编译期能做的事情提前到编译期来做，而运行期只需要去调用编译期处理好的现成产物，无需即时进行处理工作，以此在某些方面极大提升程序的运行性能。&lt;/p&gt;
&lt;div&gt;
&lt;p&gt;Roslyn 源生成器并不完全是预处理编程，因为它不对已有代码进行任何修改，仅仅是生成新的代码。但配合 &lt;code&gt;partial&lt;/code&gt; 这样的语言特性，它也一样可以做到为已存在的声明填充具体的实现，这从某种意义上讲也算是修改代码了。&lt;/p&gt;
&lt;/div&gt;
&lt;p&gt;那什么场景能用到预处理编程呢？&lt;/p&gt;
&lt;h3&gt;场景需求&lt;a href=&quot;#场景需求&quot;&gt;&lt;span&gt;&lt;/span&gt;&lt;/a&gt;&lt;/h3&gt;
&lt;p&gt;我们从项目开发的一个经典需求——控制反转（即文初提到的 IoC）说起。&lt;/p&gt;
&lt;p&gt;控制反转要求我们事先写一个组件，例如一个类或一个方法，通过一些标记手段（例如 attribute）让 bootstrap 组件或固定的服务项自动发现这个组件（收集依赖），并在合适的地方使用它。这与传统的由我们手动向服务项注册组件的流程恰恰是相反的，它不再需要“调用注册方法”这一过程，将控制权转交给服务项，也就少了对服务项的直接依赖，因此极大程度上降低了代码的耦合度，可以明显提升项目可维护性。&lt;/p&gt;
&lt;p&gt;最典型的控制反转实现就是&lt;strong&gt;依赖注入&lt;/strong&gt;（dependency injection）&lt;strong&gt;模式&lt;/strong&gt;，通过声明依赖、收集依赖、注入依赖三大要素，可以直接砍掉两个模块间的依赖关系，实现解耦。&lt;/p&gt;
&lt;p&gt;依靠依赖注入模式工作的标本就是 Spring Boot，传说中的“面向注解编程”也是由其首创的。Spring Boot 的依赖注入依靠启动时对整个字节码的深度扫描（收集依赖），通过 JVM 的运行时反射特性完成注入过程。&lt;/p&gt;
&lt;p&gt;很显然，这种工作方式对程序运行速度尤其是启动速度有非常大的影响——每次启动都需要把整个 JAR 包的内容扫一遍，包内容越多，这个过程就越慢，且运行时反射实现的注入本身也会拖慢速度，由反射执行的代码必然不如直接调用效率更高。因此，Spring Boot 几乎只会在一些大型服务端看到，就是由于它实在太重太慢了，任何轻量服务和客户端开发几乎都不可能用它。&lt;/p&gt;
&lt;p&gt;那么有没有什么办法可以解决这个性能问题呢？&lt;/p&gt;
&lt;p&gt;有的兄弟，有的。我们不妨从构建工具的角度，用预处理思想看看。&lt;/p&gt;
&lt;h3&gt;引入源生成器&lt;a href=&quot;#引入源生成器&quot;&gt;&lt;span&gt;&lt;/span&gt;&lt;/a&gt;&lt;/h3&gt;
&lt;p&gt;在编译期，构建工具直接面对源代码，很显然它能得到的信息比运行期多得多，且编译期通常不怎么需要考虑性能问题——本身编译就已经是个很吃性能的活了。那 Spring Boot 在运行期对字节码进行深度扫描，为什么不能把这一过程提前到编译期进行呢？&lt;/p&gt;
&lt;p&gt;很好，思路有了：在构建工具中添加一个小插件，编译期预先收集所有依赖，然后把它硬编码到编译产物里，运行的时候直接调用就好了。&lt;/p&gt;
&lt;p&gt;这个插件是什么呢，很显然——源生成器。&lt;/p&gt;
&lt;p&gt;源生成器与这种预处理编程思想在 .NET 各类库中已经得到广泛应用，例如之前一篇文章提到的 &lt;code&gt;GeneratedRegex&lt;/code&gt; 特性，就是利用源生成器将运行期的正则表达式翻译提前到编译期。这样的用法还有不少，像是 &lt;code&gt;CommunityToolkit.Mvvm&lt;/code&gt; 的 &lt;code&gt;ObservableObject&lt;/code&gt; 自动生成可观察属性，节省大量手动构建事件的代码；标准库的 &lt;code&gt;LibraryImport&lt;/code&gt; 预处理 unmanaged 内存 marshal 过程，提升运行期的 P/Invoke 初始化性能，等等。&lt;/p&gt;
&lt;h2&gt;简要工作流程&lt;a href=&quot;#简要工作流程&quot;&gt;&lt;span&gt;&lt;/span&gt;&lt;/a&gt;&lt;/h2&gt;
&lt;p&gt;从上文其实已经可以比较清晰地看出源生成器产出代码的简要流程了，这里总结一下。&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;代码分析工具启动，加载项目引用的源生成器&lt;/li&gt;
&lt;li&gt;源生成器初始化，定义信息收集和筛选流程，注册代码生成上下文&lt;/li&gt;
&lt;li&gt;监视代码更改&lt;/li&gt;
&lt;li&gt;分析代码，收集信息，并生成需要的内容&lt;/li&gt;
&lt;li&gt;将生成的内容传给代码分析工具，经由该工具传给具体需求方，如 IDE 或构建工具&lt;/li&gt;
&lt;li&gt;重复步骤 3 ~ 6&lt;/li&gt;
&lt;/ol&gt;
&lt;p&gt;上述流程中的“代码分析工具”即 &lt;strong&gt;Roslyn&lt;/strong&gt;，学名 &lt;strong&gt;.NET 编译器平台&lt;/strong&gt;，是微软为 .NET 体系打造的一组编译与代码分析工具库。简单来讲，它就是 .NET SDK 的一部分，专门负责编译和分析代码。&lt;/p&gt;
&lt;h2&gt;结语&lt;a href=&quot;#结语&quot;&gt;&lt;span&gt;&lt;/span&gt;&lt;/a&gt;&lt;/h2&gt;
&lt;p&gt;本文讲述了 Roslyn 源生成器的概念、预处理编程的思想及源生成器的简要工作流程。&lt;/p&gt;
&lt;p&gt;那么如何自己写一个源生成器呢？&lt;/p&gt;
&lt;p&gt;同时，源生成器在处理源码时免不了跟语法结构打交道，在 .NET 开发最常见的 C# 中，各类语法在编译器眼中的结构都是什么样子的呢？&lt;/p&gt;
&lt;p&gt;欲知后事如何，且看&lt;a href=&quot;/post/note/dotnet/knowledge-sourcegeneration-2&quot;&gt;下回&lt;/a&gt;分解。&lt;/p&gt;</content:encoded><category>category:笔记</category><category>category:.NET</category><category>tag:C#</category><category>tag:.NET</category><category>tag:源生成器</category></item><item><title>2025 年终总结</title><link>https://blog.rqwq.link/post/life/endsum/2025</link><guid isPermaLink="false">life/endsum/2025</guid><description>不知不觉又过了一年呢，春节假期临近，也该回想回想这一年了。
无数“第一次”与无数重试
2025 年迎来了我人生的许多“第一次”。</description><pubDate>Wed, 21 Jan 2026 05:11:34 GMT</pubDate><content:encoded>&lt;p&gt;不知不觉又过了一年呢，春节假期临近，也该回想回想这一年了。&lt;/p&gt;
&lt;h2&gt;无数“第一次”与无数重试&lt;a href=&quot;#无数第一次与无数重试&quot;&gt;&lt;span&gt;&lt;/span&gt;&lt;/a&gt;&lt;/h2&gt;
&lt;p&gt;2025 年迎来了我人生的许多“第一次”。&lt;/p&gt;
&lt;p&gt;第一次高考，第一次上大学，第一次在外面住了半年没回家，第一次旷了一学期的水课，第一次带别人逛漫展，第一次买官方周边，第一次打算法竞赛，第一次接触树莓派和嵌入式开发，第一次主导项目架构设计，第一次配机房，第一次拿省奖，第一次负责计算机类考试的技术支持，第一次...&lt;/p&gt;
&lt;p&gt;太多了，根本数不清。&lt;/p&gt;
&lt;p&gt;从 6 月份的暑假开始，陆陆续续接触了许多之前从未有机会接触的新事物，也从未感到自己如此有过学习和进步的动力。从那时一直到寒假前，感觉自己每天都在拼命想着去接触点新知识，提升自己的能力，适应那些似乎每天都在不断变化的新事物。&lt;/p&gt;
&lt;p&gt;初次尝试总是伴随着各种失败和重试，这一年下来也不知道搞砸了多少东西。像是差点干掉了学校机房的一台机器，爆了一个树莓派 4B 的 EC，扯断了三个马达的接线柱，为了买个周边 Eco 了半个月，旷课被老师点到 5 分钟飞奔去教室道歉……当然我相信，只要敢于直面挑战，成堆的问题和错误总是会塑造一个更好的我。&lt;/p&gt;
&lt;h2&gt;社会化与新视野&lt;a href=&quot;#社会化与新视野&quot;&gt;&lt;span&gt;&lt;/span&gt;&lt;/a&gt;&lt;/h2&gt;
&lt;p&gt;从踏进大学校门那一刻起，就算是开始接触多元化的社会了。&lt;/p&gt;
&lt;p&gt;早时全在学校里面闷着，从那时开始才感受到世界播撒下的色彩是多么丰富——当然，各种奇怪的、未知的物种也的确很丰富。&lt;/p&gt;
&lt;p&gt;社会中就是存在各种对自己而言完全无法融入的元素，我们应当学会与异己共存，而非去排斥所有自己看来不合理的东西。所谓「存在即合理」嘛，管他们到底为什么，反正不影响自己就好啦。&lt;/p&gt;
&lt;p&gt;与此同时，社交也是生活里必不可少的一环。&lt;/p&gt;
&lt;p&gt;总是会有各种各样的事情需要我们与别人交流，诸如工作、学习、各种活动等等，甚至是同学聚会，都总是离不开社交。这一年下来，我社交能力也增长了不少，不过到头来，感觉各种奇怪方面的能力像是扯皮互怼增长尤为多。&lt;/p&gt;
&lt;h2&gt;开源参与和项目协作&lt;a href=&quot;#开源参与和项目协作&quot;&gt;&lt;span&gt;&lt;/span&gt;&lt;/a&gt;&lt;/h2&gt;
&lt;p&gt;年初二三月时加入了 GitHub 的 PCL-Community 组织，开始与开源贡献者们一同为 PCL CE 提交代码。&lt;/p&gt;
&lt;p&gt;期间解决了不少难题，结识了很多同样热爱开源和 .NET 生态的伙伴们，也见证了组织这一年的成长与进步。&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;不妙，要被小洛包包场了&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;参与了不少规范与底层架构的设计和制作，自己的 C# 实力随着项目开发也增长了不少。似乎也就是从这时起，我的「main language」已经彻底从 JVM 的 Kotlin 转向了 C#，也发掘了不少 .NET 生态的优越性与强大的项目能力。&lt;/p&gt;
&lt;h2&gt;AIGC 的普及与未来的严峻挑战&lt;a href=&quot;#aigc-的普及与未来的严峻挑战&quot;&gt;&lt;span&gt;&lt;/span&gt;&lt;/a&gt;&lt;/h2&gt;
&lt;p&gt;从 2022 年 11 月首个面向大众的 LLM 生成式对话工具 ChatGPT 发布开始，截至今日，AIGC 用了短短三年的时间就向全人类证明了它巨大的生产力潜能。&lt;/p&gt;
&lt;p&gt;2025 这一整年，科技与互联网圈子的主要内容，被各式各样 AI 工具长期霸占，并伴随着在未来几年内继续霸占下去的趋势。从年初开始，AI 视觉听觉、AI 生图、AI 视频制作、AI 多模态对话、AI 代码辅助等等各种不同姿态的 AIGC 工具层出不穷，AI 逐渐深入到了我们的学习、工作乃至日常生活中，已然掀起了巨大的生产力变革。&lt;/p&gt;
&lt;p&gt;也因此，我们常说，2025 年是互联网被 AI 冲刷的支离破碎的一年。&lt;/p&gt;
&lt;p&gt;这一年发展最令人印象深刻的就是 AI 辅助编程工具，也就是我们常听到的 Coding Agent。像是境外的 Claude Code、Cursor，以及内地的 Trae 等工具，在这短短一年时间里，已经有了给出详细的自然语言描述就能帮你完成整个项目这样的强大能力。虽然 AI 写的代码质量不算太高，有时还会有各种奇妙的 Bug，但是能独立完成一个项目的架构设计与各处不同功能的代码实现，已经是一个巨大的进步了。这意味着在 AI 的辅助下，相比没有 AI 的时代，&lt;strong&gt;我们完全有希望将一个项目或目标的平均工期压缩数倍&lt;/strong&gt;——因为一个完整的项目中，曾经最耗时间的往往就是现阶段 AI 完全有能力做好的简单、重复的工作。&lt;/p&gt;
&lt;p&gt;同样，在大众化方向，AI 也没让我们失望过。今年最让人印象深刻的就是字节的一个叫“豆包”的东西，它是国产多模态 AI 对话工具的突出代表。这一年我周边的同学和老师似乎都像是突然被嵌入了什么信息一样，都开始用起了豆包。虽然我觉得它不怎么好用吧，但是他们用的挺舒服的，也的确在某种程度上提高了不少生产力——当然，什么都指望 AI 会让人变笨也是真的。事实证明，没有足够媒介素养的人用 AI 用多了会让自己的智商变得比 AI 低，我身边的案例也比比皆是。&lt;/p&gt;
&lt;p&gt;AIGC 发展的同时也带来了许多问题，就像上文提到的一个很明显的现象，什么都指望 AI 会让人变笨。&lt;/p&gt;
&lt;p&gt;但最严峻的问题当属 AIGC 普及对人类生产力的挑战：很多曾经需要大量人力来做的事情，现在只需要 AI 配合少量人力就能完成，而且比纯人力完成得好，那要人干嘛用呢？由此就引入了社会上的一些现象，比如，这一年有不少人实际上就是因为 AI 丢了工作——他们做的不如 AI 好，或是企业单纯由于引入 AI 而不需要那么多人了，所以他们被辞退了。这类现象势必对整个人类社会产生深远的影响，未来 AI 必然会在更高程度上取代人力，因此那些显得“多余”出来的人究竟何去何从，是值得社会好好考量的问题。&lt;/p&gt;
&lt;p&gt;对此，我也没少思考过，如何才能做到不被 AI 替代，到底什么是 AI 无法替代的。&lt;/p&gt;
&lt;p&gt;最后，附上一句来自我老师的观点。&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;以后的社会，必然是 &lt;strong&gt;会用 AI&lt;/strong&gt; 的人淘汰掉 &lt;strong&gt;不会用 AI&lt;/strong&gt; 的人。&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;将 AI 作为工具去提高&lt;strong&gt;自己&lt;/strong&gt;的生产力，而非仅仅依赖 AI 完成一切，才不会被这个社会丢下。&lt;/p&gt;
&lt;h2&gt;结语&lt;a href=&quot;#结语&quot;&gt;&lt;span&gt;&lt;/span&gt;&lt;/a&gt;&lt;/h2&gt;
&lt;p&gt;由于没有什么其他很值得的回忆的事情了，便就此结束罢。&lt;/p&gt;
&lt;p&gt;不知不觉一年又过去，希望 2026 会更精彩呀。&lt;/p&gt;
&lt;p&gt;最后，附上一段从一本书的后记摘来的文案。&lt;/p&gt;

摘自《优化的艺术》后记：关于未来——在这片星空下（作者：lhx077）
&lt;div&gt;
&lt;p&gt;……&lt;/p&gt;
&lt;p&gt;在这几年的技术苦旅中，我无数次地回溯到 Native 层，回溯到那些由 C++ 构建的基石，我必须坦白，即便到了 2025 年，我依然是一个 C++ 的坚定维护者，我迷恋那种亲手控制每一个字节的快感，迷恋那种贴着金属裸奔的锋利，但这种爱如今却夹杂着深深的无力感，看着最近发布的 C++ 标准，我仿佛看到了一位曾经精干的战神，如今却穿上了层层叠叠、繁复累赘的铠甲，步履蹒跚，标准委员会似乎陷入了一种“特性的贪婪”，试图把所有现代语言的语法糖都塞进这个古老的躯壳里，Modules 的落地依然步履维艰，Concept 虽然美好却让报错信息依旧像天书，语法的复杂性已经超越了人类大脑的解析缓存，我们曾经引以为傲的 “零开销抽象”，在日益膨胀的标准库面前，显得有些讽刺。&lt;/p&gt;
&lt;p&gt;与之相对的，是 Rust 的崛起。&lt;/p&gt;
&lt;p&gt;实话，我到现在依然在情感上 “反对” 全面倒向 Rust，我厌恶它那像教导主任一样喋喋不休的编译器，厌恶它为了安全而牺牲的编码自由度，但理智告诉我，我必须理解它，甚至必须承认它的优越性，Rust 对 C++ 的颠覆，不是性能上的，而是工程哲学上的——C++ 相信程序员 “知道自己在做什么”，而 Rust 假设 “你会犯错，所以我要没收你的枪”，这种近乎偏执的安全性，在 2025 年这个 AI 生成代码泛滥的时代，这就引出了一个让我最近深思许久的观点：Rust 或许是目前世界上最适合 AI 编程的语言。为什么？因为 AI 是基于概率的，它是会 “幻觉” 的，当 AI 用 C++ 写代码时，它可能会写出一个极其隐蔽的内存泄漏或指针越界，而人类很难在 Review 时发现；但当 AI 写 Rust 时，Rust 那严苛到变态的 Borrow Checker 就成了 AI 最好的 “守门员”，只要代码能通过编译，就至少证明了它在内存安全上是逻辑自洽的，AI 的创造力加上 Rust 的约束力，这简直是天作之合。顺着这个逻辑，在这个黄昏的随想中，我甚至敢于做一个更大胆的预测：在不远的未来，或许会出现一种完全 “AI 专用” 的编程语言，它不再为人而设计，不再考虑可读性、不再考虑语法糖是否优雅，它的语法结构将完全符合大模型的 Transformer 注意力机制，它的底层逻辑将直接映射数学证明，它可能是一种充满了形式化验证约束、对人类来说如同天书、但对 AI 来说如鱼得水的语言，在那时，编程将彻底分化，人类负责架构与逻辑的 Prompt，而这种 “AI 汇编” 将高效、零错误地运行在世界的底层，这或许才是编程语言进化的终极形态。&lt;/p&gt;
&lt;p&gt;……&lt;/p&gt;
&lt;p&gt;大家都在追逐热点。昨天是 Web3，今天是 LLM Agent。很少有人愿意静下心来，去啃一本像《深入理解计算机系统》这样的 “老书”，很少有人愿意去调试一个复杂死锁的堆栈，很少有人愿意去关心一个字节对齐对性能的影响。许多新人被灌输了错误的观念：只要能跑通 Demo 就是胜利。他们用着最先进的框架，却连 HTTP 状态码都认不全；他们在大谈微服务架构，却连进程和线程的区别都说不清。&lt;/p&gt;
&lt;p&gt;在这本书的后记中，我想对所有读者，尤其是年轻的开发者说：慢一点。别被 AI 的效率蒙蔽了双眼。代码不仅是工具，它是逻辑的艺术，是思维的结晶。在 2025 年，脚踏实地变得前所未有的奢侈，也前所未有的重要。当所有人都在用 AI 生成代码时，代码的产量会爆炸式增长，而代码的质量会急剧恶化，未来的软件世界将充斥着大量由 AI 生成的、还能跑但谁也看不懂的 “黑盒代码”，这时候，谁能站出来拯救系统？是那些只会写 Prompt 的人吗？不，是那些真正理解内存布局、理解线程调度、理解 TCP/IP 协议栈、理解编译器优化的 Deep Programmer，只有当你深刻理解了.NET 的底层机制，你才能一眼看出 AI 生成的代码中潜藏的 GC 压力；只有当你精通 C++ 与 Rust 的内存模型，你才能在 AI 出现幻觉时，修正那些致命的逻辑错误。AI 是放大器，它让平庸者更平庸，因为他们更依赖工具；它让卓越者更卓越，因为工具让他们从繁琐中解脱，去思考更本质的架构。&lt;/p&gt;
&lt;p&gt;……&lt;/p&gt;
&lt;p&gt;如果你问我，在这个 AI 横行的时代，人类程序员最后的尊严是什么？&lt;/p&gt;
&lt;p&gt;我想，就是当系统崩溃、AI 束手无策、所有黑盒都失效的那一刻，你点燃一支烟，淡定地打开调试器，看着那一行行代码，说一句：“别慌，我知道发生了什么。”&lt;/p&gt;
&lt;/div&gt;
</content:encoded><category>category:随笔</category><category>category:日常</category><category>tag:年终总结</category></item><item><title>记一次基于 Template 创建仓库的爆炸经历</title><link>https://blog.rqwq.link/post/life/daily/do-not-use-github-template-feature</link><guid isPermaLink="false">life/daily/do-not-use-github-template-feature</guid><description>不要轻易使用 GitHub 的 Template 功能，除非...你真的只是需要一个 template</description><pubDate>Tue, 20 Jan 2026 14:47:03 GMT</pubDate><content:encoded>&lt;h2&gt;误用 Template 小故事&lt;a href=&quot;#误用-template-小故事&quot;&gt;&lt;span&gt;&lt;/span&gt;&lt;/a&gt;&lt;/h2&gt;
&lt;p&gt;GitHub 的 Template 功能不会保留来源仓库的任何 history，也就是所有内容看起来都出自你自己，通过一个叫 &lt;code&gt;Initial commit&lt;/code&gt; 的提交。&lt;/p&gt;
&lt;p&gt;起因是今天上午给一个用了 template 的仓库（没错就是 blog repo）同步上游，寻思它本来就没有 history 对吧，干脆 squash merge 进去得了。&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;&lt;span&gt;&lt;span&gt;git&lt;/span&gt;&lt;span&gt; remote&lt;/span&gt;&lt;span&gt; add&lt;/span&gt;&lt;span&gt; upstream&lt;/span&gt;&lt;span&gt; https://github.com/cosZone/astro-koharu.git&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;git&lt;/span&gt;&lt;span&gt; fetch&lt;/span&gt;&lt;span&gt; upstream&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;git&lt;/span&gt;&lt;span&gt; --squash&lt;/span&gt;&lt;span&gt; --allow-unrelated-histories&lt;/span&gt;&lt;span&gt; upstream/main&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;然后不出意外，就出意外了，这个 git 操作造成了 68 个 conflicts。对，虽然很不合理，但是它就是有 68 个，所有上游更改过和我更改过的文件全 conflict 了！&lt;/p&gt;
&lt;p&gt;然后查了查才发现，原因就是没有 history 导致 Git 不知道这个更改是基于什么版本的，也不知道当前的版本跟更改有什么关系，只好把它标成 conflict 了——因为它没办法找 diff。&lt;/p&gt;
&lt;p&gt;然后的然后，我下午又以同样的方式同步了一次，结果它又 conflict 了！这次倒不是因为没 history，哦，也可以说是因为没 history，因为 squash 并不会保留 history，而是直接把所有 commits 合并为一个 commit 交上去——因此仍然没办法找 diff。&lt;/p&gt;
&lt;p&gt;因此请不要轻易使用 GitHub 的 Template 功能！否则你也会被 conflicts 炸掉的！&lt;/p&gt;
&lt;p&gt;于是我就忆起很多 repo 明明有需要同步的需求但是还是把自己作为 template 提供，比如我的上游 &lt;a href=&quot;https://github.com/cosZone/astro-koharu&quot;&gt;cosZone/astro-koharu&lt;/a&gt;。遂在此呼吁此类仓库的作者不再将仓库作为 template 提供，而是建议使用者直接 fork，这样解决了更新的冲突问题，还能刷 fork 数量，一举两得。&lt;/p&gt;
&lt;h2&gt;适合 Template 的情况&lt;a href=&quot;#适合-template-的情况&quot;&gt;&lt;span&gt;&lt;/span&gt;&lt;/a&gt;&lt;/h2&gt;
&lt;p&gt;也有的仓库是适合作为 template 提供的，那么，什么仓库适合呢？&lt;/p&gt;
&lt;p&gt;答案是确实符合&lt;strong&gt;模板&lt;/strong&gt;这个概念的仓库，比如一个 .NET 的 hello world 仓库，一个基于某个 SDK 的参考应用程序，一段调用 API 的示例代码，等等。这类仓库一定不会产生需要&lt;strong&gt;后续同步更改&lt;/strong&gt;的需求，因为使用者只是参考仓库的设计思路、使用方式或是文件结构，实际内容全是使用者自己产生的。&lt;/p&gt;
&lt;p&gt;因此有一个简单的判断方法，就是思考这个仓库的用户需不需要频繁同步更改。如果需要同步，就不适合 template，反之则通常是适合的。&lt;/p&gt;
&lt;h2&gt;VSCode 小技巧&lt;a href=&quot;#vscode-小技巧&quot;&gt;&lt;span&gt;&lt;/span&gt;&lt;/a&gt;&lt;/h2&gt;
&lt;p&gt;最后放一个我在解决冲突的时候发现的小技巧：VSCode 的 Merge Editor 是可以直接打开的，而不需要进文件编辑页面再手动点按钮。&lt;/p&gt;
&lt;p&gt;方法很简单，去 VSCode 设置里搜索 &lt;code&gt;git.mergeEditor&lt;/code&gt; 然后勾上就好了。&lt;/p&gt;</content:encoded><category>category:随笔</category><category>category:日常</category><category>tag:Git</category><category>tag:GitHub</category><category>tag:VSCode</category></item><item><title>macOS 禁用烦人更新提示</title><link>https://blog.rqwq.link/post/life/trick/macos-disable-update-notification</link><guid isPermaLink="false">life/trick/macos-disable-update-notification</guid><description>前两天 macOS 又在给我推送那个烦人的液态玻璃更新了，苹果你对你造的破烂 M1 是真自信啊。
遂寻如何禁用更新，得此法：
defaults write com.apple.SoftwareUpdate MajorOSUserNotificationDate -date &quot;2030-02-07</description><pubDate>Mon, 19 Jan 2026 09:59:44 GMT</pubDate><content:encoded>&lt;p&gt;前两天 macOS 又在给我推送那个烦人的液态玻璃更新了，苹果你对你造的破烂 M1 是真自信啊。&lt;/p&gt;
&lt;p&gt;遂寻如何禁用更新，得此法：&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;&lt;span&gt;&lt;span&gt;defaults&lt;/span&gt;&lt;span&gt; write&lt;/span&gt;&lt;span&gt; com.apple.SoftwareUpdate&lt;/span&gt;&lt;span&gt; MajorOSUserNotificationDate&lt;/span&gt;&lt;span&gt; -date&lt;/span&gt;&lt;span&gt; &quot;2030-02-07 23:22:47 +0000&quot;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;这样 2030 年以前它就不会拿更新烦你了。&lt;/p&gt;
&lt;p&gt;什么你问 2030 年以后怎么办？到那时候 M1 还能不能用都不知道，管他呢。&lt;/p&gt;</content:encoded><category>category:随笔</category><category>category:奇淫巧技</category><category>tag:macOS</category><category>tag:Apple</category><category>tag:更新</category></item><item><title>.NET 内存模型简介</title><link>https://blog.rqwq.link/post/note/dotnet/knowledge-memorymodel</link><guid isPermaLink="false">note/dotnet/knowledge-memorymodel</guid><description>简介
本文记录 .NET 内存模型的基本概念，详细解释其中的堆与栈为何物及它们的具体作用。没有意外的话，这里介绍的内存模型应该与大多数语言基本一致，因为现代面向对象语言用的基本都是同一套东西，无非就是手动管理内存与自动管理内存的区别，有关这一点区别也会在后面说明。</description><pubDate>Sun, 18 Jan 2026 16:25:01 GMT</pubDate><content:encoded>&lt;h2&gt;简介&lt;a href=&quot;#简介&quot;&gt;&lt;span&gt;&lt;/span&gt;&lt;/a&gt;&lt;/h2&gt;
&lt;p&gt;本文记录 .NET 内存模型的基本概念，详细解释其中的&lt;strong&gt;堆&lt;/strong&gt;与&lt;strong&gt;栈&lt;/strong&gt;为何物及它们的具体作用。没有意外的话，这里介绍的内存模型应该与大多数语言基本一致，因为现代面向对象语言用的基本都是同一套东西，无非就是手动管理内存与自动管理内存的区别，有关这一点区别也会在后面说明。&lt;/p&gt;
&lt;p&gt;若无特殊说明，本文所有代码示例均使用 C#。&lt;/p&gt;
&lt;p&gt;.NET 托管内存主要分为两部分：负责存储代码执行各层逻辑中必要数据的&lt;strong&gt;栈&lt;/strong&gt;，与用来丢各种杂七杂八东西的&lt;strong&gt;堆&lt;/strong&gt;。这两部分共同构成了 CLR 程序运行过程主要的内存占用，但其存储内容和管理逻辑有显著的区别。&lt;/p&gt;
&lt;p&gt;栈是典型的后进先出（LIFO）结构——没错就是数据结构经常提到的那个栈，它主要存储程序运行中的各类方法、调用层以及每层调用中局部的临时变量。每个线程都有自己的栈，每有一个方法调用，就会在最顶部新建一层，而每一层栈的生命周期通常都很短——只要对应的方法结束了，它对应的那一层栈空间就会被释放。&lt;/p&gt;
&lt;p&gt;而堆是一种非线性的存储结构，它存的可就多了，什么东西都有。当你写一个 &lt;code&gt;var a = new StringBuilder();&lt;/code&gt; 的时候，就有至少一个东西，也就是你 new 出来的那个对象，被丢到了堆里。当然远不止这些，因为一个 &lt;code&gt;StringBuilder&lt;/code&gt; 在构造自己的时候还会去创建别的对象，它们也会被丢到堆里面。&lt;/p&gt;
&lt;h2&gt;内存分配的基本流程&lt;a href=&quot;#内存分配的基本流程&quot;&gt;&lt;span&gt;&lt;/span&gt;&lt;/a&gt;&lt;/h2&gt;
&lt;p&gt;让我们从程序的主方法说起。&lt;/p&gt;
&lt;p&gt;我们知道，每个 .NET 程序都会从被 CLR（公共语言运行时）调用的主方法开始运行，也就是我们经常看到的：&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;&lt;span&gt;&lt;span&gt;[&lt;/span&gt;&lt;span&gt;STAThread&lt;/span&gt;&lt;span&gt;]&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;public&lt;/span&gt;&lt;span&gt; static&lt;/span&gt;&lt;span&gt; int&lt;/span&gt;&lt;span&gt; Main&lt;/span&gt;&lt;span&gt;()&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;{&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;    var&lt;/span&gt;&lt;span&gt; a&lt;/span&gt;&lt;span&gt; =&lt;/span&gt;&lt;span&gt; &quot;Hello, world!&quot;&lt;/span&gt;&lt;span&gt;;&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;    Console.&lt;/span&gt;&lt;span&gt;WriteLine&lt;/span&gt;&lt;span&gt;(a);&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;    return&lt;/span&gt;&lt;span&gt; 0&lt;/span&gt;&lt;span&gt;;&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;}&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;这个示例写了一个最基本的程序，向控制台输出 &lt;code&gt;Hello, world!&lt;/code&gt;。&lt;/p&gt;
&lt;p&gt;首先，它是在程序的主线程启动的，主线程有自己的栈空间，在 &lt;code&gt;Main&lt;/code&gt; 方法被调用时，主线程的栈里面就会被分配一块空间，用来存储 &lt;code&gt;Main&lt;/code&gt; 方法本身定义、预留的返回值、方法参数以及方法中临时的局部变量。&lt;/p&gt;
&lt;p&gt;然后，代码就开始执行了。第一行我们定义了一个变量 &lt;code&gt;a&lt;/code&gt;，并将一个字符串赋给了它，这时，堆里面就会多一个字符串 &lt;code&gt;Hello, world!&lt;/code&gt; 的对象，与此对应的，栈里面多了一个引用（也就是一个地址值，取决于操作系统，这个值可能是一个 32 位整形，也可能是 64 位），指向堆中的那个字符串。&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;&lt;em&gt;在真实环境中，这里的字符串会被丢进常量池，但现在我们不考虑这一点优化，仅认为它就是一个正常的对象。&lt;/em&gt;&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;那么这时候局部变量 &lt;code&gt;a&lt;/code&gt; 的值是什么呢？答案很明显——就是一个引用。这个地址值会存在栈中，供方法在其逻辑中使用，比如下面一行调用了 &lt;code&gt;Console.WriteLine&lt;/code&gt; 方法，将 &lt;code&gt;a&lt;/code&gt; 传了进去。这个调用又会在栈顶部新建一层，给 &lt;code&gt;WriteLine&lt;/code&gt; 方法使用，同时对于字符串 &lt;code&gt;Hello, world!&lt;/code&gt; 的引用也会被传进去。在 &lt;code&gt;WriteLine&lt;/code&gt; 方法中，这个传进来的引用会被用于在堆中寻找对应的对象，也就是地址值所对应的内存空间真实的数据。然后，这个数据，也就是 &lt;code&gt;Hello, world!&lt;/code&gt; 这段内容，就被输出到控制台了。&lt;/p&gt;
&lt;p&gt;紧接着，方法 &lt;code&gt;WriteLine&lt;/code&gt; 结束，执行流程返回到了 &lt;code&gt;Main&lt;/code&gt; 方法中，与 &lt;code&gt;WriteLine&lt;/code&gt; 对应的栈空间——也就是栈顶那一部分空间，会被直接释放。然后，执行到了 &lt;code&gt;return 0&lt;/code&gt; 语句，&lt;code&gt;Main&lt;/code&gt; 方法也随之结束了，返回值 &lt;code&gt;0&lt;/code&gt; 被写入栈中给返回值预留的空间，程序执行结束，最终 CLR 从最后的栈空间中读到 &lt;code&gt;Main&lt;/code&gt; 方法的返回值 &lt;code&gt;0&lt;/code&gt; 作为程序结束代码并退出。&lt;/p&gt;
&lt;p&gt;现在梳理一下上面的流程，内存中存了这些东西：&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;&lt;span&gt;&lt;span&gt;infographic hierarchy-structure&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;data&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;  items&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;    - label 栈 (Stack)&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;      children&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;        - label Main&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;          children&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;            - label 方法参数: void&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;            - label 变量: a (string ref)&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;            - label WriteLine 参数/返回值&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;            - label 返回值: 0&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;        - label Console.WriteLine&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;          children&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;            - label 方法参数: 传入的 a&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;            - label WriteLine 内部逻辑&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;            - label 返回值: void&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;    - label 堆 (Heap)&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;      children&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;        - label 对象: &quot;Hello, world!&quot; (string)&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;        - label 其他在 Console.WriteLine 中创建的对象&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;可知 .NET 内存模型遵循的原则是：小数据和临时占用放到栈里，大数据和可能长期使用的内容放到堆里。与此同时，可见栈中存在一个现象——方法的调用处与被调用的方法需要共享一小部分连续内存，这是栈内存实现方式的一个先天优势。&lt;/p&gt;
&lt;p&gt;这个原则在 C++ / Java 等等各种语言中也能见到，基本可以说是个计算机界的标准了。&lt;/p&gt;
&lt;h2&gt;栈与堆的使用策略&lt;a href=&quot;#栈与堆的使用策略&quot;&gt;&lt;span&gt;&lt;/span&gt;&lt;/a&gt;&lt;/h2&gt;
&lt;p&gt;我们暂且忽略在具体分配时的方法签名及返回值等基本部分，栈通常存储&lt;strong&gt;值类型本身&lt;/strong&gt;和局部变量中&lt;strong&gt;指向引用类型的引用&lt;/strong&gt;，而堆通常存储各处的引用对应的&lt;strong&gt;实际对象数据&lt;/strong&gt;。&lt;/p&gt;
&lt;p&gt;说到这里，就不得不提一下 .NET CLR 中存在的两个重要概念：值类型、引用类型。&lt;/p&gt;
&lt;p&gt;以 C# 为例，&lt;strong&gt;值类型&lt;/strong&gt;就是使用 &lt;code&gt;struct&lt;/code&gt; 声明的类型，而&lt;strong&gt;引用类型&lt;/strong&gt;则是使用 &lt;code&gt;class&lt;/code&gt; 及变体 &lt;code&gt;record&lt;/code&gt; 声明的类型。特殊的 &lt;code&gt;enum&lt;/code&gt; 在正常使用时是值类型，但实际上是引用类型（在某些情况下也会自动变为引用类型），这是一种性能优化，此处暂且不详细展开。&lt;/p&gt;
&lt;p&gt;特别地，C# 中包含了很多基本数据类型关键字，例如 &lt;code&gt;int&lt;/code&gt; &lt;code&gt;long&lt;/code&gt; &lt;code&gt;short&lt;/code&gt; &lt;code&gt;byte&lt;/code&gt; &lt;code&gt;float&lt;/code&gt; &lt;code&gt;double&lt;/code&gt; 等。这些关键字实际上是指向 CLR 值类型的语法糖，例如 &lt;code&gt;int&lt;/code&gt; 实际上是 &lt;code&gt;System.Int32&lt;/code&gt;，&lt;code&gt;long&lt;/code&gt; 实际上是 &lt;code&gt;System.Int64&lt;/code&gt;，&lt;code&gt;double&lt;/code&gt; 实际上是 &lt;code&gt;System.Double&lt;/code&gt;。&lt;/p&gt;
&lt;p&gt;来看一段具体的代码：&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;&lt;span&gt;&lt;span&gt;public&lt;/span&gt;&lt;span&gt; struct&lt;/span&gt;&lt;span&gt; ValueTypeTest&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;{&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;    public&lt;/span&gt;&lt;span&gt; int&lt;/span&gt;&lt;span&gt; Number&lt;/span&gt;&lt;span&gt;;&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;    public&lt;/span&gt;&lt;span&gt; ReferenceTypeTest&lt;/span&gt;&lt;span&gt; TestRef&lt;/span&gt;&lt;span&gt;;&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;}&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;public&lt;/span&gt;&lt;span&gt; class&lt;/span&gt;&lt;span&gt; ReferenceTypeTest&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;{&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;    public&lt;/span&gt;&lt;span&gt; int&lt;/span&gt;&lt;span&gt; Number&lt;/span&gt;&lt;span&gt;;&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;    public&lt;/span&gt;&lt;span&gt; ValueTypeTest&lt;/span&gt;&lt;span&gt; TestVal&lt;/span&gt;&lt;span&gt;;&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;    public&lt;/span&gt;&lt;span&gt; ReferenceTypeTest&lt;/span&gt;&lt;span&gt; TestRef&lt;/span&gt;&lt;span&gt;;&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;}&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;public&lt;/span&gt;&lt;span&gt; static&lt;/span&gt;&lt;span&gt; class&lt;/span&gt;&lt;span&gt; Program&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;{&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;    [&lt;/span&gt;&lt;span&gt;STAThread&lt;/span&gt;&lt;span&gt;]&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;    public&lt;/span&gt;&lt;span&gt; static&lt;/span&gt;&lt;span&gt; int&lt;/span&gt;&lt;span&gt; Main&lt;/span&gt;&lt;span&gt;()&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;    {&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;        var&lt;/span&gt;&lt;span&gt; testRef1&lt;/span&gt;&lt;span&gt; =&lt;/span&gt;&lt;span&gt; new&lt;/span&gt;&lt;span&gt; ReferenceTypeTest&lt;/span&gt;&lt;span&gt;();&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;        var&lt;/span&gt;&lt;span&gt; testVal&lt;/span&gt;&lt;span&gt; =&lt;/span&gt;&lt;span&gt; new&lt;/span&gt;&lt;span&gt; ValueTypeTest&lt;/span&gt;&lt;span&gt; { TestRef &lt;/span&gt;&lt;span&gt;=&lt;/span&gt;&lt;span&gt; testRef1 };&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;        var&lt;/span&gt;&lt;span&gt; testRef2&lt;/span&gt;&lt;span&gt; =&lt;/span&gt;&lt;span&gt; new&lt;/span&gt;&lt;span&gt; ReferenceTypeTest&lt;/span&gt;&lt;span&gt;() { TestRef &lt;/span&gt;&lt;span&gt;=&lt;/span&gt;&lt;span&gt; testRef1, TestVal &lt;/span&gt;&lt;span&gt;=&lt;/span&gt;&lt;span&gt; testVal };&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;        return&lt;/span&gt;&lt;span&gt; 0&lt;/span&gt;&lt;span&gt;;&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;    }&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;}&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;在代码的 &lt;code&gt;Main&lt;/code&gt; 方法中，我们声明了一个值类型和一个引用类型。&lt;/p&gt;
&lt;p&gt;值类型包含另一个值类型 &lt;code&gt;int&lt;/code&gt; 的数据和一个引用类型的引用，而引用类型包含值类型 &lt;code&gt;int&lt;/code&gt; 的数据和值类型 &lt;code&gt;ValueTypeTest&lt;/code&gt; 的数据，同时也包含了其本身类型的一个引用。&lt;/p&gt;
&lt;p&gt;那这段代码的内存分配是什么样子的呢？&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;&lt;span&gt;&lt;span&gt;infographic hierarchy-structure&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;data&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;  desc 此处已省略方法本身在栈中占用的基本空间，VTT 与 RTT 为 ValueTypeTest 和 ReferenceTypeTest 的简写&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;  items&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;    - label 栈 (Stack)&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;      children&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;        - label Main 方法&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;          children&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;            - label RTT #1 引用&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;            - label VTT #1 本体&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;            - label RTT #2 引用&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;        - label Main - VTT #1&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;          children&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;            - label int #1 本体&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;            - label RTT #1 引用&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;    - label 堆 (Heap)&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;      children&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;        - label RTT #1 本体&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;          children&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;            - label int #2 本体&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;            - label VTT #2 本体&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;            - label RTT 空引用&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;        - label RTT #2 本体&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;          children&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;            - label int #3 本体&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;            - label VTT #3 本体&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;            - label RTT #1 引用&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;        - label RTT #1 - VTT #2&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;          children&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;            - label int #4 本体&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;            - label RTT 空引用&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;        - label RTT #2 - VTT #3&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;          children&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;            - label int #5 本体&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;            - label RTT #1 引用&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;可见，大多数数据都会被丢到堆里，栈里面只有各种引用和临时的值类型数据。由于平时我们写代码时使用&lt;strong&gt;类&lt;/strong&gt;远多于使用&lt;strong&gt;结构体&lt;/strong&gt;，因此堆内存占用通常应远大于栈内存。&lt;/p&gt;
&lt;h2&gt;为什么要这样设计？&lt;a href=&quot;#为什么要这样设计&quot;&gt;&lt;span&gt;&lt;/span&gt;&lt;/a&gt;&lt;/h2&gt;
&lt;p&gt;从以上流程中我们不难得出，栈只负责存储引用和小数据，而大多数实际数据都存在堆里面。&lt;/p&gt;
&lt;p&gt;这种设计实际上源自对性能和灵活性的取舍。&lt;/p&gt;
&lt;p&gt;回想栈的主要作用，我们会发现它紧密伴随着程序的执行逻辑——于是，栈内存的管理效率直接决定了程序代码的运行效率。换句话说，在 CPU 速度足够的情况下，栈内存管理速度就决定了一段代码执行速度的上限。&lt;/p&gt;
&lt;p&gt;由此可见，栈需要有极高的性能来支撑程序的运行。因此它采用了非常简单的存储逻辑，即一块连续地址配合一个指针来管理分配与释放：指针前移就分配，指针后移就释放。这样的逻辑就决定了栈的 LIFO 特色，也正合适满足了方法调用间共享一段连续内存的需求。&lt;/p&gt;
&lt;p&gt;如此一来就圆满了对吧？但同时，这个逻辑也决定了栈只适合预先可知的固定内存分配逻辑，不适合存储复杂的、需要随时灵活分配和释放的内容。而由类初始化而来的对象正是这种灵活到极致的内容：它们可能一瞬间就释放，也可能连续存活好几天，更可能嵌套起多种不同的生命周期。&lt;/p&gt;
&lt;p&gt;要应对这种内存需求，计算机需要在一块连续或甚至不连续的内存中用各种复杂的算法逻辑来管理分配与释放，以保证性能的同时尽可能提高内存空间的使用效率。这块内存就是堆，一个什么都能塞的存储池，它随时准备好接受新的分配，也随时准备把用完的内存空间还给操作系统供其他程序利用。&lt;/p&gt;
&lt;h2&gt;堆内存与内存管理&lt;a href=&quot;#堆内存与内存管理&quot;&gt;&lt;span&gt;&lt;/span&gt;&lt;/a&gt;&lt;/h2&gt;
&lt;p&gt;由上可知，栈内存管理非常简单，分配与释放都是伴随程序执行自动完成的。而堆内存具有非常灵活的特点，它随时都能接受内存分配和回收请求并完成对应操作。那么，什么时候应该分配，什么时候应该回收呢？&lt;/p&gt;
&lt;p&gt;这就引出了一个老生常谈的问题——内存管理。&lt;/p&gt;
&lt;p&gt;根据语言与运行时的区别，内存管理可分为两种主要流派：自动管理与手动管理。主流的面向对象语言如 C#、Java、JavaScript 等大多数都采用自动管理，而像 C++、Rust 这样较为原生的语言则采用手动管理。&lt;/p&gt;
&lt;p&gt;手动管理即在代码中手动触发堆内存的分配与释放，而这引入了一个问题：一但管理不善，就会导致有些内存空间在用完后不会被释放，而是持续被占用。这种现象就叫&lt;strong&gt;内存泄露&lt;/strong&gt;（Memory Leak），内存泄露一旦积攒起来，程序的内存占用可能不断膨胀，最后影响到其他程序的正常工作甚至是操作系统的稳定性。因此除了上述提到的较为原生的语言，几乎所有语言均考虑易用性和程序的稳健程度而选择了不同程度的自动管理。&lt;/p&gt;
&lt;p&gt;这就得提到微软从 Java 码头叼来的经典自动内存管理机制——&lt;strong&gt;垃圾回收器&lt;/strong&gt;（Garbage Collector，即 GC）。&lt;/p&gt;
&lt;h2&gt;.NET 垃圾回收机制&lt;a href=&quot;#net-垃圾回收机制&quot;&gt;&lt;span&gt;&lt;/span&gt;&lt;/a&gt;&lt;/h2&gt;
&lt;p&gt;.NET GC 基于&lt;strong&gt;分代假设&lt;/strong&gt;：对象生存的时间越短，被回收的可能性就越大；而存活越久的对象，越倾向于继续存活。&lt;/p&gt;
&lt;p&gt;于是堆内存被 GC 分为三代：&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;Gen 0：最年轻的代，包含新分配的对象，绝大多数对象在这里就会被回收。&lt;/li&gt;
&lt;li&gt;Gen 1：作为 Gen 0 和 Gen 2 之间的缓冲区，存放从 Gen 0 幸存下来的对象。&lt;/li&gt;
&lt;li&gt;Gen 2：包含长期存在的对象，例如静态变量或在多次回收中幸存的对象。&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;其中 Gen 0 与 Gen 1 会共享一块叫做&lt;strong&gt;临时段&lt;/strong&gt;（ephemeral segment）的内存空间，这块空间大小不固定，根据微软文档的描述，取决于运行环境，它通常具有从 256M 到 4G 不等的大小。&lt;/p&gt;
&lt;p&gt;当临时段不足以分配新的对象，GC 便会被触发，在堆内存中寻找可以被回收的空间，同时使用一些算法来优化仍然存在的空间分配。&lt;/p&gt;
&lt;p&gt;GC 触发时，首先从根域开始遍历所有引用，标记不会被回收的对象，然后根据剩余空间重定向原来的引用地址，最后移动仍然被使用的空间，以尽可能释放出连续内存，减少内存碎片。在一次标准的 GC 活动中，Gen 0 的内存会被优先释放，当仍然释放不出足够的空间时，就轮到了 Gen 1。Gen 2 在正常情况下不会被回收，除非实在没有其他内存可用（例如收到了系统的内存不足消息），这时 Full GC 会被触发，清理从 Gen 0 到 Gen 2 的整个堆内存区域。&lt;/p&gt;
&lt;p&gt;此处仅简单介绍，若要了解详细的 GC 工作原理，请参阅文档 &lt;a href=&quot;https://learn.microsoft.com/en-us/dotnet/standard/garbage-collection/fundamentals&quot;&gt;Fundamentals of garbage collection&lt;/a&gt;。&lt;/p&gt;</content:encoded><category>category:笔记</category><category>category:.NET</category><category>tag:.NET</category><category>tag:C#</category><category>tag:CLR</category><category>tag:内存</category></item><item><title>.NET 新特性：基于代码生成的正则表达式</title><link>https://blog.rqwq.link/post/note/dotnet/feature-generatedregex</link><guid isPermaLink="false">note/dotnet/feature-generatedregex</guid><description>说到正则表达式这一老生常谈的话题，最容易被提及的就是它的性能——由于需要将字符串形式的表达式即时翻译成逻辑结构，这东西在实际使用时经常会以各种姿态浪费性能，甚至于有人宁肯自己写一堆 if-else 都不肯使用正则表达式，就为了避免巨大的翻译性能代价。</description><pubDate>Sun, 04 Jan 2026 03:45:23 GMT</pubDate><content:encoded>&lt;p&gt;说到&lt;strong&gt;正则表达式&lt;/strong&gt;这一老生常谈的话题，最容易被提及的就是它的性能——由于需要将字符串形式的表达式即时翻译成逻辑结构，这东西在实际使用时经常会以各种姿态浪费性能，甚至于有人宁肯自己写一堆 if-else 都不肯使用正则表达式，就为了避免巨大的翻译性能代价。&lt;/p&gt;
&lt;p&gt;那么，有没有什么办法可以优化正则表达式的性能嘞？&lt;/p&gt;
&lt;p&gt;答案是有的，实际上 .NET 早就引入过一个特性 &lt;code&gt;RegexOptions.Compiled&lt;/code&gt;，添加了这个 flag 的正则表达式会在初始化时预先编译为 CLR 动态代码，从而使匹配时的性能比不加这个 flag 高了好几倍，毕竟直接跑代码怎么也得比即时翻译更快吧。&lt;/p&gt;
&lt;p&gt;但是这样做也引入了一个明显的缺点：编译为动态代码的过程，远比正常初始化一个正则表达式耗时。因此这个特性只适用于那种初始化一次但使用很频繁的场景，当一个正则表达式只需要使用一次或很少几次，这样反而会因为初始化代价过高，导致最终综合性能还不如不预先编译。&lt;/p&gt;
&lt;p&gt;还有什么其他办法吗？&lt;/p&gt;
&lt;p&gt;有的兄弟，有的。这种编译性能损耗问题最简单的解决方法就是预先生成——那既然 .NET 有个叫源生成器的好东西，为什么不直接在编译期把原本运行期干的事情给干了呢？&lt;/p&gt;
&lt;p&gt;于是 .NET 7 引入了一个特性：基于代码生成的正则表达式。它可以在编译期根据你给出的正则表达式直接生成匹配逻辑代码，从而秒了一切优化——运行时根本就不存在表达式本身了，直接就是代码，还优化什么。&lt;/p&gt;
&lt;p&gt;这个特性通过 &lt;code&gt;GeneratedRegex&lt;/code&gt; 注解修饰一个 &lt;code&gt;partial&lt;/code&gt; 方法来触发，使用方法很简单：&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;&lt;span&gt;&lt;span&gt;using&lt;/span&gt;&lt;span&gt; System&lt;/span&gt;&lt;span&gt;.&lt;/span&gt;&lt;span&gt;Text&lt;/span&gt;&lt;span&gt;.&lt;/span&gt;&lt;span&gt;RegularExpressions&lt;/span&gt;&lt;span&gt;;&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;namespace&lt;/span&gt;&lt;span&gt; Your&lt;/span&gt;&lt;span&gt;.&lt;/span&gt;&lt;span&gt;Namespace&lt;/span&gt;&lt;span&gt;;&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;public&lt;/span&gt;&lt;span&gt; static&lt;/span&gt;&lt;span&gt; partial&lt;/span&gt;&lt;span&gt; class&lt;/span&gt;&lt;span&gt; RegexCollect&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;{&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;    [&lt;/span&gt;&lt;span&gt;GeneratedRegex&lt;/span&gt;&lt;span&gt;(&lt;/span&gt;&lt;span&gt;@&quot;\r\n|\n|\r&quot;&lt;/span&gt;&lt;span&gt;)]&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;    private&lt;/span&gt;&lt;span&gt; static&lt;/span&gt;&lt;span&gt; partial&lt;/span&gt;&lt;span&gt; Regex&lt;/span&gt;&lt;span&gt; _NewLine&lt;/span&gt;&lt;span&gt;();&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;    public&lt;/span&gt;&lt;span&gt; static&lt;/span&gt;&lt;span&gt; readonly&lt;/span&gt;&lt;span&gt; Regex&lt;/span&gt;&lt;span&gt; NewLine&lt;/span&gt;&lt;span&gt; =&lt;/span&gt;&lt;span&gt; _NewLine&lt;/span&gt;&lt;span&gt;();&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;}&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;在这个示例中，&lt;code&gt;partial&lt;/code&gt; 方法 &lt;code&gt;_NewLine()&lt;/code&gt; 被 &lt;code&gt;GeneratedRegex&lt;/code&gt; 修饰，并传入了正则表达式。然后，我们通过一个 &lt;code&gt;readonly&lt;/code&gt; 的字段 &lt;code&gt;NewLine&lt;/code&gt; 来承接方法返回的内容，即一个包含预先生成代码的正则表达式对象，最后直接像调用正常的 &lt;code&gt;Regex&lt;/code&gt; 对象一样使用它就好啦！&lt;/p&gt;
&lt;p&gt;&lt;code&gt;GeneratedRegex&lt;/code&gt; 注解的定义如下：&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;&lt;span&gt;&lt;span&gt;public&lt;/span&gt;&lt;span&gt; GeneratedRegexAttribute&lt;/span&gt;&lt;span&gt;([&lt;/span&gt;&lt;span&gt;StringSyntax&lt;/span&gt;&lt;span&gt;(&lt;/span&gt;&lt;span&gt;&quot;Regex&quot;&lt;/span&gt;&lt;span&gt;, &lt;/span&gt;&lt;span&gt;new&lt;/span&gt;&lt;span&gt; object&lt;/span&gt;&lt;span&gt;[] {&lt;/span&gt;&lt;span&gt;&quot;options&quot;&lt;/span&gt;&lt;span&gt;})] &lt;/span&gt;&lt;span&gt;string&lt;/span&gt;&lt;span&gt; pattern, RegexOptions options);&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;观察可知，使用时只需传入与 &lt;code&gt;Regex&lt;/code&gt; 的构造方法相同的两个参数 &lt;code&gt;pattern&lt;/code&gt; &lt;code&gt;options&lt;/code&gt; 即可。需要特别注意的是：这里的 &lt;code&gt;options&lt;/code&gt; 传入 &lt;code&gt;RegexOptions.Compiled&lt;/code&gt; 是没有任何作用的，因为它本身就已经是预编译了。&lt;/p&gt;
&lt;p&gt;上述代码会触发源生成器生成以下内容：&lt;/p&gt;

由于内容很长，这里折叠起来了，点我展开
&lt;pre&gt;&lt;code&gt;&lt;span&gt;&lt;span&gt;namespace&lt;/span&gt;&lt;span&gt; Your&lt;/span&gt;&lt;span&gt;.&lt;/span&gt;&lt;span&gt;Namespace&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;{&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;    partial&lt;/span&gt;&lt;span&gt; class&lt;/span&gt;&lt;span&gt; RegexCollect&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;    {&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;        /// &amp;lt;&lt;/span&gt;&lt;span&gt;remarks&lt;/span&gt;&lt;span&gt;&amp;gt;&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;        /// Pattern:&amp;lt;&lt;/span&gt;&lt;span&gt;br&lt;/span&gt;&lt;span&gt;/&amp;gt;&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;        /// &amp;lt;&lt;/span&gt;&lt;span&gt;code&lt;/span&gt;&lt;span&gt;&amp;gt;\\r\\n|\\n|\\r&amp;lt;/&lt;/span&gt;&lt;span&gt;code&lt;/span&gt;&lt;span&gt;&amp;gt;&amp;lt;&lt;/span&gt;&lt;span&gt;br&lt;/span&gt;&lt;span&gt;/&amp;gt;&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;        /// Explanation:&amp;lt;&lt;/span&gt;&lt;span&gt;br&lt;/span&gt;&lt;span&gt;/&amp;gt;&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;        /// &amp;lt;&lt;/span&gt;&lt;span&gt;code&lt;/span&gt;&lt;span&gt;&amp;gt;&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;        /// ○ Match with 2 alternative expressions, atomically.&amp;lt;&lt;/span&gt;&lt;span&gt;br&lt;/span&gt;&lt;span&gt;/&amp;gt;&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;        ///     ○ Match the string &quot;\r\n&quot;.&amp;lt;&lt;/span&gt;&lt;span&gt;br&lt;/span&gt;&lt;span&gt;/&amp;gt;&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;        ///     ○ Match a character in the set [\n\r].&amp;lt;&lt;/span&gt;&lt;span&gt;br&lt;/span&gt;&lt;span&gt;/&amp;gt;&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;        /// &amp;lt;/&lt;/span&gt;&lt;span&gt;code&lt;/span&gt;&lt;span&gt;&amp;gt;&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;        /// &amp;lt;/&lt;/span&gt;&lt;span&gt;remarks&lt;/span&gt;&lt;span&gt;&amp;gt;&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;        [&lt;/span&gt;&lt;span&gt;global&lt;/span&gt;&lt;span&gt;::&lt;/span&gt;&lt;span&gt;System&lt;/span&gt;&lt;span&gt;.&lt;/span&gt;&lt;span&gt;CodeDom&lt;/span&gt;&lt;span&gt;.&lt;/span&gt;&lt;span&gt;Compiler&lt;/span&gt;&lt;span&gt;.&lt;/span&gt;&lt;span&gt;GeneratedCodeAttribute&lt;/span&gt;&lt;span&gt;(&lt;/span&gt;&lt;span&gt;&quot;System.Text.RegularExpressions.Generator&quot;&lt;/span&gt;&lt;span&gt;, &lt;/span&gt;&lt;span&gt;&quot;8.0.13.2707&quot;&lt;/span&gt;&lt;span&gt;)]&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;        private&lt;/span&gt;&lt;span&gt; static&lt;/span&gt;&lt;span&gt; partial&lt;/span&gt;&lt;span&gt; global&lt;/span&gt;&lt;span&gt;::&lt;/span&gt;&lt;span&gt;System&lt;/span&gt;&lt;span&gt;.&lt;/span&gt;&lt;span&gt;Text&lt;/span&gt;&lt;span&gt;.&lt;/span&gt;&lt;span&gt;RegularExpressions&lt;/span&gt;&lt;span&gt;.&lt;/span&gt;&lt;span&gt;Regex&lt;/span&gt;&lt;span&gt; _NewLine&lt;/span&gt;&lt;span&gt;() &lt;/span&gt;&lt;span&gt;=&amp;gt;&lt;/span&gt;&lt;span&gt; global&lt;/span&gt;&lt;span&gt;::&lt;/span&gt;&lt;span&gt;System.Text.RegularExpressions.Generated._NewLine_1.Instance;&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;    }&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;}&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;namespace&lt;/span&gt;&lt;span&gt; System&lt;/span&gt;&lt;span&gt;.&lt;/span&gt;&lt;span&gt;Text&lt;/span&gt;&lt;span&gt;.&lt;/span&gt;&lt;span&gt;RegularExpressions&lt;/span&gt;&lt;span&gt;.&lt;/span&gt;&lt;span&gt;Generated&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;{&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;    using&lt;/span&gt;&lt;span&gt; System&lt;/span&gt;&lt;span&gt;;&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;    using&lt;/span&gt;&lt;span&gt; System&lt;/span&gt;&lt;span&gt;.&lt;/span&gt;&lt;span&gt;Buffers&lt;/span&gt;&lt;span&gt;;&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;    using&lt;/span&gt;&lt;span&gt; System&lt;/span&gt;&lt;span&gt;.&lt;/span&gt;&lt;span&gt;CodeDom&lt;/span&gt;&lt;span&gt;.&lt;/span&gt;&lt;span&gt;Compiler&lt;/span&gt;&lt;span&gt;;&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;    using&lt;/span&gt;&lt;span&gt; System&lt;/span&gt;&lt;span&gt;.&lt;/span&gt;&lt;span&gt;Collections&lt;/span&gt;&lt;span&gt;;&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;    using&lt;/span&gt;&lt;span&gt; System&lt;/span&gt;&lt;span&gt;.&lt;/span&gt;&lt;span&gt;ComponentModel&lt;/span&gt;&lt;span&gt;;&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;    using&lt;/span&gt;&lt;span&gt; System&lt;/span&gt;&lt;span&gt;.&lt;/span&gt;&lt;span&gt;Globalization&lt;/span&gt;&lt;span&gt;;&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;    using&lt;/span&gt;&lt;span&gt; System&lt;/span&gt;&lt;span&gt;.&lt;/span&gt;&lt;span&gt;Runtime&lt;/span&gt;&lt;span&gt;.&lt;/span&gt;&lt;span&gt;CompilerServices&lt;/span&gt;&lt;span&gt;;&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;    using&lt;/span&gt;&lt;span&gt; System&lt;/span&gt;&lt;span&gt;.&lt;/span&gt;&lt;span&gt;Text&lt;/span&gt;&lt;span&gt;.&lt;/span&gt;&lt;span&gt;RegularExpressions&lt;/span&gt;&lt;span&gt;;&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;    using&lt;/span&gt;&lt;span&gt; System&lt;/span&gt;&lt;span&gt;.&lt;/span&gt;&lt;span&gt;Threading&lt;/span&gt;&lt;span&gt;;&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;    /// &amp;lt;&lt;/span&gt;&lt;span&gt;summary&lt;/span&gt;&lt;span&gt;&amp;gt;Custom &amp;lt;&lt;/span&gt;&lt;span&gt;see&lt;/span&gt;&lt;span&gt; cref&lt;/span&gt;&lt;span&gt;=&lt;/span&gt;&lt;span&gt;&quot;Regex&quot;&lt;/span&gt;&lt;span&gt;/&amp;gt;-derived type for the _NewLine method.&amp;lt;/&lt;/span&gt;&lt;span&gt;summary&lt;/span&gt;&lt;span&gt;&amp;gt;&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;    [&lt;/span&gt;&lt;span&gt;GeneratedCodeAttribute&lt;/span&gt;&lt;span&gt;(&lt;/span&gt;&lt;span&gt;&quot;System.Text.RegularExpressions.Generator&quot;&lt;/span&gt;&lt;span&gt;, &lt;/span&gt;&lt;span&gt;&quot;8.0.13.2707&quot;&lt;/span&gt;&lt;span&gt;)]&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;    [&lt;/span&gt;&lt;span&gt;SkipLocalsInit&lt;/span&gt;&lt;span&gt;]&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;    file&lt;/span&gt;&lt;span&gt; sealed&lt;/span&gt;&lt;span&gt; class&lt;/span&gt;&lt;span&gt; _NewLine_1&lt;/span&gt;&lt;span&gt; : &lt;/span&gt;&lt;span&gt;Regex&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;    {&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;        /// &amp;lt;&lt;/span&gt;&lt;span&gt;summary&lt;/span&gt;&lt;span&gt;&amp;gt;Cached, thread-safe singleton instance.&amp;lt;/&lt;/span&gt;&lt;span&gt;summary&lt;/span&gt;&lt;span&gt;&amp;gt;&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;        internal&lt;/span&gt;&lt;span&gt; static&lt;/span&gt;&lt;span&gt; readonly&lt;/span&gt;&lt;span&gt; _NewLine_1&lt;/span&gt;&lt;span&gt; Instance&lt;/span&gt;&lt;span&gt; =&lt;/span&gt;&lt;span&gt; new&lt;/span&gt;&lt;span&gt;();&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;    &lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;        /// &amp;lt;&lt;/span&gt;&lt;span&gt;summary&lt;/span&gt;&lt;span&gt;&amp;gt;Initializes the instance.&amp;lt;/&lt;/span&gt;&lt;span&gt;summary&lt;/span&gt;&lt;span&gt;&amp;gt;&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;        private&lt;/span&gt;&lt;span&gt; _NewLine_1&lt;/span&gt;&lt;span&gt;()&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;        {&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;            base&lt;/span&gt;&lt;span&gt;.pattern &lt;/span&gt;&lt;span&gt;=&lt;/span&gt;&lt;span&gt; &quot;&lt;/span&gt;&lt;span&gt;\\&lt;/span&gt;&lt;span&gt;r&lt;/span&gt;&lt;span&gt;\\&lt;/span&gt;&lt;span&gt;n|&lt;/span&gt;&lt;span&gt;\\&lt;/span&gt;&lt;span&gt;n|&lt;/span&gt;&lt;span&gt;\\&lt;/span&gt;&lt;span&gt;r&quot;&lt;/span&gt;&lt;span&gt;;&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;            base&lt;/span&gt;&lt;span&gt;.roptions &lt;/span&gt;&lt;span&gt;=&lt;/span&gt;&lt;span&gt; RegexOptions.None;&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;            ValidateMatchTimeout&lt;/span&gt;&lt;span&gt;(Utilities.s_defaultTimeout);&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;            base&lt;/span&gt;&lt;span&gt;.internalMatchTimeout &lt;/span&gt;&lt;span&gt;=&lt;/span&gt;&lt;span&gt; Utilities.s_defaultTimeout;&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;            base&lt;/span&gt;&lt;span&gt;.factory &lt;/span&gt;&lt;span&gt;=&lt;/span&gt;&lt;span&gt; new&lt;/span&gt;&lt;span&gt; RunnerFactory&lt;/span&gt;&lt;span&gt;();&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;            base&lt;/span&gt;&lt;span&gt;.capsize &lt;/span&gt;&lt;span&gt;=&lt;/span&gt;&lt;span&gt; 1&lt;/span&gt;&lt;span&gt;;&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;        }&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;            &lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;        /// &amp;lt;&lt;/span&gt;&lt;span&gt;summary&lt;/span&gt;&lt;span&gt;&amp;gt;Provides a factory for creating &amp;lt;&lt;/span&gt;&lt;span&gt;see&lt;/span&gt;&lt;span&gt; cref&lt;/span&gt;&lt;span&gt;=&lt;/span&gt;&lt;span&gt;&quot;RegexRunner&quot;&lt;/span&gt;&lt;span&gt;/&amp;gt; instances to be used by methods on &amp;lt;&lt;/span&gt;&lt;span&gt;see&lt;/span&gt;&lt;span&gt; cref&lt;/span&gt;&lt;span&gt;=&lt;/span&gt;&lt;span&gt;&quot;Regex&quot;&lt;/span&gt;&lt;span&gt;/&amp;gt;.&amp;lt;/&lt;/span&gt;&lt;span&gt;summary&lt;/span&gt;&lt;span&gt;&amp;gt;&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;        private&lt;/span&gt;&lt;span&gt; sealed&lt;/span&gt;&lt;span&gt; class&lt;/span&gt;&lt;span&gt; RunnerFactory&lt;/span&gt;&lt;span&gt; : &lt;/span&gt;&lt;span&gt;RegexRunnerFactory&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;        {&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;            /// &amp;lt;&lt;/span&gt;&lt;span&gt;summary&lt;/span&gt;&lt;span&gt;&amp;gt;Creates an instance of a &amp;lt;&lt;/span&gt;&lt;span&gt;see&lt;/span&gt;&lt;span&gt; cref&lt;/span&gt;&lt;span&gt;=&lt;/span&gt;&lt;span&gt;&quot;RegexRunner&quot;&lt;/span&gt;&lt;span&gt;/&amp;gt; used by methods on &amp;lt;&lt;/span&gt;&lt;span&gt;see&lt;/span&gt;&lt;span&gt; cref&lt;/span&gt;&lt;span&gt;=&lt;/span&gt;&lt;span&gt;&quot;Regex&quot;&lt;/span&gt;&lt;span&gt;/&amp;gt;.&amp;lt;/&lt;/span&gt;&lt;span&gt;summary&lt;/span&gt;&lt;span&gt;&amp;gt;&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;            protected&lt;/span&gt;&lt;span&gt; override&lt;/span&gt;&lt;span&gt; RegexRunner&lt;/span&gt;&lt;span&gt; CreateInstance&lt;/span&gt;&lt;span&gt;() &lt;/span&gt;&lt;span&gt;=&amp;gt;&lt;/span&gt;&lt;span&gt; new&lt;/span&gt;&lt;span&gt; Runner&lt;/span&gt;&lt;span&gt;();&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;        &lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;            /// &amp;lt;&lt;/span&gt;&lt;span&gt;summary&lt;/span&gt;&lt;span&gt;&amp;gt;Provides the runner that contains the custom logic implementing the specified regular expression.&amp;lt;/&lt;/span&gt;&lt;span&gt;summary&lt;/span&gt;&lt;span&gt;&amp;gt;&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;            private&lt;/span&gt;&lt;span&gt; sealed&lt;/span&gt;&lt;span&gt; class&lt;/span&gt;&lt;span&gt; Runner&lt;/span&gt;&lt;span&gt; : &lt;/span&gt;&lt;span&gt;RegexRunner&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;            {&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;                /// &amp;lt;&lt;/span&gt;&lt;span&gt;summary&lt;/span&gt;&lt;span&gt;&amp;gt;Scan the &amp;lt;&lt;/span&gt;&lt;span&gt;paramref&lt;/span&gt;&lt;span&gt; name&lt;/span&gt;&lt;span&gt;=&lt;/span&gt;&lt;span&gt;&quot;inputSpan&quot;&lt;/span&gt;&lt;span&gt;/&amp;gt; starting from base.runtextstart for the next match.&amp;lt;/&lt;/span&gt;&lt;span&gt;summary&lt;/span&gt;&lt;span&gt;&amp;gt;&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;                /// &amp;lt;&lt;/span&gt;&lt;span&gt;param&lt;/span&gt;&lt;span&gt; name&lt;/span&gt;&lt;span&gt;=&lt;/span&gt;&lt;span&gt;&quot;inputSpan&quot;&lt;/span&gt;&lt;span&gt;&amp;gt;The text being scanned by the regular expression.&amp;lt;/&lt;/span&gt;&lt;span&gt;param&lt;/span&gt;&lt;span&gt;&amp;gt;&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;                protected&lt;/span&gt;&lt;span&gt; override&lt;/span&gt;&lt;span&gt; void&lt;/span&gt;&lt;span&gt; Scan&lt;/span&gt;&lt;span&gt;(&lt;/span&gt;&lt;span&gt;ReadOnlySpan&lt;/span&gt;&lt;span&gt;&amp;lt;&lt;/span&gt;&lt;span&gt;char&lt;/span&gt;&lt;span&gt;&amp;gt; &lt;/span&gt;&lt;span&gt;inputSpan&lt;/span&gt;&lt;span&gt;)&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;                {&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;                    // Search until we can&apos;t find a valid starting position, we find a match, or we reach the end of the input.&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;                    while&lt;/span&gt;&lt;span&gt; (&lt;/span&gt;&lt;span&gt;TryFindNextPossibleStartingPosition&lt;/span&gt;&lt;span&gt;(inputSpan) &lt;/span&gt;&lt;span&gt;&amp;amp;&amp;amp;&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;                           !&lt;/span&gt;&lt;span&gt;TryMatchAtCurrentPosition&lt;/span&gt;&lt;span&gt;(inputSpan) &lt;/span&gt;&lt;span&gt;&amp;amp;&amp;amp;&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;                           base&lt;/span&gt;&lt;span&gt;.runtextpos &lt;/span&gt;&lt;span&gt;!=&lt;/span&gt;&lt;span&gt; inputSpan.Length)&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;                    {&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;                        base&lt;/span&gt;&lt;span&gt;.runtextpos&lt;/span&gt;&lt;span&gt;++&lt;/span&gt;&lt;span&gt;;&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;                        if&lt;/span&gt;&lt;span&gt; (Utilities.s_hasTimeout)&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;                        {&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;                            base&lt;/span&gt;&lt;span&gt;.&lt;/span&gt;&lt;span&gt;CheckTimeout&lt;/span&gt;&lt;span&gt;();&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;                        }&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;                    }&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;                }&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;        &lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;                /// &amp;lt;&lt;/span&gt;&lt;span&gt;summary&lt;/span&gt;&lt;span&gt;&amp;gt;Search &amp;lt;&lt;/span&gt;&lt;span&gt;paramref&lt;/span&gt;&lt;span&gt; name&lt;/span&gt;&lt;span&gt;=&lt;/span&gt;&lt;span&gt;&quot;inputSpan&quot;&lt;/span&gt;&lt;span&gt;/&amp;gt; starting from base.runtextpos for the next location a match could possibly start.&amp;lt;/&lt;/span&gt;&lt;span&gt;summary&lt;/span&gt;&lt;span&gt;&amp;gt;&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;                /// &amp;lt;&lt;/span&gt;&lt;span&gt;param&lt;/span&gt;&lt;span&gt; name&lt;/span&gt;&lt;span&gt;=&lt;/span&gt;&lt;span&gt;&quot;inputSpan&quot;&lt;/span&gt;&lt;span&gt;&amp;gt;The text being scanned by the regular expression.&amp;lt;/&lt;/span&gt;&lt;span&gt;param&lt;/span&gt;&lt;span&gt;&amp;gt;&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;                /// &amp;lt;&lt;/span&gt;&lt;span&gt;returns&lt;/span&gt;&lt;span&gt;&amp;gt;true if a possible match was found; false if no more matches are possible.&amp;lt;/&lt;/span&gt;&lt;span&gt;returns&lt;/span&gt;&lt;span&gt;&amp;gt;&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;                private&lt;/span&gt;&lt;span&gt; bool&lt;/span&gt;&lt;span&gt; TryFindNextPossibleStartingPosition&lt;/span&gt;&lt;span&gt;(&lt;/span&gt;&lt;span&gt;ReadOnlySpan&lt;/span&gt;&lt;span&gt;&amp;lt;&lt;/span&gt;&lt;span&gt;char&lt;/span&gt;&lt;span&gt;&amp;gt; &lt;/span&gt;&lt;span&gt;inputSpan&lt;/span&gt;&lt;span&gt;)&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;                {&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;                    int&lt;/span&gt;&lt;span&gt; pos&lt;/span&gt;&lt;span&gt; =&lt;/span&gt;&lt;span&gt; base&lt;/span&gt;&lt;span&gt;.runtextpos;&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;                    &lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;                    // Empty matches aren&apos;t possible.&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;                    if&lt;/span&gt;&lt;span&gt; ((&lt;/span&gt;&lt;span&gt;uint&lt;/span&gt;&lt;span&gt;)pos &lt;/span&gt;&lt;span&gt;&amp;lt;&lt;/span&gt;&lt;span&gt; (&lt;/span&gt;&lt;span&gt;uint&lt;/span&gt;&lt;span&gt;)inputSpan.Length)&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;                    {&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;                        // The pattern begins with a character in the set [\n\r].&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;                        // Find the next occurrence. If it can&apos;t be found, there&apos;s no match.&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;                        int&lt;/span&gt;&lt;span&gt; i&lt;/span&gt;&lt;span&gt; =&lt;/span&gt;&lt;span&gt; inputSpan.&lt;/span&gt;&lt;span&gt;Slice&lt;/span&gt;&lt;span&gt;(pos).&lt;/span&gt;&lt;span&gt;IndexOfAny&lt;/span&gt;&lt;span&gt;(&lt;/span&gt;&lt;span&gt;&apos;&lt;/span&gt;&lt;span&gt;\n&lt;/span&gt;&lt;span&gt;&apos;&lt;/span&gt;&lt;span&gt;, &lt;/span&gt;&lt;span&gt;&apos;&lt;/span&gt;&lt;span&gt;\r&lt;/span&gt;&lt;span&gt;&apos;&lt;/span&gt;&lt;span&gt;);&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;                        if&lt;/span&gt;&lt;span&gt; (i &lt;/span&gt;&lt;span&gt;&amp;gt;=&lt;/span&gt;&lt;span&gt; 0&lt;/span&gt;&lt;span&gt;)&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;                        {&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;                            base&lt;/span&gt;&lt;span&gt;.runtextpos &lt;/span&gt;&lt;span&gt;=&lt;/span&gt;&lt;span&gt; pos &lt;/span&gt;&lt;span&gt;+&lt;/span&gt;&lt;span&gt; i;&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;                            return&lt;/span&gt;&lt;span&gt; true&lt;/span&gt;&lt;span&gt;;&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;                        }&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;                    }&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;                    &lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;                    // No match found.&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;                    base&lt;/span&gt;&lt;span&gt;.runtextpos &lt;/span&gt;&lt;span&gt;=&lt;/span&gt;&lt;span&gt; inputSpan.Length;&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;                    return&lt;/span&gt;&lt;span&gt; false&lt;/span&gt;&lt;span&gt;;&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;                }&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;        &lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;                /// &amp;lt;&lt;/span&gt;&lt;span&gt;summary&lt;/span&gt;&lt;span&gt;&amp;gt;Determine whether &amp;lt;&lt;/span&gt;&lt;span&gt;paramref&lt;/span&gt;&lt;span&gt; name&lt;/span&gt;&lt;span&gt;=&lt;/span&gt;&lt;span&gt;&quot;inputSpan&quot;&lt;/span&gt;&lt;span&gt;/&amp;gt; at base.runtextpos is a match for the regular expression.&amp;lt;/&lt;/span&gt;&lt;span&gt;summary&lt;/span&gt;&lt;span&gt;&amp;gt;&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;                /// &amp;lt;&lt;/span&gt;&lt;span&gt;param&lt;/span&gt;&lt;span&gt; name&lt;/span&gt;&lt;span&gt;=&lt;/span&gt;&lt;span&gt;&quot;inputSpan&quot;&lt;/span&gt;&lt;span&gt;&amp;gt;The text being scanned by the regular expression.&amp;lt;/&lt;/span&gt;&lt;span&gt;param&lt;/span&gt;&lt;span&gt;&amp;gt;&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;                /// &amp;lt;&lt;/span&gt;&lt;span&gt;returns&lt;/span&gt;&lt;span&gt;&amp;gt;true if the regular expression matches at the current position; otherwise, false.&amp;lt;/&lt;/span&gt;&lt;span&gt;returns&lt;/span&gt;&lt;span&gt;&amp;gt;&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;                private&lt;/span&gt;&lt;span&gt; bool&lt;/span&gt;&lt;span&gt; TryMatchAtCurrentPosition&lt;/span&gt;&lt;span&gt;(&lt;/span&gt;&lt;span&gt;ReadOnlySpan&lt;/span&gt;&lt;span&gt;&amp;lt;&lt;/span&gt;&lt;span&gt;char&lt;/span&gt;&lt;span&gt;&amp;gt; &lt;/span&gt;&lt;span&gt;inputSpan&lt;/span&gt;&lt;span&gt;)&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;                {&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;                    int&lt;/span&gt;&lt;span&gt; pos&lt;/span&gt;&lt;span&gt; =&lt;/span&gt;&lt;span&gt; base&lt;/span&gt;&lt;span&gt;.runtextpos;&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;                    int&lt;/span&gt;&lt;span&gt; matchStart&lt;/span&gt;&lt;span&gt; =&lt;/span&gt;&lt;span&gt; pos;&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;                    char&lt;/span&gt;&lt;span&gt; ch&lt;/span&gt;&lt;span&gt;;&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;                    ReadOnlySpan&lt;/span&gt;&lt;span&gt;&amp;lt;&lt;/span&gt;&lt;span&gt;char&lt;/span&gt;&lt;span&gt;&amp;gt; &lt;/span&gt;&lt;span&gt;slice&lt;/span&gt;&lt;span&gt; =&lt;/span&gt;&lt;span&gt; inputSpan.&lt;/span&gt;&lt;span&gt;Slice&lt;/span&gt;&lt;span&gt;(pos);&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;                    &lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;                    // Match with 2 alternative expressions, atomically.&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;                    {&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;                        int&lt;/span&gt;&lt;span&gt; alternation_starting_pos&lt;/span&gt;&lt;span&gt; =&lt;/span&gt;&lt;span&gt; pos;&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;                        &lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;                        // Branch 0&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;                        {&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;                            // Match the string &quot;\r\n&quot;.&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;                            if&lt;/span&gt;&lt;span&gt; (&lt;/span&gt;&lt;span&gt;!&lt;/span&gt;&lt;span&gt;slice.&lt;/span&gt;&lt;span&gt;StartsWith&lt;/span&gt;&lt;span&gt;(&lt;/span&gt;&lt;span&gt;&quot;&lt;/span&gt;&lt;span&gt;\r\n&lt;/span&gt;&lt;span&gt;&quot;&lt;/span&gt;&lt;span&gt;))&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;                            {&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;                                goto&lt;/span&gt;&lt;span&gt; AlternationBranch&lt;/span&gt;&lt;span&gt;;&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;                            }&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;                            &lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;                            pos &lt;/span&gt;&lt;span&gt;+=&lt;/span&gt;&lt;span&gt; 2&lt;/span&gt;&lt;span&gt;;&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;                            slice &lt;/span&gt;&lt;span&gt;=&lt;/span&gt;&lt;span&gt; inputSpan.&lt;/span&gt;&lt;span&gt;Slice&lt;/span&gt;&lt;span&gt;(pos);&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;                            goto&lt;/span&gt;&lt;span&gt; AlternationMatch&lt;/span&gt;&lt;span&gt;;&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;                            &lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;                            AlternationBranch&lt;/span&gt;&lt;span&gt;:&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;                            pos &lt;/span&gt;&lt;span&gt;=&lt;/span&gt;&lt;span&gt; alternation_starting_pos;&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;                            slice &lt;/span&gt;&lt;span&gt;=&lt;/span&gt;&lt;span&gt; inputSpan.&lt;/span&gt;&lt;span&gt;Slice&lt;/span&gt;&lt;span&gt;(pos);&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;                        }&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;                        &lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;                        // Branch 1&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;                        {&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;                            // Match a character in the set [\n\r].&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;                            if&lt;/span&gt;&lt;span&gt; (slice.IsEmpty &lt;/span&gt;&lt;span&gt;||&lt;/span&gt;&lt;span&gt; (((ch &lt;/span&gt;&lt;span&gt;=&lt;/span&gt;&lt;span&gt; slice[&lt;/span&gt;&lt;span&gt;0&lt;/span&gt;&lt;span&gt;]) &lt;/span&gt;&lt;span&gt;!=&lt;/span&gt;&lt;span&gt; &apos;&lt;/span&gt;&lt;span&gt;\n&lt;/span&gt;&lt;span&gt;&apos;&lt;/span&gt;&lt;span&gt;) &lt;/span&gt;&lt;span&gt;&amp;amp;&lt;/span&gt;&lt;span&gt; (ch &lt;/span&gt;&lt;span&gt;!=&lt;/span&gt;&lt;span&gt; &apos;&lt;/span&gt;&lt;span&gt;\r&lt;/span&gt;&lt;span&gt;&apos;&lt;/span&gt;&lt;span&gt;)))&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;                            {&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;                                return&lt;/span&gt;&lt;span&gt; false&lt;/span&gt;&lt;span&gt;; &lt;/span&gt;&lt;span&gt;// The input didn&apos;t match.&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;                            }&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;                            &lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;                            pos&lt;/span&gt;&lt;span&gt;++&lt;/span&gt;&lt;span&gt;;&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;                            slice &lt;/span&gt;&lt;span&gt;=&lt;/span&gt;&lt;span&gt; inputSpan.&lt;/span&gt;&lt;span&gt;Slice&lt;/span&gt;&lt;span&gt;(pos);&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;                        }&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;                        &lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;                        AlternationMatch&lt;/span&gt;&lt;span&gt;:;&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;                    }&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;                    &lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;                    // The input matched.&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;                    base&lt;/span&gt;&lt;span&gt;.runtextpos &lt;/span&gt;&lt;span&gt;=&lt;/span&gt;&lt;span&gt; pos;&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;                    base&lt;/span&gt;&lt;span&gt;.&lt;/span&gt;&lt;span&gt;Capture&lt;/span&gt;&lt;span&gt;(&lt;/span&gt;&lt;span&gt;0&lt;/span&gt;&lt;span&gt;, matchStart, pos);&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;                    return&lt;/span&gt;&lt;span&gt; true&lt;/span&gt;&lt;span&gt;;&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;                }&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;            }&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;        }&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;    }&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;}&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;

&lt;p&gt;观察生成的内容可见，源生成器创建了一个定制化的 &lt;code&gt;Regex&lt;/code&gt; 子类，并直接生成了正则表达式对应的匹配逻辑。这样，在运行时调用时，这段逻辑会被直接使用，彻底告别动态编译，极大程度上提升了正则表达式的性能。&lt;/p&gt;</content:encoded><category>category:笔记</category><category>category:.NET</category><category>tag:.NET</category><category>tag:C#</category><category>tag:正则表达式</category><category>tag:源生成器</category></item><item><title>Hello World</title><link>https://blog.rqwq.link/post/hello-world</link><guid isPermaLink="false">hello-world</guid><description>你好，世界！这是模板自带的随笔。
关于这篇文章
这篇文章故意没有设置 description 字段，用于测试以下功能：

自动描述提取 - 系统会自动从正文提取前 150 字作为描述
AI 摘要 - 如果运行了 pnpm generate:summaries，会使用 AI 生成的摘要

随笔分类</description><pubDate>Thu, 04 Jan 2024 16:00:00 GMT</pubDate><content:encoded>&lt;p&gt;你好，世界！这是模板自带的随笔。&lt;/p&gt;
&lt;h2&gt;关于这篇文章&lt;a href=&quot;#关于这篇文章&quot;&gt;&lt;span&gt;&lt;/span&gt;&lt;/a&gt;&lt;/h2&gt;
&lt;p&gt;这篇文章故意没有设置 &lt;code&gt;description&lt;/code&gt; 字段，用于测试以下功能：&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;&lt;strong&gt;自动描述提取&lt;/strong&gt; - 系统会自动从正文提取前 150 字作为描述&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;AI 摘要&lt;/strong&gt; - 如果运行了 &lt;code&gt;pnpm generate:summaries&lt;/code&gt;，会使用 AI 生成的摘要&lt;/li&gt;
&lt;/ol&gt;
&lt;h2&gt;随笔分类&lt;a href=&quot;#随笔分类&quot;&gt;&lt;span&gt;&lt;/span&gt;&lt;/a&gt;&lt;/h2&gt;
&lt;p&gt;随笔分类适合记录：&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;生活感悟&lt;/li&gt;
&lt;li&gt;年度总结&lt;/li&gt;
&lt;li&gt;随想随记&lt;/li&gt;
&lt;li&gt;个人成长&lt;/li&gt;
&lt;/ul&gt;
&lt;h2&gt;写作建议&lt;a href=&quot;#写作建议&quot;&gt;&lt;span&gt;&lt;/span&gt;&lt;/a&gt;&lt;/h2&gt;
&lt;p&gt;写博客是一件有趣的事情。这里有一些建议：&lt;/p&gt;
&lt;h3&gt;保持更新&lt;a href=&quot;#保持更新&quot;&gt;&lt;span&gt;&lt;/span&gt;&lt;/a&gt;&lt;/h3&gt;
&lt;p&gt;定期更新能让博客保持活力。哪怕是简短的记录，也比长期沉寂好。&lt;/p&gt;
&lt;h3&gt;记录真实&lt;a href=&quot;#记录真实&quot;&gt;&lt;span&gt;&lt;/span&gt;&lt;/a&gt;&lt;/h3&gt;
&lt;p&gt;真实的记录比完美的文字更有价值。不必追求每篇都是精品。&lt;/p&gt;
&lt;h3&gt;享受过程&lt;a href=&quot;#享受过程&quot;&gt;&lt;span&gt;&lt;/span&gt;&lt;/a&gt;&lt;/h3&gt;
&lt;p&gt;写作本身就是一种思考和整理的过程，享受这个过程吧。&lt;/p&gt;
&lt;h2&gt;结语&lt;a href=&quot;#结语&quot;&gt;&lt;span&gt;&lt;/span&gt;&lt;/a&gt;&lt;/h2&gt;
&lt;p&gt;这就是一篇简单的随笔示例。希望你也能在这里记录自己的故事。&lt;/p&gt;
&lt;p&gt;祝你写作愉快！&lt;/p&gt;</content:encoded><category>category:随笔</category><category>tag:随笔</category><category>tag:生活</category></item></channel></rss>