<?xml version="1.0" encoding="utf-8"?><?xml-stylesheet type="text/xsl" href="https://zzxzzk115.github.io/blog/zh/atom.xsl"?>
<feed xmlns="http://www.w3.org/2005/Atom" xml:lang="zh-CN">
  <id>https://zzxzzk115.github.io/blog/zh/</id>
  <title>Lazy_V 的博客</title>
  <subtitle>图形、游戏，以及我捣鼓的小东西。张可宣 / Lazy_V 的技术笔记。</subtitle>
  <updated>2026-10-18T15:45:34.144Z</updated>
  <generator>@vuepress/plugin-feed</generator>
  <link rel="self" href="https://zzxzzk115.github.io/blog/zh/atom.xml"/>
  <link rel="alternate" href="https://zzxzzk115.github.io/blog/zh/"/>
  <category term="游戏引擎"/>
  <category term="PhD生活"/>
  <category term="游戏开发"/>
  <category term="算法"/>
  <category term="编程语言"/>
  <category term="随笔"/>
  <category term="后端"/>
  <contributor>
    <name>Lazy_V</name>
  </contributor>
  <entry>
    <title type="text">GodotSteam 等自编译版本在 macOS 上反复弹窗确认权限？几行命令就够了</title>
    <id>https://zzxzzk115.github.io/blog/zh/posts/game-engine/godotsteam-macos-permission.html</id>
    <link href="https://zzxzzk115.github.io/blog/zh/posts/game-engine/godotsteam-macos-permission.html"/>
    <updated>2026-04-12T17:19:35.000Z</updated>
    <summary type="html"><![CDATA[<p>很简单，先重制相关权限</p>
<div class="language-bash line-numbers-mode" data-highlighter="shiki" data-ext="bash" style="--shiki-light:#383A42;--shiki-dark:#abb2bf;--shiki-light-bg:#FAFAFA;--shiki-dark-bg:#282c34"><pre class="shiki shiki-themes one-light one-dark-pro vp-code"><code class="language-bash"><span class="line"><span style="--shiki-light:#4078F2;--shiki-dark:#61AFEF">tccutil</span><span style="--shiki-light:#50A14F;--shiki-dark:#98C379"> reset</span><span style="--shiki-light:#50A14F;--shiki-dark:#98C379"> All</span><span style="--shiki-light:#50A14F;--shiki-dark:#98C379"> org.godotengine.godot</span></span></code></pre>
<div class="line-numbers" aria-hidden="true" style="counter-reset:line-number 0"><div class="line-number"></div></div></div>]]></summary>
    <content type="html"><![CDATA[<p>很简单，先重制相关权限</p>
<div class="language-bash line-numbers-mode" data-highlighter="shiki" data-ext="bash" style="--shiki-light:#383A42;--shiki-dark:#abb2bf;--shiki-light-bg:#FAFAFA;--shiki-dark-bg:#282c34"><pre class="shiki shiki-themes one-light one-dark-pro vp-code"><code class="language-bash"><span class="line"><span style="--shiki-light:#4078F2;--shiki-dark:#61AFEF">tccutil</span><span style="--shiki-light:#50A14F;--shiki-dark:#98C379"> reset</span><span style="--shiki-light:#50A14F;--shiki-dark:#98C379"> All</span><span style="--shiki-light:#50A14F;--shiki-dark:#98C379"> org.godotengine.godot</span></span></code></pre>
<div class="line-numbers" aria-hidden="true" style="counter-reset:line-number 0"><div class="line-number"></div></div></div><p>然后 <code>Settings -&gt; Privacy &amp; Security -&gt; Full Disk Access</code> 栏目点 <code>+</code> 加入 GodotSteam (或其他自编译版本)</p>
<p>最后，强制重新签名：</p>
<div class="language-bash line-numbers-mode" data-highlighter="shiki" data-ext="bash" style="--shiki-light:#383A42;--shiki-dark:#abb2bf;--shiki-light-bg:#FAFAFA;--shiki-dark-bg:#282c34"><pre class="shiki shiki-themes one-light one-dark-pro vp-code"><code class="language-bash"><span class="line"><span style="--shiki-light:#A0A1A7;--shiki-light-font-style:italic;--shiki-dark:#7F848E;--shiki-dark-font-style:italic"># 路径填具体对应的，这里我把 GodotSteam 放到 /Applications 里了</span></span>
<span class="line"><span style="--shiki-light:#4078F2;--shiki-dark:#61AFEF">codesign</span><span style="--shiki-light:#986801;--shiki-dark:#D19A66"> --force</span><span style="--shiki-light:#986801;--shiki-dark:#D19A66"> --deep</span><span style="--shiki-light:#986801;--shiki-dark:#D19A66"> --sign</span><span style="--shiki-light:#50A14F;--shiki-dark:#98C379"> -</span><span style="--shiki-light:#50A14F;--shiki-dark:#98C379"> /Applications/GodotSteam.app</span></span></code></pre>
<div class="line-numbers" aria-hidden="true" style="counter-reset:line-number 0"><div class="line-number"></div><div class="line-number"></div></div></div>]]></content>
    <author>
      <name>Lazy_V</name>
    </author>
    <category term="游戏引擎"/>
    <contributor>
      <name>Lazy_V</name>
    </contributor>
    <published>2026-04-12T00:00:00.000Z</published>
    <rights>Copyright by Lazy_V</rights>
  </entry>
  <entry>
    <title type="text">IEEE TVCG 投稿指南</title>
    <id>https://zzxzzk115.github.io/blog/zh/posts/phd-life/ieee-tvcg-guidelines.html</id>
    <link href="https://zzxzzk115.github.io/blog/zh/posts/phd-life/ieee-tvcg-guidelines.html"/>
    <updated>2026-03-30T12:59:38.000Z</updated>
    <summary type="html"><![CDATA[<p>投稿前看看指南。</p>
]]></summary>
    <content type="html"><![CDATA[<p>投稿前看看指南。</p>
<!-- more -->
<h1>IEEE TVCG 投稿指南（通用总结）</h1>
<blockquote>
<p>面向 IEEE Computer Society 期刊（重点：IEEE Transactions on Visualization and Computer Graphics, TVCG）的通用投稿流程与要求总结。</p>
<p>本文为 AI 总结内容，总结自：<a href="https://www.computer.org/publications/author-resources#pubs" target="_blank" rel="noopener noreferrer">https://www.computer.org/publications/author-resources#pubs</a></p>
</blockquote>
]]></content>
    <author>
      <name>Lazy_V</name>
    </author>
    <category term="PhD生活"/>
    <contributor>
      <name>Lazy_V</name>
    </contributor>
    <published>2026-03-30T00:00:00.000Z</published>
    <rights>Copyright by Lazy_V</rights>
  </entry>
  <entry>
    <title type="text">无需头显，使用 Meta XR 仿真器，开发 OpenXR PC 原生应用</title>
    <id>https://zzxzzk115.github.io/blog/zh/posts/phd-life/openxr-simulator.html</id>
    <link href="https://zzxzzk115.github.io/blog/zh/posts/phd-life/openxr-simulator.html"/>
    <updated>2025-02-12T00:09:03.000Z</updated>
    <summary type="html"><![CDATA[<p>如果你没有 XR 头显，但你想开发 OpenXR 应用，你可以使用 Meta XR 仿真器，它还不错。</p>
]]></summary>
    <content type="html"><![CDATA[<p>如果你没有 XR 头显，但你想开发 OpenXR 应用，你可以使用 Meta XR 仿真器，它还不错。</p>
<!-- more -->
<h2>下载 Meta XR Simulator</h2>
<figure><img src="https://zzxzzk115.github.io/blog/images/posts/phd-life/openxr-simulator/download_meta_xr_simulator.png" alt="下载 Meta XR Simulator" tabindex="0" loading="lazy"><figcaption>下载 Meta XR Simulator</figcaption></figure>
<p>从 <a href="https://developers.meta.com/horizon/downloads/package/meta-xr-simulator" target="_blank" rel="noopener noreferrer">这里</a> 下载。</p>
<p>你将会得到一个压缩文件，你需要解压它，并获取必要文件。</p>
<p>我们只需要 <code>MetaXRSimulator</code> 文件夹。将它拷贝到你的本地机器的某处，例如 <code>C:\MetaXRSimulator</code>。</p>
<h2>配置默认 OpenXR 运行时 &amp; 运行</h2>
<p>在 Windows 上，有两个选项可供配置当前活跃的 OpenXR 运行时：</p>
<ul>
<li>Windows 注册表项</li>
<li>环境变量</li>
</ul>
<p>我建议你在 IDE 或者终端里使用一个临时的环境变量 (<code>XR_RUNTIME_JSON</code>) 去设置当前活跃的 OpenXR 运行时，这将不会替换系统默认的 OpenXR 运行时。</p>

<p>设置好环境变量后，在同终端内启动你的 OpenXR 应用即可！</p>
<p>玩得开心！</p>
]]></content>
    <author>
      <name>Lazy_V</name>
    </author>
    <category term="PhD生活"/>
    <contributor>
      <name>Lazy_V</name>
    </contributor>
    <published>2025-02-11T00:00:00.000Z</published>
    <rights>Copyright by Lazy_V</rights>
  </entry>
  <entry>
    <title type="text">使用 GBDK 绘制简单的精灵</title>
    <id>https://zzxzzk115.github.io/blog/zh/game-dev/retro/gameboy/gbdk/003-gbdk-simple-sprites.html</id>
    <link href="https://zzxzzk115.github.io/blog/zh/game-dev/retro/gameboy/gbdk/003-gbdk-simple-sprites.html"/>
    <updated>2026-10-17T16:08:05.000Z</updated>
    <summary type="html"><![CDATA[<p>GameBoy 屏幕分辨率为 160*144，对于现在而言，这是非常低的，但是这个分辨率对于那个像素时代而言，却已经足以显示一个个生动活泼的角色了。</p>
<p><a href="https://en.wikipedia.org/wiki/Sprite_(computer_graphics)" target="_blank" rel="noopener noreferrer">精灵 (Sprite)</a>，在计算机图形学中指 2D 的位图，并经常使用于 2D 游戏中。</p>
<p>举个例子，超级马里奥中，马里奥对应的位图就是精灵。</p>
<p>我们如何使用 GBDK 将一个精灵绘制到 GameBoy 的屏幕中呢？</p>
]]></summary>
    <content type="html"><![CDATA[<p>GameBoy 屏幕分辨率为 160*144，对于现在而言，这是非常低的，但是这个分辨率对于那个像素时代而言，却已经足以显示一个个生动活泼的角色了。</p>
<p><a href="https://en.wikipedia.org/wiki/Sprite_(computer_graphics)" target="_blank" rel="noopener noreferrer">精灵 (Sprite)</a>，在计算机图形学中指 2D 的位图，并经常使用于 2D 游戏中。</p>
<p>举个例子，超级马里奥中，马里奥对应的位图就是精灵。</p>
<p>我们如何使用 GBDK 将一个精灵绘制到 GameBoy 的屏幕中呢？</p>
<!-- more -->
<p>答案是，使用工具，设计好我们的精灵，最后将精灵数据导出为 C 数组。</p>
<p>这里，我们使用最为流行的 GBTD。</p>
<div class="hint-container info">
<p class="hint-container-title">如果你想造轮子</p>
<p>当然，自己写一个工具也是可以的，只要自己写的工具最后导出的 C 数组符合 GBDK 需要的 C 数组格式就行。</p>
</div>
<h2>GBTD 简介</h2>
<p>在 GameBoy 的世界中，地图多半是由瓦块所组成的瓦块地图 (Tile Map)，精灵实际上也是由一些小小的瓦块组成的。</p>
<p>GBTD, GameBoy Tile Designer，主要用于设计瓦块 (Tile)，并最终导出兼容 GBDK 的 C 数组。</p>
<p>它的界面是这样的：</p>
<figure><img src="https://zzxzzk115.github.io/blog/images/game-dev/retro/gameboy/gbdk/gbtd_interface_01.png" alt="GBTD 的界面" tabindex="0" loading="lazy"><figcaption>GBTD 的界面</figcaption></figure>
<div class="hint-container tip">
<p class="hint-container-title">关于 MacOS/Linux 运行 GBTD 和 GBMB</p>
<p>如果没有搭建好环境，请参考上一篇文章，先搭建好环境。</p>
<p>环境搭建好后，且跟着教程一起配置了 alias 后，可以直接使用别名打开：</p>
<div class="language-bash line-numbers-mode" data-highlighter="shiki" data-ext="bash" style="--shiki-light:#383A42;--shiki-dark:#abb2bf;--shiki-light-bg:#FAFAFA;--shiki-dark-bg:#282c34"><pre class="shiki shiki-themes one-light one-dark-pro vp-code"><code class="language-bash"><span class="line"><span style="--shiki-light:#A0A1A7;--shiki-light-font-style:italic;--shiki-dark:#7F848E;--shiki-dark-font-style:italic"># 打开 GBTD</span></span>
<span class="line"><span style="--shiki-light:#4078F2;--shiki-dark:#61AFEF">gbtd</span></span>
<span class="line"></span>
<span class="line"><span style="--shiki-light:#A0A1A7;--shiki-light-font-style:italic;--shiki-dark:#7F848E;--shiki-dark-font-style:italic"># 打开 GBMB</span></span>
<span class="line"><span style="--shiki-light:#4078F2;--shiki-dark:#61AFEF">gbmb</span></span></code></pre>
<div class="line-numbers" aria-hidden="true" style="counter-reset:line-number 0"><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div></div></div></div>
<h2>绘制第一个简单的精灵</h2>
<p>接下来，我们绘制一个 <code>8*8</code> 的笑脸，并显示在靠近屏幕中央的位置。</p>
<div class="hint-container info">
<p class="hint-container-title">为什么是 8*8</p>
<p>GameBoy 中，精灵的渲染有两种模式（二选一）：</p>
<ul>
<li><code>8*8</code></li>
<li><code>8*16</code></li>
</ul>
<p>每个瓦块的尺寸最小为 <code>8*8</code>，所以最简单的精灵都是用一个瓦块，这里我们希望精灵不要过于迷你，所以还是选择 <code>8*8</code> 的尺寸，实际上，<code>1*1</code> 都是可以的，只是依旧还是需要一个 <code>8*8</code> 的瓦块来表达。复杂一点的精灵，可能会使用 <code>8*16</code> 模式。更复杂一点的精灵，甚至可能是多个 <code>8*8</code> 的瓦片组合而成的。</p>
</div>
<p>GBTD 的最下方有一个颜色选择器。</p>
<p>左侧 L R 显示的是鼠标左键和鼠标右键对应要在画布上绘制的颜色索引。</p>
<p>右侧的四个色块代表 0 到 3 四个索引色，每个索引对应的颜色是我们自己通过调色板进行配置的，默认调色板是从白到黑的渐变的四个颜色。</p>
<figure><img src="https://zzxzzk115.github.io/blog/images/game-dev/retro/gameboy/gbdk/gbtd_color_picker.png" alt="GBTD 颜色选择器" tabindex="0" loading="lazy"><figcaption>GBTD 颜色选择器</figcaption></figure>
<p>GBTD 的左侧工具栏，默认选择了画笔工具，画笔底下是油漆桶工具。往下四个是移动工具，可以自行试试效果。再往下是垂直翻转、水平翻转、旋转以及自动更新。</p>
<figure><img src="https://zzxzzk115.github.io/blog/images/game-dev/retro/gameboy/gbdk/gbtd_left_toolbar.png" alt="GBTD 左侧工具栏" tabindex="0" loading="lazy"><figcaption>GBTD 左侧工具栏</figcaption></figure>
<div class="hint-container info">
<p class="hint-container-title">GBTD 不存在橡皮擦工具</p>
<p>GBTD 不存在橡皮擦工具，这是因为 GameBoy 的颜色模式是索引色模式，而非 RGBA 模式。</p>
<p>如果需要擦除某个画错的部分，你要做的就是使用另一个索引色来填充画错的部分。</p>
</div>
<p>当我们在颜色选择器的右侧色块区域使用鼠标左键点击其中一个色块，则鼠标左键即将绘制的颜色索引会变成你点击的那个色块的索引值，简而言之就是接下来你的鼠标左键在画布上绘制的时候画笔（或油漆桶）的颜色变成了你所选色块的颜色。鼠标右键也是一样的逻辑。这样方便我们鼠标左右两个键在画布上绘制图形。</p>
<p>我们首先选择画笔工具，使用鼠标左键点击颜色选择器右侧的索引值为 3 的黑色色块，然后在画布上绘制一个笑脸，当然，你也可以按照自己的想法绘制，只要是 <code>8*8</code> 及以内的就行。</p>
<p>然后选择油漆桶工具，选择索引值为 1 的浅灰色色块，然后填充笑脸。</p>
<p>画好后大致长这样（没认真画，教程嘛，意思意思）：</p>
<figure><img src="https://zzxzzk115.github.io/blog/images/game-dev/retro/gameboy/gbdk/first_sprite_smile_face.png" alt="第一个精灵：笑脸" tabindex="0" loading="lazy"><figcaption>第一个精灵：笑脸</figcaption></figure>
<p>绘制好了，我们就可以开始导出成 GBDK 兼容的 C 数组了。</p>
<p>当然，为了备份，你应该先保存一下。保存后，你将得到一个以 <code>.gbr</code> 后缀的文件，下次再用 GBTD 打开它就能继续编辑。</p>
<h2>导出绘制的精灵</h2>
<p>最上面主菜单点击 <code>Files/ExportTo</code>，然后配置如下：</p>
<figure><img src="https://zzxzzk115.github.io/blog/images/game-dev/retro/gameboy/gbdk/export_smile_face_as_gbdk_c_array.png" alt="导出笑脸为 GBDK 支持的 C 数组" tabindex="0" loading="lazy"><figcaption>导出笑脸为 GBDK 支持的 C 数组</figcaption></figure>
<div class="hint-container info">
<p class="hint-container-title">关键参数介绍</p>
<p>Filename - 导出文件的名称，这里我们填入 <code>Smile.c</code></p>
<p>Type - 导出数据的类型，这里我们选择 <code>GBDK C file</code>，代表导出文件中包含的数据是兼容 GBDK 的 C 数组</p>
<p>Label - 导出 C 数组的名称，我们在代码中会用到，这里我们命名为 <code>Smile</code></p>
<p>Format - 颜色格式，这里我们选择 <code>Gameboy 4 color</code></p>
</div>
<p>点击 <code>OK</code> 即可导出。导出成功后，会在 <code>.gbr</code> 所在同级目录下得到 <code>Smile.h</code> 和 <code>Smile.c</code> 文件。</p>
<p><code>.h</code> 文件中是数组相关的声明，而 <code>.c</code> 文件中是具体定义，存放了实际的精灵数据。</p>
<p>下面，我们就可以利用 C 数组，开始编写程序，绘制精灵了。</p>
<h2>开始编程吧！</h2>
<h3>使用 Visual Studio Code + Makefile 进行项目搭建</h3>
<div class="hint-container info">
<p class="hint-container-title">相关信息</p>
<p>之前的 helloworld 程序由于是单文件，过于简单，所以没有提到 Visual Studio Code 的配置以及使用 Makefile。</p>
<p>后面的项目中，我们都会使用 Visual Studio Code + Makefile 的模式来进行开发。</p>
</div>
<p>我们创建一个 <code>gb-dev</code> 目录，并在它下面创建一个 <code>first-sprite</code> 目录。</p>
<p>然后使用 Visual Studio Code 打开 <code>gb-dev</code> 目录。</p>
<p>首先配置 <code>includePath</code>，让我们可以拥有智能提示。在 <code>gb-dev</code> 目录下创建一个 <code>.vscode</code> 文件夹，在该文件夹下创建一个名为 <code>c_cpp_properties.json</code> 的配置文件：</p>
<div class="language-json line-numbers-mode" data-highlighter="shiki" data-ext="json" style="--shiki-light:#383A42;--shiki-dark:#abb2bf;--shiki-light-bg:#FAFAFA;--shiki-dark-bg:#282c34"><pre class="shiki shiki-themes one-light one-dark-pro vp-code"><code class="language-json"><span class="line"><span style="--shiki-light:#383A42;--shiki-dark:#ABB2BF">{</span></span>
<span class="line"><span style="--shiki-light:#E45649;--shiki-dark:#E06C75">    "configurations"</span><span style="--shiki-light:#383A42;--shiki-dark:#ABB2BF">: [</span></span>
<span class="line"><span style="--shiki-light:#383A42;--shiki-dark:#ABB2BF">        {</span></span>
<span class="line"><span style="--shiki-light:#E45649;--shiki-dark:#E06C75">            "name"</span><span style="--shiki-light:#383A42;--shiki-dark:#ABB2BF">: </span><span style="--shiki-light:#50A14F;--shiki-dark:#98C379">"GBDK_VSC"</span><span style="--shiki-light:#383A42;--shiki-dark:#ABB2BF">,</span></span>
<span class="line"><span style="--shiki-light:#E45649;--shiki-dark:#E06C75">            "includePath"</span><span style="--shiki-light:#383A42;--shiki-dark:#ABB2BF">: [</span></span>
<span class="line"><span style="--shiki-light:#50A14F;--shiki-dark:#98C379">                "${GBDKDIR}/include/**"</span></span>
<span class="line"><span style="--shiki-light:#383A42;--shiki-dark:#ABB2BF">            ],</span></span>
<span class="line"><span style="--shiki-light:#E45649;--shiki-dark:#E06C75">            "defines"</span><span style="--shiki-light:#383A42;--shiki-dark:#ABB2BF">: [],</span></span>
<span class="line"><span style="--shiki-light:#E45649;--shiki-dark:#E06C75">            "macFrameworkPath"</span><span style="--shiki-light:#383A42;--shiki-dark:#ABB2BF">: [],</span></span>
<span class="line"><span style="--shiki-light:#E45649;--shiki-dark:#E06C75">            "compilerPath"</span><span style="--shiki-light:#383A42;--shiki-dark:#ABB2BF">: </span><span style="--shiki-light:#50A14F;--shiki-dark:#98C379">"${GBDKDIR}/bin/lcc"</span><span style="--shiki-light:#383A42;--shiki-dark:#ABB2BF">,</span></span>
<span class="line"><span style="--shiki-light:#E45649;--shiki-dark:#E06C75">            "cStandard"</span><span style="--shiki-light:#383A42;--shiki-dark:#ABB2BF">: </span><span style="--shiki-light:#50A14F;--shiki-dark:#98C379">"gnu99"</span></span>
<span class="line"><span style="--shiki-light:#383A42;--shiki-dark:#ABB2BF">        }</span></span>
<span class="line"><span style="--shiki-light:#383A42;--shiki-dark:#ABB2BF">    ],</span></span>
<span class="line"><span style="--shiki-light:#E45649;--shiki-dark:#E06C75">    "version"</span><span style="--shiki-light:#383A42;--shiki-dark:#ABB2BF">: </span><span style="--shiki-light:#986801;--shiki-dark:#D19A66">4</span></span>
<span class="line"><span style="--shiki-light:#383A42;--shiki-dark:#ABB2BF">}</span></span></code></pre>
<div class="line-numbers" aria-hidden="true" style="counter-reset:line-number 0"><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div></div></div><p>这样，我们以后的代码就拥有智能提示了。</p>
<p>然后我们将之前导出的 <code>Smile.h</code> 和 <code>Smile.c</code> 拷贝到 <code>first-sprite</code> 目录下。</p>
<p>接着我们就可以在 <code>first-sprite</code> 目录下，开始编写主程序 <code>FirstSprite.c</code> 了：</p>
<div class="language-c line-numbers-mode" data-highlighter="shiki" data-ext="c" style="--shiki-light:#383A42;--shiki-dark:#abb2bf;--shiki-light-bg:#FAFAFA;--shiki-dark-bg:#282c34"><pre class="shiki shiki-themes one-light one-dark-pro vp-code"><code class="language-c"><span class="line"><span style="--shiki-light:#A626A4;--shiki-dark:#C678DD">#include</span><span style="--shiki-light:#50A14F;--shiki-dark:#98C379"> &#x3C;gb/gb.h></span></span>
<span class="line"><span style="--shiki-light:#A626A4;--shiki-dark:#C678DD">#include</span><span style="--shiki-light:#50A14F;--shiki-dark:#98C379"> &#x3C;stdio.h></span></span>
<span class="line"><span style="--shiki-light:#A626A4;--shiki-dark:#C678DD">#include</span><span style="--shiki-light:#50A14F;--shiki-dark:#98C379"> "Smile.h"</span></span>
<span class="line"></span>
<span class="line"><span style="--shiki-light:#A626A4;--shiki-dark:#C678DD">void</span><span style="--shiki-light:#4078F2;--shiki-dark:#61AFEF"> main</span><span style="--shiki-light:#383A42;--shiki-dark:#ABB2BF">()</span></span>
<span class="line"><span style="--shiki-light:#383A42;--shiki-dark:#ABB2BF">{</span></span>
<span class="line"><span style="--shiki-light:#A0A1A7;--shiki-light-font-style:italic;--shiki-dark:#7F848E;--shiki-dark-font-style:italic">    // 设置精灵数据</span></span>
<span class="line"><span style="--shiki-light:#A0A1A7;--shiki-light-font-style:italic;--shiki-dark:#7F848E;--shiki-dark-font-style:italic">    // 从第 0 号位的 Tile 开始，设置 No.0 的瓦块的数据</span></span>
<span class="line"><span style="--shiki-light:#A0A1A7;--shiki-light-font-style:italic;--shiki-dark:#7F848E;--shiki-dark-font-style:italic">    // 数据来源于 Smile 数组</span></span>
<span class="line"><span style="--shiki-light:#4078F2;--shiki-dark:#61AFEF">    set_sprite_data</span><span style="--shiki-light:#383A42;--shiki-dark:#ABB2BF">(</span><span style="--shiki-light:#986801;--shiki-dark:#D19A66">0</span><span style="--shiki-light:#383A42;--shiki-dark:#ABB2BF">, </span><span style="--shiki-light:#986801;--shiki-dark:#D19A66">1</span><span style="--shiki-light:#383A42;--shiki-dark:#ABB2BF">, Smile);</span></span>
<span class="line"><span style="--shiki-light:#383A42;--shiki-dark:#ABB2BF">    </span></span>
<span class="line"><span style="--shiki-light:#A0A1A7;--shiki-light-font-style:italic;--shiki-dark:#7F848E;--shiki-dark-font-style:italic">    // 设置精灵使用哪个瓦块的数据</span></span>
<span class="line"><span style="--shiki-light:#A0A1A7;--shiki-light-font-style:italic;--shiki-dark:#7F848E;--shiki-dark-font-style:italic">    // 这里是 No.0 精灵使用 No.0 瓦块数据</span></span>
<span class="line"><span style="--shiki-light:#4078F2;--shiki-dark:#61AFEF">    set_sprite_tile</span><span style="--shiki-light:#383A42;--shiki-dark:#ABB2BF">(</span><span style="--shiki-light:#986801;--shiki-dark:#D19A66">0</span><span style="--shiki-light:#383A42;--shiki-dark:#ABB2BF">, </span><span style="--shiki-light:#986801;--shiki-dark:#D19A66">0</span><span style="--shiki-light:#383A42;--shiki-dark:#ABB2BF">);</span></span>
<span class="line"></span>
<span class="line"><span style="--shiki-light:#A0A1A7;--shiki-light-font-style:italic;--shiki-dark:#7F848E;--shiki-dark-font-style:italic">    // 一个宏，内部设置 LCDC 寄存器的第 1 位为 1，</span></span>
<span class="line"><span style="--shiki-light:#A0A1A7;--shiki-light-font-style:italic;--shiki-dark:#7F848E;--shiki-dark-font-style:italic">    // 目的是开启精灵层，代表显示所有精灵</span></span>
<span class="line"><span style="--shiki-light:#383A42;--shiki-dark:#ABB2BF">    SHOW_SPRITES;</span></span>
<span class="line"></span>
<span class="line"><span style="--shiki-light:#A0A1A7;--shiki-light-font-style:italic;--shiki-dark:#7F848E;--shiki-dark-font-style:italic">    // 将精灵 No.0 移动到 (84, 78) 的位置，即屏幕中央附近</span></span>
<span class="line"><span style="--shiki-light:#4078F2;--shiki-dark:#61AFEF">    move_sprite</span><span style="--shiki-light:#383A42;--shiki-dark:#ABB2BF">(</span><span style="--shiki-light:#986801;--shiki-dark:#D19A66">0</span><span style="--shiki-light:#383A42;--shiki-dark:#ABB2BF">, </span><span style="--shiki-light:#986801;--shiki-dark:#D19A66">84</span><span style="--shiki-light:#383A42;--shiki-dark:#ABB2BF">, </span><span style="--shiki-light:#986801;--shiki-dark:#D19A66">78</span><span style="--shiki-light:#383A42;--shiki-dark:#ABB2BF">);</span></span>
<span class="line"><span style="--shiki-light:#383A42;--shiki-dark:#ABB2BF">}</span></span></code></pre>
<div class="line-numbers" aria-hidden="true" style="counter-reset:line-number 0"><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div></div></div><p><code>set_sprite_data</code> 的第二个参数是瓦块数量，这里只有一个瓦块，因此应传入 <code>1</code>。参数说明见 <a href="https://gbdk.org/docs/api/gb_8h.html" target="_blank" rel="noopener noreferrer">GBDK API 文档</a>。</p>
<p>随后，我们编写一个叫做 <code>Makefile</code> 的文件。实际上，Makefile 是 make 的配置文件，make 可以方便我们设定编译参数以及具体每个模块的编译流程。这些模块的编译流程大多是重复的，所以使用 make 这套规则去编译，节约了我们的时间，也让编译流程更加清晰。</p>
<p>我们的 <code>Makefile</code> 如下：</p>
<div class="language-makefile line-numbers-mode" data-highlighter="shiki" data-ext="makefile" style="--shiki-light:#383A42;--shiki-dark:#abb2bf;--shiki-light-bg:#FAFAFA;--shiki-dark-bg:#282c34"><pre class="shiki shiki-themes one-light one-dark-pro vp-code"><code class="language-makefile"><span class="line"><span style="--shiki-light:#A0A1A7;--shiki-light-font-style:italic;--shiki-dark:#7F848E;--shiki-dark-font-style:italic"># 设置 LCC 编译器执行路径</span></span>
<span class="line"><span style="--shiki-light:#E45649;--shiki-dark:#E06C75">CC</span><span style="--shiki-light:#383A42;--shiki-dark:#ABB2BF">  =</span><span style="--shiki-light:#50A14F;--shiki-dark:#98C379"> $(</span><span style="--shiki-light:#E45649;--shiki-dark:#E06C75">GBDKDIR</span><span style="--shiki-light:#50A14F;--shiki-dark:#98C379">)</span><span style="--shiki-light:#383A42;--shiki-dark:#E5C07B">/bin/lcc</span></span>
<span class="line"></span>
<span class="line"><span style="--shiki-light:#A0A1A7;--shiki-light-font-style:italic;--shiki-dark:#7F848E;--shiki-dark-font-style:italic"># 设置 SameBoy 执行路径</span></span>
<span class="line"><span style="--shiki-light:#E45649;--shiki-dark:#E06C75">EMU</span><span style="--shiki-light:#383A42;--shiki-dark:#ABB2BF"> =</span><span style="--shiki-light:#383A42;--shiki-dark:#E5C07B"> /Applications/SameBoy.app/Contents/MacOS/SameBoy</span></span>
<span class="line"></span>
<span class="line"><span style="--shiki-light:#A0A1A7;--shiki-light-font-style:italic;--shiki-dark:#7F848E;--shiki-dark-font-style:italic"># 默认行为，编译各个编译单元，最后链接得到 FirstSprite.gb</span></span>
<span class="line"><span style="--shiki-light:#4078F2;--shiki-dark:#61AFEF">all</span><span style="--shiki-light:#383A42;--shiki-dark:#ABB2BF">:</span></span>
<span class="line"><span style="--shiki-light:#50A14F;--shiki-dark:#98C379">	$(</span><span style="--shiki-light:#E45649;--shiki-dark:#E06C75">CC</span><span style="--shiki-light:#50A14F;--shiki-dark:#98C379">)</span><span style="--shiki-light:#383A42;--shiki-dark:#E5C07B"> -Wa-l -Wl-m -Wl-j -o FirstSprite.gb Smile.c FirstSprite.c</span></span>
<span class="line"></span>
<span class="line"><span style="--shiki-light:#A0A1A7;--shiki-light-font-style:italic;--shiki-dark:#7F848E;--shiki-dark-font-style:italic"># 编译得到 FirstSprite.gb 后，使用 SameBoy 启动</span></span>
<span class="line"><span style="--shiki-light:#4078F2;--shiki-dark:#61AFEF">run</span><span style="--shiki-light:#383A42;--shiki-dark:#ABB2BF">:</span><span style="--shiki-light:#383A42;--shiki-dark:#E06C75"> all</span></span>
<span class="line"><span style="--shiki-light:#50A14F;--shiki-dark:#98C379">	$(</span><span style="--shiki-light:#E45649;--shiki-dark:#E06C75">EMU</span><span style="--shiki-light:#50A14F;--shiki-dark:#98C379">)</span><span style="--shiki-light:#383A42;--shiki-dark:#E5C07B"> FirstSprite.gb</span></span>
<span class="line"></span>
<span class="line"><span style="--shiki-light:#A0A1A7;--shiki-light-font-style:italic;--shiki-dark:#7F848E;--shiki-dark-font-style:italic"># 清理，删除中间文件和目标文件</span></span>
<span class="line"><span style="--shiki-light:#4078F2;--shiki-dark:#61AFEF">clean</span><span style="--shiki-light:#383A42;--shiki-dark:#ABB2BF">:</span></span>
<span class="line"><span style="--shiki-light:#383A42;--shiki-dark:#E5C07B">	rm -f *.o *.lst *.map *.gb *.ihx *.sym *.cdb *.adb *.asm *.noi</span></span></code></pre>
<div class="line-numbers" aria-hidden="true" style="counter-reset:line-number 0"><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div></div></div><p>现在，make 支持几种不同的命令参数：</p>
<ul>
<li>无，即默认行为，默认为 Makefile 中第一个出现的目标，在我们的设置中，为 all</li>
<li>all，编译链接最终得到 <code>FirstSprite.gb</code> ，即 ROM 文件</li>
<li>run，编译得到 ROM 后，使用 SameBoy 模拟器运行该 ROM 文件</li>
<li>clean，清理，删除中间文件和目标文件</li>
</ul>
<p>我们可以直接使用 <code>make run</code> 来编译、链接并运行。</p>
<p>最后的效果如下：</p>
<figure><img src="https://zzxzzk115.github.io/blog/images/game-dev/retro/gameboy/gbdk/successfully_showed_first_sprite.png" alt="成功在屏幕中显示第一个精灵" tabindex="0" loading="lazy"><figcaption>成功在屏幕中显示第一个精灵</figcaption></figure>
<p>最后还可以使用 <code>make clean</code> 来删除中间文件和目标文件，回到只有源码和 Makefile 等文件的状态。</p>
<div class="hint-container tip">
<p class="hint-container-title">提示</p>
<p><code>gb-dev</code> 源码<a href="https://github.com/zzxzzk115/blog/tree/master/src/.vuepress/public/src/gb-dev/" target="_blank" rel="noopener noreferrer">看这里</a></p>
</div>
]]></content>
    <author>
      <name>Lazy_V</name>
    </author>
    <category term="游戏开发"/>
    <contributor>
      <name>Lazy_V</name>
    </contributor>
    <published>2022-12-20T00:00:00.000Z</published>
    <rights>Copyright by Lazy_V</rights>
  </entry>
  <entry>
    <title type="text">GBDK 环境搭建</title>
    <id>https://zzxzzk115.github.io/blog/zh/game-dev/retro/gameboy/gbdk/002-gbdk-preparation.html</id>
    <link href="https://zzxzzk115.github.io/blog/zh/game-dev/retro/gameboy/gbdk/002-gbdk-preparation.html"/>
    <updated>2026-10-17T16:08:05.000Z</updated>
    <summary type="html"><![CDATA[<p>GBDK 是开源工具，我们可以在 GitHub 上直接下载最新的 Release 版本。</p>
<p>我最近习惯使用 MacOS，这里我首先介绍如何在 MacOS 上搭建 GBDK 环境，并成功编译 Hello world 程序。Linux 和 MacOS 是一样的步骤。Windows 也是类似的步骤，如果有问题，欢迎随时在评论区留言。</p>
]]></summary>
    <content type="html"><![CDATA[<p>GBDK 是开源工具，我们可以在 GitHub 上直接下载最新的 Release 版本。</p>
<p>我最近习惯使用 MacOS，这里我首先介绍如何在 MacOS 上搭建 GBDK 环境，并成功编译 Hello world 程序。Linux 和 MacOS 是一样的步骤。Windows 也是类似的步骤，如果有问题，欢迎随时在评论区留言。</p>
<!-- more -->
<div class="hint-container info">
<p class="hint-container-title">本文使用的版本</p>
<p>本文记录的是 2022 年 12 月的环境，保留了当时的版本与下载链接。新环境请根据上游发布页选择对应版本。</p>
</div>
<h2>下载 GBDK</h2>
<p>撰写本文时，GBDK 的稳定版本为 v4.1.1，<a href="https://github.com/gbdk-2020/gbdk-2020/releases/download/4.1.1/gbdk-macos.tar.gz" target="_blank" rel="noopener noreferrer">点击直接下载</a>。</p>
<p>你也可以访问<a href="https://github.com/gbdk-2020/gbdk-2020/releases" target="_blank" rel="noopener noreferrer">这里</a>下载最新的版本。</p>
<h2>文件结构介绍</h2>
<p>解压下载得到的压缩包后，我们得到一个名为 <code>gbdk</code> 的文件夹，其中的文件结构如下：</p>
<ul>
<li>
 bin/ (包含所有预编译好的可执行程序)</li>
<li>
 include/ (包含相关头文件)</li>
<li>
 lib/ (包含相关库文件)</li>
<li>
 examples/ (包含提供的样例)</li>
<li>
 licenses/ (包含所有许可证)</li>
<li>
 gbdk_manual.pdf (gbdk手册)</li>
<li>
 sdccman.pdf (sdcc手册)</li>
<li>
 ChangeLog (更新日志)</li>
<li>
 README (自述文件)</li>
</ul>
<p>接下来我们需要把 <code>gbdk</code> 文件夹移动到一个合适的位置，并添加环境变量。</p>
<h2>移动 gbdk 文件夹，并添加相关的环境变量</h2>
<p>可以把该文件夹拷贝到 <code>/opt/gbdk</code>：</p>
<div class="language-bash line-numbers-mode" data-highlighter="shiki" data-ext="bash" style="--shiki-light:#383A42;--shiki-dark:#abb2bf;--shiki-light-bg:#FAFAFA;--shiki-dark-bg:#282c34"><pre class="shiki shiki-themes one-light one-dark-pro vp-code"><code class="language-bash"><span class="line"><span style="--shiki-light:#4078F2;--shiki-dark:#61AFEF">sudo</span><span style="--shiki-light:#50A14F;--shiki-dark:#98C379"> cp</span><span style="--shiki-light:#986801;--shiki-dark:#D19A66"> -r</span><span style="--shiki-light:#50A14F;--shiki-dark:#98C379"> gbdk</span><span style="--shiki-light:#50A14F;--shiki-dark:#98C379"> /opt/gbdk</span></span></code></pre>
<div class="line-numbers" aria-hidden="true" style="counter-reset:line-number 0"><div class="line-number"></div></div></div><p>然后设置相关的环境变量。</p>
<div class="hint-container tip">
<p class="hint-container-title">提示</p>
<p>为了永久生效，可以编辑 /etc/profile 或者你所使用的 shell 配置文件：</p>
</div>

<div class="hint-container info">
<p class="hint-container-title">相关信息</p>
<p>GBDKDIR 系统环境变量是必须设置的内容，除此之外，你可以选择将 bin 目录设置到 PATH 变量中，这样我们可以更方便的使用一些预编译的可执行程序。</p>
</div>
<h2>准备好为 GBDK 而修正的 GBTD 与 GBMB</h2>
<p>GBTD (GameBoy Tile Designer) 和 GBMB (GameBoy Map Builder) 分别用于瓦块设计与地图编辑。前者可以绘制精灵和瓦块，后者使用前者已绘制好的瓦块进行地图的编辑。</p>
<p>我们后续会用到这两个工具，这里我们先下载准备好。<a href="https://github.com/gbdk-2020/GBTD_GBMB/releases/download/2.4.5/GBTD_GBMB_release.zip" target="_blank" rel="noopener noreferrer">点击下载</a></p>
<p>最新版本在<a href="https://github.com/gbdk-2020/GBTD_GBMB/releases" target="_blank" rel="noopener noreferrer">这个页面</a>。</p>
<p>下载好并解压后，我们会得到 <code>GBTD_GBMB_release</code> 文件夹，里面包括 <code>GBTD</code> 文件夹和 <code>GBMB</code> 文件夹。</p>
<p>我们在 <code>gbdk</code> 文件夹下新建 <code>tools</code> 目录，然后将解压后得到的 <code>GBTD</code> 目录和 <code>GBMB</code> 目录拷贝到 <code>tools</code> 目录。</p>
<p>MacOS 和 Linux 用户可以使用 shell 来快速操作：</p>
<div class="language-bash line-numbers-mode" data-highlighter="shiki" data-ext="bash" style="--shiki-light:#383A42;--shiki-dark:#abb2bf;--shiki-light-bg:#FAFAFA;--shiki-dark-bg:#282c34"><pre class="shiki shiki-themes one-light one-dark-pro vp-code"><code class="language-bash"><span class="line"><span style="--shiki-light:#4078F2;--shiki-dark:#61AFEF">sudo</span><span style="--shiki-light:#50A14F;--shiki-dark:#98C379"> mkdir</span><span style="--shiki-light:#986801;--shiki-dark:#D19A66"> -p</span><span style="--shiki-light:#E45649;--shiki-dark:#E06C75"> $GBDKDIR</span><span style="--shiki-light:#50A14F;--shiki-dark:#98C379">/tools</span></span>
<span class="line"><span style="--shiki-light:#0184BC;--shiki-dark:#56B6C2">cd</span><span style="--shiki-light:#50A14F;--shiki-dark:#98C379"> GBTD_GBMB_release</span></span>
<span class="line"><span style="--shiki-light:#4078F2;--shiki-dark:#61AFEF">sudo</span><span style="--shiki-light:#50A14F;--shiki-dark:#98C379"> cp</span><span style="--shiki-light:#986801;--shiki-dark:#D19A66"> -r</span><span style="--shiki-light:#50A14F;--shiki-dark:#98C379"> GBTD</span><span style="--shiki-light:#E45649;--shiki-dark:#E06C75"> $GBDKDIR</span><span style="--shiki-light:#50A14F;--shiki-dark:#98C379">/tools/</span></span>
<span class="line"><span style="--shiki-light:#4078F2;--shiki-dark:#61AFEF">sudo</span><span style="--shiki-light:#50A14F;--shiki-dark:#98C379"> cp</span><span style="--shiki-light:#986801;--shiki-dark:#D19A66"> -r</span><span style="--shiki-light:#50A14F;--shiki-dark:#98C379"> GBMB</span><span style="--shiki-light:#E45649;--shiki-dark:#E06C75"> $GBDKDIR</span><span style="--shiki-light:#50A14F;--shiki-dark:#98C379">/tools/</span></span></code></pre>
<div class="line-numbers" aria-hidden="true" style="counter-reset:line-number 0"><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div></div></div><div class="hint-container warning">
<p class="hint-container-title">重点注意</p>
<p>如果工具无法保存 <code>.ini</code> 配置，请检查文件所有者，并仅为自己的账户授予所需的写入权限。原始步骤直接使用 <code>chmod 777</code>，没有必要开放给所有用户写入。若不需要系统级安装，也可以把工具放在用户自己的目录下。</p>
</div>
<div class="hint-container tip">
<p class="hint-container-title">提示</p>
<p>MacOS 和 Linux 需要准备 Wine 环境：</p>
<blockquote>
<p>Wine (Wine is not an emulator, 递归式命名)，可以在 MacOS 和 Linux 上，通过本地库，链接 DLL 的形式，运行 Windows 应用。</p>
</blockquote>

</div>
<div class="hint-container tip">
<p class="hint-container-title">MacOS 和 Linux 使用 Wine 运行 GBTD 和 GBMB 的后续流程</p>
<p>上面这些都配置好后，我们就可以这样运行 GBTD 和 GBMB：</p>
<div class="language-bash line-numbers-mode" data-highlighter="shiki" data-ext="bash" style="--shiki-light:#383A42;--shiki-dark:#abb2bf;--shiki-light-bg:#FAFAFA;--shiki-dark-bg:#282c34"><pre class="shiki shiki-themes one-light one-dark-pro vp-code"><code class="language-bash"><span class="line"><span style="--shiki-light:#4078F2;--shiki-dark:#61AFEF">wine</span><span style="--shiki-light:#E45649;--shiki-dark:#E06C75"> $GBDKDIR</span><span style="--shiki-light:#50A14F;--shiki-dark:#98C379">/tools/GBTD/gbtd.exe</span></span>
<span class="line"><span style="--shiki-light:#4078F2;--shiki-dark:#61AFEF">wine</span><span style="--shiki-light:#E45649;--shiki-dark:#E06C75"> $GBDKDIR</span><span style="--shiki-light:#50A14F;--shiki-dark:#98C379">/tools/GBMB/gbmb.exe</span></span></code></pre>
<div class="line-numbers" aria-hidden="true" style="counter-reset:line-number 0"><div class="line-number"></div><div class="line-number"></div></div></div><p>觉得麻烦，建议使用 alias 命令创建别名，建议写入到 /etc/profile，当然也可以跟上面配置环境变量一样，写入到 shell 的配置文件中：</p>
<div class="language-bash line-numbers-mode" data-highlighter="shiki" data-ext="bash" style="--shiki-light:#383A42;--shiki-dark:#abb2bf;--shiki-light-bg:#FAFAFA;--shiki-dark-bg:#282c34"><pre class="shiki shiki-themes one-light one-dark-pro vp-code"><code class="language-bash"><span class="line"><span style="--shiki-light:#A0A1A7;--shiki-light-font-style:italic;--shiki-dark:#7F848E;--shiki-dark-font-style:italic"># 添加到 /etc/profile</span></span>
<span class="line"><span style="--shiki-light:#0184BC;--shiki-dark:#56B6C2">echo</span><span style="--shiki-light:#50A14F;--shiki-dark:#98C379"> "alias gbtd='wine </span><span style="--shiki-light:#E45649;--shiki-dark:#E06C75">$GBDKDIR</span><span style="--shiki-light:#50A14F;--shiki-dark:#98C379">/tools/GBTD/gbtd.exe'"</span><span style="--shiki-light:#383A42;--shiki-dark:#ABB2BF"> | </span><span style="--shiki-light:#4078F2;--shiki-dark:#61AFEF">sudo</span><span style="--shiki-light:#50A14F;--shiki-dark:#98C379"> tee</span><span style="--shiki-light:#986801;--shiki-dark:#D19A66"> -a</span><span style="--shiki-light:#50A14F;--shiki-dark:#98C379"> /etc/profile</span></span>
<span class="line"><span style="--shiki-light:#0184BC;--shiki-dark:#56B6C2">echo</span><span style="--shiki-light:#50A14F;--shiki-dark:#98C379"> "alias gbmb='wine </span><span style="--shiki-light:#E45649;--shiki-dark:#E06C75">$GBDKDIR</span><span style="--shiki-light:#50A14F;--shiki-dark:#98C379">/tools/GBMB/gbmb.exe'"</span><span style="--shiki-light:#383A42;--shiki-dark:#ABB2BF"> | </span><span style="--shiki-light:#4078F2;--shiki-dark:#61AFEF">sudo</span><span style="--shiki-light:#50A14F;--shiki-dark:#98C379"> tee</span><span style="--shiki-light:#986801;--shiki-dark:#D19A66"> -a</span><span style="--shiki-light:#50A14F;--shiki-dark:#98C379"> /etc/profile</span></span>
<span class="line"></span>
<span class="line"><span style="--shiki-light:#A0A1A7;--shiki-light-font-style:italic;--shiki-dark:#7F848E;--shiki-dark-font-style:italic"># 添加到 ~/.zshrc</span></span>
<span class="line"><span style="--shiki-light:#0184BC;--shiki-dark:#56B6C2">echo</span><span style="--shiki-light:#50A14F;--shiki-dark:#98C379"> "alias gbtd='wine </span><span style="--shiki-light:#E45649;--shiki-dark:#E06C75">$GBDKDIR</span><span style="--shiki-light:#50A14F;--shiki-dark:#98C379">/tools/GBTD/gbtd.exe'"</span><span style="--shiki-light:#383A42;--shiki-dark:#ABB2BF"> | </span><span style="--shiki-light:#4078F2;--shiki-dark:#61AFEF">sudo</span><span style="--shiki-light:#50A14F;--shiki-dark:#98C379"> tee</span><span style="--shiki-light:#986801;--shiki-dark:#D19A66"> -a</span><span style="--shiki-light:#50A14F;--shiki-dark:#98C379"> ~/.zshrc</span></span>
<span class="line"><span style="--shiki-light:#0184BC;--shiki-dark:#56B6C2">echo</span><span style="--shiki-light:#50A14F;--shiki-dark:#98C379"> "alias gbmb='wine </span><span style="--shiki-light:#E45649;--shiki-dark:#E06C75">$GBDKDIR</span><span style="--shiki-light:#50A14F;--shiki-dark:#98C379">/tools/GBMB/gbmb.exe'"</span><span style="--shiki-light:#383A42;--shiki-dark:#ABB2BF"> | </span><span style="--shiki-light:#4078F2;--shiki-dark:#61AFEF">sudo</span><span style="--shiki-light:#50A14F;--shiki-dark:#98C379"> tee</span><span style="--shiki-light:#986801;--shiki-dark:#D19A66"> -a</span><span style="--shiki-light:#50A14F;--shiki-dark:#98C379"> ~/.zshrc</span></span>
<span class="line"></span>
<span class="line"><span style="--shiki-light:#A0A1A7;--shiki-light-font-style:italic;--shiki-dark:#7F848E;--shiki-dark-font-style:italic"># 添加到 ~/.bashrc</span></span>
<span class="line"><span style="--shiki-light:#0184BC;--shiki-dark:#56B6C2">echo</span><span style="--shiki-light:#50A14F;--shiki-dark:#98C379"> "alias gbtd='wine </span><span style="--shiki-light:#E45649;--shiki-dark:#E06C75">$GBDKDIR</span><span style="--shiki-light:#50A14F;--shiki-dark:#98C379">/tools/GBTD/gbtd.exe'"</span><span style="--shiki-light:#383A42;--shiki-dark:#ABB2BF"> | </span><span style="--shiki-light:#4078F2;--shiki-dark:#61AFEF">sudo</span><span style="--shiki-light:#50A14F;--shiki-dark:#98C379"> tee</span><span style="--shiki-light:#986801;--shiki-dark:#D19A66"> -a</span><span style="--shiki-light:#50A14F;--shiki-dark:#98C379"> ~/.bashrc</span></span>
<span class="line"><span style="--shiki-light:#0184BC;--shiki-dark:#56B6C2">echo</span><span style="--shiki-light:#50A14F;--shiki-dark:#98C379"> "alias gbmb='wine </span><span style="--shiki-light:#E45649;--shiki-dark:#E06C75">$GBDKDIR</span><span style="--shiki-light:#50A14F;--shiki-dark:#98C379">/tools/GBMB/gbmb.exe'"</span><span style="--shiki-light:#383A42;--shiki-dark:#ABB2BF"> | </span><span style="--shiki-light:#4078F2;--shiki-dark:#61AFEF">sudo</span><span style="--shiki-light:#50A14F;--shiki-dark:#98C379"> tee</span><span style="--shiki-light:#986801;--shiki-dark:#D19A66"> -a</span><span style="--shiki-light:#50A14F;--shiki-dark:#98C379"> ~/.bashrc</span></span></code></pre>
<div class="line-numbers" aria-hidden="true" style="counter-reset:line-number 0"><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div></div></div></div>
<h2>准备好模拟器 - SameBoy</h2>
<p>由于我使用 MacOS，我考虑使用开源、跨平台的模拟器：<a href="https://github.com/LIJI32/SameBoy" target="_blank" rel="noopener noreferrer">SameBoy</a>，它功能丰富，支持调试，方便我们开发。</p>
<p>打开它的下载页面，下载对应的版本即可：https://sameboy.github.io/downloads/</p>
<p>这里我下载 <a href="https://github.com/LIJI32/SameBoy/releases/download/v0.15.8/sameboy_cocoa_v0.15.8.zip" target="_blank" rel="noopener noreferrer">MacOS 最新 v0.15.8 版</a>。</p>
<p>解压后是一个 app，拖到 <code>应用程序</code> (英文系统中是 <code>Applications</code>) 中即可完成安装。</p>
<h2>Hello, world!</h2>
<p>接下来编写我们第一个程序：在 GameBoy 的屏幕里打印一行 &quot;Hello,world!&quot;</p>
<h3>编写源码</h3>
<p><code>helloworld.c</code>:</p>
<div class="language-c line-numbers-mode" data-highlighter="shiki" data-ext="c" style="--shiki-light:#383A42;--shiki-dark:#abb2bf;--shiki-light-bg:#FAFAFA;--shiki-dark-bg:#282c34"><pre class="shiki shiki-themes one-light one-dark-pro vp-code"><code class="language-c"><span class="line"><span style="--shiki-light:#A626A4;--shiki-dark:#C678DD">#include</span><span style="--shiki-light:#50A14F;--shiki-dark:#98C379"> &#x3C;gb/gb.h></span></span>
<span class="line"><span style="--shiki-light:#A626A4;--shiki-dark:#C678DD">#include</span><span style="--shiki-light:#50A14F;--shiki-dark:#98C379"> &#x3C;stdio.h></span></span>
<span class="line"></span>
<span class="line"><span style="--shiki-light:#A626A4;--shiki-dark:#C678DD">void</span><span style="--shiki-light:#4078F2;--shiki-dark:#61AFEF"> main</span><span style="--shiki-light:#383A42;--shiki-dark:#ABB2BF">()</span></span>
<span class="line"><span style="--shiki-light:#383A42;--shiki-dark:#ABB2BF">{</span></span>
<span class="line"><span style="--shiki-light:#4078F2;--shiki-dark:#61AFEF">    printf</span><span style="--shiki-light:#383A42;--shiki-dark:#ABB2BF">(</span><span style="--shiki-light:#50A14F;--shiki-dark:#98C379">"HELLO, WORLD!"</span><span style="--shiki-light:#383A42;--shiki-dark:#ABB2BF">);</span></span>
<span class="line"><span style="--shiki-light:#383A42;--shiki-dark:#ABB2BF">}</span></span></code></pre>
<div class="line-numbers" aria-hidden="true" style="counter-reset:line-number 0"><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div></div></div><h3>命令行编译</h3>
<p>在 <code>helloworld.c</code> 的同级目录打开命令行，并执行：</p>
<div class="language-bash line-numbers-mode" data-highlighter="shiki" data-ext="bash" style="--shiki-light:#383A42;--shiki-dark:#abb2bf;--shiki-light-bg:#FAFAFA;--shiki-dark-bg:#282c34"><pre class="shiki shiki-themes one-light one-dark-pro vp-code"><code class="language-bash"><span class="line"><span style="--shiki-light:#E45649;--shiki-dark:#E06C75">$GBDKDIR</span><span style="--shiki-light:#383A42;--shiki-dark:#ABB2BF">/bin/lcc -Wa-l -Wl-m -Wl-j -o helloworld.gb helloworld.c</span></span></code></pre>
<div class="line-numbers" aria-hidden="true" style="counter-reset:line-number 0"><div class="line-number"></div></div></div><p>执行结束后，会发现目录下编译得到了 <code>helloworld.gb</code>，也就是我们熟悉的 ROM 文件啦，放到模拟器可以直接运行。</p>
<h3>用 SameBoy 打开，看看效果吧</h3>
<figure><img src="https://zzxzzk115.github.io/blog/images/game-dev/retro/gameboy/gbdk/helloworld_samboy.png" alt="Hello, world!" tabindex="0" loading="lazy"><figcaption>Hello, world!</figcaption></figure>
<div class="hint-container tip">
<p class="hint-container-title">提示</p>
<p>可以将 SameBoy 也使用 alias 取个别名：</p>
<div class="language-bash line-numbers-mode" data-highlighter="shiki" data-ext="bash" style="--shiki-light:#383A42;--shiki-dark:#abb2bf;--shiki-light-bg:#FAFAFA;--shiki-dark-bg:#282c34"><pre class="shiki shiki-themes one-light one-dark-pro vp-code"><code class="language-bash"><span class="line"><span style="--shiki-light:#A0A1A7;--shiki-light-font-style:italic;--shiki-dark:#7F848E;--shiki-dark-font-style:italic"># 添加到 /etc/profile</span></span>
<span class="line"><span style="--shiki-light:#0184BC;--shiki-dark:#56B6C2">echo</span><span style="--shiki-light:#50A14F;--shiki-dark:#98C379"> "alias sameboy=/Applications/SameBoy.app/Contents/MacOS/SameBoy"</span><span style="--shiki-light:#383A42;--shiki-dark:#ABB2BF"> | </span><span style="--shiki-light:#4078F2;--shiki-dark:#61AFEF">sudo</span><span style="--shiki-light:#50A14F;--shiki-dark:#98C379"> tee</span><span style="--shiki-light:#986801;--shiki-dark:#D19A66"> -a</span><span style="--shiki-light:#50A14F;--shiki-dark:#98C379"> /etc/profile</span></span>
<span class="line"></span>
<span class="line"><span style="--shiki-light:#A0A1A7;--shiki-light-font-style:italic;--shiki-dark:#7F848E;--shiki-dark-font-style:italic"># 添加到 ~/.zshrc</span></span>
<span class="line"><span style="--shiki-light:#0184BC;--shiki-dark:#56B6C2">echo</span><span style="--shiki-light:#50A14F;--shiki-dark:#98C379"> "alias sameboy=/Applications/SameBoy.app/Contents/MacOS/SameBoy"</span><span style="--shiki-light:#383A42;--shiki-dark:#ABB2BF"> | </span><span style="--shiki-light:#4078F2;--shiki-dark:#61AFEF">sudo</span><span style="--shiki-light:#50A14F;--shiki-dark:#98C379"> tee</span><span style="--shiki-light:#986801;--shiki-dark:#D19A66"> -a</span><span style="--shiki-light:#50A14F;--shiki-dark:#98C379"> ~/.zshrc</span></span>
<span class="line"></span>
<span class="line"><span style="--shiki-light:#A0A1A7;--shiki-light-font-style:italic;--shiki-dark:#7F848E;--shiki-dark-font-style:italic"># 添加到 ~/.bashrc</span></span>
<span class="line"><span style="--shiki-light:#0184BC;--shiki-dark:#56B6C2">echo</span><span style="--shiki-light:#50A14F;--shiki-dark:#98C379"> "alias sameboy=/Applications/SameBoy.app/Contents/MacOS/SameBoy"</span><span style="--shiki-light:#383A42;--shiki-dark:#ABB2BF"> | </span><span style="--shiki-light:#4078F2;--shiki-dark:#61AFEF">sudo</span><span style="--shiki-light:#50A14F;--shiki-dark:#98C379"> tee</span><span style="--shiki-light:#986801;--shiki-dark:#D19A66"> -a</span><span style="--shiki-light:#50A14F;--shiki-dark:#98C379"> ~/.bashrc</span></span></code></pre>
<div class="line-numbers" aria-hidden="true" style="counter-reset:line-number 0"><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div></div></div><p>然后我们就可以使用</p>
<div class="language-bash line-numbers-mode" data-highlighter="shiki" data-ext="bash" style="--shiki-light:#383A42;--shiki-dark:#abb2bf;--shiki-light-bg:#FAFAFA;--shiki-dark-bg:#282c34"><pre class="shiki shiki-themes one-light one-dark-pro vp-code"><code class="language-bash"><span class="line"><span style="--shiki-light:#4078F2;--shiki-dark:#61AFEF">sameboy</span><span style="--shiki-light:#50A14F;--shiki-dark:#98C379"> helloworld.gb</span></span></code></pre>
<div class="line-numbers" aria-hidden="true" style="counter-reset:line-number 0"><div class="line-number"></div></div></div><p>这种形式直接运行游戏了。</p>
</div>
<p>恭喜，到这里，你已经入门了！🎉🎉🎉</p>
]]></content>
    <author>
      <name>Lazy_V</name>
    </author>
    <category term="游戏开发"/>
    <contributor>
      <name>Lazy_V</name>
    </contributor>
    <published>2022-12-18T19:10:00.000Z</published>
    <rights>Copyright by Lazy_V</rights>
  </entry>
  <entry>
    <title type="text">GBDK 简介</title>
    <id>https://zzxzzk115.github.io/blog/zh/game-dev/retro/gameboy/gbdk/001-gbdk-intro.html</id>
    <link href="https://zzxzzk115.github.io/blog/zh/game-dev/retro/gameboy/gbdk/001-gbdk-intro.html"/>
    <updated>2026-10-17T16:08:05.000Z</updated>
    <summary type="html"><![CDATA[<p>对于经典的 GB/GBC 开发，高手们习惯使用汇编。但是使用汇编需要我们首先对 GB/GBC 整个硬件原理和完整的指令集有了完整的了解后才合适，这样的成本太高了。</p>
<p>现代的 GB/GBC 开发，至少需要使用高级编程语言，例如 C 语言。我们可以利用 GBDK 和 C 语言，进行 GB/GBC 开发。</p>
<p><a href="https://github.com/gbdk-2020/gbdk-2020" target="_blank" rel="noopener noreferrer">GBDK</a> (GameBoy Development Kit) 即 GameBoy 开发工具包，主要包含一个 C 语言编译器： SDCC。它包括编译器、汇编器和链接器。GBDK 还提供面向受支持硬件的库。Game Boy 使用 Sharp SM83 CPU，其指令集与 Intel 8080、Z80 有渊源，但它并不是完整的 Z80，因此不能将 Z80 代码和库与 Game Boy 代码直接互换。</p>]]></summary>
    <content type="html"><![CDATA[<p>对于经典的 GB/GBC 开发，高手们习惯使用汇编。但是使用汇编需要我们首先对 GB/GBC 整个硬件原理和完整的指令集有了完整的了解后才合适，这样的成本太高了。</p>
<p>现代的 GB/GBC 开发，至少需要使用高级编程语言，例如 C 语言。我们可以利用 GBDK 和 C 语言，进行 GB/GBC 开发。</p>
<p><a href="https://github.com/gbdk-2020/gbdk-2020" target="_blank" rel="noopener noreferrer">GBDK</a> (GameBoy Development Kit) 即 GameBoy 开发工具包，主要包含一个 C 语言编译器： SDCC。它包括编译器、汇编器和链接器。GBDK 还提供面向受支持硬件的库。Game Boy 使用 Sharp SM83 CPU，其指令集与 Intel 8080、Z80 有渊源，但它并不是完整的 Z80，因此不能将 Z80 代码和库与 Game Boy 代码直接互换。</p>
<p>GBDK 包含了我们编译相关的所有内容，接下来我们开始进行环境搭建吧！</p>
]]></content>
    <author>
      <name>Lazy_V</name>
    </author>
    <category term="游戏开发"/>
    <contributor>
      <name>Lazy_V</name>
    </contributor>
    <published>2022-12-18T19:05:00.000Z</published>
    <rights>Copyright by Lazy_V</rights>
  </entry>
  <entry>
    <title type="text">GB/GBC 游戏开发：简介</title>
    <id>https://zzxzzk115.github.io/blog/zh/game-dev/retro/gameboy/intro.html</id>
    <link href="https://zzxzzk115.github.io/blog/zh/game-dev/retro/gameboy/intro.html"/>
    <updated>2022-12-20T18:02:21.000Z</updated>
    <summary type="html"><![CDATA[<div class="hint-container info">
<p class="hint-container-title">前言</p>
<p>任天堂 GameBoy 曾是风靡全球的掌机，不过很可惜，我小时候并没有接触过它。当我长大后了解它以后，对它产生了浓厚的兴趣。我希望我可以开发一些可以在 GameBoy 上运行的游戏。</p>
</div>
]]></summary>
    <content type="html"><![CDATA[<div class="hint-container info">
<p class="hint-container-title">前言</p>
<p>任天堂 GameBoy 曾是风靡全球的掌机，不过很可惜，我小时候并没有接触过它。当我长大后了解它以后，对它产生了浓厚的兴趣。我希望我可以开发一些可以在 GameBoy 上运行的游戏。</p>
</div>
<!-- more -->
<h2>我与 GB/GBC 游戏开发的故事</h2>
<p>2018 年，我了解到 <a href="https://github.com/gbdk-2020/gbdk-2020" target="_blank" rel="noopener noreferrer">GBDK</a> 这个开发工具，并利用它，和我当时已掌握的 C 语言，写过一些小游戏和 DEMO。不过那些都是半成品，就不打算展示了。</p>
<p>2020 年，我了解到 <a href="https://github.com/chrismaltby/gb-studio" target="_blank" rel="noopener noreferrer">GB-Studio</a> 这个让人人都能进行 GB/GBC 游戏开发的神奇工具。你并不需要编程相关的知识就可以使用它开始进行游戏创作。</p>
<p>2021 年，疫情期间我的创作欲比较强烈，成功使用 GB-Studio 制作了一款属于 GBC 的猫里奥：<a href="https://zzxzzk115.github.io/blog/zh/game-dev/001-games-gallery.html#catmario-gb" target="_blank">传送门</a></p>
<h2>开启 GB/GBC 的开发之旅吧</h2>
<p>现在，我想分享一些 GB/GBC 游戏开发相关的知识，给大家带来<strong>现代</strong>的 GB/GBC 游戏开发教程系列。</p>
<p>对于开发工具，我打算先从比较硬核的 GBDK 开始，毕竟 GB-Studio 上手比较简单，可以放后面讲。</p>
<p>我并不打算先讲 GB/GBC 的硬件原理，这样过于枯燥。我会在讲开发细节的时候，介绍 GB/GBC 某个硬件模块的参数和设置，在实现效果的前提下，解释原理。</p>
<p>接下来你可以选择的教程：</p>
<ul>
<li><a href="https://zzxzzk115.github.io/blog/zh/game-dev/retro/gameboy/gbdk/001-gbdk-intro.html" target="_blank">使用 GBDK 开发</a></li>
</ul>
]]></content>
    <author>
      <name>Lazy_V</name>
    </author>
    <category term="游戏开发"/>
    <contributor>
      <name>Lazy_V</name>
    </contributor>
    <published>2022-12-18T19:00:00.000Z</published>
    <rights>Copyright by Lazy_V</rights>
  </entry>
  <entry>
    <title type="text">解决 DP 问题的算法框架</title>
    <id>https://zzxzzk115.github.io/blog/zh/posts/algorithms/dynamic-programming/framework.html</id>
    <link href="https://zzxzzk115.github.io/blog/zh/posts/algorithms/dynamic-programming/framework.html"/>
    <updated>2022-12-17T14:58:15.000Z</updated>
    <summary type="html"><![CDATA[<p>动态规划问题在算法中属于复杂的问题. 然而，当你理解它的关键概念和算法框架后，解决它实际上是很简单的。</p>
]]></summary>
    <content type="html"><![CDATA[<p>动态规划问题在算法中属于复杂的问题. 然而，当你理解它的关键概念和算法框架后，解决它实际上是很简单的。</p>
<!-- more -->
<h2>什么是动态规划 (DP) ?</h2>
<p>动态规划（DP）是一种通过将复杂问题分解为更小的子问题从而解决它们的方法。这是一种自下而上的方法，这意味着它从解决较小的子问题开始，然后使用这些子问题的结果去得到更大问题的结果。</p>
<p>要解决 DP 问题，问题必须具有以下属性：</p>
<p>1.最优子结构：问题的最优解可以从其子问题的最优解得到。</p>
<p>2.重叠的子问题：问题可以分解为多次重复使用的子问题。</p>
<h2>一个经典的问题 : 斐波那契</h2>
<p>举个例子，当我们计算斐波那契数列的时候，我们可以使用 DP。</p>
<div class="hint-container tip">
<p class="hint-container-title">提示</p>
<p>斐波那契数列是一系列数字，其中每个数字都是前两个数字的总和，从0和1开始。顺序如下：0、1、1、2、3、5、8、13、21、34等。该序列以比萨的莱昂纳多命名，他也被称为斐波那契。1202年，他在《Liber Abaci》一书中介绍了西欧数学的顺序。</p>
</div>
<p>一般而言，我们通常使用递归函数来计算斐波那契数列，就像下面这样：</p>
<div class="language-cpp line-numbers-mode" data-highlighter="shiki" data-ext="cpp" style="--shiki-light:#383A42;--shiki-dark:#abb2bf;--shiki-light-bg:#FAFAFA;--shiki-dark-bg:#282c34"><pre class="shiki shiki-themes one-light one-dark-pro vp-code"><code class="language-cpp"><span class="line"><span style="--shiki-light:#A626A4;--shiki-dark:#C678DD">int</span><span style="--shiki-light:#4078F2;--shiki-dark:#61AFEF"> Fibonacci</span><span style="--shiki-light:#383A42;--shiki-dark:#ABB2BF">(</span><span style="--shiki-light:#A626A4;--shiki-dark:#C678DD">int</span><span style="--shiki-light:#383A42;--shiki-light-font-style:inherit;--shiki-dark:#E06C75;--shiki-dark-font-style:italic"> n</span><span style="--shiki-light:#383A42;--shiki-dark:#ABB2BF">) {</span></span>
<span class="line"><span style="--shiki-light:#A626A4;--shiki-dark:#C678DD">  if</span><span style="--shiki-light:#383A42;--shiki-dark:#ABB2BF"> (</span><span style="--shiki-light:#E45649;--shiki-dark:#E06C75">n</span><span style="--shiki-light:#A626A4;--shiki-dark:#C678DD"> ==</span><span style="--shiki-light:#986801;--shiki-dark:#D19A66"> 0</span><span style="--shiki-light:#383A42;--shiki-dark:#ABB2BF">) {</span></span>
<span class="line"><span style="--shiki-light:#A626A4;--shiki-dark:#C678DD">		return</span><span style="--shiki-light:#986801;--shiki-dark:#D19A66"> 0</span><span style="--shiki-light:#383A42;--shiki-dark:#ABB2BF">;</span></span>
<span class="line"><span style="--shiki-light:#383A42;--shiki-dark:#ABB2BF">	}</span></span>
<span class="line"><span style="--shiki-light:#A626A4;--shiki-dark:#C678DD">	if</span><span style="--shiki-light:#383A42;--shiki-dark:#ABB2BF"> (</span><span style="--shiki-light:#E45649;--shiki-dark:#E06C75">n</span><span style="--shiki-light:#A626A4;--shiki-dark:#C678DD"> ==</span><span style="--shiki-light:#986801;--shiki-dark:#D19A66"> 1</span><span style="--shiki-light:#A626A4;--shiki-dark:#56B6C2"> ||</span><span style="--shiki-light:#E45649;--shiki-dark:#E06C75"> n</span><span style="--shiki-light:#A626A4;--shiki-dark:#C678DD"> ==</span><span style="--shiki-light:#986801;--shiki-dark:#D19A66"> 2</span><span style="--shiki-light:#383A42;--shiki-dark:#ABB2BF">) {</span></span>
<span class="line"><span style="--shiki-light:#A626A4;--shiki-dark:#C678DD">		return</span><span style="--shiki-light:#986801;--shiki-dark:#D19A66"> 1</span><span style="--shiki-light:#383A42;--shiki-dark:#ABB2BF">;</span></span>
<span class="line"><span style="--shiki-light:#383A42;--shiki-dark:#ABB2BF">	}</span></span>
<span class="line"><span style="--shiki-light:#A626A4;--shiki-dark:#C678DD">	return</span><span style="--shiki-light:#4078F2;--shiki-dark:#61AFEF"> Fibonacci</span><span style="--shiki-light:#383A42;--shiki-dark:#ABB2BF">(</span><span style="--shiki-light:#E45649;--shiki-dark:#E06C75">n</span><span style="--shiki-light:#A626A4;--shiki-dark:#C678DD"> -</span><span style="--shiki-light:#986801;--shiki-dark:#D19A66"> 1</span><span style="--shiki-light:#383A42;--shiki-dark:#ABB2BF">) </span><span style="--shiki-light:#A626A4;--shiki-dark:#C678DD">+</span><span style="--shiki-light:#4078F2;--shiki-dark:#61AFEF"> Fibonacci</span><span style="--shiki-light:#383A42;--shiki-dark:#ABB2BF">(</span><span style="--shiki-light:#E45649;--shiki-dark:#E06C75">n</span><span style="--shiki-light:#A626A4;--shiki-dark:#C678DD"> -</span><span style="--shiki-light:#986801;--shiki-dark:#D19A66"> 2</span><span style="--shiki-light:#383A42;--shiki-dark:#ABB2BF">);</span></span>
<span class="line"><span style="--shiki-light:#383A42;--shiki-dark:#ABB2BF">}</span></span></code></pre>
<div class="line-numbers" aria-hidden="true" style="counter-reset:line-number 0"><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div></div></div><p>我们使用的技术并非 DP，而只是递归。</p>
<p>函数调用树存在多个节点，但是，多数节点是重复的。</p>
<p>我有一个公式：</p>
<p>DP = 递归(Recursion) + 备忘录(Memoization) 或者 制表(Tabulation) + 可选的其他改进</p>
<p>这是什么意思？</p>
<p>其实，备忘录和制表是用于提高动态规划算法性能的两种技术。</p>
<p>备忘录是一种技术，涉及将中间计算的结果存储在表格中，以便以后可以重复使用。这可以通过避免多次重新计算相同的子问题，帮助降低算法的整体时间复杂性。它是自上而下的。</p>
<p>另一方面，制表是一种涉及将所有子问题最终结果存储在表中的技术。这允许算法避免将解决方案重新计算到子问题，只需在表中查找预计算的结果。它是自下而上的。</p>
<p>备忘录和制表都可以用来加快动态规划算法。这两种技术之间的选择通常取决于正在解决的具体问题和可用内存量。</p>
<h3>使用备忘录</h3>
<p>为了使用备忘录，我们可以定义一个查找表 (Look-up table) 或者 缓存表 (Cache table) 去保存已经计算过的结果。我们通常使用哈希表去定义该结构。</p>
<p>在 C++ 中，我们将使用 std::unordered_map。</p>
<p>举个例子，下面我们使用备忘录来解决斐波那契问题：</p>
<div class="language-cpp line-numbers-mode" data-highlighter="shiki" data-ext="cpp" style="--shiki-light:#383A42;--shiki-dark:#abb2bf;--shiki-light-bg:#FAFAFA;--shiki-dark-bg:#282c34"><pre class="shiki shiki-themes one-light one-dark-pro vp-code"><code class="language-cpp"><span class="line"><span style="--shiki-light:#383A42;--shiki-dark:#ABB2BF">std::</span><span style="--shiki-light:#E45649;--shiki-dark:#E06C75">unordered_map</span><span style="--shiki-light:#A626A4;--shiki-dark:#C678DD">&#x3C;int</span><span style="--shiki-light:#383A42;--shiki-dark:#ABB2BF">, </span><span style="--shiki-light:#A626A4;--shiki-dark:#C678DD">int></span><span style="--shiki-light:#E45649;--shiki-dark:#E06C75"> memo</span><span style="--shiki-light:#383A42;--shiki-dark:#ABB2BF">;</span></span>
<span class="line"></span>
<span class="line"><span style="--shiki-light:#A626A4;--shiki-dark:#C678DD">int</span><span style="--shiki-light:#4078F2;--shiki-dark:#61AFEF"> Fibonacci</span><span style="--shiki-light:#383A42;--shiki-dark:#ABB2BF">(</span><span style="--shiki-light:#A626A4;--shiki-dark:#C678DD">int</span><span style="--shiki-light:#383A42;--shiki-light-font-style:inherit;--shiki-dark:#E06C75;--shiki-dark-font-style:italic"> n</span><span style="--shiki-light:#383A42;--shiki-dark:#ABB2BF">) {</span></span>
<span class="line"><span style="--shiki-light:#A626A4;--shiki-dark:#C678DD">  if</span><span style="--shiki-light:#383A42;--shiki-dark:#ABB2BF"> (</span><span style="--shiki-light:#E45649;--shiki-dark:#E06C75">n</span><span style="--shiki-light:#A626A4;--shiki-dark:#C678DD"> ==</span><span style="--shiki-light:#986801;--shiki-dark:#D19A66"> 0</span><span style="--shiki-light:#383A42;--shiki-dark:#ABB2BF">) {</span></span>
<span class="line"><span style="--shiki-light:#A626A4;--shiki-dark:#C678DD">		return</span><span style="--shiki-light:#986801;--shiki-dark:#D19A66"> 0</span><span style="--shiki-light:#383A42;--shiki-dark:#ABB2BF">;</span></span>
<span class="line"><span style="--shiki-light:#383A42;--shiki-dark:#ABB2BF">	}</span></span>
<span class="line"><span style="--shiki-light:#A626A4;--shiki-dark:#C678DD">	if</span><span style="--shiki-light:#383A42;--shiki-dark:#ABB2BF"> (</span><span style="--shiki-light:#E45649;--shiki-dark:#E06C75">n</span><span style="--shiki-light:#A626A4;--shiki-dark:#C678DD"> ==</span><span style="--shiki-light:#986801;--shiki-dark:#D19A66"> 1</span><span style="--shiki-light:#A626A4;--shiki-dark:#56B6C2"> ||</span><span style="--shiki-light:#E45649;--shiki-dark:#E06C75"> n</span><span style="--shiki-light:#A626A4;--shiki-dark:#C678DD"> ==</span><span style="--shiki-light:#986801;--shiki-dark:#D19A66"> 2</span><span style="--shiki-light:#383A42;--shiki-dark:#ABB2BF">) {</span></span>
<span class="line"><span style="--shiki-light:#A626A4;--shiki-dark:#C678DD">		return</span><span style="--shiki-light:#986801;--shiki-dark:#D19A66"> 1</span><span style="--shiki-light:#383A42;--shiki-dark:#ABB2BF">;</span></span>
<span class="line"><span style="--shiki-light:#383A42;--shiki-dark:#ABB2BF">	}</span></span>
<span class="line"><span style="--shiki-light:#A626A4;--shiki-dark:#C678DD">  if</span><span style="--shiki-light:#383A42;--shiki-dark:#ABB2BF"> (</span><span style="--shiki-light:#E45649;--shiki-dark:#E5C07B">memo</span><span style="--shiki-light:#383A42;--shiki-dark:#ABB2BF">.</span><span style="--shiki-light:#4078F2;--shiki-dark:#61AFEF">count</span><span style="--shiki-light:#383A42;--shiki-dark:#ABB2BF">(</span><span style="--shiki-light:#E45649;--shiki-dark:#E06C75">n</span><span style="--shiki-light:#383A42;--shiki-dark:#ABB2BF">) </span><span style="--shiki-light:#A626A4;--shiki-dark:#C678DD">></span><span style="--shiki-light:#986801;--shiki-dark:#D19A66"> 0</span><span style="--shiki-light:#383A42;--shiki-dark:#ABB2BF">) {</span></span>
<span class="line"><span style="--shiki-light:#A626A4;--shiki-dark:#C678DD">    return</span><span style="--shiki-light:#E45649;--shiki-dark:#E5C07B"> memo</span><span style="--shiki-light:#383A42;--shiki-dark:#ABB2BF">[</span><span style="--shiki-light:#E45649;--shiki-dark:#E06C75">n</span><span style="--shiki-light:#383A42;--shiki-dark:#ABB2BF">];</span></span>
<span class="line"><span style="--shiki-light:#383A42;--shiki-dark:#ABB2BF">  } </span><span style="--shiki-light:#A626A4;--shiki-dark:#C678DD">else</span><span style="--shiki-light:#383A42;--shiki-dark:#ABB2BF"> {</span></span>
<span class="line"><span style="--shiki-light:#E45649;--shiki-dark:#E5C07B">    memo</span><span style="--shiki-light:#383A42;--shiki-dark:#ABB2BF">[</span><span style="--shiki-light:#E45649;--shiki-dark:#E06C75">n</span><span style="--shiki-light:#383A42;--shiki-dark:#ABB2BF">] </span><span style="--shiki-light:#A626A4;--shiki-dark:#C678DD">=</span><span style="--shiki-light:#4078F2;--shiki-dark:#61AFEF"> Fibonacci</span><span style="--shiki-light:#383A42;--shiki-dark:#ABB2BF">(</span><span style="--shiki-light:#E45649;--shiki-dark:#E06C75">n</span><span style="--shiki-light:#A626A4;--shiki-dark:#C678DD"> -</span><span style="--shiki-light:#986801;--shiki-dark:#D19A66"> 1</span><span style="--shiki-light:#383A42;--shiki-dark:#ABB2BF">) </span><span style="--shiki-light:#A626A4;--shiki-dark:#C678DD">+</span><span style="--shiki-light:#4078F2;--shiki-dark:#61AFEF"> Fibonacci</span><span style="--shiki-light:#383A42;--shiki-dark:#ABB2BF">(</span><span style="--shiki-light:#E45649;--shiki-dark:#E06C75">n</span><span style="--shiki-light:#A626A4;--shiki-dark:#C678DD"> -</span><span style="--shiki-light:#986801;--shiki-dark:#D19A66"> 2</span><span style="--shiki-light:#383A42;--shiki-dark:#ABB2BF">);</span></span>
<span class="line"><span style="--shiki-light:#383A42;--shiki-dark:#ABB2BF">  }</span></span>
<span class="line"><span style="--shiki-light:#A626A4;--shiki-dark:#C678DD">	return</span><span style="--shiki-light:#E45649;--shiki-dark:#E5C07B"> memo</span><span style="--shiki-light:#383A42;--shiki-dark:#ABB2BF">[</span><span style="--shiki-light:#E45649;--shiki-dark:#E06C75">n</span><span style="--shiki-light:#383A42;--shiki-dark:#ABB2BF">];</span></span>
<span class="line"><span style="--shiki-light:#383A42;--shiki-dark:#ABB2BF">}</span></span></code></pre>
<div class="line-numbers" aria-hidden="true" style="counter-reset:line-number 0"><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div></div></div><p>现在，所有重复的节点都不存在了，我们只需要计算 <code>Fibonacci(i)</code> 一次。</p>
<h3>使用制表</h3>
<p>我们也可以使用制表来解决这个问题。要使用制表，我们可以定义一个数组或者一个向量来保存子问题的结果。</p>
<p>在 C++ 中，我们将使用 std::vector。</p>
<p>下面是使用制表实现的 Fibonacci 函数：</p>
<div class="language-cpp line-numbers-mode" data-highlighter="shiki" data-ext="cpp" style="--shiki-light:#383A42;--shiki-dark:#abb2bf;--shiki-light-bg:#FAFAFA;--shiki-dark-bg:#282c34"><pre class="shiki shiki-themes one-light one-dark-pro vp-code"><code class="language-cpp"><span class="line"><span style="--shiki-light:#A626A4;--shiki-dark:#C678DD">int</span><span style="--shiki-light:#4078F2;--shiki-dark:#61AFEF"> Fibonacci</span><span style="--shiki-light:#383A42;--shiki-dark:#ABB2BF">(</span><span style="--shiki-light:#A626A4;--shiki-dark:#C678DD">int</span><span style="--shiki-light:#383A42;--shiki-light-font-style:inherit;--shiki-dark:#E06C75;--shiki-dark-font-style:italic"> n</span><span style="--shiki-light:#383A42;--shiki-dark:#ABB2BF">) {</span></span>
<span class="line"><span style="--shiki-light:#A626A4;--shiki-dark:#C678DD">  if</span><span style="--shiki-light:#383A42;--shiki-dark:#ABB2BF"> (</span><span style="--shiki-light:#E45649;--shiki-dark:#E06C75">n</span><span style="--shiki-light:#A626A4;--shiki-dark:#C678DD"> ==</span><span style="--shiki-light:#986801;--shiki-dark:#D19A66"> 0</span><span style="--shiki-light:#383A42;--shiki-dark:#ABB2BF">) {</span></span>
<span class="line"><span style="--shiki-light:#A626A4;--shiki-dark:#C678DD">		return</span><span style="--shiki-light:#986801;--shiki-dark:#D19A66"> 0</span><span style="--shiki-light:#383A42;--shiki-dark:#ABB2BF">;</span></span>
<span class="line"><span style="--shiki-light:#383A42;--shiki-dark:#ABB2BF">	}</span></span>
<span class="line"><span style="--shiki-light:#A626A4;--shiki-dark:#C678DD">	if</span><span style="--shiki-light:#383A42;--shiki-dark:#ABB2BF"> (</span><span style="--shiki-light:#E45649;--shiki-dark:#E06C75">n</span><span style="--shiki-light:#A626A4;--shiki-dark:#C678DD"> ==</span><span style="--shiki-light:#986801;--shiki-dark:#D19A66"> 1</span><span style="--shiki-light:#A626A4;--shiki-dark:#56B6C2"> ||</span><span style="--shiki-light:#E45649;--shiki-dark:#E06C75"> n</span><span style="--shiki-light:#A626A4;--shiki-dark:#C678DD"> ==</span><span style="--shiki-light:#986801;--shiki-dark:#D19A66"> 2</span><span style="--shiki-light:#383A42;--shiki-dark:#ABB2BF">) {</span></span>
<span class="line"><span style="--shiki-light:#A626A4;--shiki-dark:#C678DD">		return</span><span style="--shiki-light:#986801;--shiki-dark:#D19A66"> 1</span><span style="--shiki-light:#383A42;--shiki-dark:#ABB2BF">;</span></span>
<span class="line"><span style="--shiki-light:#383A42;--shiki-dark:#ABB2BF">	}</span></span>
<span class="line"><span style="--shiki-light:#383A42;--shiki-dark:#ABB2BF">  std::</span><span style="--shiki-light:#C18401;--shiki-dark:#E5C07B">vector</span><span style="--shiki-light:#383A42;--shiki-dark:#ABB2BF">&#x3C;</span><span style="--shiki-light:#A626A4;--shiki-dark:#C678DD">int</span><span style="--shiki-light:#383A42;--shiki-dark:#ABB2BF">> </span><span style="--shiki-light:#4078F2;--shiki-dark:#61AFEF">d</span><span style="--shiki-light:#383A42;--shiki-dark:#ABB2BF">(</span><span style="--shiki-light:#E45649;--shiki-dark:#E06C75">n</span><span style="--shiki-light:#A626A4;--shiki-dark:#C678DD"> +</span><span style="--shiki-light:#986801;--shiki-dark:#D19A66"> 1</span><span style="--shiki-light:#383A42;--shiki-dark:#ABB2BF">);</span></span>
<span class="line"><span style="--shiki-light:#A626A4;--shiki-dark:#C678DD">  for</span><span style="--shiki-light:#383A42;--shiki-dark:#ABB2BF"> (</span><span style="--shiki-light:#A626A4;--shiki-dark:#C678DD">int</span><span style="--shiki-light:#E45649;--shiki-dark:#E06C75"> i</span><span style="--shiki-light:#A626A4;--shiki-dark:#C678DD"> =</span><span style="--shiki-light:#986801;--shiki-dark:#D19A66"> 3</span><span style="--shiki-light:#383A42;--shiki-dark:#ABB2BF">; </span><span style="--shiki-light:#E45649;--shiki-dark:#E06C75">i</span><span style="--shiki-light:#A626A4;--shiki-dark:#C678DD"> &#x3C;=</span><span style="--shiki-light:#E45649;--shiki-dark:#E06C75"> n</span><span style="--shiki-light:#383A42;--shiki-dark:#ABB2BF">; </span><span style="--shiki-light:#E45649;--shiki-dark:#E06C75">i</span><span style="--shiki-light:#A626A4;--shiki-dark:#C678DD">++</span><span style="--shiki-light:#383A42;--shiki-dark:#ABB2BF">) {</span></span>
<span class="line"><span style="--shiki-light:#E45649;--shiki-dark:#E5C07B">    d</span><span style="--shiki-light:#383A42;--shiki-dark:#ABB2BF">[</span><span style="--shiki-light:#E45649;--shiki-dark:#E06C75">i</span><span style="--shiki-light:#383A42;--shiki-dark:#ABB2BF">] </span><span style="--shiki-light:#A626A4;--shiki-dark:#C678DD">=</span><span style="--shiki-light:#E45649;--shiki-dark:#E5C07B"> d</span><span style="--shiki-light:#383A42;--shiki-dark:#ABB2BF">[</span><span style="--shiki-light:#E45649;--shiki-dark:#E06C75">i</span><span style="--shiki-light:#A626A4;--shiki-dark:#C678DD"> -</span><span style="--shiki-light:#986801;--shiki-dark:#D19A66"> 1</span><span style="--shiki-light:#383A42;--shiki-dark:#ABB2BF">] </span><span style="--shiki-light:#A626A4;--shiki-dark:#C678DD">+</span><span style="--shiki-light:#E45649;--shiki-dark:#E5C07B"> d</span><span style="--shiki-light:#383A42;--shiki-dark:#ABB2BF">[</span><span style="--shiki-light:#E45649;--shiki-dark:#E06C75">i</span><span style="--shiki-light:#A626A4;--shiki-dark:#C678DD"> -</span><span style="--shiki-light:#986801;--shiki-dark:#D19A66"> 2</span><span style="--shiki-light:#383A42;--shiki-dark:#ABB2BF">];</span></span>
<span class="line"><span style="--shiki-light:#383A42;--shiki-dark:#ABB2BF">  }</span></span>
<span class="line"><span style="--shiki-light:#A626A4;--shiki-dark:#C678DD">  return</span><span style="--shiki-light:#E45649;--shiki-dark:#E5C07B"> d</span><span style="--shiki-light:#383A42;--shiki-dark:#ABB2BF">[</span><span style="--shiki-light:#E45649;--shiki-dark:#E06C75">n</span><span style="--shiki-light:#383A42;--shiki-dark:#ABB2BF">];</span></span>
<span class="line"><span style="--shiki-light:#383A42;--shiki-dark:#ABB2BF">}</span></span></code></pre>
<div class="line-numbers" aria-hidden="true" style="counter-reset:line-number 0"><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div></div></div><h3>可选的其他改进</h3>
<p>有一种叫 &quot;状态压缩&quot; 的改进方式。</p>
<p>状态压缩是动态规划中使用的一种技术，用于减少存储计算中间结果所需的内存量。
它涉及使用较小的数据结构（如位向量或哈希表）而不是数组或矩阵等较大的数据结构来表示子问题状态。
这有助于降低算法的空间复杂性，并有可能解决更大的问题或具有更复杂的子问题的问题。</p>
<p>针对当前情况，我们将 <code>d</code> 替换为 <code>pre</code> 和 <code>curr</code> 两个整数：</p>
<div class="language-cpp line-numbers-mode" data-highlighter="shiki" data-ext="cpp" style="--shiki-light:#383A42;--shiki-dark:#abb2bf;--shiki-light-bg:#FAFAFA;--shiki-dark-bg:#282c34"><pre class="shiki shiki-themes one-light one-dark-pro vp-code"><code class="language-cpp"><span class="line"><span style="--shiki-light:#A626A4;--shiki-dark:#C678DD">int</span><span style="--shiki-light:#4078F2;--shiki-dark:#61AFEF"> Fibonacci</span><span style="--shiki-light:#383A42;--shiki-dark:#ABB2BF">(</span><span style="--shiki-light:#A626A4;--shiki-dark:#C678DD">int</span><span style="--shiki-light:#383A42;--shiki-light-font-style:inherit;--shiki-dark:#E06C75;--shiki-dark-font-style:italic"> n</span><span style="--shiki-light:#383A42;--shiki-dark:#ABB2BF">) {</span></span>
<span class="line"><span style="--shiki-light:#A626A4;--shiki-dark:#C678DD">  if</span><span style="--shiki-light:#383A42;--shiki-dark:#ABB2BF"> (</span><span style="--shiki-light:#E45649;--shiki-dark:#E06C75">n</span><span style="--shiki-light:#A626A4;--shiki-dark:#C678DD"> ==</span><span style="--shiki-light:#986801;--shiki-dark:#D19A66"> 0</span><span style="--shiki-light:#383A42;--shiki-dark:#ABB2BF">) {</span></span>
<span class="line"><span style="--shiki-light:#A626A4;--shiki-dark:#C678DD">		return</span><span style="--shiki-light:#986801;--shiki-dark:#D19A66"> 0</span><span style="--shiki-light:#383A42;--shiki-dark:#ABB2BF">;</span></span>
<span class="line"><span style="--shiki-light:#383A42;--shiki-dark:#ABB2BF">	}</span></span>
<span class="line"><span style="--shiki-light:#A626A4;--shiki-dark:#C678DD">	if</span><span style="--shiki-light:#383A42;--shiki-dark:#ABB2BF"> (</span><span style="--shiki-light:#E45649;--shiki-dark:#E06C75">n</span><span style="--shiki-light:#A626A4;--shiki-dark:#C678DD"> ==</span><span style="--shiki-light:#986801;--shiki-dark:#D19A66"> 1</span><span style="--shiki-light:#A626A4;--shiki-dark:#56B6C2"> ||</span><span style="--shiki-light:#E45649;--shiki-dark:#E06C75"> n</span><span style="--shiki-light:#A626A4;--shiki-dark:#C678DD"> ==</span><span style="--shiki-light:#986801;--shiki-dark:#D19A66"> 2</span><span style="--shiki-light:#383A42;--shiki-dark:#ABB2BF">) {</span></span>
<span class="line"><span style="--shiki-light:#A626A4;--shiki-dark:#C678DD">		return</span><span style="--shiki-light:#986801;--shiki-dark:#D19A66"> 1</span><span style="--shiki-light:#383A42;--shiki-dark:#ABB2BF">;</span></span>
<span class="line"><span style="--shiki-light:#383A42;--shiki-dark:#ABB2BF">	}</span></span>
<span class="line"><span style="--shiki-light:#A626A4;--shiki-dark:#C678DD">  int</span><span style="--shiki-light:#E45649;--shiki-dark:#E06C75"> pre</span><span style="--shiki-light:#A626A4;--shiki-dark:#C678DD"> =</span><span style="--shiki-light:#986801;--shiki-dark:#D19A66"> 1</span><span style="--shiki-light:#383A42;--shiki-dark:#ABB2BF">;</span></span>
<span class="line"><span style="--shiki-light:#A626A4;--shiki-dark:#C678DD">  int</span><span style="--shiki-light:#E45649;--shiki-dark:#E06C75"> curr</span><span style="--shiki-light:#A626A4;--shiki-dark:#C678DD"> =</span><span style="--shiki-light:#986801;--shiki-dark:#D19A66"> 1</span><span style="--shiki-light:#383A42;--shiki-dark:#ABB2BF">;</span></span>
<span class="line"><span style="--shiki-light:#A626A4;--shiki-dark:#C678DD">  for</span><span style="--shiki-light:#383A42;--shiki-dark:#ABB2BF"> (</span><span style="--shiki-light:#A626A4;--shiki-dark:#C678DD">int</span><span style="--shiki-light:#E45649;--shiki-dark:#E06C75"> i</span><span style="--shiki-light:#A626A4;--shiki-dark:#C678DD"> =</span><span style="--shiki-light:#986801;--shiki-dark:#D19A66"> 3</span><span style="--shiki-light:#383A42;--shiki-dark:#ABB2BF">; </span><span style="--shiki-light:#E45649;--shiki-dark:#E06C75">i</span><span style="--shiki-light:#A626A4;--shiki-dark:#C678DD"> &#x3C;=</span><span style="--shiki-light:#E45649;--shiki-dark:#E06C75"> n</span><span style="--shiki-light:#383A42;--shiki-dark:#ABB2BF">; </span><span style="--shiki-light:#E45649;--shiki-dark:#E06C75">i</span><span style="--shiki-light:#A626A4;--shiki-dark:#C678DD">++</span><span style="--shiki-light:#383A42;--shiki-dark:#ABB2BF">) {</span></span>
<span class="line"><span style="--shiki-light:#A626A4;--shiki-dark:#C678DD">    int</span><span style="--shiki-light:#E45649;--shiki-dark:#E06C75"> sum</span><span style="--shiki-light:#A626A4;--shiki-dark:#C678DD"> =</span><span style="--shiki-light:#E45649;--shiki-dark:#E06C75"> pre</span><span style="--shiki-light:#A626A4;--shiki-dark:#C678DD"> +</span><span style="--shiki-light:#E45649;--shiki-dark:#E06C75"> curr</span><span style="--shiki-light:#383A42;--shiki-dark:#ABB2BF">;</span></span>
<span class="line"><span style="--shiki-light:#E45649;--shiki-dark:#E06C75">    pre</span><span style="--shiki-light:#A626A4;--shiki-dark:#C678DD"> =</span><span style="--shiki-light:#E45649;--shiki-dark:#E06C75"> curr</span><span style="--shiki-light:#383A42;--shiki-dark:#ABB2BF">;</span></span>
<span class="line"><span style="--shiki-light:#E45649;--shiki-dark:#E06C75">    curr</span><span style="--shiki-light:#A626A4;--shiki-dark:#C678DD"> =</span><span style="--shiki-light:#E45649;--shiki-dark:#E06C75"> sum</span><span style="--shiki-light:#383A42;--shiki-dark:#ABB2BF">;</span></span>
<span class="line"><span style="--shiki-light:#383A42;--shiki-dark:#ABB2BF">  }</span></span>
<span class="line"><span style="--shiki-light:#A626A4;--shiki-dark:#C678DD">  return</span><span style="--shiki-light:#E45649;--shiki-dark:#E06C75"> curr</span><span style="--shiki-light:#383A42;--shiki-dark:#ABB2BF">;</span></span>
<span class="line"><span style="--shiki-light:#383A42;--shiki-dark:#ABB2BF">}</span></span></code></pre>
<div class="line-numbers" aria-hidden="true" style="counter-reset:line-number 0"><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div></div></div><h2>框架</h2>
<ol>
<li>
<p>找到所有的 base cases。</p>
<p>在斐波那契问题中，所有的 base cases 包括 <code>n == 0</code>、 <code>n == 1</code> 以及 <code>n == 2</code>。</p>
</li>
<li>
<p>找到所有的状态。</p>
<p>在斐波那契问题中，所有的状态即是所有的非负数。</p>
</li>
<li>
<p>找到状态转移方程。</p>
<p>在斐波那契问题中，状态转移方程为 : dp[i] = dp[i - 1] + dp[i - 2]</p>
</li>
</ol>
<div class="hint-container tip">
<p class="hint-container-title">提示</p>
<p>状态转换方程是一个数学方程，描述了系统状态如何随时间变化。
在动态规划的背景下，状态转换方程是一种方程，它描述了如何从最优解到其子问题构建问题的最优解。
通过使用状态转换方程，可以通过将其分解为较小的子问题，然后将最优解与这些子问题相结合，以找到较大问题的最优解来解决问题。</p>
</div>
<ol start="4">
<li>找到所有的选择，并弄明白怎么去定义 DP 表。</li>
</ol>
]]></content>
    <author>
      <name>Lazy_V</name>
    </author>
    <category term="算法"/>
    <contributor>
      <name>Lazy_V</name>
    </contributor>
    <published>2022-12-09T00:00:00.000Z</published>
    <rights>Copyright by Lazy_V</rights>
  </entry>
  <entry>
    <title type="text">深入探究 C# 11 的静态抽象接口方法</title>
    <id>https://zzxzzk115.github.io/blog/zh/posts/programming-languages/csharp/static-abstract-members-in-interfaces.html</id>
    <link href="https://zzxzzk115.github.io/blog/zh/posts/programming-languages/csharp/static-abstract-members-in-interfaces.html"/>
    <updated>2022-12-17T14:58:15.000Z</updated>
    <summary type="html"><![CDATA[<p>静态抽象接口方法是 C# 11 (对应 .NET 7.0) 引入的新特性。本篇我们来探究一下为什么要有它、它是什么，以及怎么使用它。</p>
]]></summary>
    <content type="html"><![CDATA[<p>静态抽象接口方法是 C# 11 (对应 .NET 7.0) 引入的新特性。本篇我们来探究一下为什么要有它、它是什么，以及怎么使用它。</p>
<!-- more -->
<h2>为什么要有静态抽象接口方法</h2>
<p>回顾我们曾经的需求：</p>
<ol>
<li>
<p>我们希望在泛型中进行数学运算，但是编译器无法得知 <code>T</code> 的运行时类型，从而无法得知 <code>T</code> 是否支持诸如加减乘除的数学运算，故无法实现类似下面这样的代码：</p>
<div class="language-csharp line-numbers-mode" data-highlighter="shiki" data-ext="csharp" style="--shiki-light:#383A42;--shiki-dark:#abb2bf;--shiki-light-bg:#FAFAFA;--shiki-dark-bg:#282c34"><pre class="shiki shiki-themes one-light one-dark-pro vp-code"><code class="language-csharp"><span class="line"><span style="--shiki-light:#A626A4;--shiki-dark:#C678DD">public</span><span style="--shiki-light:#C18401;--shiki-dark:#E5C07B"> T</span><span style="--shiki-light:#4078F2;--shiki-dark:#61AFEF"> Add</span><span style="--shiki-light:#383A42;--shiki-dark:#ABB2BF">&#x3C;</span><span style="--shiki-light:#C18401;--shiki-dark:#E5C07B">T</span><span style="--shiki-light:#383A42;--shiki-dark:#ABB2BF">>(</span><span style="--shiki-light:#C18401;--shiki-dark:#E5C07B">T</span><span style="--shiki-light:#383A42;--shiki-dark:#E5C07B"> a</span><span style="--shiki-light:#383A42;--shiki-dark:#ABB2BF">, </span><span style="--shiki-light:#C18401;--shiki-dark:#E5C07B">T</span><span style="--shiki-light:#383A42;--shiki-dark:#E5C07B"> b</span><span style="--shiki-light:#383A42;--shiki-dark:#ABB2BF">)</span></span>
<span class="line"><span style="--shiki-light:#383A42;--shiki-dark:#ABB2BF">{</span></span>
<span class="line"><span style="--shiki-light:#A626A4;--shiki-dark:#C678DD">    return</span><span style="--shiki-light:#383A42;--shiki-dark:#E06C75"> a</span><span style="--shiki-light:#383A42;--shiki-dark:#56B6C2"> +</span><span style="--shiki-light:#383A42;--shiki-dark:#E06C75"> b</span><span style="--shiki-light:#383A42;--shiki-dark:#ABB2BF">;</span></span>
<span class="line"><span style="--shiki-light:#383A42;--shiki-dark:#ABB2BF">}</span></span></code></pre>
<div class="line-numbers" aria-hidden="true" style="counter-reset:line-number 0"><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div></div></div></li>
<li>
<p>我们希望某些情况下，<code>T</code> 作为编译期能够确定的类型，能够调用 <code>T</code> 的公有静态方法。然而，接口中无法声明静态接口方法，来允许我们进行 <code>T.XXX()</code>; 的操作。要是能够让接口中声明静态接口方法就好了。我们曾经的幻想：</p>
<div class="language-csharp line-numbers-mode" data-highlighter="shiki" data-ext="csharp" style="--shiki-light:#383A42;--shiki-dark:#abb2bf;--shiki-light-bg:#FAFAFA;--shiki-dark-bg:#282c34"><pre class="shiki shiki-themes one-light one-dark-pro vp-code"><code class="language-csharp"><span class="line"><span style="--shiki-light:#A626A4;--shiki-dark:#C678DD">public</span><span style="--shiki-light:#A626A4;--shiki-dark:#C678DD"> interface</span><span style="--shiki-light:#C18401;--shiki-dark:#E5C07B"> ISomeInterface</span><span style="--shiki-light:#383A42;--shiki-dark:#ABB2BF">&#x3C;</span><span style="--shiki-light:#C18401;--shiki-dark:#E5C07B">T</span><span style="--shiki-light:#383A42;--shiki-dark:#ABB2BF">> </span><span style="--shiki-light:#A626A4;--shiki-dark:#C678DD">where</span><span style="--shiki-light:#C18401;--shiki-dark:#E5C07B"> T</span><span style="--shiki-light:#383A42;--shiki-dark:#ABB2BF"> : </span><span style="--shiki-light:#C18401;--shiki-dark:#E5C07B">ISomeInterface</span><span style="--shiki-light:#383A42;--shiki-dark:#ABB2BF">&#x3C;</span><span style="--shiki-light:#C18401;--shiki-dark:#E5C07B">T</span><span style="--shiki-light:#383A42;--shiki-dark:#ABB2BF">></span></span>
<span class="line"><span style="--shiki-light:#383A42;--shiki-dark:#ABB2BF">{</span></span>
<span class="line"><span style="--shiki-light:#A626A4;--shiki-dark:#C678DD">    static</span><span style="--shiki-light:#A626A4;--shiki-dark:#C678DD"> void</span><span style="--shiki-light:#4078F2;--shiki-dark:#61AFEF"> SomeStaticMethod</span><span style="--shiki-light:#383A42;--shiki-dark:#ABB2BF">();</span></span>
<span class="line"><span style="--shiki-light:#383A42;--shiki-dark:#ABB2BF">}</span></span>
<span class="line"></span>
<span class="line"><span style="--shiki-light:#A626A4;--shiki-dark:#C678DD">public</span><span style="--shiki-light:#A626A4;--shiki-dark:#C678DD"> void</span><span style="--shiki-light:#4078F2;--shiki-dark:#61AFEF"> Test</span><span style="--shiki-light:#383A42;--shiki-dark:#ABB2BF">&#x3C;</span><span style="--shiki-light:#C18401;--shiki-dark:#E5C07B">T</span><span style="--shiki-light:#383A42;--shiki-dark:#ABB2BF">>() </span><span style="--shiki-light:#A626A4;--shiki-dark:#C678DD">where</span><span style="--shiki-light:#C18401;--shiki-dark:#E5C07B"> T</span><span style="--shiki-light:#383A42;--shiki-dark:#ABB2BF"> : </span><span style="--shiki-light:#C18401;--shiki-dark:#E5C07B">ISomeInterface</span><span style="--shiki-light:#383A42;--shiki-dark:#ABB2BF">&#x3C;</span><span style="--shiki-light:#C18401;--shiki-dark:#E5C07B">T</span><span style="--shiki-light:#383A42;--shiki-dark:#ABB2BF">></span></span>
<span class="line"><span style="--shiki-light:#383A42;--shiki-dark:#ABB2BF">{</span></span>
<span class="line"><span style="--shiki-light:#383A42;--shiki-dark:#E5C07B">    T</span><span style="--shiki-light:#383A42;--shiki-dark:#ABB2BF">.</span><span style="--shiki-light:#4078F2;--shiki-dark:#61AFEF">SomeStaticMethod</span><span style="--shiki-light:#383A42;--shiki-dark:#ABB2BF">();</span></span>
<span class="line"><span style="--shiki-light:#383A42;--shiki-dark:#ABB2BF">}</span></span></code></pre>
<div class="line-numbers" aria-hidden="true" style="counter-reset:line-number 0"><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div></div></div></li>
</ol>
<p>为了满足这些需求，微软携手社区开发者们，共同推出了静态抽象接口方法，大家以前的幻想现在已经得以实现。</p>
<h2>什么是静态抽象接口方法</h2>
<p>静态抽象接口方法是 C# 11 (对应 .NET 7.0) 引入的新特性。使用最新的 Visual Studio 2022 版本，安装过 .NET 7.0 运行时，即可尝鲜。</p>
<p>它支持在接口中声明 static abstract 方法 (官方说支持在接口中声明 static virtual 方法，但我开启了 preview 后依旧报错，暂时不知道 static virtual 如何使用)。</p>
<h2>怎么样使用静态抽象接口方法</h2>
<p>举个例子：</p>
<p>我们可以利用上述新特性，定义如下接口：</p>
<div class="language-csharp line-numbers-mode" data-highlighter="shiki" data-ext="csharp" style="--shiki-light:#383A42;--shiki-dark:#abb2bf;--shiki-light-bg:#FAFAFA;--shiki-dark-bg:#282c34"><pre class="shiki shiki-themes one-light one-dark-pro vp-code"><code class="language-csharp"><span class="line"><span style="--shiki-light:#A626A4;--shiki-dark:#C678DD">public</span><span style="--shiki-light:#A626A4;--shiki-dark:#C678DD"> interface</span><span style="--shiki-light:#C18401;--shiki-dark:#E5C07B"> IGetNext</span><span style="--shiki-light:#383A42;--shiki-dark:#ABB2BF">&#x3C;</span><span style="--shiki-light:#C18401;--shiki-dark:#E5C07B">T</span><span style="--shiki-light:#383A42;--shiki-dark:#ABB2BF">> </span><span style="--shiki-light:#A626A4;--shiki-dark:#C678DD">where</span><span style="--shiki-light:#C18401;--shiki-dark:#E5C07B"> T</span><span style="--shiki-light:#383A42;--shiki-dark:#ABB2BF"> : </span><span style="--shiki-light:#C18401;--shiki-dark:#E5C07B">IGetNext</span><span style="--shiki-light:#383A42;--shiki-dark:#ABB2BF">&#x3C;</span><span style="--shiki-light:#C18401;--shiki-dark:#E5C07B">T</span><span style="--shiki-light:#383A42;--shiki-dark:#ABB2BF">></span></span>
<span class="line"><span style="--shiki-light:#383A42;--shiki-dark:#ABB2BF">{</span></span>
<span class="line"><span style="--shiki-light:#A626A4;--shiki-dark:#C678DD">    static</span><span style="--shiki-light:#A626A4;--shiki-dark:#C678DD"> abstract</span><span style="--shiki-light:#C18401;--shiki-dark:#E5C07B"> T</span><span style="--shiki-light:#A626A4;--shiki-dark:#C678DD"> operator</span><span style="--shiki-light:#4078F2;--shiki-dark:#61AFEF"> ++</span><span style="--shiki-light:#383A42;--shiki-dark:#ABB2BF">(</span><span style="--shiki-light:#C18401;--shiki-dark:#E5C07B">T</span><span style="--shiki-light:#383A42;--shiki-dark:#E5C07B"> other</span><span style="--shiki-light:#383A42;--shiki-dark:#ABB2BF">);</span></span>
<span class="line"><span style="--shiki-light:#383A42;--shiki-dark:#ABB2BF">}</span></span></code></pre>
<div class="line-numbers" aria-hidden="true" style="counter-reset:line-number 0"><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div></div></div><blockquote>
<p>许多运算符都强制要求其参数必须与类型匹配，或者是按照约束要实现包含类型的类型参数，所以这里，我们约束 <code>T</code> 必须实现 <code>IGetNext&lt;T&gt;</code>。</p>
</blockquote>
<p>然后，定义一个 <code>RepeatSequence</code> 结构去实现上面的接口，该结构创建由 <code>‘A'</code> 组成的字符串，每个 <code>++</code> 操作都使得向字符串中添加一个 <code>'A'</code>。</p>
<div class="language-csharp line-numbers-mode" data-highlighter="shiki" data-ext="csharp" style="--shiki-light:#383A42;--shiki-dark:#abb2bf;--shiki-light-bg:#FAFAFA;--shiki-dark-bg:#282c34"><pre class="shiki shiki-themes one-light one-dark-pro vp-code"><code class="language-csharp"><span class="line"><span style="--shiki-light:#A626A4;--shiki-dark:#C678DD">public</span><span style="--shiki-light:#A626A4;--shiki-dark:#C678DD"> struct</span><span style="--shiki-light:#C18401;--shiki-dark:#E5C07B"> RepeatSequence</span><span style="--shiki-light:#383A42;--shiki-dark:#ABB2BF"> : </span><span style="--shiki-light:#C18401;--shiki-dark:#E5C07B">IGetNext</span><span style="--shiki-light:#383A42;--shiki-dark:#ABB2BF">&#x3C;</span><span style="--shiki-light:#C18401;--shiki-dark:#E5C07B">RepeatSequence</span><span style="--shiki-light:#383A42;--shiki-dark:#ABB2BF">></span></span>
<span class="line"><span style="--shiki-light:#383A42;--shiki-dark:#ABB2BF">{</span></span>
<span class="line"><span style="--shiki-light:#A626A4;--shiki-dark:#C678DD">    private</span><span style="--shiki-light:#A626A4;--shiki-dark:#C678DD"> const</span><span style="--shiki-light:#A626A4;--shiki-dark:#C678DD"> char</span><span style="--shiki-light:#E45649;--shiki-dark:#E06C75"> Ch</span><span style="--shiki-light:#383A42;--shiki-dark:#56B6C2"> =</span><span style="--shiki-light:#50A14F;--shiki-dark:#98C379"> 'A'</span><span style="--shiki-light:#383A42;--shiki-dark:#ABB2BF">;</span></span>
<span class="line"><span style="--shiki-light:#A626A4;--shiki-dark:#C678DD">    public</span><span style="--shiki-light:#A626A4;--shiki-dark:#C678DD"> string</span><span style="--shiki-light:#E45649;--shiki-dark:#E06C75"> Text</span><span style="--shiki-light:#383A42;--shiki-dark:#56B6C2"> =</span><span style="--shiki-light:#383A42;--shiki-dark:#ABB2BF"> new </span><span style="--shiki-light:#A626A4;--shiki-dark:#C678DD">string</span><span style="--shiki-light:#383A42;--shiki-dark:#ABB2BF">(</span><span style="--shiki-light:#383A42;--shiki-dark:#E06C75">Ch</span><span style="--shiki-light:#383A42;--shiki-dark:#ABB2BF">, </span><span style="--shiki-light:#986801;--shiki-dark:#D19A66">1</span><span style="--shiki-light:#383A42;--shiki-dark:#ABB2BF">);</span></span>
<span class="line"></span>
<span class="line"><span style="--shiki-light:#A626A4;--shiki-dark:#C678DD">    public</span><span style="--shiki-light:#4078F2;--shiki-dark:#61AFEF"> RepeatSequence</span><span style="--shiki-light:#383A42;--shiki-dark:#ABB2BF">() {}</span></span>
<span class="line"></span>
<span class="line"><span style="--shiki-light:#A626A4;--shiki-dark:#C678DD">    public</span><span style="--shiki-light:#A626A4;--shiki-dark:#C678DD"> static</span><span style="--shiki-light:#C18401;--shiki-dark:#E5C07B"> RepeatSequence</span><span style="--shiki-light:#A626A4;--shiki-dark:#C678DD"> operator</span><span style="--shiki-light:#4078F2;--shiki-dark:#61AFEF"> ++</span><span style="--shiki-light:#383A42;--shiki-dark:#ABB2BF">(</span><span style="--shiki-light:#C18401;--shiki-dark:#E5C07B">RepeatSequence</span><span style="--shiki-light:#383A42;--shiki-dark:#E5C07B"> other</span><span style="--shiki-light:#383A42;--shiki-dark:#ABB2BF">)</span></span>
<span class="line"><span style="--shiki-light:#383A42;--shiki-dark:#ABB2BF">        => </span><span style="--shiki-light:#383A42;--shiki-dark:#E06C75">other</span><span style="--shiki-light:#383A42;--shiki-dark:#ABB2BF"> with { </span><span style="--shiki-light:#383A42;--shiki-dark:#E06C75">Text</span><span style="--shiki-light:#383A42;--shiki-dark:#56B6C2"> =</span><span style="--shiki-light:#383A42;--shiki-dark:#E5C07B"> other</span><span style="--shiki-light:#383A42;--shiki-dark:#ABB2BF">.</span><span style="--shiki-light:#383A42;--shiki-dark:#E5C07B">Text</span><span style="--shiki-light:#383A42;--shiki-dark:#56B6C2"> +</span><span style="--shiki-light:#383A42;--shiki-dark:#E06C75"> Ch</span><span style="--shiki-light:#383A42;--shiki-dark:#ABB2BF"> };</span></span>
<span class="line"></span>
<span class="line"><span style="--shiki-light:#A626A4;--shiki-dark:#C678DD">    public</span><span style="--shiki-light:#A626A4;--shiki-dark:#C678DD"> override</span><span style="--shiki-light:#A626A4;--shiki-dark:#C678DD"> string</span><span style="--shiki-light:#4078F2;--shiki-dark:#61AFEF"> ToString</span><span style="--shiki-light:#383A42;--shiki-dark:#ABB2BF">() => </span><span style="--shiki-light:#383A42;--shiki-dark:#E06C75">Text</span><span style="--shiki-light:#383A42;--shiki-dark:#ABB2BF">;</span></span>
<span class="line"><span style="--shiki-light:#383A42;--shiki-dark:#ABB2BF">}</span></span></code></pre>
<div class="line-numbers" aria-hidden="true" style="counter-reset:line-number 0"><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div></div></div><p>然后我们可以编写测试代码，打印看看结果：</p>
<div class="language-csharp line-numbers-mode" data-highlighter="shiki" data-ext="csharp" style="--shiki-light:#383A42;--shiki-dark:#abb2bf;--shiki-light-bg:#FAFAFA;--shiki-dark-bg:#282c34"><pre class="shiki shiki-themes one-light one-dark-pro vp-code"><code class="language-csharp"><span class="line"><span style="--shiki-light:#A626A4;--shiki-dark:#C678DD">var</span><span style="--shiki-light:#383A42;--shiki-dark:#E06C75"> str</span><span style="--shiki-light:#383A42;--shiki-dark:#56B6C2"> =</span><span style="--shiki-light:#383A42;--shiki-dark:#ABB2BF"> new </span><span style="--shiki-light:#C18401;--shiki-dark:#E5C07B">RepeatSequence</span><span style="--shiki-light:#383A42;--shiki-dark:#ABB2BF">();</span></span>
<span class="line"></span>
<span class="line"><span style="--shiki-light:#A626A4;--shiki-dark:#C678DD">for</span><span style="--shiki-light:#383A42;--shiki-dark:#ABB2BF"> (</span><span style="--shiki-light:#A626A4;--shiki-dark:#C678DD">int</span><span style="--shiki-light:#383A42;--shiki-dark:#E06C75"> i</span><span style="--shiki-light:#383A42;--shiki-dark:#56B6C2"> =</span><span style="--shiki-light:#986801;--shiki-dark:#D19A66"> 0</span><span style="--shiki-light:#383A42;--shiki-dark:#ABB2BF">; </span><span style="--shiki-light:#383A42;--shiki-dark:#E06C75">i</span><span style="--shiki-light:#383A42;--shiki-dark:#56B6C2"> &#x3C;</span><span style="--shiki-light:#986801;--shiki-dark:#D19A66"> 10</span><span style="--shiki-light:#383A42;--shiki-dark:#ABB2BF">; </span><span style="--shiki-light:#383A42;--shiki-dark:#E06C75">i</span><span style="--shiki-light:#383A42;--shiki-dark:#56B6C2">++</span><span style="--shiki-light:#383A42;--shiki-dark:#ABB2BF">)</span></span>
<span class="line"><span style="--shiki-light:#383A42;--shiki-dark:#E5C07B">    Console</span><span style="--shiki-light:#383A42;--shiki-dark:#ABB2BF">.</span><span style="--shiki-light:#4078F2;--shiki-dark:#61AFEF">WriteLine</span><span style="--shiki-light:#383A42;--shiki-dark:#ABB2BF">(</span><span style="--shiki-light:#383A42;--shiki-dark:#E06C75">str</span><span style="--shiki-light:#383A42;--shiki-dark:#56B6C2">++</span><span style="--shiki-light:#383A42;--shiki-dark:#ABB2BF">);</span></span></code></pre>
<div class="line-numbers" aria-hidden="true" style="counter-reset:line-number 0"><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div></div></div><p>得到输出：</p>
<div class="language- line-numbers-mode" data-highlighter="shiki" data-ext style="--shiki-light:#383A42;--shiki-dark:#abb2bf;--shiki-light-bg:#FAFAFA;--shiki-dark-bg:#282c34"><pre class="shiki shiki-themes one-light one-dark-pro vp-code"><code class="language-"><span class="line"><span>A</span></span>
<span class="line"><span>AA</span></span>
<span class="line"><span>AAA</span></span>
<span class="line"><span>AAAA</span></span>
<span class="line"><span>AAAAA</span></span>
<span class="line"><span>AAAAAA</span></span>
<span class="line"><span>AAAAAAA</span></span>
<span class="line"><span>AAAAAAAA</span></span>
<span class="line"><span>AAAAAAAAA</span></span>
<span class="line"><span>AAAAAAAAAA</span></span></code></pre>
<div class="line-numbers" aria-hidden="true" style="counter-reset:line-number 0"><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div></div></div><h3>Playground</h3>
<iframe style="width: 100%; height: 400px;" src="https://sharplab.io/#v2:EYLgtghglgdgNAFxAJwK7wCYgNQB8ACATAIwCwAUBfgMwAEsCApsgGYQDGjtAkgOKMIAcowAeCADwAVAHy0A7gAtmXSbRA9+Q0RJkUA3hVpHa+YgDZaEYAGcEyDglqqA9gAdmEBM+S1s2ABQuCErIAJQA3BQAvhRUdLZo7I4ASozungDKjACOqIwwnGoaAsJi4qnpCFm5+ZzS+obGrshQAG6eXOzOMLa07AoQPgDCCrQAvLQA5ACCk5HkxiZ0pgAMTtrjtDCMcibEK/4jcLTEEY1G50u0FYyZOXkFjP6htHoxlAvGNHsWN3c1j1obg8Xh8fn8fyq91qXGcwWYoUuizGsjhIXkUGCr3WYk2aOYADpJBtsLQRrQovNLt9nK1mC0MFxVk5nBk7LAAObPcayYliebvCgMZgwCAAGxMhD2AHYGp8jKYLPgACy0ACy0Bg/lWAG0ALqWZAc6yI+WvJHGdo+BKbba7SHVB6cZ5Us2LFjeWj+Bj0TYrcK+8Qnf30PymxYRr7EACc/gSYfmi3eUSAA"></iframe>
<h2>从代码角度分析</h2>
<h3>看看 IL</h3>
<p>如下接口：</p>
<div class="language-csharp line-numbers-mode" data-highlighter="shiki" data-ext="csharp" style="--shiki-light:#383A42;--shiki-dark:#abb2bf;--shiki-light-bg:#FAFAFA;--shiki-dark-bg:#282c34"><pre class="shiki shiki-themes one-light one-dark-pro vp-code"><code class="language-csharp"><span class="line"><span style="--shiki-light:#A626A4;--shiki-dark:#C678DD">public</span><span style="--shiki-light:#A626A4;--shiki-dark:#C678DD"> interface</span><span style="--shiki-light:#C18401;--shiki-dark:#E5C07B"> IDeepInSourceCode</span><span style="--shiki-light:#383A42;--shiki-dark:#ABB2BF">&#x3C;</span><span style="--shiki-light:#C18401;--shiki-dark:#E5C07B">T</span><span style="--shiki-light:#383A42;--shiki-dark:#ABB2BF">> </span><span style="--shiki-light:#A626A4;--shiki-dark:#C678DD">where</span><span style="--shiki-light:#C18401;--shiki-dark:#E5C07B"> T</span><span style="--shiki-light:#383A42;--shiki-dark:#ABB2BF"> : </span><span style="--shiki-light:#C18401;--shiki-dark:#E5C07B">IDeepInSourceCode</span><span style="--shiki-light:#383A42;--shiki-dark:#ABB2BF">&#x3C;</span><span style="--shiki-light:#C18401;--shiki-dark:#E5C07B">T</span><span style="--shiki-light:#383A42;--shiki-dark:#ABB2BF">></span></span>
<span class="line"><span style="--shiki-light:#383A42;--shiki-dark:#ABB2BF">{</span></span>
<span class="line"><span style="--shiki-light:#A626A4;--shiki-dark:#C678DD">    void</span><span style="--shiki-light:#4078F2;--shiki-dark:#61AFEF"> SomeMethod</span><span style="--shiki-light:#383A42;--shiki-dark:#ABB2BF">();</span></span>
<span class="line"><span style="--shiki-light:#A626A4;--shiki-dark:#C678DD">    static</span><span style="--shiki-light:#A626A4;--shiki-dark:#C678DD"> abstract</span><span style="--shiki-light:#A626A4;--shiki-dark:#C678DD"> void</span><span style="--shiki-light:#4078F2;--shiki-dark:#61AFEF"> SomeStaticAbstractMethod</span><span style="--shiki-light:#383A42;--shiki-dark:#ABB2BF">();</span></span>
<span class="line"><span style="--shiki-light:#383A42;--shiki-dark:#ABB2BF">}</span></span></code></pre>
<div class="line-numbers" aria-hidden="true" style="counter-reset:line-number 0"><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div></div></div><p>它的 IL：</p>
<div class="language-csharp line-numbers-mode" data-highlighter="shiki" data-ext="csharp" style="--shiki-light:#383A42;--shiki-dark:#abb2bf;--shiki-light-bg:#FAFAFA;--shiki-dark-bg:#282c34"><pre class="shiki shiki-themes one-light one-dark-pro vp-code"><code class="language-csharp"><span class="line"><span style="--shiki-light:#A0A1A7;--shiki-light-font-style:italic;--shiki-dark:#7F848E;--shiki-dark-font-style:italic">// Type: StaticAbstractInterfaceMethods.IDeepInSourceCode`1 </span></span>
<span class="line"><span style="--shiki-light:#A0A1A7;--shiki-light-font-style:italic;--shiki-dark:#7F848E;--shiki-dark-font-style:italic">// Assembly: StaticAbstractInterfaceMethods, Version=1.0.0.0, Culture=neutral, PublicKeyToken=null</span></span>
<span class="line"><span style="--shiki-light:#A0A1A7;--shiki-light-font-style:italic;--shiki-dark:#7F848E;--shiki-dark-font-style:italic">// MVID: 27A20D9E-C658-48F7-8AC2-47D46316D62D</span></span>
<span class="line"><span style="--shiki-light:#A0A1A7;--shiki-light-font-style:italic;--shiki-dark:#7F848E;--shiki-dark-font-style:italic">// Location: F:\static_abstract\StaticAbstractInterfaceMethods\bin\Debug\net7.0\StaticAbstractInterfaceMethods.dll</span></span>
<span class="line"><span style="--shiki-light:#A0A1A7;--shiki-light-font-style:italic;--shiki-dark:#7F848E;--shiki-dark-font-style:italic">// Sequence point data from f:\static_abstract\staticabstractinterfacemethods\bin\debug\net7.0\staticabstractinterfacemethods.pdb</span></span>
<span class="line"></span>
<span class="line"><span style="--shiki-light:#383A42;--shiki-dark:#ABB2BF">.</span><span style="--shiki-light:#383A42;--shiki-dark:#E5C07B">class</span><span style="--shiki-light:#A626A4;--shiki-dark:#C678DD"> interface</span><span style="--shiki-light:#C18401;--shiki-dark:#E5C07B"> public</span><span style="--shiki-light:#383A42;--shiki-dark:#ABB2BF"> abstract auto ansi</span></span>
<span class="line"><span style="--shiki-light:#383A42;--shiki-dark:#ABB2BF">  StaticAbstractInterfaceMethods.IDeepInSourceCode`1&#x3C;(</span><span style="--shiki-light:#C18401;--shiki-dark:#E5C07B">class</span><span style="--shiki-light:#C18401;--shiki-dark:#E5C07B"> StaticAbstractInterfaceMethods</span><span style="--shiki-light:#383A42;--shiki-dark:#ABB2BF">.</span><span style="--shiki-light:#C18401;--shiki-dark:#E5C07B">IDeepInSourceCode</span><span style="--shiki-light:#383A42;--shiki-dark:#ABB2BF">`1&#x3C;!0</span><span style="--shiki-light:#A0A1A7;--shiki-light-font-style:italic;--shiki-dark:#7F848E;--shiki-dark-font-style:italic">/*T*/</span><span style="--shiki-light:#383A42;--shiki-dark:#ABB2BF">>) T></span></span>
<span class="line"><span style="--shiki-light:#383A42;--shiki-dark:#ABB2BF">{</span></span>
<span class="line"><span style="--shiki-light:#383A42;--shiki-dark:#ABB2BF">  .param constraint [1] </span><span style="--shiki-light:#A0A1A7;--shiki-light-font-style:italic;--shiki-dark:#7F848E;--shiki-dark-font-style:italic">/*T*/</span><span style="--shiki-light:#383A42;--shiki-dark:#ABB2BF">, class StaticAbstractInterfaceMethods.IDeepInSourceCode`1&#x3C;!0</span><span style="--shiki-light:#A0A1A7;--shiki-light-font-style:italic;--shiki-dark:#7F848E;--shiki-dark-font-style:italic">/*T*/</span><span style="--shiki-light:#383A42;--shiki-dark:#ABB2BF">></span></span>
<span class="line"><span style="--shiki-light:#383A42;--shiki-dark:#ABB2BF">    .custom instance void System.Runtime.CompilerServices.NullableAttribute::.ctor([</span><span style="--shiki-light:#C18401;--shiki-dark:#E5C07B">in</span><span style="--shiki-light:#383A42;--shiki-dark:#ABB2BF">] unsigned int8)</span></span>
<span class="line"><span style="--shiki-light:#383A42;--shiki-dark:#ABB2BF">      = (01 00 01 00 00 ) </span><span style="--shiki-light:#A0A1A7;--shiki-light-font-style:italic;--shiki-dark:#7F848E;--shiki-dark-font-style:italic">// .....</span></span>
<span class="line"><span style="--shiki-light:#A0A1A7;--shiki-light-font-style:italic;--shiki-dark:#7F848E;--shiki-dark-font-style:italic">      // unsigned int8(1) // 0x01</span></span>
<span class="line"></span>
<span class="line"><span style="--shiki-light:#383A42;--shiki-dark:#ABB2BF">  .method </span><span style="--shiki-light:#A626A4;--shiki-dark:#C678DD">public</span><span style="--shiki-light:#383A42;--shiki-dark:#ABB2BF"> hidebysig </span><span style="--shiki-light:#A626A4;--shiki-dark:#C678DD">virtual</span><span style="--shiki-light:#383A42;--shiki-dark:#ABB2BF"> newslot </span><span style="--shiki-light:#A626A4;--shiki-dark:#C678DD">abstract</span><span style="--shiki-light:#C18401;--shiki-dark:#E5C07B"> instance</span><span style="--shiki-light:#4078F2;--shiki-dark:#ABB2BF"> void</span></span>
<span class="line"><span style="--shiki-light:#4078F2;--shiki-dark:#61AFEF">    SomeMethod</span><span style="--shiki-light:#383A42;--shiki-dark:#ABB2BF">() cil managed</span></span>
<span class="line"><span style="--shiki-light:#383A42;--shiki-dark:#ABB2BF">  {</span></span>
<span class="line"><span style="--shiki-light:#A0A1A7;--shiki-light-font-style:italic;--shiki-dark:#7F848E;--shiki-dark-font-style:italic">    // Can't find a body</span></span>
<span class="line"><span style="--shiki-light:#383A42;--shiki-dark:#ABB2BF">  } </span><span style="--shiki-light:#A0A1A7;--shiki-light-font-style:italic;--shiki-dark:#7F848E;--shiki-dark-font-style:italic">// end of method IDeepInSourceCode`1::SomeMethod</span></span>
<span class="line"></span>
<span class="line"><span style="--shiki-light:#383A42;--shiki-dark:#ABB2BF">  .method </span><span style="--shiki-light:#A626A4;--shiki-dark:#C678DD">public</span><span style="--shiki-light:#383A42;--shiki-dark:#ABB2BF"> hidebysig </span><span style="--shiki-light:#A626A4;--shiki-dark:#C678DD">static</span><span style="--shiki-light:#A626A4;--shiki-dark:#C678DD"> virtual</span><span style="--shiki-light:#A626A4;--shiki-dark:#C678DD"> abstract</span><span style="--shiki-light:#383A42;--shiki-dark:#ABB2BF"> void</span></span>
<span class="line"><span style="--shiki-light:#383A42;--shiki-dark:#ABB2BF">    SomeStaticAbstractMethod() </span><span style="--shiki-light:#C18401;--shiki-dark:#E5C07B">cil</span><span style="--shiki-light:#4078F2;--shiki-dark:#ABB2BF"> managed</span></span>
<span class="line"><span style="--shiki-light:#383A42;--shiki-dark:#ABB2BF">  {</span></span>
<span class="line"><span style="--shiki-light:#A0A1A7;--shiki-light-font-style:italic;--shiki-dark:#7F848E;--shiki-dark-font-style:italic">    // Can't find a body</span></span>
<span class="line"><span style="--shiki-light:#383A42;--shiki-dark:#ABB2BF">  } </span><span style="--shiki-light:#A0A1A7;--shiki-light-font-style:italic;--shiki-dark:#7F848E;--shiki-dark-font-style:italic">// end of method IDeepInSourceCode`1::SomeStaticAbstractMethod</span></span>
<span class="line"><span style="--shiki-light:#383A42;--shiki-dark:#ABB2BF">} </span><span style="--shiki-light:#A0A1A7;--shiki-light-font-style:italic;--shiki-dark:#7F848E;--shiki-dark-font-style:italic">// end of class StaticAbstractInterfaceMethods.IDeepInSourceCode`1</span></span></code></pre>
<div class="line-numbers" aria-hidden="true" style="counter-reset:line-number 0"><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div></div></div><p>对比发现，实际上，static abstract 方法和普通接口方法的区别在于，<code>newslot</code> 和 <code>instance</code> 关键字变成了 <code>static</code>。</p>
<p>这意味着，实现 static abstract 接口方法的具体实现方法，必须是一个以 `public static 开头的方法。</p>
<h3>试一试继承关系</h3>
<p>接着上面的接口，我们定义两个类型：</p>
<div class="language-csharp line-numbers-mode" data-highlighter="shiki" data-ext="csharp" style="--shiki-light:#383A42;--shiki-dark:#abb2bf;--shiki-light-bg:#FAFAFA;--shiki-dark-bg:#282c34"><pre class="shiki shiki-themes one-light one-dark-pro vp-code"><code class="language-csharp"><span class="line"><span style="--shiki-light:#A626A4;--shiki-dark:#C678DD">public</span><span style="--shiki-light:#A626A4;--shiki-dark:#C678DD"> class</span><span style="--shiki-light:#C18401;--shiki-dark:#E5C07B"> DeepInSourceCodeBase</span><span style="--shiki-light:#383A42;--shiki-dark:#ABB2BF"> : </span><span style="--shiki-light:#C18401;--shiki-dark:#E5C07B">IDeepInSourceCode</span><span style="--shiki-light:#383A42;--shiki-dark:#ABB2BF">&#x3C;</span><span style="--shiki-light:#C18401;--shiki-dark:#E5C07B">DeepInSourceCodeBase</span><span style="--shiki-light:#383A42;--shiki-dark:#ABB2BF">></span></span>
<span class="line"><span style="--shiki-light:#383A42;--shiki-dark:#ABB2BF">{</span></span>
<span class="line"><span style="--shiki-light:#A626A4;--shiki-dark:#C678DD">    public</span><span style="--shiki-light:#A626A4;--shiki-dark:#C678DD"> virtual</span><span style="--shiki-light:#A626A4;--shiki-dark:#C678DD"> void</span><span style="--shiki-light:#4078F2;--shiki-dark:#61AFEF"> SomeMethod</span><span style="--shiki-light:#383A42;--shiki-dark:#ABB2BF">()</span></span>
<span class="line"><span style="--shiki-light:#383A42;--shiki-dark:#ABB2BF">    {</span></span>
<span class="line"><span style="--shiki-light:#383A42;--shiki-dark:#E5C07B">        Console</span><span style="--shiki-light:#383A42;--shiki-dark:#ABB2BF">.</span><span style="--shiki-light:#4078F2;--shiki-dark:#61AFEF">WriteLine</span><span style="--shiki-light:#383A42;--shiki-dark:#ABB2BF">(</span><span style="--shiki-light:#50A14F;--shiki-dark:#98C379">"SomeMethodBase is invoked."</span><span style="--shiki-light:#383A42;--shiki-dark:#ABB2BF">);</span></span>
<span class="line"><span style="--shiki-light:#383A42;--shiki-dark:#ABB2BF">    }</span></span>
<span class="line"></span>
<span class="line"><span style="--shiki-light:#A626A4;--shiki-dark:#C678DD">    public</span><span style="--shiki-light:#A626A4;--shiki-dark:#C678DD"> static</span><span style="--shiki-light:#A626A4;--shiki-dark:#C678DD"> void</span><span style="--shiki-light:#4078F2;--shiki-dark:#61AFEF"> SomeStaticAbstractMethod</span><span style="--shiki-light:#383A42;--shiki-dark:#ABB2BF">()</span></span>
<span class="line"><span style="--shiki-light:#383A42;--shiki-dark:#ABB2BF">    {</span></span>
<span class="line"><span style="--shiki-light:#383A42;--shiki-dark:#E5C07B">        Console</span><span style="--shiki-light:#383A42;--shiki-dark:#ABB2BF">.</span><span style="--shiki-light:#4078F2;--shiki-dark:#61AFEF">WriteLine</span><span style="--shiki-light:#383A42;--shiki-dark:#ABB2BF">(</span><span style="--shiki-light:#50A14F;--shiki-dark:#98C379">"SomeStaticAbstractMethodBase is invoked."</span><span style="--shiki-light:#383A42;--shiki-dark:#ABB2BF">);</span></span>
<span class="line"><span style="--shiki-light:#383A42;--shiki-dark:#ABB2BF">    }</span></span>
<span class="line"><span style="--shiki-light:#383A42;--shiki-dark:#ABB2BF">}</span></span>
<span class="line"></span>
<span class="line"><span style="--shiki-light:#A626A4;--shiki-dark:#C678DD">public</span><span style="--shiki-light:#A626A4;--shiki-dark:#C678DD"> class</span><span style="--shiki-light:#C18401;--shiki-dark:#E5C07B"> DeepInSourceCodeDerived</span><span style="--shiki-light:#383A42;--shiki-dark:#ABB2BF"> : </span><span style="--shiki-light:#C18401;--shiki-dark:#E5C07B">DeepInSourceCodeBase</span><span style="--shiki-light:#383A42;--shiki-dark:#ABB2BF">, </span><span style="--shiki-light:#C18401;--shiki-dark:#E5C07B">IDeepInSourceCode</span><span style="--shiki-light:#383A42;--shiki-dark:#ABB2BF">&#x3C;</span><span style="--shiki-light:#C18401;--shiki-dark:#E5C07B">DeepInSourceCodeDerived</span><span style="--shiki-light:#383A42;--shiki-dark:#ABB2BF">></span></span>
<span class="line"><span style="--shiki-light:#383A42;--shiki-dark:#ABB2BF">{</span></span>
<span class="line"><span style="--shiki-light:#A626A4;--shiki-dark:#C678DD">    public</span><span style="--shiki-light:#A626A4;--shiki-dark:#C678DD"> override</span><span style="--shiki-light:#A626A4;--shiki-dark:#C678DD"> void</span><span style="--shiki-light:#4078F2;--shiki-dark:#61AFEF"> SomeMethod</span><span style="--shiki-light:#383A42;--shiki-dark:#ABB2BF">()</span></span>
<span class="line"><span style="--shiki-light:#383A42;--shiki-dark:#ABB2BF">    {</span></span>
<span class="line"><span style="--shiki-light:#383A42;--shiki-dark:#E5C07B">        Console</span><span style="--shiki-light:#383A42;--shiki-dark:#ABB2BF">.</span><span style="--shiki-light:#4078F2;--shiki-dark:#61AFEF">WriteLine</span><span style="--shiki-light:#383A42;--shiki-dark:#ABB2BF">(</span><span style="--shiki-light:#50A14F;--shiki-dark:#98C379">"SomeMethodDerived is invoked."</span><span style="--shiki-light:#383A42;--shiki-dark:#ABB2BF">);</span></span>
<span class="line"><span style="--shiki-light:#383A42;--shiki-dark:#ABB2BF">    }</span></span>
<span class="line"></span>
<span class="line"><span style="--shiki-light:#A626A4;--shiki-dark:#C678DD">    public</span><span style="--shiki-light:#A626A4;--shiki-dark:#C678DD"> new</span><span style="--shiki-light:#A626A4;--shiki-dark:#C678DD"> static</span><span style="--shiki-light:#A626A4;--shiki-dark:#C678DD"> void</span><span style="--shiki-light:#4078F2;--shiki-dark:#61AFEF"> SomeStaticAbstractMethod</span><span style="--shiki-light:#383A42;--shiki-dark:#ABB2BF">()</span></span>
<span class="line"><span style="--shiki-light:#383A42;--shiki-dark:#ABB2BF">    {</span></span>
<span class="line"><span style="--shiki-light:#383A42;--shiki-dark:#E5C07B">        Console</span><span style="--shiki-light:#383A42;--shiki-dark:#ABB2BF">.</span><span style="--shiki-light:#4078F2;--shiki-dark:#61AFEF">WriteLine</span><span style="--shiki-light:#383A42;--shiki-dark:#ABB2BF">(</span><span style="--shiki-light:#50A14F;--shiki-dark:#98C379">"SomeStaticAbstractMethodDerived is invoked."</span><span style="--shiki-light:#383A42;--shiki-dark:#ABB2BF">);</span></span>
<span class="line"><span style="--shiki-light:#383A42;--shiki-dark:#ABB2BF">    }</span></span>
<span class="line"><span style="--shiki-light:#383A42;--shiki-dark:#ABB2BF">}</span></span></code></pre>
<div class="line-numbers" aria-hidden="true" style="counter-reset:line-number 0"><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div></div></div><p><code>DeepInSourceCodeBase</code> 实现了 <code>IDeepInSourceCode&lt;DeepInSourceCodeBase&gt;</code> 接口。</p>
<p><code>DeepInSourceCodeDerived</code> 派生自 <code>DeepInSourceCodeBase</code>，重写了 <code>SomeMethod</code> 方法，覆盖了 <code>SomeStaticAbstractMethod</code> 方法。</p>
<p>写一个测试代码：</p>
<div class="language-csharp line-numbers-mode" data-highlighter="shiki" data-ext="csharp" style="--shiki-light:#383A42;--shiki-dark:#abb2bf;--shiki-light-bg:#FAFAFA;--shiki-dark-bg:#282c34"><pre class="shiki shiki-themes one-light one-dark-pro vp-code"><code class="language-csharp"><span class="line"><span style="--shiki-light:#A626A4;--shiki-dark:#C678DD">public</span><span style="--shiki-light:#A626A4;--shiki-dark:#C678DD"> class</span><span style="--shiki-light:#C18401;--shiki-dark:#E5C07B"> DeepInSourceCodeTester</span></span>
<span class="line"><span style="--shiki-light:#383A42;--shiki-dark:#ABB2BF">{</span></span>
<span class="line"><span style="--shiki-light:#A626A4;--shiki-dark:#C678DD">    public</span><span style="--shiki-light:#A626A4;--shiki-dark:#C678DD"> static</span><span style="--shiki-light:#A626A4;--shiki-dark:#C678DD"> void</span><span style="--shiki-light:#4078F2;--shiki-dark:#61AFEF"> Test</span><span style="--shiki-light:#383A42;--shiki-dark:#ABB2BF">&#x3C;</span><span style="--shiki-light:#C18401;--shiki-dark:#E5C07B">T1</span><span style="--shiki-light:#383A42;--shiki-dark:#ABB2BF">, </span><span style="--shiki-light:#C18401;--shiki-dark:#E5C07B">T2</span><span style="--shiki-light:#383A42;--shiki-dark:#ABB2BF">>() </span></span>
<span class="line"><span style="--shiki-light:#A626A4;--shiki-dark:#C678DD">        where</span><span style="--shiki-light:#C18401;--shiki-dark:#E5C07B"> T1</span><span style="--shiki-light:#383A42;--shiki-dark:#ABB2BF"> : </span><span style="--shiki-light:#C18401;--shiki-dark:#E5C07B">IDeepInSourceCode</span><span style="--shiki-light:#383A42;--shiki-dark:#ABB2BF">&#x3C;</span><span style="--shiki-light:#C18401;--shiki-dark:#E5C07B">T1</span><span style="--shiki-light:#383A42;--shiki-dark:#ABB2BF">>, new()</span></span>
<span class="line"><span style="--shiki-light:#A626A4;--shiki-dark:#C678DD">        where</span><span style="--shiki-light:#C18401;--shiki-dark:#E5C07B"> T2</span><span style="--shiki-light:#383A42;--shiki-dark:#ABB2BF"> : </span><span style="--shiki-light:#C18401;--shiki-dark:#E5C07B">T1</span><span style="--shiki-light:#383A42;--shiki-dark:#ABB2BF">, new()</span></span>
<span class="line"><span style="--shiki-light:#383A42;--shiki-dark:#ABB2BF">    {</span></span>
<span class="line"><span style="--shiki-light:#C18401;--shiki-dark:#E5C07B">        T1</span><span style="--shiki-light:#383A42;--shiki-dark:#E06C75"> t1</span><span style="--shiki-light:#383A42;--shiki-dark:#56B6C2"> =</span><span style="--shiki-light:#383A42;--shiki-dark:#ABB2BF"> new </span><span style="--shiki-light:#C18401;--shiki-dark:#E5C07B">T1</span><span style="--shiki-light:#383A42;--shiki-dark:#ABB2BF">();</span></span>
<span class="line"><span style="--shiki-light:#C18401;--shiki-dark:#E5C07B">        T2</span><span style="--shiki-light:#383A42;--shiki-dark:#E06C75"> t2</span><span style="--shiki-light:#383A42;--shiki-dark:#56B6C2"> =</span><span style="--shiki-light:#383A42;--shiki-dark:#ABB2BF"> new </span><span style="--shiki-light:#C18401;--shiki-dark:#E5C07B">T2</span><span style="--shiki-light:#383A42;--shiki-dark:#ABB2BF">();</span></span>
<span class="line"><span style="--shiki-light:#383A42;--shiki-dark:#E5C07B">        t1</span><span style="--shiki-light:#383A42;--shiki-dark:#ABB2BF">.</span><span style="--shiki-light:#4078F2;--shiki-dark:#61AFEF">SomeMethod</span><span style="--shiki-light:#383A42;--shiki-dark:#ABB2BF">();</span></span>
<span class="line"><span style="--shiki-light:#383A42;--shiki-dark:#E5C07B">        t2</span><span style="--shiki-light:#383A42;--shiki-dark:#ABB2BF">.</span><span style="--shiki-light:#4078F2;--shiki-dark:#61AFEF">SomeMethod</span><span style="--shiki-light:#383A42;--shiki-dark:#ABB2BF">();</span></span>
<span class="line"><span style="--shiki-light:#383A42;--shiki-dark:#E5C07B">        T1</span><span style="--shiki-light:#383A42;--shiki-dark:#ABB2BF">.</span><span style="--shiki-light:#4078F2;--shiki-dark:#61AFEF">SomeStaticAbstractMethod</span><span style="--shiki-light:#383A42;--shiki-dark:#ABB2BF">();</span></span>
<span class="line"><span style="--shiki-light:#383A42;--shiki-dark:#E5C07B">        T2</span><span style="--shiki-light:#383A42;--shiki-dark:#ABB2BF">.</span><span style="--shiki-light:#4078F2;--shiki-dark:#61AFEF">SomeStaticAbstractMethod</span><span style="--shiki-light:#383A42;--shiki-dark:#ABB2BF">();</span></span>
<span class="line"><span style="--shiki-light:#383A42;--shiki-dark:#ABB2BF">    }</span></span>
<span class="line"><span style="--shiki-light:#383A42;--shiki-dark:#ABB2BF">}</span></span></code></pre>
<div class="line-numbers" aria-hidden="true" style="counter-reset:line-number 0"><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div></div></div><p>在 <code>Main</code> 方法中加入测试代码：</p>
<div class="language-csharp line-numbers-mode" data-highlighter="shiki" data-ext="csharp" style="--shiki-light:#383A42;--shiki-dark:#abb2bf;--shiki-light-bg:#FAFAFA;--shiki-dark-bg:#282c34"><pre class="shiki shiki-themes one-light one-dark-pro vp-code"><code class="language-csharp"><span class="line"><span style="--shiki-light:#A626A4;--shiki-dark:#C678DD">internal</span><span style="--shiki-light:#A626A4;--shiki-dark:#C678DD"> class</span><span style="--shiki-light:#C18401;--shiki-dark:#E5C07B"> Program</span></span>
<span class="line"><span style="--shiki-light:#383A42;--shiki-dark:#ABB2BF">{</span></span>
<span class="line"><span style="--shiki-light:#A626A4;--shiki-dark:#C678DD">    static</span><span style="--shiki-light:#A626A4;--shiki-dark:#C678DD"> void</span><span style="--shiki-light:#4078F2;--shiki-dark:#61AFEF"> Main</span><span style="--shiki-light:#383A42;--shiki-dark:#ABB2BF">(</span><span style="--shiki-light:#A626A4;--shiki-dark:#C678DD">string</span><span style="--shiki-light:#383A42;--shiki-dark:#ABB2BF">[] </span><span style="--shiki-light:#383A42;--shiki-dark:#E5C07B">args</span><span style="--shiki-light:#383A42;--shiki-dark:#ABB2BF">)</span></span>
<span class="line"><span style="--shiki-light:#383A42;--shiki-dark:#ABB2BF">    {</span></span>
<span class="line"><span style="--shiki-light:#383A42;--shiki-dark:#E5C07B">        DeepInSourceCodeTester</span><span style="--shiki-light:#383A42;--shiki-dark:#ABB2BF">.</span><span style="--shiki-light:#4078F2;--shiki-dark:#61AFEF">Test</span><span style="--shiki-light:#383A42;--shiki-dark:#ABB2BF">&#x3C;</span><span style="--shiki-light:#C18401;--shiki-dark:#E5C07B">DeepInSourceCodeBase</span><span style="--shiki-light:#383A42;--shiki-dark:#ABB2BF">, </span><span style="--shiki-light:#C18401;--shiki-dark:#E5C07B">DeepInSourceCodeDerived</span><span style="--shiki-light:#383A42;--shiki-dark:#ABB2BF">>();</span></span>
<span class="line"><span style="--shiki-light:#383A42;--shiki-dark:#ABB2BF">    }</span></span>
<span class="line"><span style="--shiki-light:#383A42;--shiki-dark:#ABB2BF">}</span></span></code></pre>
<div class="line-numbers" aria-hidden="true" style="counter-reset:line-number 0"><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div></div></div><p>先猜猜结果是什么？</p>
<div class="language- line-numbers-mode" data-highlighter="shiki" data-ext style="--shiki-light:#383A42;--shiki-dark:#abb2bf;--shiki-light-bg:#FAFAFA;--shiki-dark-bg:#282c34"><pre class="shiki shiki-themes one-light one-dark-pro vp-code"><code class="language-"><span class="line"><span>SomeMethodBase is invoked.</span></span>
<span class="line"><span>SomeMethodDerived is invoked.</span></span>
<span class="line"><span>SomeStaticAbstractMethodBase is invoked.</span></span>
<span class="line"><span>SomeStaticAbstractMethodDerived is invoked.</span></span></code></pre>
<div class="line-numbers" aria-hidden="true" style="counter-reset:line-number 0"><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div></div></div><p>你会猜测是这样的结果吗？</p>
<p>其实不是。</p>
<p>这里结果是：</p>
<div class="language- line-numbers-mode" data-highlighter="shiki" data-ext style="--shiki-light:#383A42;--shiki-dark:#abb2bf;--shiki-light-bg:#FAFAFA;--shiki-dark-bg:#282c34"><pre class="shiki shiki-themes one-light one-dark-pro vp-code"><code class="language-"><span class="line"><span>SomeMethodBase is invoked.</span></span>
<span class="line"><span>SomeMethodDerived is invoked.</span></span>
<span class="line"><span>SomeStaticAbstractMethodBase is invoked.</span></span>
<span class="line"><span>SomeStaticAbstractMethodBase is invoked.</span></span></code></pre>
<div class="line-numbers" aria-hidden="true" style="counter-reset:line-number 0"><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div></div></div><p>官方有专门的说明：https://learn.microsoft.com/en-us/dotnet/csharp/language-reference/keywords/interface#static-abstract-and-virtual-members</p>
<p>当我们把 <code>where T2 : T1, new()</code> 改为 <code>where T2 : IDeepInSourceCode&lt;T2&gt;, new()</code> 结果就是我们猜测的那样了。</p>
<h3>Playground</h3>
<iframe style="width: 100%; height: 400px;" src="https://sharplab.io/#v2:EYLgtghglgdgNAFxAJwK7wCYgNQB8ACATAIwCwAUBfgMwAEsCApsgGYQDGjtAkgCKOMADtxgBlAPapknAMLiMjADwAVAHy0A7gAtmXZbRA9+QkRKmz5StRQDeFWg9r4ALLQlhGAWUYIt8gBQAlADc9o74xABstBDAAM4IyBwITq7ujKIIEAhQ7ACC8YnJ3r4BIRQAvhRUdES0xsJiktKMcgoAQhBxXIZ8Ao1mLW1KDabNFh1djKq2YQ40tABuUMgIqBAANqlu4h4lfhhBc7R25I7nTsQAnP4AROn78p3d9HH0MIviANaMGAB0t3KZ0cVUowPmtSi23SmWyuQKCSS7AQj0OgWOpwu4WudxhWRy+UKSJRPgOzy4UDesE+P3+gNC4NooNBNSchHq/TG5laln4yCgi1+Bg5Jia3OG5LgRk5YqGlkUo1lE0YfIFvxm5Ex4To4kFyH5Cmhuy8pLKGOO5wiN3uxtRqsFGFe7xpvwBQPOLMZCxgjA0l2iLh2HlhBIRRWRqKOjK1WKtuONIfhROKpow9qFlOd31d9OOzOq5AWdUVg2VykYCWYsy9kIDrnLCRUxClykIqiCtAtjm0uloymIwr6otLPIUTdUUp9GijWIcPeQenZhn7k99M8cMYu/doCAHAF5aFO+8QggzZ332Qh2Qej63T12HLu/g9U/fGecr8/ba/3bP+1/g3xJNERTUo0TPP9CAAjIgMJECIx/CCHHzMEGGYGBNjZS4AHZq0tKFA08aAYH8CIAAYAG0AF0YmQABzOJ0WjB8RQGcZR0YBsmGQP4uIVGURwlKYpRLdjhnTDB21/ZDKgoIA="></iframe>
<h2>.NET 如何支持<a href="https://learn.microsoft.com/zh-cn/dotnet/standard/generics/math" target="_blank" rel="noopener noreferrer">泛型数学</a></h2>
<p>为了支持泛型数学，<strong>.NET 7.0 将所有的基元类型进行了重新实现</strong>。</p>
<p>以 <code>Int32</code> 为例，它现在要实现更多的接口，这些接口就是为了进行一些泛型数学运算。</p>
<div class="language-csharp line-numbers-mode" data-highlighter="shiki" data-ext="csharp" style="--shiki-light:#383A42;--shiki-dark:#abb2bf;--shiki-light-bg:#FAFAFA;--shiki-dark-bg:#282c34"><pre class="shiki shiki-themes one-light one-dark-pro vp-code"><code class="language-csharp"><span class="line"><span style="--shiki-light:#A626A4;--shiki-dark:#C678DD">public</span><span style="--shiki-light:#A626A4;--shiki-dark:#C678DD"> readonly</span><span style="--shiki-light:#A626A4;--shiki-dark:#C678DD"> struct</span><span style="--shiki-light:#C18401;--shiki-dark:#E5C07B"> Int32</span></span>
<span class="line"><span style="--shiki-light:#383A42;--shiki-dark:#ABB2BF">        : </span><span style="--shiki-light:#C18401;--shiki-dark:#E5C07B">IComparable</span><span style="--shiki-light:#383A42;--shiki-dark:#ABB2BF">,</span></span>
<span class="line"><span style="--shiki-light:#C18401;--shiki-dark:#E5C07B">          IConvertible</span><span style="--shiki-light:#383A42;--shiki-dark:#ABB2BF">,</span></span>
<span class="line"><span style="--shiki-light:#C18401;--shiki-dark:#E5C07B">          ISpanFormattable</span><span style="--shiki-light:#383A42;--shiki-dark:#ABB2BF">,</span></span>
<span class="line"><span style="--shiki-light:#C18401;--shiki-dark:#E5C07B">          IComparable</span><span style="--shiki-light:#383A42;--shiki-dark:#ABB2BF">&#x3C;</span><span style="--shiki-light:#A626A4;--shiki-dark:#C678DD">int</span><span style="--shiki-light:#383A42;--shiki-dark:#ABB2BF">>,</span></span>
<span class="line"><span style="--shiki-light:#C18401;--shiki-dark:#E5C07B">          IEquatable</span><span style="--shiki-light:#383A42;--shiki-dark:#ABB2BF">&#x3C;</span><span style="--shiki-light:#A626A4;--shiki-dark:#C678DD">int</span><span style="--shiki-light:#383A42;--shiki-dark:#ABB2BF">>,</span></span>
<span class="line"><span style="--shiki-light:#C18401;--shiki-dark:#E5C07B">          IBinaryInteger</span><span style="--shiki-light:#383A42;--shiki-dark:#ABB2BF">&#x3C;</span><span style="--shiki-light:#A626A4;--shiki-dark:#C678DD">int</span><span style="--shiki-light:#383A42;--shiki-dark:#ABB2BF">>,</span></span>
<span class="line"><span style="--shiki-light:#C18401;--shiki-dark:#E5C07B">          IMinMaxValue</span><span style="--shiki-light:#383A42;--shiki-dark:#ABB2BF">&#x3C;</span><span style="--shiki-light:#A626A4;--shiki-dark:#C678DD">int</span><span style="--shiki-light:#383A42;--shiki-dark:#ABB2BF">>,</span></span>
<span class="line"><span style="--shiki-light:#C18401;--shiki-dark:#E5C07B">          ISignedNumber</span><span style="--shiki-light:#383A42;--shiki-dark:#ABB2BF">&#x3C;</span><span style="--shiki-light:#A626A4;--shiki-dark:#C678DD">int</span><span style="--shiki-light:#383A42;--shiki-dark:#ABB2BF">></span></span></code></pre>
<div class="line-numbers" aria-hidden="true" style="counter-reset:line-number 0"><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div></div></div><p><code>Int32</code> 除了之前实现的一些接口，目前还实现了以下新的接口：</p>
<ul>
<li>
<p>IBinaryInterger&lt;int&gt;</p>
<div class="language-csharp line-numbers-mode" data-highlighter="shiki" data-ext="csharp" style="--shiki-light:#383A42;--shiki-dark:#abb2bf;--shiki-light-bg:#FAFAFA;--shiki-dark-bg:#282c34"><pre class="shiki shiki-themes one-light one-dark-pro vp-code"><code class="language-csharp"><span class="line"><span style="--shiki-light:#A626A4;--shiki-dark:#C678DD">public</span><span style="--shiki-light:#A626A4;--shiki-dark:#C678DD"> interface</span><span style="--shiki-light:#C18401;--shiki-dark:#E5C07B"> IBinaryInteger</span><span style="--shiki-light:#383A42;--shiki-dark:#ABB2BF">&#x3C;</span><span style="--shiki-light:#C18401;--shiki-dark:#E5C07B">TSelf</span><span style="--shiki-light:#383A42;--shiki-dark:#ABB2BF">></span></span>
<span class="line"><span style="--shiki-light:#383A42;--shiki-dark:#ABB2BF">        : </span><span style="--shiki-light:#C18401;--shiki-dark:#E5C07B">IBinaryNumber</span><span style="--shiki-light:#383A42;--shiki-dark:#ABB2BF">&#x3C;</span><span style="--shiki-light:#C18401;--shiki-dark:#E5C07B">TSelf</span><span style="--shiki-light:#383A42;--shiki-dark:#ABB2BF">>,</span></span>
<span class="line"><span style="--shiki-light:#C18401;--shiki-dark:#E5C07B">          IShiftOperators</span><span style="--shiki-light:#383A42;--shiki-dark:#ABB2BF">&#x3C;</span><span style="--shiki-light:#C18401;--shiki-dark:#E5C07B">TSelf</span><span style="--shiki-light:#383A42;--shiki-dark:#ABB2BF">, </span><span style="--shiki-light:#A626A4;--shiki-dark:#C678DD">int</span><span style="--shiki-light:#383A42;--shiki-dark:#ABB2BF">, </span><span style="--shiki-light:#C18401;--shiki-dark:#E5C07B">TSelf</span><span style="--shiki-light:#383A42;--shiki-dark:#ABB2BF">></span></span>
<span class="line"><span style="--shiki-light:#A626A4;--shiki-dark:#C678DD">        where</span><span style="--shiki-light:#C18401;--shiki-dark:#E5C07B"> TSelf</span><span style="--shiki-light:#383A42;--shiki-dark:#ABB2BF"> : </span><span style="--shiki-light:#C18401;--shiki-dark:#E5C07B">IBinaryInteger</span><span style="--shiki-light:#383A42;--shiki-dark:#ABB2BF">&#x3C;</span><span style="--shiki-light:#C18401;--shiki-dark:#E5C07B">TSelf</span><span style="--shiki-light:#383A42;--shiki-dark:#ABB2BF">>?</span></span></code></pre>
<div class="line-numbers" aria-hidden="true" style="counter-reset:line-number 0"><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div></div></div><ul>
<li>
<p>IBinaryNumber&lt;int&gt;</p>
<div class="language-csharp line-numbers-mode" data-highlighter="shiki" data-ext="csharp" style="--shiki-light:#383A42;--shiki-dark:#abb2bf;--shiki-light-bg:#FAFAFA;--shiki-dark-bg:#282c34"><pre class="shiki shiki-themes one-light one-dark-pro vp-code"><code class="language-csharp"><span class="line"><span style="--shiki-light:#A626A4;--shiki-dark:#C678DD">public</span><span style="--shiki-light:#A626A4;--shiki-dark:#C678DD"> interface</span><span style="--shiki-light:#C18401;--shiki-dark:#E5C07B"> IBinaryNumber</span><span style="--shiki-light:#383A42;--shiki-dark:#ABB2BF">&#x3C;</span><span style="--shiki-light:#C18401;--shiki-dark:#E5C07B">TSelf</span><span style="--shiki-light:#383A42;--shiki-dark:#ABB2BF">></span></span>
<span class="line"><span style="--shiki-light:#383A42;--shiki-dark:#ABB2BF">        : </span><span style="--shiki-light:#C18401;--shiki-dark:#E5C07B">IBitwiseOperators</span><span style="--shiki-light:#383A42;--shiki-dark:#ABB2BF">&#x3C;</span><span style="--shiki-light:#C18401;--shiki-dark:#E5C07B">TSelf</span><span style="--shiki-light:#383A42;--shiki-dark:#ABB2BF">, </span><span style="--shiki-light:#C18401;--shiki-dark:#E5C07B">TSelf</span><span style="--shiki-light:#383A42;--shiki-dark:#ABB2BF">, </span><span style="--shiki-light:#C18401;--shiki-dark:#E5C07B">TSelf</span><span style="--shiki-light:#383A42;--shiki-dark:#ABB2BF">>,</span></span>
<span class="line"><span style="--shiki-light:#C18401;--shiki-dark:#E5C07B">          INumber</span><span style="--shiki-light:#383A42;--shiki-dark:#ABB2BF">&#x3C;</span><span style="--shiki-light:#C18401;--shiki-dark:#E5C07B">TSelf</span><span style="--shiki-light:#383A42;--shiki-dark:#ABB2BF">></span></span>
<span class="line"><span style="--shiki-light:#A626A4;--shiki-dark:#C678DD">        where</span><span style="--shiki-light:#C18401;--shiki-dark:#E5C07B"> TSelf</span><span style="--shiki-light:#383A42;--shiki-dark:#ABB2BF"> : </span><span style="--shiki-light:#C18401;--shiki-dark:#E5C07B">IBinaryNumber</span><span style="--shiki-light:#383A42;--shiki-dark:#ABB2BF">&#x3C;</span><span style="--shiki-light:#C18401;--shiki-dark:#E5C07B">TSelf</span><span style="--shiki-light:#383A42;--shiki-dark:#ABB2BF">>?</span></span></code></pre>
<div class="line-numbers" aria-hidden="true" style="counter-reset:line-number 0"><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div></div></div><ul>
<li>
<p>IBitwiseOperators&lt;int, int, int&gt;</p>
<p>声明了重载 <code>&amp;</code>、<code>|</code>、<code>~</code>、<code>!</code>  四个运算符的 static abstract 方法</p>
</li>
<li>
<p>INumber&lt;int&gt;</p>
<div class="language-csharp line-numbers-mode" data-highlighter="shiki" data-ext="csharp" style="--shiki-light:#383A42;--shiki-dark:#abb2bf;--shiki-light-bg:#FAFAFA;--shiki-dark-bg:#282c34"><pre class="shiki shiki-themes one-light one-dark-pro vp-code"><code class="language-csharp"><span class="line"><span style="--shiki-light:#A626A4;--shiki-dark:#C678DD">public</span><span style="--shiki-light:#A626A4;--shiki-dark:#C678DD"> interface</span><span style="--shiki-light:#C18401;--shiki-dark:#E5C07B"> INumber</span><span style="--shiki-light:#383A42;--shiki-dark:#ABB2BF">&#x3C;</span><span style="--shiki-light:#C18401;--shiki-dark:#E5C07B">TSelf</span><span style="--shiki-light:#383A42;--shiki-dark:#ABB2BF">></span></span>
<span class="line"><span style="--shiki-light:#383A42;--shiki-dark:#ABB2BF">        : </span><span style="--shiki-light:#C18401;--shiki-dark:#E5C07B">IComparable</span><span style="--shiki-light:#383A42;--shiki-dark:#ABB2BF">,</span></span>
<span class="line"><span style="--shiki-light:#C18401;--shiki-dark:#E5C07B">          IComparable</span><span style="--shiki-light:#383A42;--shiki-dark:#ABB2BF">&#x3C;</span><span style="--shiki-light:#C18401;--shiki-dark:#E5C07B">TSelf</span><span style="--shiki-light:#383A42;--shiki-dark:#ABB2BF">>,</span></span>
<span class="line"><span style="--shiki-light:#C18401;--shiki-dark:#E5C07B">          IComparisonOperators</span><span style="--shiki-light:#383A42;--shiki-dark:#ABB2BF">&#x3C;</span><span style="--shiki-light:#C18401;--shiki-dark:#E5C07B">TSelf</span><span style="--shiki-light:#383A42;--shiki-dark:#ABB2BF">, </span><span style="--shiki-light:#C18401;--shiki-dark:#E5C07B">TSelf</span><span style="--shiki-light:#383A42;--shiki-dark:#ABB2BF">, </span><span style="--shiki-light:#A626A4;--shiki-dark:#C678DD">bool</span><span style="--shiki-light:#383A42;--shiki-dark:#ABB2BF">>,</span></span>
<span class="line"><span style="--shiki-light:#C18401;--shiki-dark:#E5C07B">          IModulusOperators</span><span style="--shiki-light:#383A42;--shiki-dark:#ABB2BF">&#x3C;</span><span style="--shiki-light:#C18401;--shiki-dark:#E5C07B">TSelf</span><span style="--shiki-light:#383A42;--shiki-dark:#ABB2BF">, </span><span style="--shiki-light:#C18401;--shiki-dark:#E5C07B">TSelf</span><span style="--shiki-light:#383A42;--shiki-dark:#ABB2BF">, </span><span style="--shiki-light:#C18401;--shiki-dark:#E5C07B">TSelf</span><span style="--shiki-light:#383A42;--shiki-dark:#ABB2BF">>,</span></span>
<span class="line"><span style="--shiki-light:#C18401;--shiki-dark:#E5C07B">          INumberBase</span><span style="--shiki-light:#383A42;--shiki-dark:#ABB2BF">&#x3C;</span><span style="--shiki-light:#C18401;--shiki-dark:#E5C07B">TSelf</span><span style="--shiki-light:#383A42;--shiki-dark:#ABB2BF">></span></span>
<span class="line"><span style="--shiki-light:#A626A4;--shiki-dark:#C678DD">        where</span><span style="--shiki-light:#C18401;--shiki-dark:#E5C07B"> TSelf</span><span style="--shiki-light:#383A42;--shiki-dark:#ABB2BF"> : </span><span style="--shiki-light:#C18401;--shiki-dark:#E5C07B">INumber</span><span style="--shiki-light:#383A42;--shiki-dark:#ABB2BF">&#x3C;</span><span style="--shiki-light:#C18401;--shiki-dark:#E5C07B">TSelf</span><span style="--shiki-light:#383A42;--shiki-dark:#ABB2BF">>?</span></span></code></pre>
<div class="line-numbers" aria-hidden="true" style="counter-reset:line-number 0"><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div></div></div><ul>
<li>
<p>IComparisonOperators&lt;int, int, int&gt;</p>
<p>声明了重载 <code>&gt;</code>、<code>&lt;</code>、<code>&gt;=</code>、<code>&lt;=</code> 四个运算符的 static abstract 方法</p>
</li>
<li>
<p>IModulusOperators&lt;int, int, int&gt;</p>
<p>声明了重载 <code>%</code> 运算符的  static abstract 方法</p>
</li>
<li>
<p>INumberBase&lt;int&gt;</p>
<div class="language-csharp line-numbers-mode" data-highlighter="shiki" data-ext="csharp" style="--shiki-light:#383A42;--shiki-dark:#abb2bf;--shiki-light-bg:#FAFAFA;--shiki-dark-bg:#282c34"><pre class="shiki shiki-themes one-light one-dark-pro vp-code"><code class="language-csharp"><span class="line"><span style="--shiki-light:#A626A4;--shiki-dark:#C678DD">public</span><span style="--shiki-light:#A626A4;--shiki-dark:#C678DD"> interface</span><span style="--shiki-light:#C18401;--shiki-dark:#E5C07B"> INumberBase</span><span style="--shiki-light:#383A42;--shiki-dark:#ABB2BF">&#x3C;</span><span style="--shiki-light:#C18401;--shiki-dark:#E5C07B">TSelf</span><span style="--shiki-light:#383A42;--shiki-dark:#ABB2BF">></span></span>
<span class="line"><span style="--shiki-light:#383A42;--shiki-dark:#ABB2BF">        : </span><span style="--shiki-light:#C18401;--shiki-dark:#E5C07B">IAdditionOperators</span><span style="--shiki-light:#383A42;--shiki-dark:#ABB2BF">&#x3C;</span><span style="--shiki-light:#C18401;--shiki-dark:#E5C07B">TSelf</span><span style="--shiki-light:#383A42;--shiki-dark:#ABB2BF">, </span><span style="--shiki-light:#C18401;--shiki-dark:#E5C07B">TSelf</span><span style="--shiki-light:#383A42;--shiki-dark:#ABB2BF">, </span><span style="--shiki-light:#C18401;--shiki-dark:#E5C07B">TSelf</span><span style="--shiki-light:#383A42;--shiki-dark:#ABB2BF">>,</span></span>
<span class="line"><span style="--shiki-light:#C18401;--shiki-dark:#E5C07B">          IAdditiveIdentity</span><span style="--shiki-light:#383A42;--shiki-dark:#ABB2BF">&#x3C;</span><span style="--shiki-light:#C18401;--shiki-dark:#E5C07B">TSelf</span><span style="--shiki-light:#383A42;--shiki-dark:#ABB2BF">, </span><span style="--shiki-light:#C18401;--shiki-dark:#E5C07B">TSelf</span><span style="--shiki-light:#383A42;--shiki-dark:#ABB2BF">>,</span></span>
<span class="line"><span style="--shiki-light:#C18401;--shiki-dark:#E5C07B">          IDecrementOperators</span><span style="--shiki-light:#383A42;--shiki-dark:#ABB2BF">&#x3C;</span><span style="--shiki-light:#C18401;--shiki-dark:#E5C07B">TSelf</span><span style="--shiki-light:#383A42;--shiki-dark:#ABB2BF">>,</span></span>
<span class="line"><span style="--shiki-light:#C18401;--shiki-dark:#E5C07B">          IDivisionOperators</span><span style="--shiki-light:#383A42;--shiki-dark:#ABB2BF">&#x3C;</span><span style="--shiki-light:#C18401;--shiki-dark:#E5C07B">TSelf</span><span style="--shiki-light:#383A42;--shiki-dark:#ABB2BF">, </span><span style="--shiki-light:#C18401;--shiki-dark:#E5C07B">TSelf</span><span style="--shiki-light:#383A42;--shiki-dark:#ABB2BF">, </span><span style="--shiki-light:#C18401;--shiki-dark:#E5C07B">TSelf</span><span style="--shiki-light:#383A42;--shiki-dark:#ABB2BF">>,</span></span>
<span class="line"><span style="--shiki-light:#C18401;--shiki-dark:#E5C07B">          IEquatable</span><span style="--shiki-light:#383A42;--shiki-dark:#ABB2BF">&#x3C;</span><span style="--shiki-light:#C18401;--shiki-dark:#E5C07B">TSelf</span><span style="--shiki-light:#383A42;--shiki-dark:#ABB2BF">>,</span></span>
<span class="line"><span style="--shiki-light:#C18401;--shiki-dark:#E5C07B">          IEqualityOperators</span><span style="--shiki-light:#383A42;--shiki-dark:#ABB2BF">&#x3C;</span><span style="--shiki-light:#C18401;--shiki-dark:#E5C07B">TSelf</span><span style="--shiki-light:#383A42;--shiki-dark:#ABB2BF">, </span><span style="--shiki-light:#C18401;--shiki-dark:#E5C07B">TSelf</span><span style="--shiki-light:#383A42;--shiki-dark:#ABB2BF">, </span><span style="--shiki-light:#A626A4;--shiki-dark:#C678DD">bool</span><span style="--shiki-light:#383A42;--shiki-dark:#ABB2BF">>,</span></span>
<span class="line"><span style="--shiki-light:#C18401;--shiki-dark:#E5C07B">          IIncrementOperators</span><span style="--shiki-light:#383A42;--shiki-dark:#ABB2BF">&#x3C;</span><span style="--shiki-light:#C18401;--shiki-dark:#E5C07B">TSelf</span><span style="--shiki-light:#383A42;--shiki-dark:#ABB2BF">>,</span></span>
<span class="line"><span style="--shiki-light:#C18401;--shiki-dark:#E5C07B">          IMultiplicativeIdentity</span><span style="--shiki-light:#383A42;--shiki-dark:#ABB2BF">&#x3C;</span><span style="--shiki-light:#C18401;--shiki-dark:#E5C07B">TSelf</span><span style="--shiki-light:#383A42;--shiki-dark:#ABB2BF">, </span><span style="--shiki-light:#C18401;--shiki-dark:#E5C07B">TSelf</span><span style="--shiki-light:#383A42;--shiki-dark:#ABB2BF">>,</span></span>
<span class="line"><span style="--shiki-light:#C18401;--shiki-dark:#E5C07B">          IMultiplyOperators</span><span style="--shiki-light:#383A42;--shiki-dark:#ABB2BF">&#x3C;</span><span style="--shiki-light:#C18401;--shiki-dark:#E5C07B">TSelf</span><span style="--shiki-light:#383A42;--shiki-dark:#ABB2BF">, </span><span style="--shiki-light:#C18401;--shiki-dark:#E5C07B">TSelf</span><span style="--shiki-light:#383A42;--shiki-dark:#ABB2BF">, </span><span style="--shiki-light:#C18401;--shiki-dark:#E5C07B">TSelf</span><span style="--shiki-light:#383A42;--shiki-dark:#ABB2BF">>,</span></span>
<span class="line"><span style="--shiki-light:#C18401;--shiki-dark:#E5C07B">          ISpanFormattable</span><span style="--shiki-light:#383A42;--shiki-dark:#ABB2BF">,</span></span>
<span class="line"><span style="--shiki-light:#C18401;--shiki-dark:#E5C07B">          ISpanParsable</span><span style="--shiki-light:#383A42;--shiki-dark:#ABB2BF">&#x3C;</span><span style="--shiki-light:#C18401;--shiki-dark:#E5C07B">TSelf</span><span style="--shiki-light:#383A42;--shiki-dark:#ABB2BF">>,</span></span>
<span class="line"><span style="--shiki-light:#C18401;--shiki-dark:#E5C07B">          ISubtractionOperators</span><span style="--shiki-light:#383A42;--shiki-dark:#ABB2BF">&#x3C;</span><span style="--shiki-light:#C18401;--shiki-dark:#E5C07B">TSelf</span><span style="--shiki-light:#383A42;--shiki-dark:#ABB2BF">, </span><span style="--shiki-light:#C18401;--shiki-dark:#E5C07B">TSelf</span><span style="--shiki-light:#383A42;--shiki-dark:#ABB2BF">, </span><span style="--shiki-light:#C18401;--shiki-dark:#E5C07B">TSelf</span><span style="--shiki-light:#383A42;--shiki-dark:#ABB2BF">>,</span></span>
<span class="line"><span style="--shiki-light:#C18401;--shiki-dark:#E5C07B">          IUnaryPlusOperators</span><span style="--shiki-light:#383A42;--shiki-dark:#ABB2BF">&#x3C;</span><span style="--shiki-light:#C18401;--shiki-dark:#E5C07B">TSelf</span><span style="--shiki-light:#383A42;--shiki-dark:#ABB2BF">, </span><span style="--shiki-light:#C18401;--shiki-dark:#E5C07B">TSelf</span><span style="--shiki-light:#383A42;--shiki-dark:#ABB2BF">>,</span></span>
<span class="line"><span style="--shiki-light:#C18401;--shiki-dark:#E5C07B">          IUnaryNegationOperators</span><span style="--shiki-light:#383A42;--shiki-dark:#ABB2BF">&#x3C;</span><span style="--shiki-light:#C18401;--shiki-dark:#E5C07B">TSelf</span><span style="--shiki-light:#383A42;--shiki-dark:#ABB2BF">, </span><span style="--shiki-light:#C18401;--shiki-dark:#E5C07B">TSelf</span><span style="--shiki-light:#383A42;--shiki-dark:#ABB2BF">></span></span>
<span class="line"><span style="--shiki-light:#A626A4;--shiki-dark:#C678DD">        where</span><span style="--shiki-light:#C18401;--shiki-dark:#E5C07B"> TSelf</span><span style="--shiki-light:#383A42;--shiki-dark:#ABB2BF"> : </span><span style="--shiki-light:#C18401;--shiki-dark:#E5C07B">INumberBase</span><span style="--shiki-light:#383A42;--shiki-dark:#ABB2BF">&#x3C;</span><span style="--shiki-light:#C18401;--shiki-dark:#E5C07B">TSelf</span><span style="--shiki-light:#383A42;--shiki-dark:#ABB2BF">>?</span></span></code></pre>
<div class="line-numbers" aria-hidden="true" style="counter-reset:line-number 0"><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div></div></div><p>简述：主要声明了四则运算相关运算符的重载 static abstract 方法。</p>
</li>
</ul>
</li>
</ul>
</li>
<li>
<p>IShiftOperators&lt;int, int, int&gt;</p>
<p>声明了重载 <code>&lt;&lt;</code>、<code>&gt;&gt;</code>、<code>&gt;&gt;&gt;</code> 三个移位运算符的 static abstract 方法</p>
</li>
</ul>
</li>
<li>
<p>IMinMaxValue&lt;int&gt;</p>
<p>声明了 <code>MinValue</code>、<code>MaxValue</code> 两个 static abstract 属性，表示具有最大最小值属性。</p>
</li>
<li>
<p>ISignedNumber&lt;int&gt;</p>
<p>声明了 <code>NegativeOne</code> static abstract 属性，表示具有 -1 属性。</p>
</li>
</ul>
<p>如此一来，<code>Int32</code> 可以作为泛型数学的泛型参数。</p>
<p>除了 <code>Int32</code>，其他诸如 <code>Single</code>、<code>Double</code>、<code>Byte</code> 这些基元类型也都实现了很多这种用于泛型数学运算的接口。</p>
<p>举一个简单的例子：</p>
<div class="language-csharp line-numbers-mode" data-highlighter="shiki" data-ext="csharp" style="--shiki-light:#383A42;--shiki-dark:#abb2bf;--shiki-light-bg:#FAFAFA;--shiki-dark-bg:#282c34"><pre class="shiki shiki-themes one-light one-dark-pro vp-code"><code class="language-csharp"><span class="line"><span style="--shiki-light:#A626A4;--shiki-dark:#C678DD">public</span><span style="--shiki-light:#A626A4;--shiki-dark:#C678DD"> static</span><span style="--shiki-light:#A626A4;--shiki-dark:#C678DD"> void</span><span style="--shiki-light:#4078F2;--shiki-dark:#61AFEF"> MultiplyAndSub</span><span style="--shiki-light:#383A42;--shiki-dark:#ABB2BF">&#x3C;</span><span style="--shiki-light:#C18401;--shiki-dark:#E5C07B">T</span><span style="--shiki-light:#383A42;--shiki-dark:#ABB2BF">>(</span><span style="--shiki-light:#C18401;--shiki-dark:#E5C07B">T</span><span style="--shiki-light:#383A42;--shiki-dark:#E5C07B"> t1</span><span style="--shiki-light:#383A42;--shiki-dark:#ABB2BF">, </span><span style="--shiki-light:#C18401;--shiki-dark:#E5C07B">T</span><span style="--shiki-light:#383A42;--shiki-dark:#E5C07B"> t2</span><span style="--shiki-light:#383A42;--shiki-dark:#ABB2BF">, </span><span style="--shiki-light:#C18401;--shiki-dark:#E5C07B">T</span><span style="--shiki-light:#383A42;--shiki-dark:#E5C07B"> t3</span><span style="--shiki-light:#383A42;--shiki-dark:#ABB2BF">)</span></span>
<span class="line"><span style="--shiki-light:#A626A4;--shiki-dark:#C678DD">    where</span><span style="--shiki-light:#C18401;--shiki-dark:#E5C07B"> T</span><span style="--shiki-light:#383A42;--shiki-dark:#ABB2BF"> : </span><span style="--shiki-light:#C18401;--shiki-dark:#E5C07B">IMultiplyOperators</span><span style="--shiki-light:#383A42;--shiki-dark:#ABB2BF">&#x3C;</span><span style="--shiki-light:#C18401;--shiki-dark:#E5C07B">T</span><span style="--shiki-light:#383A42;--shiki-dark:#ABB2BF">, </span><span style="--shiki-light:#C18401;--shiki-dark:#E5C07B">T</span><span style="--shiki-light:#383A42;--shiki-dark:#ABB2BF">, </span><span style="--shiki-light:#C18401;--shiki-dark:#E5C07B">T</span><span style="--shiki-light:#383A42;--shiki-dark:#ABB2BF">>, </span><span style="--shiki-light:#C18401;--shiki-dark:#E5C07B">ISubtractionOperators</span><span style="--shiki-light:#383A42;--shiki-dark:#ABB2BF">&#x3C;</span><span style="--shiki-light:#C18401;--shiki-dark:#E5C07B">T</span><span style="--shiki-light:#383A42;--shiki-dark:#ABB2BF">, </span><span style="--shiki-light:#C18401;--shiki-dark:#E5C07B">T</span><span style="--shiki-light:#383A42;--shiki-dark:#ABB2BF">, </span><span style="--shiki-light:#C18401;--shiki-dark:#E5C07B">T</span><span style="--shiki-light:#383A42;--shiki-dark:#ABB2BF">></span></span>
<span class="line"><span style="--shiki-light:#383A42;--shiki-dark:#ABB2BF">{</span></span>
<span class="line"><span style="--shiki-light:#383A42;--shiki-dark:#E5C07B">    Console</span><span style="--shiki-light:#383A42;--shiki-dark:#ABB2BF">.</span><span style="--shiki-light:#4078F2;--shiki-dark:#61AFEF">WriteLine</span><span style="--shiki-light:#383A42;--shiki-dark:#ABB2BF">(</span><span style="--shiki-light:#383A42;--shiki-dark:#E06C75">t1</span><span style="--shiki-light:#383A42;--shiki-dark:#56B6C2"> *</span><span style="--shiki-light:#383A42;--shiki-dark:#E06C75"> t2</span><span style="--shiki-light:#383A42;--shiki-dark:#56B6C2"> -</span><span style="--shiki-light:#383A42;--shiki-dark:#E06C75"> t3</span><span style="--shiki-light:#383A42;--shiki-dark:#ABB2BF">);</span></span>
<span class="line"><span style="--shiki-light:#383A42;--shiki-dark:#ABB2BF">}</span></span></code></pre>
<div class="line-numbers" aria-hidden="true" style="counter-reset:line-number 0"><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div></div></div><p>上述方法约束 <code>T</code> 类型支持乘法和减法运算。</p>
<p>可以这样调用上述方法：</p>
<div class="language-csharp line-numbers-mode" data-highlighter="shiki" data-ext="csharp" style="--shiki-light:#383A42;--shiki-dark:#abb2bf;--shiki-light-bg:#FAFAFA;--shiki-dark-bg:#282c34"><pre class="shiki shiki-themes one-light one-dark-pro vp-code"><code class="language-csharp"><span class="line"><span style="--shiki-light:#4078F2;--shiki-dark:#61AFEF">MultiplyAndSub</span><span style="--shiki-light:#383A42;--shiki-dark:#ABB2BF">(</span><span style="--shiki-light:#986801;--shiki-dark:#D19A66">1</span><span style="--shiki-light:#383A42;--shiki-dark:#ABB2BF">, </span><span style="--shiki-light:#986801;--shiki-dark:#D19A66">2</span><span style="--shiki-light:#383A42;--shiki-dark:#ABB2BF">, </span><span style="--shiki-light:#986801;--shiki-dark:#D19A66">3</span><span style="--shiki-light:#383A42;--shiki-dark:#ABB2BF">);</span></span>
<span class="line"><span style="--shiki-light:#4078F2;--shiki-dark:#61AFEF">MultiplyAndSub</span><span style="--shiki-light:#383A42;--shiki-dark:#ABB2BF">(</span><span style="--shiki-light:#986801;--shiki-dark:#D19A66">0.1f</span><span style="--shiki-light:#383A42;--shiki-dark:#ABB2BF">, </span><span style="--shiki-light:#986801;--shiki-dark:#D19A66">0.2f</span><span style="--shiki-light:#383A42;--shiki-dark:#ABB2BF">, </span><span style="--shiki-light:#986801;--shiki-dark:#D19A66">0.3f</span><span style="--shiki-light:#383A42;--shiki-dark:#ABB2BF">);</span></span>
<span class="line"><span style="--shiki-light:#4078F2;--shiki-dark:#61AFEF">MultiplyAndSub</span><span style="--shiki-light:#383A42;--shiki-dark:#ABB2BF">(</span><span style="--shiki-light:#986801;--shiki-dark:#D19A66">0.1d</span><span style="--shiki-light:#383A42;--shiki-dark:#ABB2BF">, </span><span style="--shiki-light:#986801;--shiki-dark:#D19A66">0.2d</span><span style="--shiki-light:#383A42;--shiki-dark:#ABB2BF">, </span><span style="--shiki-light:#986801;--shiki-dark:#D19A66">0.3d</span><span style="--shiki-light:#383A42;--shiki-dark:#ABB2BF">);</span></span></code></pre>
<div class="line-numbers" aria-hidden="true" style="counter-reset:line-number 0"><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div></div></div><p>当然，我们也可以自己定义一个类型，然后实现 乘法、减法 相关的接口：</p>
<div class="language-csharp line-numbers-mode" data-highlighter="shiki" data-ext="csharp" style="--shiki-light:#383A42;--shiki-dark:#abb2bf;--shiki-light-bg:#FAFAFA;--shiki-dark-bg:#282c34"><pre class="shiki shiki-themes one-light one-dark-pro vp-code"><code class="language-csharp"><span class="line"><span style="--shiki-light:#A626A4;--shiki-dark:#C678DD">public</span><span style="--shiki-light:#A626A4;--shiki-dark:#C678DD"> struct</span><span style="--shiki-light:#C18401;--shiki-dark:#E5C07B"> MultiplyAndSubCustomStruct</span><span style="--shiki-light:#383A42;--shiki-dark:#ABB2BF"> : </span></span>
<span class="line"><span style="--shiki-light:#C18401;--shiki-dark:#E5C07B">    IMultiplyOperators</span><span style="--shiki-light:#383A42;--shiki-dark:#ABB2BF">&#x3C;</span><span style="--shiki-light:#C18401;--shiki-dark:#E5C07B">MultiplyAndSubCustomStruct</span><span style="--shiki-light:#383A42;--shiki-dark:#ABB2BF">, </span><span style="--shiki-light:#C18401;--shiki-dark:#E5C07B">MultiplyAndSubCustomStruct</span><span style="--shiki-light:#383A42;--shiki-dark:#ABB2BF">, </span><span style="--shiki-light:#C18401;--shiki-dark:#E5C07B">MultiplyAndSubCustomStruct</span><span style="--shiki-light:#383A42;--shiki-dark:#ABB2BF">>,</span></span>
<span class="line"><span style="--shiki-light:#C18401;--shiki-dark:#E5C07B">    ISubtractionOperators</span><span style="--shiki-light:#383A42;--shiki-dark:#ABB2BF">&#x3C;</span><span style="--shiki-light:#C18401;--shiki-dark:#E5C07B">MultiplyAndSubCustomStruct</span><span style="--shiki-light:#383A42;--shiki-dark:#ABB2BF">, </span><span style="--shiki-light:#C18401;--shiki-dark:#E5C07B">MultiplyAndSubCustomStruct</span><span style="--shiki-light:#383A42;--shiki-dark:#ABB2BF">, </span><span style="--shiki-light:#C18401;--shiki-dark:#E5C07B">MultiplyAndSubCustomStruct</span><span style="--shiki-light:#383A42;--shiki-dark:#ABB2BF">></span></span>
<span class="line"><span style="--shiki-light:#383A42;--shiki-dark:#ABB2BF">{</span></span>
<span class="line"><span style="--shiki-light:#A626A4;--shiki-dark:#C678DD">    public</span><span style="--shiki-light:#4078F2;--shiki-dark:#61AFEF"> MultiplyAndSubCustomStruct</span><span style="--shiki-light:#383A42;--shiki-dark:#ABB2BF">(</span><span style="--shiki-light:#A626A4;--shiki-dark:#C678DD">int</span><span style="--shiki-light:#383A42;--shiki-dark:#E5C07B"> value</span><span style="--shiki-light:#383A42;--shiki-dark:#ABB2BF">)</span></span>
<span class="line"><span style="--shiki-light:#383A42;--shiki-dark:#ABB2BF">    {</span></span>
<span class="line"><span style="--shiki-light:#383A42;--shiki-dark:#E06C75">        Value</span><span style="--shiki-light:#383A42;--shiki-dark:#56B6C2"> =</span><span style="--shiki-light:#E45649;--shiki-dark:#E06C75"> value</span><span style="--shiki-light:#383A42;--shiki-dark:#ABB2BF">;</span></span>
<span class="line"><span style="--shiki-light:#383A42;--shiki-dark:#ABB2BF">    }</span></span>
<span class="line"></span>
<span class="line"><span style="--shiki-light:#A626A4;--shiki-dark:#C678DD">    public</span><span style="--shiki-light:#A626A4;--shiki-dark:#C678DD"> int</span><span style="--shiki-light:#E45649;--shiki-dark:#E06C75"> Value</span><span style="--shiki-light:#383A42;--shiki-dark:#ABB2BF">;</span></span>
<span class="line"></span>
<span class="line"><span style="--shiki-light:#A626A4;--shiki-dark:#C678DD">    public</span><span style="--shiki-light:#A626A4;--shiki-dark:#C678DD"> static</span><span style="--shiki-light:#C18401;--shiki-dark:#E5C07B"> MultiplyAndSubCustomStruct</span><span style="--shiki-light:#A626A4;--shiki-dark:#C678DD"> operator</span><span style="--shiki-light:#4078F2;--shiki-dark:#61AFEF"> *</span><span style="--shiki-light:#383A42;--shiki-dark:#ABB2BF">(</span><span style="--shiki-light:#C18401;--shiki-dark:#E5C07B">MultiplyAndSubCustomStruct</span><span style="--shiki-light:#383A42;--shiki-dark:#E5C07B"> left</span><span style="--shiki-light:#383A42;--shiki-dark:#ABB2BF">, </span><span style="--shiki-light:#C18401;--shiki-dark:#E5C07B">MultiplyAndSubCustomStruct</span><span style="--shiki-light:#383A42;--shiki-dark:#E5C07B"> right</span><span style="--shiki-light:#383A42;--shiki-dark:#ABB2BF">)</span></span>
<span class="line"><span style="--shiki-light:#383A42;--shiki-dark:#ABB2BF">    {</span></span>
<span class="line"><span style="--shiki-light:#A626A4;--shiki-dark:#C678DD">        return</span><span style="--shiki-light:#383A42;--shiki-dark:#ABB2BF"> new </span><span style="--shiki-light:#C18401;--shiki-dark:#E5C07B">MultiplyAndSubCustomStruct</span><span style="--shiki-light:#383A42;--shiki-dark:#ABB2BF">(</span><span style="--shiki-light:#383A42;--shiki-dark:#E5C07B">left</span><span style="--shiki-light:#383A42;--shiki-dark:#ABB2BF">.</span><span style="--shiki-light:#383A42;--shiki-dark:#E5C07B">Value</span><span style="--shiki-light:#383A42;--shiki-dark:#56B6C2"> *</span><span style="--shiki-light:#383A42;--shiki-dark:#E5C07B"> right</span><span style="--shiki-light:#383A42;--shiki-dark:#ABB2BF">.</span><span style="--shiki-light:#383A42;--shiki-dark:#E5C07B">Value</span><span style="--shiki-light:#383A42;--shiki-dark:#ABB2BF">);</span></span>
<span class="line"><span style="--shiki-light:#383A42;--shiki-dark:#ABB2BF">    }</span></span>
<span class="line"></span>
<span class="line"><span style="--shiki-light:#A626A4;--shiki-dark:#C678DD">    public</span><span style="--shiki-light:#A626A4;--shiki-dark:#C678DD"> static</span><span style="--shiki-light:#C18401;--shiki-dark:#E5C07B"> MultiplyAndSubCustomStruct</span><span style="--shiki-light:#A626A4;--shiki-dark:#C678DD"> operator</span><span style="--shiki-light:#4078F2;--shiki-dark:#61AFEF"> -</span><span style="--shiki-light:#383A42;--shiki-dark:#ABB2BF">(</span><span style="--shiki-light:#C18401;--shiki-dark:#E5C07B">MultiplyAndSubCustomStruct</span><span style="--shiki-light:#383A42;--shiki-dark:#E5C07B"> left</span><span style="--shiki-light:#383A42;--shiki-dark:#ABB2BF">, </span><span style="--shiki-light:#C18401;--shiki-dark:#E5C07B">MultiplyAndSubCustomStruct</span><span style="--shiki-light:#383A42;--shiki-dark:#E5C07B"> right</span><span style="--shiki-light:#383A42;--shiki-dark:#ABB2BF">)</span></span>
<span class="line"><span style="--shiki-light:#383A42;--shiki-dark:#ABB2BF">    {</span></span>
<span class="line"><span style="--shiki-light:#A626A4;--shiki-dark:#C678DD">        return</span><span style="--shiki-light:#383A42;--shiki-dark:#ABB2BF"> new </span><span style="--shiki-light:#C18401;--shiki-dark:#E5C07B">MultiplyAndSubCustomStruct</span><span style="--shiki-light:#383A42;--shiki-dark:#ABB2BF">(</span><span style="--shiki-light:#383A42;--shiki-dark:#E5C07B">left</span><span style="--shiki-light:#383A42;--shiki-dark:#ABB2BF">.</span><span style="--shiki-light:#383A42;--shiki-dark:#E5C07B">Value</span><span style="--shiki-light:#383A42;--shiki-dark:#56B6C2"> -</span><span style="--shiki-light:#383A42;--shiki-dark:#E5C07B"> right</span><span style="--shiki-light:#383A42;--shiki-dark:#ABB2BF">.</span><span style="--shiki-light:#383A42;--shiki-dark:#E5C07B">Value</span><span style="--shiki-light:#383A42;--shiki-dark:#ABB2BF">);</span></span>
<span class="line"><span style="--shiki-light:#383A42;--shiki-dark:#ABB2BF">    }</span></span>
<span class="line"></span>
<span class="line"><span style="--shiki-light:#A626A4;--shiki-dark:#C678DD">    public</span><span style="--shiki-light:#A626A4;--shiki-dark:#C678DD"> string</span><span style="--shiki-light:#4078F2;--shiki-dark:#61AFEF"> ToString</span><span style="--shiki-light:#383A42;--shiki-dark:#ABB2BF">(</span><span style="--shiki-light:#A626A4;--shiki-dark:#C678DD">string</span><span style="--shiki-light:#383A42;--shiki-dark:#ABB2BF">? </span><span style="--shiki-light:#383A42;--shiki-dark:#E5C07B">format</span><span style="--shiki-light:#383A42;--shiki-dark:#ABB2BF">, </span><span style="--shiki-light:#C18401;--shiki-dark:#E5C07B">IFormatProvider</span><span style="--shiki-light:#383A42;--shiki-dark:#ABB2BF">? </span><span style="--shiki-light:#383A42;--shiki-dark:#E5C07B">formatProvider</span><span style="--shiki-light:#383A42;--shiki-dark:#ABB2BF">)</span></span>
<span class="line"><span style="--shiki-light:#383A42;--shiki-dark:#ABB2BF">    {</span></span>
<span class="line"><span style="--shiki-light:#A626A4;--shiki-dark:#C678DD">        return</span><span style="--shiki-light:#383A42;--shiki-dark:#E5C07B"> Value</span><span style="--shiki-light:#383A42;--shiki-dark:#ABB2BF">.</span><span style="--shiki-light:#4078F2;--shiki-dark:#61AFEF">ToString</span><span style="--shiki-light:#383A42;--shiki-dark:#ABB2BF">(</span><span style="--shiki-light:#383A42;--shiki-dark:#E06C75">format</span><span style="--shiki-light:#383A42;--shiki-dark:#ABB2BF">, </span><span style="--shiki-light:#383A42;--shiki-dark:#E06C75">formatProvider</span><span style="--shiki-light:#383A42;--shiki-dark:#ABB2BF">);</span></span>
<span class="line"><span style="--shiki-light:#383A42;--shiki-dark:#ABB2BF">    }</span></span>
<span class="line"><span style="--shiki-light:#383A42;--shiki-dark:#ABB2BF">}</span></span></code></pre>
<div class="line-numbers" aria-hidden="true" style="counter-reset:line-number 0"><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div></div></div><p>然后也可以调用 <code>MultiplyAndSub</code>：</p>
<div class="language-csharp line-numbers-mode" data-highlighter="shiki" data-ext="csharp" style="--shiki-light:#383A42;--shiki-dark:#abb2bf;--shiki-light-bg:#FAFAFA;--shiki-dark-bg:#282c34"><pre class="shiki shiki-themes one-light one-dark-pro vp-code"><code class="language-csharp"><span class="line"><span style="--shiki-light:#4078F2;--shiki-dark:#61AFEF">MultiplyAndSub</span><span style="--shiki-light:#383A42;--shiki-dark:#ABB2BF">(new </span><span style="--shiki-light:#C18401;--shiki-dark:#E5C07B">MultiplyAndSubCustomStruct</span><span style="--shiki-light:#383A42;--shiki-dark:#ABB2BF">(</span><span style="--shiki-light:#986801;--shiki-dark:#D19A66">1</span><span style="--shiki-light:#383A42;--shiki-dark:#ABB2BF">), new </span><span style="--shiki-light:#C18401;--shiki-dark:#E5C07B">MultiplyAndSubCustomStruct</span><span style="--shiki-light:#383A42;--shiki-dark:#ABB2BF">(</span><span style="--shiki-light:#986801;--shiki-dark:#D19A66">2</span><span style="--shiki-light:#383A42;--shiki-dark:#ABB2BF">), new </span><span style="--shiki-light:#C18401;--shiki-dark:#E5C07B">MultiplyAndSubCustomStruct</span><span style="--shiki-light:#383A42;--shiki-dark:#ABB2BF">(</span><span style="--shiki-light:#986801;--shiki-dark:#D19A66">3</span><span style="--shiki-light:#383A42;--shiki-dark:#ABB2BF">));</span></span></code></pre>
<div class="line-numbers" aria-hidden="true" style="counter-reset:line-number 0"><div class="line-number"></div></div></div><p>结果是 <code>-1</code>，符合预期。</p>
]]></content>
    <author>
      <name>Lazy_V</name>
    </author>
    <category term="编程语言"/>
    <contributor>
      <name>Lazy_V</name>
    </contributor>
    <published>2022-11-30T00:00:00.000Z</published>
    <rights>Copyright by Lazy_V</rights>
  </entry>
  <entry>
    <title type="text">新的开始</title>
    <id>https://zzxzzk115.github.io/blog/zh/posts/jottings/new-beginning.html</id>
    <link href="https://zzxzzk115.github.io/blog/zh/posts/jottings/new-beginning.html"/>
    <updated>2025-02-12T00:00:16.000Z</updated>
    <summary type="html"><![CDATA[<p>阿里云、腾讯云的学生优惠马上就都用完了，后续服务器的费用还是不小的。为了节约，我打算还是用回 GitHub Pages。这次，我并不打算使用 Hexo，而是考虑使用具有 Vue 特性的更强大的静态页面生成器：VuePress。我选择 Hope 作为主题，然后慢慢加入一些内容进来。希望这个博客更简约、更清爽，更适合阅读。</p>
<p>为了提高我的英文写作能力，我决定，后续发布的大多数文章都会有中英两个版本。</p>
]]></summary>
    <content type="html"><![CDATA[<p>阿里云、腾讯云的学生优惠马上就都用完了，后续服务器的费用还是不小的。为了节约，我打算还是用回 GitHub Pages。这次，我并不打算使用 Hexo，而是考虑使用具有 Vue 特性的更强大的静态页面生成器：VuePress。我选择 Hope 作为主题，然后慢慢加入一些内容进来。希望这个博客更简约、更清爽，更适合阅读。</p>
<p>为了提高我的英文写作能力，我决定，后续发布的大多数文章都会有中英两个版本。</p>
]]></content>
    <author>
      <name>Lazy_V</name>
    </author>
    <category term="随笔"/>
    <contributor>
      <name>Lazy_V</name>
    </contributor>
    <published>2022-11-27T00:00:00.000Z</published>
    <rights>Copyright by Lazy_V</rights>
  </entry>
  <entry>
    <title type="text">在 MongoDB 中实现自增 ID</title>
    <id>https://zzxzzk115.github.io/blog/zh/posts/backend/mongodb/auto-increment-id-in-mongodb.html</id>
    <link href="https://zzxzzk115.github.io/blog/zh/posts/backend/mongodb/auto-increment-id-in-mongodb.html"/>
    <updated>2022-12-17T14:58:15.000Z</updated>
    <summary type="html"><![CDATA[<p>在 MongoDB 中，每个文档都有一个 object id (Bson ID)。但有时我们创建新模型的时候，需要能够自增的 ID，并且 ID 应该是从 1 开始的整数。怎么解决这个问题呢？</p>
]]></summary>
    <content type="html"><![CDATA[<p>在 MongoDB 中，每个文档都有一个 object id (Bson ID)。但有时我们创建新模型的时候，需要能够自增的 ID，并且 ID 应该是从 1 开始的整数。怎么解决这个问题呢？</p>
<!-- more -->
<h2>怎么解决这个问题</h2>
<p>为了解决这个问题，我们需要创建一个名为 AutoIncrementIdCollection 的新集合，其中我们插入一些有两个字段的模型（AutoIncrementIdModel）：</p>
<ol>
<li>CollectionName --&gt; 集合的名称</li>
<li>Sequence --&gt; 集合当前的序列值，默认值为 0。</li>
</ol>
<p>每次在将新的索引模型 M 插入集合 C 之前，我们都会找到一个 CollectionName 为 C 的 AutoIncrementIdModel，并将其序列加一，然后使用该序列创建 M（M 的 id 字段由序列初始化）并将其插入 C。</p>
]]></content>
    <author>
      <name>Lazy_V</name>
    </author>
    <category term="后端"/>
    <contributor>
      <name>Lazy_V</name>
    </contributor>
    <published>2022-11-27T00:00:00.000Z</published>
    <rights>Copyright by Lazy_V</rights>
  </entry>
</feed>