<?xml version="1.0" encoding="UTF-8"?><rss version="2.0" xmlns:content="http://purl.org/rss/1.0/modules/content/"><channel><title>Comet Blog</title><description>与你一起发现更大的世界</description><link>https://meteor-comet.github.io/</link><language>zh_CN</language><item><title>电气元件认识</title><link>https://meteor-comet.github.io/posts/electrical-components-guide/</link><guid isPermaLink="true">https://meteor-comet.github.io/posts/electrical-components-guide/</guid><description>非标自动化工业上位机全套电气元件手册</description><pubDate>Sat, 13 Jun 2026 00:00:00 GMT</pubDate><content:encoded>&lt;h1&gt;电气元件认识&lt;/h1&gt;
&lt;p&gt;在非标自动化上位机开发与电控系统设计中，电气元件的物理选型、工作原理、接线规范以及通信调试是保障整机稳定性的关键。如果上位机软件工程师不了解底层电气接线和传感器原理，在遇到信号误报、通信丢包或原点回零异常时，往往无从下手。&lt;/p&gt;
&lt;p&gt;本文以一套&lt;strong&gt;石墨盘自动装卸整机项目&lt;/strong&gt;的真实 BOM 清单为核心，系统性拆解整机电气架构。图文并茂地解析总线通信模块、精密电机、相机视觉、扫码枪、各类传感器以及现场接线配件的工作原理、接线电路与调试要点。&lt;/p&gt;
&lt;hr /&gt;
&lt;h2&gt;目录&lt;/h2&gt;
&lt;ul&gt;
&lt;li&gt;&lt;a href=&quot;#%E4%B8%80%E6%8E%A7%E5%88%B6%E7%B3%BB%E7%BB%9F%E4%B8%8E%E9%80%9A%E4%BF%A1%E6%80%BB%E7%BA%BF&quot;&gt;一、控制系统与通信总线&lt;/a&gt;
&lt;ul&gt;
&lt;li&gt;&lt;a href=&quot;#11-smema-%E4%BF%A1%E5%8F%B7%E8%BD%AC%E6%8E%A5%E9%9A%94%E7%A6%BB%E6%A8%A1%E5%9D%97--%E7%81%B5%E7%8C%B4-lh-smema-scm-gi-12ch&quot;&gt;1.1 SMEMA 信号转接隔离模块 — 灵猴 LH-SMEMA-SCM-GI-12CH&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;#12-%E6%9C%AC%E5%9C%B0-io-%E6%89%A9%E5%B1%95%E6%A8%A1%E5%9D%97--%E5%9B%BA%E9%AB%98-hcb5-1616-dtd01&quot;&gt;1.2 本地 IO 扩展模块 — 固高 HCB5-1616-DTD01&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;#13-%E8%BF%9C%E7%A8%8B%E6%80%BB%E7%BA%BF-io-%E6%A8%A1%E5%9D%97--%E7%81%B5%E7%8C%B4-lhs3-%E7%B3%BB%E5%88%97-ethercat-%E6%A8%A1%E5%9D%97&quot;&gt;1.3 远程总线 IO 模块 — 灵猴 LHS3 系列 EtherCAT 模块&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;#14-plc-%E6%89%A9%E5%B1%95%E6%A8%A1%E5%9D%97--%E6%B1%87%E5%B7%9D%E7%B3%BB%E5%88%97&quot;&gt;1.4 PLC 扩展模块 — 汇川系列&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;#%E4%BA%8C%E5%8A%A8%E5%8A%9B%E4%B8%8E%E6%B0%94%E5%8A%A8%E6%89%A7%E8%A1%8C%E6%9C%BA%E6%9E%84&quot;&gt;二、动力与气动执行机构&lt;/a&gt;
&lt;ul&gt;
&lt;li&gt;&lt;a href=&quot;#21-%E7%B2%BE%E5%AF%86%E7%9B%B4%E7%BA%BF%E7%94%B5%E6%9C%BA--%E7%81%B5%E7%8C%B4-bpmc06050a1c2&quot;&gt;2.1 精密直线电机 — 灵猴 BPMC06050A1C2&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;#22-%E4%BA%A4%E6%B5%81%E4%BC%BA%E6%9C%8D%E7%94%B5%E6%9C%BA%E4%B8%8E%E9%A9%B1%E5%8A%A8%E5%99%A8--%E6%B1%87%E5%B7%9D-ms1h4-20b30cb--is810n&quot;&gt;2.2 交流伺服电机与驱动器 — 汇川 MS1H4-20B30CB / IS810N&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;#23-%E6%97%A0%E5%88%B7%E7%9B%B4%E6%B5%81%E5%87%8F%E9%80%9F%E7%94%B5%E6%9C%BA--%E4%B8%AD%E5%A4%A7%E5%8A%9B%E5%BE%B7-25kld120-220gk-28s&quot;&gt;2.3 无刷直流减速电机 — 中大力德 25KLD120-220GK-28S&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;#24-%E7%9C%9F%E7%A9%BA%E4%B8%80%E4%BD%93%E9%98%80%E4%B8%8E%E7%94%B5%E7%A3%81%E9%98%80%E5%B2%9B--smc-zk2-25ea-a--emc-%E4%BA%BF%E5%A4%AA%E8%AF%BA&quot;&gt;2.4 真空一体阀与电磁阀岛 — SMC ZK2-25EA-A / EMC 亿太诺&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;#%E4%B8%89%E4%BC%A0%E6%84%9F%E5%99%A8%E4%B8%8E%E8%AF%86%E5%88%AB%E5%AE%9A%E4%BD%8D%E7%B3%BB%E7%BB%9F&quot;&gt;三、传感器与识别定位系统&lt;/a&gt;
&lt;ul&gt;
&lt;li&gt;&lt;a href=&quot;#31-%E6%A7%BD%E5%9E%8B%E5%85%89%E7%94%B5%E4%BC%A0%E6%84%9F%E5%99%A8--%E6%9D%BE%E4%B8%8B-pm-y45--pm-t45&quot;&gt;3.1 槽型光电传感器 — 松下 PM-Y45 / PM-T45&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;#32-%E5%8F%8D%E5%B0%84%E5%9E%8B%E4%B8%8E%E5%AF%B9%E5%B0%84%E5%9E%8B%E5%85%89%E7%94%B5%E4%BC%A0%E6%84%9F%E5%99%A8--%E6%9D%BE%E4%B8%8B-ex-14a--ex-13ea--ex-13eb&quot;&gt;3.2 反射型与对射型光电传感器 — 松下 EX-14A / EX-13EA / EX-13EB&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;#33-%E6%B0%94%E7%BC%B8%E7%A3%81%E6%80%A7%E4%BC%A0%E6%84%9F%E5%99%A8--smc-d-m9b&quot;&gt;3.3 气缸磁性传感器 — SMC D-M9B&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;#34-%E5%B7%A5%E4%B8%9A%E6%89%AB%E7%A0%81%E6%9E%AA--%E5%BA%B7%E8%80%90%E8%A7%86-dm262_16mm-cov&quot;&gt;3.4 工业扫码枪 — 康耐视 DM262_16MM-COV&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;#%E5%9B%9B%E5%B7%A5%E4%B8%9A%E8%A7%86%E8%A7%89%E5%AE%9A%E4%BD%8D%E7%B3%BB%E7%BB%9F&quot;&gt;四、工业视觉定位系统&lt;/a&gt;
&lt;ul&gt;
&lt;li&gt;&lt;a href=&quot;#41-%E4%B8%8A%E4%B8%8B-ccd-%E8%A7%86%E8%A7%89%E7%B3%BB%E7%BB%9F--%E5%87%8C%E4%BA%91%E5%85%89%E7%9B%B8%E6%9C%BA--rbm-%E5%85%89%E6%BA%90&quot;&gt;4.1 上/下 CCD 视觉系统 — 凌云光相机 &amp;amp; RBM 光源&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;#%E4%BA%94%E9%85%8D%E7%94%B5%E4%B8%8E%E4%BF%9D%E6%8A%A4%E5%85%83%E4%BB%B6&quot;&gt;五、配电与保护元件&lt;/a&gt;
&lt;ul&gt;
&lt;li&gt;&lt;a href=&quot;#51-%E5%BC%80%E5%85%B3%E7%94%B5%E6%BA%90%E4%B8%8E%E7%94%B5%E6%8A%97%E6%BB%A4%E6%B3%A2%E5%99%A8--%E5%BE%B7%E5%8A%9B%E8%A5%BF%E7%94%B5%E6%BA%90--%E6%B1%87%E5%B7%9D%E6%BB%A4%E6%B3%A2%E5%99%A8&quot;&gt;5.1 开关电源与电抗滤波器 — 德力西电源 / 汇川滤波器&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;#52-%E7%A9%BA%E6%B0%94%E5%BC%80%E5%85%B3--%E6%96%AD%E8%B7%AF%E5%99%A8--%E5%BE%B7%E5%8A%9B%E8%A5%BF&quot;&gt;5.2 空气开关 / 断路器 — 德力西&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;#53-%E7%94%B5%E7%A3%81%E7%BB%A7%E7%94%B5%E5%99%A8--idec-%E5%92%8C%E6%B3%89&quot;&gt;5.3 电磁继电器 — IDEC 和泉&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;#%E5%85%AD%E7%8E%B0%E5%9C%BA%E9%AB%98%E6%95%88%E8%BF%9E%E6%8E%A5%E4%B8%8E%E8%B5%B0%E7%BA%BF%E8%A7%84%E8%8C%83&quot;&gt;六、现场高效连接与走线规范&lt;/a&gt;
&lt;ul&gt;
&lt;li&gt;&lt;a href=&quot;#61-8%E4%BD%8D%E9%9B%86%E7%BA%BF%E5%88%86%E7%BA%BF%E6%8E%A5%E7%BA%BF%E7%9B%92--%E8%83%9C%E8%93%9D%E7%94%B5%E6%B0%94-h450-%E7%B3%BB%E5%88%97&quot;&gt;6.1 8位集线分线接线盒 — 胜蓝电气 H450 系列&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;#62-%E6%8B%96%E9%93%BE%E4%B8%8E%E9%AB%98%E6%9F%94%E6%80%A7%E7%BA%BF%E7%BC%86--%E6%98%93%E6%A0%BC%E6%96%AF%E7%B3%BB%E5%88%97&quot;&gt;6.2 拖链与高柔性线缆 — 易格斯系列&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;#63-m12-%E8%BF%9E%E6%8E%A5%E5%99%A8%E4%B8%8E%E7%AB%AF%E5%AD%90%E6%8E%92&quot;&gt;6.3 M12 连接器与端子排&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;#%E4%B8%83%E6%95%B4%E6%9C%BA%E7%94%B5%E6%B0%94%E9%80%89%E5%9E%8B%E9%80%9F%E6%9F%A5%E6%80%BB%E8%A1%A8&quot;&gt;七、整机电气选型速查总表&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;hr /&gt;
&lt;h2&gt;一、控制系统与通信总线&lt;/h2&gt;
&lt;p&gt;在中大型自动化整机中，为了避免复杂繁琐的现场布线，多轴系统与分布式 IO 模块多采用工业总线进行通信与数据交互。&lt;/p&gt;
&lt;h3&gt;1.1 SMEMA 信号转接隔离模块 — 灵猴 LH-SMEMA-SCM-GI-12CH&lt;/h3&gt;
&lt;p&gt;&lt;img src=&quot;/images/in-post/electrical-components/control-smema-module.png&quot; alt=&quot;SMEMA隔离模块&quot; /&gt;&lt;/p&gt;
&lt;h4&gt;1. 工作原理与物理隔离&lt;/h4&gt;
&lt;p&gt;SMEMA (Surface Mount Equipment Manufacturers Association) 标准定义了表面贴装设备之间传送 PCB 载具时的电气接口与握手规范。主要依靠两路信号线进行控制：&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;strong&gt;Ready (已准备就绪)&lt;/strong&gt;：本设备有板/托盘可以送出。&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Busy (正忙)&lt;/strong&gt;：下游设备正忙，无法接收。&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;非标流水线中，上游设备与下游设备的控制系统通常由不同厂家提供，其直流电源系统绝不能共地（存在电势差）。灵猴的 LH-SMEMA-SCM-GI-12CH 模块利用内部的&lt;strong&gt;光电耦合器&lt;/strong&gt;进行物理隔离，将电信号转换为光信号再还原，确保两端设备即便有电势差，也不会烧毁内部电路。&lt;/p&gt;
&lt;h4&gt;2. 隔离转换接线原理&lt;/h4&gt;
&lt;pre&gt;&lt;code&gt;   上游设备 (发送端)                             隔离转换模块 (LH)                       本设备 (接收端)
  ┌─────────────────┐                           ┌─────────────────────┐                 ┌───────────────┐
  │ [Ready Out 继电器]│                           │                     │                 │               │
  │  Pin 1 (输出+)  ├──────────────────────────►│ IN+                 │                 │               │
  │  Pin 2 (输出-)  ├──────────────────────────►│ IN-  (内部光耦输入)  │                 │               │
  │                 │                           │                     │                 │               │
  │                 │                           │ OUT+ (内部光耦输出)  ├────────────────►│ PLC / 控制卡 DI│
  │                 │                           │ OUT-                ├────────────────►│ 0V GND        │
  └─────────────────┘                           └─────────────────────┘                 └───────────────┘
&lt;/code&gt;&lt;/pre&gt;
&lt;h4&gt;3. 调试要点&lt;/h4&gt;
&lt;ul&gt;
&lt;li&gt;&lt;strong&gt;常开与常闭逻辑&lt;/strong&gt;：SMEMA 规范中，Ready 和 Busy 触点在正常传输时通常设计为&lt;strong&gt;常开 (NO)&lt;/strong&gt;，接线时需仔细核对线缆 Pin 脚定义，防止逻辑反转导致整线停机。&lt;/li&gt;
&lt;/ul&gt;
&lt;hr /&gt;
&lt;h3&gt;1.2 本地 IO 扩展模块 — 固高 HCB5-1616-DTD01&lt;/h3&gt;
&lt;h4&gt;1. 工作原理&lt;/h4&gt;
&lt;p&gt;固高 HCB5-1616-DTD01 本地扩展 IO 模块挂载在多轴运动控制卡的本地总线上，用于实现本地开关信号的硬件级就近采集。它提供 16 路数字输入和 16 路数字输出，有效降低主控周期的数据总线占用率。&lt;/p&gt;
&lt;h4&gt;2. NPN型接线图&lt;/h4&gt;
&lt;pre&gt;&lt;code&gt;  外部 NPN 开关/传感器                         固高 HCB5 模块输入端
  ┌──────────────────────┐                     ┌───────────────────────────┐
  │  +24V 电源线 (棕)     ├─────────────────────►│ V+ (模块 24V 输入端)       │
  │  信号输出线 (黑)      ├─────────────────────►│ INx (数字输入通道)         │
  │  0V 地线 (蓝)         ├──────────┬──────────►│ COM (公共端，接 0V)        │
  └──────────────────────┘          │          └───────────────────────────┘
  外部直流开关电源 0V ────────────────┘
&lt;/code&gt;&lt;/pre&gt;
&lt;h4&gt;3. 极性与硬件调试&lt;/h4&gt;
&lt;p&gt;该模块提供 NPN (SINK) 与 PNP (SOURCE) 极性切换端子。调试时必须根据接入的传感器输出极性进行拨码或接线对应，极性接错会导致模块指示灯无响应。&lt;/p&gt;
&lt;p&gt;&lt;em&gt;(注：由于无固高品牌的高吻合度图片，此处不配置配图。)&lt;/em&gt;&lt;/p&gt;
&lt;hr /&gt;
&lt;h3&gt;1.3 远程总线 IO 模块 — 灵猴 LHS3 系列 EtherCAT 模块&lt;/h3&gt;
&lt;p&gt;&lt;img src=&quot;/images/in-post/electrical-components/control-remote-io.png&quot; alt=&quot;远程IO模块&quot; /&gt;&lt;/p&gt;
&lt;h4&gt;1. 分布式 IO 控制机制&lt;/h4&gt;
&lt;p&gt;远程 IO 模块是现场传感器与主控系统之间的桥梁。它通过高速 &lt;strong&gt;EtherCAT 工业以太网&lt;/strong&gt; 与工控机/PLC进行周期性过程数据通信（通信周期通常为 1ms 以内）。模块直接安装在工站下方，实现“就近接线、网线总线传输”。&lt;/p&gt;
&lt;h4&gt;2. 本项目中的模块选型与信号映射&lt;/h4&gt;
&lt;p&gt;在本项目中，三种规格的灵猴总线模块分别分布在不同组件中：&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;strong&gt;LHS3-3200-XET1 (32路输入，共1个)&lt;/strong&gt;：
&lt;ul&gt;
&lt;li&gt;&lt;strong&gt;安装位置&lt;/strong&gt;：交错上料组件。&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;接入信号&lt;/strong&gt;：集中接收该工站的 17 个 EX-14A 传感器、4 个 PM-Y45 限位/原点以及气缸磁性开关。&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;LHS3-1616-XET1 (16输入/16输出，共3个)&lt;/strong&gt;：
&lt;ul&gt;
&lt;li&gt;&lt;strong&gt;安装位置&lt;/strong&gt;：中转搬运轴 (1个)、定位移栽轴 (1个)、下料搬运轴 (1个)。&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;接入信号&lt;/strong&gt;：输入主要接 PM-Y45 行程光电，输出主要控制 ZK2-25EA-A 真空阀及气阀。&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;LHS3-2408-XET1 (24输入/8输出，共4个)&lt;/strong&gt;：
&lt;ul&gt;
&lt;li&gt;&lt;strong&gt;安装位置&lt;/strong&gt;：下料翻转 (1个)、流线1 (1个)、流线2 (1个)、流线3 (1个)。&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;接入信号&lt;/strong&gt;：输入主要接收流线的对射式光电及磁性传感器，输出控制输送带滚轴电机的启停与换向。&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;h4&gt;3. 手拉手串联调试要点&lt;/h4&gt;
&lt;p&gt;所有的 LHS3 模块带有两个 RJ45 总线网口（ECAT IN 和 ECAT OUT），必须遵循菊花链（Daisy Chain）串行连接：&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;  [主控总线网口] ──► [LHS3-3200 IN] ──► [LHS3-3200 OUT] ──► [LHS3-1616 IN] ──► [LHS3-1616 OUT] ──► ...
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;在强干扰区必须使用超五类（CAT5e）或六类（CAT6）带屏蔽的双绞线（SFTP），且屏蔽层必须单端接地，防止伺服电机的高频谐波干扰总线通信。&lt;/p&gt;
&lt;hr /&gt;
&lt;h3&gt;1.4 PLC 扩展模块 — 汇川系列&lt;/h3&gt;
&lt;h4&gt;1. 工作原理与通信分工&lt;/h4&gt;
&lt;p&gt;汇川 PLC 扩展模块挂载在下位机 PLC 控制系统上，用于实现工站级安全联锁、气缸动作顺序逻辑控制以及模拟量输入等本地高速逻辑处理。它与上位机或远程 IO 系统的区别在于它受 PLC 中央处理单元的高频扫描控制，保障了逻辑执行的强实时性。&lt;/p&gt;
&lt;h4&gt;2. 调试要点&lt;/h4&gt;
&lt;ul&gt;
&lt;li&gt;&lt;strong&gt;输入输出点配置&lt;/strong&gt;：在 PLC 硬件组态中配置扩展模块的起始 I/O 地址。&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;安全联锁&lt;/strong&gt;：当上位机由于软件异常暂停时，PLC 通过扩展模块直接检测硬急停回路状态，并切断关键气阀的电源，保障现场机械安全。&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;&lt;em&gt;(注：由于无匹配的汇川PLC扩展模块图片，此处不配置配图。)&lt;/em&gt;&lt;/p&gt;
&lt;hr /&gt;
&lt;h2&gt;二、动力与气动执行机构&lt;/h2&gt;
&lt;p&gt;动力与执行机构是上位机控制指令的物理落脚点，负责驱动各搬运轴的高速定位与取放料动作。&lt;/p&gt;
&lt;h3&gt;2.1 精密直线电机 — 灵猴 BPMC06050A1C2&lt;/h3&gt;
&lt;p&gt;&lt;img src=&quot;/images/in-post/electrical-components/actuator-linear-motor.png&quot; alt=&quot;直线电机&quot; /&gt;&lt;/p&gt;
&lt;h4&gt;1. 直线驱动工作原理&lt;/h4&gt;
&lt;p&gt;直线电机（Linear Motor）将电能直接转换为直线往复运动。它取消了伺服电机+同步带/滚珠丝杠等机械转换机构：&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;strong&gt;定子&lt;/strong&gt;：铺设在动轨底座上的强力永磁磁条。&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;动子&lt;/strong&gt;：内部带有三相电磁绕组的滑块。
由于无机械磨损和物理间隙，配合滑台上反馈分辨率为 0.1μm 的光栅尺，可实现高响应、高精度的闭环位置控制。&lt;/li&gt;
&lt;/ul&gt;
&lt;h4&gt;2. 直线电机控制架构&lt;/h4&gt;
&lt;pre&gt;&lt;code&gt;  [主控卡/驱动器] ◄─── (光栅尺差分反馈) ───► [直线电机动子滑块]
         │ (UVW 三相电流)
         ▼
  [定子永磁轨道]
&lt;/code&gt;&lt;/pre&gt;
&lt;h4&gt;3. 项目实战分布&lt;/h4&gt;
&lt;ul&gt;
&lt;li&gt;&lt;strong&gt;交错上料 Y1/Y2 轴 (2个)&lt;/strong&gt;：双直线轴高速交错运行，实现石墨盘的平稳喂料。&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;中转搬运 Y 轴 (1个) &amp;amp; 下料搬运 Y 轴 (2个)&lt;/strong&gt;：采用灵猴 BPMC06050A1C2 直线电机，用于中行程/长行程的高频往复平移。&lt;/li&gt;
&lt;/ul&gt;
&lt;hr /&gt;
&lt;h3&gt;2.2 交流伺服电机与驱动器 — 汇川 MS1H4-20B30CB / IS810N&lt;/h3&gt;
&lt;p&gt;&lt;img src=&quot;/images/in-post/electrical-components/actuator-servo-motor.png&quot; alt=&quot;交流伺服电机&quot; /&gt;&lt;/p&gt;
&lt;h4&gt;1. 系统工作原理&lt;/h4&gt;
&lt;p&gt;汇川交流伺服驱动器（IS810N 系列）通过三相正弦波交流电驱动 MS1H4-20B30CB 伺服电机转子。电机尾部带有的高分辨率多圈绝对值编码器，将转子实际旋转位置和转速反馈给伺服驱动器，实现高响应的位置、速度与力矩闭环控制。&lt;/p&gt;
&lt;h4&gt;2. 项目中的伺服轴分布&lt;/h4&gt;
&lt;p&gt;本整机项目中，伺服轴多用于旋转控制和精密升降：&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;strong&gt;上料摆正 Y1 轴 (1个)&lt;/strong&gt;：驱动摆正滑台对进料石墨盘进行机械微调对齐。&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;中转搬运轴 (1个) &amp;amp; 下料搬运轴 (3个)&lt;/strong&gt;：包含中转/下料的 Z 轴（升降）、X 轴（短横移）以及下料的 R 轴（旋转）。&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;定位移栽轴 (1个) &amp;amp; 下料翻转轴 (1个)&lt;/strong&gt;：用于滑台的水平位移与 180 度翻转控制。&lt;/li&gt;
&lt;/ul&gt;
&lt;h4&gt;3. 驱动器与滤波器接线&lt;/h4&gt;
&lt;pre&gt;&lt;code&gt;  三相电源输入 ──► 滤波器 ──► 汇川 IS810N 驱动器 (R/S/T)
                                   │
                                   ├──► 电机动力线 (U/V/W) ──► 伺服电机
                                   └──► 编码器反馈线 ◄────────┘
&lt;/code&gt;&lt;/pre&gt;
&lt;hr /&gt;
&lt;h3&gt;2.3 无刷直流减速电机 — 中大力德 25KLD120-220GK-28S&lt;/h3&gt;
&lt;h4&gt;1. 工作原理与无级调速&lt;/h4&gt;
&lt;p&gt;中大力德 25KLD120-220GK-28S 属于集成一体化调速的&lt;strong&gt;无刷直流减速电机&lt;/strong&gt;。内部利用电子换向器代替机械换向器，极大减小了电刷磨损带来的粉尘与噪声。搭配减速比为 28 的减速箱，可实现低速大扭矩输出，适合流线的平稳传送。&lt;/p&gt;
&lt;h4&gt;2. 应用场景&lt;/h4&gt;
&lt;ul&gt;
&lt;li&gt;&lt;strong&gt;流线 1/2/3 的输送驱动 (3个)&lt;/strong&gt;：分别安装在三条传送线体（流线1、流线2、回流流线3）的端部，作为驱动皮带/滚线的恒速动力源，通过远程 IO 模块 LHS3-2408 输出电平逻辑，控制其启动、停止与故障报警监测。&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;&lt;em&gt;(注：由于无中大力德品牌的高吻合度图片，此处不配置配图。)&lt;/em&gt;&lt;/p&gt;
&lt;hr /&gt;
&lt;h3&gt;2.4 真空一体阀与电磁阀岛 — SMC ZK2-25EA-A / EMC 亿太诺&lt;/h3&gt;
&lt;p&gt;&lt;img src=&quot;/images/in-post/electrical-components/actuator-solenoid-valve.png&quot; alt=&quot;真空一体阀与电磁阀岛&quot; /&gt;&lt;/p&gt;
&lt;h4&gt;1. 负压吸附工作原理&lt;/h4&gt;
&lt;ul&gt;
&lt;li&gt;&lt;strong&gt;SMC ZK2-25EA-A 真空一体阀&lt;/strong&gt;：利用文丘里（Venturi）效应，通过高压压缩空气快速穿过狭窄管道在阀体内腔产生负压，从而吸起工件。它集成了空气供给阀、真空破坏阀、真空开关以及数字压力表，吸附响应速度低于 5ms。&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;EMC 亿太诺总线阀岛 (4个)&lt;/strong&gt;：将多个气阀集成在统一的总线底座上，共用气源进出气口。上位机可通过 EtherCAT 总线直接控制阀岛各电磁阀的通断，驱动气缸伸缩。&lt;/li&gt;
&lt;/ul&gt;
&lt;h4&gt;2. 控制回路示意&lt;/h4&gt;
&lt;pre&gt;&lt;code&gt;  [LHS3 输出 24V DC] ──► 供给阀通电 ──► 吸爪负压产生 ──► 真空表信号反馈 (≥-60kPa 判定吸起)
  [LHS3 输出 24V DC] ──► 破坏阀通电 ──► 气流强行吹出 ──► 快速物理剥离工件
&lt;/code&gt;&lt;/pre&gt;
&lt;h4&gt;3. 现场调试要点&lt;/h4&gt;
&lt;ul&gt;
&lt;li&gt;&lt;strong&gt;真空开关设定值&lt;/strong&gt;：在 ZK2 阀体数显面板上将真空到达检测下限设定为 &lt;code&gt;-60kPa&lt;/code&gt;。当传感器返回的高电平信号到达远程 IO 后，主控程序方可执行轴向搬运。&lt;/li&gt;
&lt;/ul&gt;
&lt;hr /&gt;
&lt;h2&gt;三、传感器与识别定位系统&lt;/h2&gt;
&lt;p&gt;传感器是上位机获取机械结构动作状态的硬件窗口，识别系统是保障物料正确流转的防错依据。&lt;/p&gt;
&lt;h3&gt;3.1 槽型光电传感器 — 松下 PM-Y45 / PM-T45&lt;/h3&gt;
&lt;p&gt;&lt;img src=&quot;/images/in-post/electrical-components/pm-y45u-slot.png&quot; alt=&quot;松下 PM-Y45U 槽型光电传感器&quot; /&gt;&lt;/p&gt;
&lt;h4&gt;1. 重复定位精度与结构差异&lt;/h4&gt;
&lt;p&gt;PM-Y45 和 PM-T45 的槽宽固定为 5mm，其内部发光二极管与光敏三极管对置。当挡片（片厚 ≥ 1.0mm）插入槽内遮断红外光时，输出信号发生跳变。其物理重复精度可达 &lt;strong&gt;0.01mm&lt;/strong&gt;。&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;strong&gt;PM-Y45 (Y型垂直插片)&lt;/strong&gt;：最常用的限位与原点传感器，挡片以垂直方向插入槽体。&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;PM-T45 (T型水平插片)&lt;/strong&gt;：适用于滑台侧边等空间摆正狭窄的结构，挡片以水平方向插入槽体。&lt;/li&gt;
&lt;/ul&gt;
&lt;h4&gt;2. 伺服/直线轴典型接线图&lt;/h4&gt;
&lt;pre&gt;&lt;code&gt;                      [松下 PM-Y45 / PM-T45 传感器]
                      ┌────────────────────────────────┐
                      │ 棕 (24V)     蓝 (0V)     黑 (Signal)│
                      └───┬───────────┬──────────┬─────┘
                          │           │          │
                          ▼           ▼          ▼
                      ┌────────────────────────────────┐
                      │ V+        V-          IN_Home  │
                      │         远程 IO 输入通道        │
                      └────────────────────────────────┘
&lt;/code&gt;&lt;/pre&gt;
&lt;h4&gt;3. 项目中的位置分布&lt;/h4&gt;
&lt;ul&gt;
&lt;li&gt;&lt;strong&gt;PM-Y45 (共 27个)&lt;/strong&gt;：广泛分布于交错上料直线轴（4个）、气缸（4个）、中转搬运轴（4个）、下料搬运轴（7个）的正负极限与原点，作为滑台回零检测与过冲保护。&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;PM-T45 (共 2个)&lt;/strong&gt;：专门用于下料搬运的旋转 R 轴（1个）和下料翻转的旋转 R 轴（1个）。&lt;/li&gt;
&lt;/ul&gt;
&lt;hr /&gt;
&lt;h3&gt;3.2 反射型与对射型光电传感器 — 松下 EX-14A / EX-13EA / EX-13EB&lt;/h3&gt;
&lt;h4&gt;1. 工作原理的区别&lt;/h4&gt;
&lt;ul&gt;
&lt;li&gt;&lt;strong&gt;松下 EX-14A&lt;/strong&gt;：属于回归反射型 (Retroreflective)。发射与接收组件集成于传感器同一壳体内。光束射向对面的镜面反射板并返回，当物体阻断返回光时触发输出。&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;松下 EX-13EA/EB&lt;/strong&gt;：属于对射型 (Through-beam)，由独立的发射端 (Emitter) 与接收端 (Receiver) 组成。光轴对齐后，当物体经过并遮断两端对射光时触发输出。&lt;/li&gt;
&lt;/ul&gt;
&lt;h4&gt;2. 配图与接线&lt;/h4&gt;
&lt;p&gt;:::carousel
&lt;img src=&quot;/images/in-post/electrical-components/ex-14a-retroreflective.png&quot; alt=&quot;松下 EX-14A 反射型光电传感器&quot; /&gt;
&amp;lt;!-- slide --&amp;gt;
&lt;img src=&quot;/images/in-post/electrical-components/ex-13ea-through-beam.png&quot; alt=&quot;松下 EX-13EA 对射型光电传感器&quot; /&gt;
:::&lt;/p&gt;
&lt;h4&gt;3. 项目实战应用位置&lt;/h4&gt;
&lt;ul&gt;
&lt;li&gt;&lt;strong&gt;EX-14A (共 26个)&lt;/strong&gt;：主要分布在交错上料组件（17个，用于盘体到位及石墨网格内物料有无判定）、定位移栽轴（2个）、流线2（6个，作气缸限位动作光电）、NG及配对组件（1个，检测有料）。&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;EX-13EA (共 20个)&lt;/strong&gt;：分布于交错上料轨道（5个）、流线1（6个）、流线2（8个）及 NG 站（1个，检测到位），检测输送链板上的载具运动状态。&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;EX-13EB (共 8个)&lt;/strong&gt;：安装于流线 3（回流线），作为托盘防夹、防叠以及回流段到位的逻辑信号输入。&lt;/li&gt;
&lt;/ul&gt;
&lt;hr /&gt;
&lt;h3&gt;3.3 气缸磁性传感器 — SMC D-M9B&lt;/h3&gt;
&lt;p&gt;&lt;img src=&quot;/images/in-post/electrical-components/sensor-magnetic.png&quot; alt=&quot;气缸磁性传感器&quot; /&gt;&lt;/p&gt;
&lt;h4&gt;1. 霍尔效应与两线制接线&lt;/h4&gt;
&lt;p&gt;SMC D-M9B 是两线制（2-Wire）固态磁性开关。它卡入气缸外壳的 T 型槽或 C 型槽中。当气缸活塞内部嵌有的永磁体移动到其下方时，磁开关内部的磁敏元件导通，输出高电平信号至输入模块。&lt;/p&gt;
&lt;h4&gt;2. 接线拓扑&lt;/h4&gt;
&lt;pre&gt;&lt;code&gt;  气缸磁性开关 D-M9B
  ┌──────────────────────┐
  │  棕色线 ─────────────┼───► LHS3 远程 IO 数字输入通道 (INx)
  │  蓝色线 ─────────────┼───► 0V GND
  └──────────────────────┘
&lt;/code&gt;&lt;/pre&gt;
&lt;h4&gt;3. 应用分布&lt;/h4&gt;
&lt;p&gt;整机共包含 11 个 D-M9B 开关，分布于：&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;strong&gt;下料搬运轴 (4个) &amp;amp; 下料翻转 (2个)&lt;/strong&gt;：检测搬运爪水平气缸与翻转升降气缸的运动极限。&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;扫码夹持气缸 (1个) &amp;amp; 流线1顶升阻挡 (4个)&lt;/strong&gt;：用于夹紧和顶升状态的直接反馈。&lt;/li&gt;
&lt;/ul&gt;
&lt;hr /&gt;
&lt;h3&gt;3.4 工业扫码枪 — 康耐视 DM262_16MM-COV&lt;/h3&gt;
&lt;p&gt;&lt;img src=&quot;/images/in-post/electrical-components/sensor-barcode-scanner.png&quot; alt=&quot;工业扫码枪&quot; /&gt;&lt;/p&gt;
&lt;h4&gt;1. 液态镜头与以太网传输&lt;/h4&gt;
&lt;p&gt;康耐视 DM262_16MM-COV 智能读码器集成了 16mm 液态镜头、高强度补光光源与 DSP 解码芯片。它支持自动快速对焦，通过 M12 以太网电缆将条码内容解析后通过 TCP/IP 直接传送给工控机上位机。&lt;/p&gt;
&lt;h4&gt;2. 调试难点与倾角调整&lt;/h4&gt;
&lt;p&gt;石墨盘通常采用激光雕刻的二维码，表面极易产生镜面反光。在设备装配时，扫码枪需呈 &lt;strong&gt;15° 倾角&lt;/strong&gt;安装，避免漫反射偏振光直射镜头，同时在调试软件中开启“偏振过滤”算法，以保障极高的条码识读率。&lt;/p&gt;
&lt;hr /&gt;
&lt;h2&gt;四、工业视觉定位系统&lt;/h2&gt;
&lt;p&gt;机器视觉系统是设备的“眼睛”，为精密机械手臂的对中及对位抓取提供空间偏移坐标补偿。&lt;/p&gt;
&lt;h3&gt;4.1 上/下 CCD 视觉系统 — 凌云光相机 &amp;amp; RBM 光源&lt;/h3&gt;
&lt;p&gt;&lt;img src=&quot;/images/in-post/electrical-components/sensor-vision-camera.png&quot; alt=&quot;视觉相机&quot; /&gt;&lt;/p&gt;
&lt;h4&gt;1. 硬件参数配置表&lt;/h4&gt;
&lt;p&gt;本项目的上、下 CCD 系统分别完成粗对准和精细极性检测：&lt;/p&gt;
&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;系统工站&lt;/th&gt;
&lt;th&gt;组件名称&lt;/th&gt;
&lt;th&gt;规格型号/焦距&lt;/th&gt;
&lt;th&gt;品牌&lt;/th&gt;
&lt;th&gt;光学功能&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;上 CCD 系统&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;相机&lt;/td&gt;
&lt;td&gt;&lt;strong&gt;LY-U2000M-19&lt;/strong&gt; (网口)&lt;/td&gt;
&lt;td&gt;凌云光&lt;/td&gt;
&lt;td&gt;2000万像素，用于石墨盘到位及大视野粗定位&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;/td&gt;
&lt;td&gt;镜头&lt;/td&gt;
&lt;td&gt;&lt;strong&gt;EG-FA25F-H&lt;/strong&gt; (25mm)&lt;/td&gt;
&lt;td&gt;凌云光&lt;/td&gt;
&lt;td&gt;25mm 定焦低几何畸变工业镜头&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;/td&gt;
&lt;td&gt;光源&lt;/td&gt;
&lt;td&gt;&lt;strong&gt;RBM-SFLK600400Y50-W&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;RBM&lt;/td&gt;
&lt;td&gt;白色漫反射背光源，产生高对比度的盘体外轮廓&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;下 CCD 系统&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;相机&lt;/td&gt;
&lt;td&gt;&lt;strong&gt;LY-U2500M-14&lt;/strong&gt; (网口)&lt;/td&gt;
&lt;td&gt;凌云光&lt;/td&gt;
&lt;td&gt;2500万像素，高分率精细纠偏定位&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;/td&gt;
&lt;td&gt;镜头&lt;/td&gt;
&lt;td&gt;&lt;strong&gt;EG-FA35F-H&lt;/strong&gt; (35mm)&lt;/td&gt;
&lt;td&gt;凌云光&lt;/td&gt;
&lt;td&gt;35mm 精密定焦镜头，凸显微距细节特征&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;/td&gt;
&lt;td&gt;光源&lt;/td&gt;
&lt;td&gt;&lt;strong&gt;RBM-FLK200200Y40-R&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;RBM&lt;/td&gt;
&lt;td&gt;红色环形光源，用以强化芯片引脚及极性刻字特征&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;
&lt;h4&gt;2. 网卡优化注意事项&lt;/h4&gt;
&lt;p&gt;工业相机的 GigE 千兆网口直连工控机。由于图像数据流极高，上位机网卡配置中必须：&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;启用**巨帧（Jumbo Packet）**并设置其大小为 &lt;code&gt;9014 字节&lt;/code&gt;。&lt;/li&gt;
&lt;li&gt;关闭&lt;strong&gt;网卡节能选项&lt;/strong&gt;，防止网卡处于低功耗模式引起视觉图像丢包。&lt;/li&gt;
&lt;/ul&gt;
&lt;hr /&gt;
&lt;h2&gt;五、配电与保护元件&lt;/h2&gt;
&lt;p&gt;配电与保护元件构成柜内直流控制电源及交流电机的电能保护屏障。&lt;/p&gt;
&lt;h3&gt;5.1 开关电源与电抗滤波器 — 德力西电源 / 汇川滤波器&lt;/h3&gt;
&lt;p&gt;&lt;img src=&quot;/images/in-post/electrical-components/power-supply.png&quot; alt=&quot;开关电源&quot; /&gt;&lt;/p&gt;
&lt;h4&gt;1. 工作原理&lt;/h4&gt;
&lt;ul&gt;
&lt;li&gt;&lt;strong&gt;德力西 24V DC 开关电源&lt;/strong&gt;：将交流 AC 220V 电源转换为稳定的 24V DC 弱电电源，主要为远程 IO 模块及电磁阀组供电。&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;汇川直流电抗滤波器&lt;/strong&gt;：串联在伺服驱动器 IS810N 输入前端，用于抑制高频换向引起的输入侧电网谐波干扰。&lt;/li&gt;
&lt;/ul&gt;
&lt;h4&gt;2. 接线分配&lt;/h4&gt;
&lt;p&gt;控制柜供电回路采用 3 个德力西开关电源，实现&lt;strong&gt;控制弱电与执行动力电物理分离&lt;/strong&gt;：电源 1 为主控卡及传感器供电；电源 2 和 3 专门供电给真空阀和阻挡电磁阀。&lt;/p&gt;
&lt;hr /&gt;
&lt;h3&gt;5.2 空气开关 / 断路器 — 德力西&lt;/h3&gt;
&lt;p&gt;&lt;img src=&quot;/images/in-post/electrical-components/control-circuit-breaker.png&quot; alt=&quot;空气开关&quot; /&gt;&lt;/p&gt;
&lt;h4&gt;1. 热磁脱扣脱机机制&lt;/h4&gt;
&lt;p&gt;断路器（空气开关）负责整机动力母线及分支电源的短路和过载保护。德力西断路器具备热敏双金属片（用于过载保护）与电磁脱扣器（用于短路瞬时切断），可在几毫秒内熔断脱扣。&lt;/p&gt;
&lt;h4&gt;2. 柜内分级保护&lt;/h4&gt;
&lt;pre&gt;&lt;code&gt;  柜内主进线 (32A) ──► 伺服驱动路断路器 (16A) ──► 驱动器输入
                     ──► 开关电源断路器 (6A)  ──► 开关电源输入
&lt;/code&gt;&lt;/pre&gt;
&lt;hr /&gt;
&lt;h3&gt;5.3 电磁继电器 — IDEC 和泉&lt;/h3&gt;
&lt;p&gt;&lt;img src=&quot;/images/in-post/electrical-components/control-relay.png&quot; alt=&quot;电磁继电器&quot; /&gt;&lt;/p&gt;
&lt;h4&gt;1. 电气中转与信号隔离&lt;/h4&gt;
&lt;p&gt;IDEC 电磁继电器采用低功耗 24V DC 电磁线圈，当线圈有电时，产生的电磁吸力使机械触点闭合，用于实现“小电流控制大电流”或弱电系统对 AC 220V 辅助设备（如排风扇、警报器）的电气隔离中转。&lt;/p&gt;
&lt;h4&gt;2. 接线规范&lt;/h4&gt;
&lt;ul&gt;
&lt;li&gt;继电器底座上的 Pin 1 与 Pin 2 为线圈端子，引线两端需套清晰的线号管，并接旁路二极管，用以吸收线圈断电时产生的反向电动势，延长远程输出通道的寿命。&lt;/li&gt;
&lt;/ul&gt;
&lt;hr /&gt;
&lt;h2&gt;六、现场高效连接与走线规范&lt;/h2&gt;
&lt;p&gt;工业布线与拖链走线规范，是延长移动电缆物理弯曲寿命、削减电磁耦合干扰的基础保障。&lt;/p&gt;
&lt;h3&gt;6.1 8位集线分线接线盒 — 胜蓝电气 H450 系列&lt;/h3&gt;
&lt;p&gt;&lt;img src=&quot;/images/in-post/electrical-components/connector-junction-box.png&quot; alt=&quot;8位接线盒&quot; /&gt;&lt;/p&gt;
&lt;h4&gt;1. 坦克拖链中多线穿束的痛点与集线优势&lt;/h4&gt;
&lt;p&gt;在精密直线滑台（活动端）上通常布满 6~8 个槽型或反射型传感器。若为每个传感器单独引出电缆，会使得坦克拖链内部塞满，电缆在频繁弯曲中因剧烈摩擦而快速折断。
&lt;strong&gt;胜蓝电气 (SIRON) H450 8位分线盒&lt;/strong&gt;彻底解决了此痛点：&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;所有传感器（带 M8 插头）直接插入安装在机构末端的接线盒端口中。&lt;/li&gt;
&lt;li&gt;接线盒内部将 24V V+、0V GND 公共端子并联。&lt;/li&gt;
&lt;li&gt;最终仅引出一根拖链高柔屏蔽电缆接入柜内的远程输入总线模块，从而将多根传感器线束简化为单根电缆。&lt;/li&gt;
&lt;/ul&gt;
&lt;h4&gt;2. 接线连接拓扑&lt;/h4&gt;
&lt;pre&gt;&lt;code&gt;  [松下传感器 1 (M8)] ──► [Port 1] ──┐
  [松下传感器 2 (M8)] ──► [Port 2] ──┼─► [H450接线盒] ──► (10芯高柔屏蔽线) ──► [LHS3 总线 IO 模块]
  ...                              │
  [磁性传感器 8 (M8)] ──► [Port 8] ──┘
&lt;/code&gt;&lt;/pre&gt;
&lt;h4&gt;3. 现场型号配置&lt;/h4&gt;
&lt;ul&gt;
&lt;li&gt;&lt;strong&gt;交错上料直线轴&lt;/strong&gt;：配置 4 个 &lt;strong&gt;H450-8TF-5000/200&lt;/strong&gt; 接线盒（自带 5 米高柔拖链电缆）。&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;中转、定位、下料、流线各滑台&lt;/strong&gt;：配置 16 个 &lt;strong&gt;H450-8-F&lt;/strong&gt; 面板集线模块。&lt;/li&gt;
&lt;/ul&gt;
&lt;hr /&gt;
&lt;h3&gt;6.2 拖链与高柔性线缆 — 易格斯系列&lt;/h3&gt;
&lt;p&gt;&lt;img src=&quot;/images/in-post/electrical-components/cable-drag-chain.png&quot; alt=&quot;拖链&quot; /&gt;&lt;/p&gt;
&lt;h4&gt;1. 行程弯曲寿命与选型&lt;/h4&gt;
&lt;p&gt;设备在运行期间，拖链内电缆需随机械手滑台进行长期往复弯曲。因此，拖链弯曲半径（R）必须设置为电缆外径的 &lt;code&gt;8~10倍&lt;/code&gt;，并且拖链内电缆总截面不得超过拖链内截面的 &lt;code&gt;60%&lt;/code&gt;，确保其在拖链里拥有自然滑动与散热空间。&lt;/p&gt;
&lt;h4&gt;2. 强弱电分离屏蔽规范&lt;/h4&gt;
&lt;p&gt;为了防止伺服电机的高频脉冲动力电流耦合干扰敏感的传感器弱电信号，动力线与编码器线、通信线必须采用&lt;strong&gt;独立线槽&lt;/strong&gt;或&lt;strong&gt;拖链物理分流隔板&lt;/strong&gt;，其平行走线间距必须保持在 &lt;code&gt;200mm&lt;/code&gt; 以上。&lt;/p&gt;
&lt;hr /&gt;
&lt;h3&gt;6.3 M12 连接器与端子排&lt;/h3&gt;
&lt;h4&gt;1. M12 快速插座接线&lt;/h4&gt;
&lt;p&gt;M12 连接器具有螺纹锁固的紧凑防尘构造（IP67等级），是工业级高防护传感器的首选接口。&lt;/p&gt;
&lt;p&gt;:::carousel
&lt;img src=&quot;/images/in-post/electrical-components/connector-m12.png&quot; alt=&quot;M12 连接器&quot; /&gt;
&amp;lt;!-- slide --&amp;gt;
&lt;img src=&quot;/images/in-post/electrical-components/terminal-block.png&quot; alt=&quot;端子排&quot; /&gt;
:::&lt;/p&gt;
&lt;h4&gt;2. M12 A-Code 4 芯引脚引线规范&lt;/h4&gt;
&lt;ul&gt;
&lt;li&gt;&lt;strong&gt;Pin 1 (棕色线)&lt;/strong&gt;：接电源 &lt;code&gt;24V DC&lt;/code&gt; 正极。&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Pin 2 (白色线)&lt;/strong&gt;：接气缸/动作到位第二路输出（如有）。&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Pin 3 (蓝色线)&lt;/strong&gt;：接电源 &lt;code&gt;0V GND&lt;/code&gt; 负极。&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Pin 4 (黑色线)&lt;/strong&gt;：接主要数字量信号输出 &lt;code&gt;OUT1&lt;/code&gt;。&lt;/li&gt;
&lt;/ul&gt;
&lt;h4&gt;3. 端子排电气连接与标号&lt;/h4&gt;
&lt;p&gt;柜内标准 DIN 导轨上安装弹簧插拔式端子排。剥线后套上热缩号码管，清晰标明每一支路接线去向（如 &lt;code&gt;X1-02-LHS3-DI01&lt;/code&gt;），多股铜导线剥线后必须采用压线钳冷压 O 型或针型裸端子再接入，禁止将裸露铜丝直接插入端子。&lt;/p&gt;
&lt;hr /&gt;
&lt;h2&gt;七、整机电气选型速查总表&lt;/h2&gt;
&lt;p&gt;结合前文各章节所述，整理本整机项目中完整核心电气 BOM 选型参数，便于调试与物理核对：&lt;/p&gt;
&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;安装部位 / 工站&lt;/th&gt;
&lt;th&gt;元件类型&lt;/th&gt;
&lt;th&gt;规格型号/核心参数&lt;/th&gt;
&lt;th&gt;数量&lt;/th&gt;
&lt;th&gt;单位&lt;/th&gt;
&lt;th&gt;品牌&lt;/th&gt;
&lt;th&gt;功能与去向说明&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;电控配盘&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;SMEMA信号转接隔离模块&lt;/td&gt;
&lt;td&gt;LH-SMEMA-SCM-GI-12CH&lt;/td&gt;
&lt;td&gt;1&lt;/td&gt;
&lt;td&gt;PCS&lt;/td&gt;
&lt;td&gt;Linkhou (灵猴)&lt;/td&gt;
&lt;td&gt;信号光电隔离，联接上下游设备流线&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;/td&gt;
&lt;td&gt;IO扩展模块&lt;/td&gt;
&lt;td&gt;HCB5-1616-DTD01&lt;/td&gt;
&lt;td&gt;2&lt;/td&gt;
&lt;td&gt;PCS&lt;/td&gt;
&lt;td&gt;Googol (固高)&lt;/td&gt;
&lt;td&gt;扩展运动控制卡本地 I/O 点位&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;/td&gt;
&lt;td&gt;直流电抗滤波器&lt;/td&gt;
&lt;td&gt;-&lt;/td&gt;
&lt;td&gt;3&lt;/td&gt;
&lt;td&gt;PCS&lt;/td&gt;
&lt;td&gt;INOVANCE (汇川)&lt;/td&gt;
&lt;td&gt;抑制驱动器换向时引起的谐波干扰&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;/td&gt;
&lt;td&gt;伺服驱动器&lt;/td&gt;
&lt;td&gt;IS810N&lt;/td&gt;
&lt;td&gt;8&lt;/td&gt;
&lt;td&gt;PCS&lt;/td&gt;
&lt;td&gt;INOVANCE (汇川)&lt;/td&gt;
&lt;td&gt;驱动 7 个伺服轴电机及 1 个空备轴&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;/td&gt;
&lt;td&gt;空气开关&lt;/td&gt;
&lt;td&gt;-&lt;/td&gt;
&lt;td&gt;7&lt;/td&gt;
&lt;td&gt;PCS&lt;/td&gt;
&lt;td&gt;DELIXI (德力西)&lt;/td&gt;
&lt;td&gt;控制柜配电回路的过载与短路保护&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;/td&gt;
&lt;td&gt;继电器&lt;/td&gt;
&lt;td&gt;-&lt;/td&gt;
&lt;td&gt;6&lt;/td&gt;
&lt;td&gt;PCS&lt;/td&gt;
&lt;td&gt;宝莹&lt;/td&gt;
&lt;td&gt;强电信号中转与弱电信号隔离&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;/td&gt;
&lt;td&gt;继电器&lt;/td&gt;
&lt;td&gt;-&lt;/td&gt;
&lt;td&gt;7&lt;/td&gt;
&lt;td&gt;PCS&lt;/td&gt;
&lt;td&gt;IDEC (和泉)&lt;/td&gt;
&lt;td&gt;强电信号中转与弱电信号隔离&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;/td&gt;
&lt;td&gt;电源&lt;/td&gt;
&lt;td&gt;-&lt;/td&gt;
&lt;td&gt;3&lt;/td&gt;
&lt;td&gt;PCS&lt;/td&gt;
&lt;td&gt;DELIXI (德力西)&lt;/td&gt;
&lt;td&gt;提供 24V DC 柜内与工站控制弱电&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;/td&gt;
&lt;td&gt;滤波器&lt;/td&gt;
&lt;td&gt;-&lt;/td&gt;
&lt;td&gt;2&lt;/td&gt;
&lt;td&gt;PCS&lt;/td&gt;
&lt;td&gt;-&lt;/td&gt;
&lt;td&gt;电路输入端无线电射频噪声抑制&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;/td&gt;
&lt;td&gt;PLC扩展模块&lt;/td&gt;
&lt;td&gt;-&lt;/td&gt;
&lt;td&gt;1&lt;/td&gt;
&lt;td&gt;PCS&lt;/td&gt;
&lt;td&gt;INOVANCE (汇川)&lt;/td&gt;
&lt;td&gt;下位机 PLC 本地本地总线 I/O 扩充&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;交错上料组件&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;直线电机&lt;/td&gt;
&lt;td&gt;-&lt;/td&gt;
&lt;td&gt;2&lt;/td&gt;
&lt;td&gt;PCS&lt;/td&gt;
&lt;td&gt;-&lt;/td&gt;
&lt;td&gt;双直线电机 Y1/Y2 交错平稳喂料&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;/td&gt;
&lt;td&gt;直线电机上的光电传感器&lt;/td&gt;
&lt;td&gt;PM-Y45&lt;/td&gt;
&lt;td&gt;4&lt;/td&gt;
&lt;td&gt;PCS&lt;/td&gt;
&lt;td&gt;Panasonic (松下)&lt;/td&gt;
&lt;td&gt;Y1、Y2 直线滑台极限限位保护&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;/td&gt;
&lt;td&gt;并排气缸的光电传感器&lt;/td&gt;
&lt;td&gt;PM-Y45&lt;/td&gt;
&lt;td&gt;4&lt;/td&gt;
&lt;td&gt;PCS&lt;/td&gt;
&lt;td&gt;Panasonic (松下)&lt;/td&gt;
&lt;td&gt;供给侧定位挡气缸到位信号采集&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;/td&gt;
&lt;td&gt;反射型光电传感器&lt;/td&gt;
&lt;td&gt;EX-14A&lt;/td&gt;
&lt;td&gt;17&lt;/td&gt;
&lt;td&gt;PCS&lt;/td&gt;
&lt;td&gt;Panasonic (松下)&lt;/td&gt;
&lt;td&gt;载具托盘与石墨网格有料/无料判断&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;/td&gt;
&lt;td&gt;对射型光电传感器&lt;/td&gt;
&lt;td&gt;EX-13EA&lt;/td&gt;
&lt;td&gt;5&lt;/td&gt;
&lt;td&gt;PCS&lt;/td&gt;
&lt;td&gt;Panasonic (松下)&lt;/td&gt;
&lt;td&gt;上料通道进料区托盘到位判定&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;/td&gt;
&lt;td&gt;M12的16位接线盒随线电缆线&lt;/td&gt;
&lt;td&gt;H450-8TF-5000/200&lt;/td&gt;
&lt;td&gt;4&lt;/td&gt;
&lt;td&gt;PCS&lt;/td&gt;
&lt;td&gt;SIRON (胜蓝电气)&lt;/td&gt;
&lt;td&gt;坦克拖链中 16 路传感器快速汇流接线&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;/td&gt;
&lt;td&gt;远程输入IO模块 (32DI)&lt;/td&gt;
&lt;td&gt;LHS3-3200-XET1&lt;/td&gt;
&lt;td&gt;1&lt;/td&gt;
&lt;td&gt;PCS&lt;/td&gt;
&lt;td&gt;Linkhou (灵猴)&lt;/td&gt;
&lt;td&gt;采集交错上料站全部开关量传感器信号&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;上料摆正轴&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;伺服电机&lt;/td&gt;
&lt;td&gt;MS1H4-20B30CB&lt;/td&gt;
&lt;td&gt;1&lt;/td&gt;
&lt;td&gt;PCS&lt;/td&gt;
&lt;td&gt;INOVANCE (汇川)&lt;/td&gt;
&lt;td&gt;石墨盘物料机械对齐摆正定位驱动&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;/td&gt;
&lt;td&gt;槽型光电传感器&lt;/td&gt;
&lt;td&gt;PM-Y45&lt;/td&gt;
&lt;td&gt;2&lt;/td&gt;
&lt;td&gt;PCS&lt;/td&gt;
&lt;td&gt;Panasonic (松下)&lt;/td&gt;
&lt;td&gt;摆正伺服滑台的行程极限与回零原点&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;中转搬运轴&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;远程IO模块 (16I/16O)&lt;/td&gt;
&lt;td&gt;LHS3-1616-XET1&lt;/td&gt;
&lt;td&gt;1&lt;/td&gt;
&lt;td&gt;PCS&lt;/td&gt;
&lt;td&gt;Linkhou (灵猴)&lt;/td&gt;
&lt;td&gt;中转工站电磁阀输出及开关量输入采集&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;/td&gt;
&lt;td&gt;直线电机&lt;/td&gt;
&lt;td&gt;BPMC06050A1C2&lt;/td&gt;
&lt;td&gt;1&lt;/td&gt;
&lt;td&gt;PCS&lt;/td&gt;
&lt;td&gt;Linkhou (灵猴)&lt;/td&gt;
&lt;td&gt;搬运主滑台的高频精密水平横移&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;/td&gt;
&lt;td&gt;槽型光电传感器&lt;/td&gt;
&lt;td&gt;PM-Y45&lt;/td&gt;
&lt;td&gt;2&lt;/td&gt;
&lt;td&gt;PCS&lt;/td&gt;
&lt;td&gt;Panasonic (松下)&lt;/td&gt;
&lt;td&gt;搬运直线电机轴的正负极限安全开关&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;/td&gt;
&lt;td&gt;伺服电机&lt;/td&gt;
&lt;td&gt;MS1H4-20B30CB&lt;/td&gt;
&lt;td&gt;1&lt;/td&gt;
&lt;td&gt;PCS&lt;/td&gt;
&lt;td&gt;INOVANCE (汇川)&lt;/td&gt;
&lt;td&gt;控制搬运滑台的垂直 Z 轴升降&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;/td&gt;
&lt;td&gt;槽型光电传感器&lt;/td&gt;
&lt;td&gt;PM-Y45&lt;/td&gt;
&lt;td&gt;2&lt;/td&gt;
&lt;td&gt;PCS&lt;/td&gt;
&lt;td&gt;Panasonic (松下)&lt;/td&gt;
&lt;td&gt;升降伺服 Z 轴的原点定位及极限开关&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;/td&gt;
&lt;td&gt;真空一体阀&lt;/td&gt;
&lt;td&gt;ZK2-25EA-A&lt;/td&gt;
&lt;td&gt;4&lt;/td&gt;
&lt;td&gt;PCS&lt;/td&gt;
&lt;td&gt;SMC&lt;/td&gt;
&lt;td&gt;机械手臂吸嘴吸附与吹气破坏控制&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;/td&gt;
&lt;td&gt;Mini型接线盒&lt;/td&gt;
&lt;td&gt;H450-8-F&lt;/td&gt;
&lt;td&gt;2&lt;/td&gt;
&lt;td&gt;PCS&lt;/td&gt;
&lt;td&gt;SIRON (胜蓝电气)&lt;/td&gt;
&lt;td&gt;集中汇流并连活动末端的传感器引线&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;定位移栽轴&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;伺服电机&lt;/td&gt;
&lt;td&gt;MS1H4-20B30CB&lt;/td&gt;
&lt;td&gt;1&lt;/td&gt;
&lt;td&gt;PCS&lt;/td&gt;
&lt;td&gt;INOVANCE (汇川)&lt;/td&gt;
&lt;td&gt;驱动移栽轴滑台实现物料精准对中&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;/td&gt;
&lt;td&gt;槽型光电传感器&lt;/td&gt;
&lt;td&gt;PM-Y45&lt;/td&gt;
&lt;td&gt;2&lt;/td&gt;
&lt;td&gt;PCS&lt;/td&gt;
&lt;td&gt;Panasonic (松下)&lt;/td&gt;
&lt;td&gt;对中伺服滑台原点定位及限位&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;/td&gt;
&lt;td&gt;真空一体阀&lt;/td&gt;
&lt;td&gt;ZK2-25EA-A&lt;/td&gt;
&lt;td&gt;2&lt;/td&gt;
&lt;td&gt;PCS&lt;/td&gt;
&lt;td&gt;SMC&lt;/td&gt;
&lt;td&gt;吸附托盘定位移载控制&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;/td&gt;
&lt;td&gt;远程IO模块 (16I/16O)&lt;/td&gt;
&lt;td&gt;LHS3-1616-XET1&lt;/td&gt;
&lt;td&gt;1&lt;/td&gt;
&lt;td&gt;PCS&lt;/td&gt;
&lt;td&gt;Linkhou (灵猴)&lt;/td&gt;
&lt;td&gt;移载工站数字信号与气动阀控制&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;/td&gt;
&lt;td&gt;Mini型接线盒&lt;/td&gt;
&lt;td&gt;H450-8-F&lt;/td&gt;
&lt;td&gt;2&lt;/td&gt;
&lt;td&gt;PCS&lt;/td&gt;
&lt;td&gt;SIRON (胜蓝电气)&lt;/td&gt;
&lt;td&gt;对中移栽机构活动末端集线盒&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;/td&gt;
&lt;td&gt;反射型光电传感器&lt;/td&gt;
&lt;td&gt;EX-14A&lt;/td&gt;
&lt;td&gt;2&lt;/td&gt;
&lt;td&gt;PCS&lt;/td&gt;
&lt;td&gt;Panasonic (松下)&lt;/td&gt;
&lt;td&gt;移载站物料阻挡到位检测&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;下料搬运轴&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;远程IO模块 (16I/16O)&lt;/td&gt;
&lt;td&gt;LHS3-1616-XET1&lt;/td&gt;
&lt;td&gt;1&lt;/td&gt;
&lt;td&gt;PCS&lt;/td&gt;
&lt;td&gt;Linkhou (灵猴)&lt;/td&gt;
&lt;td&gt;下料搬运组件分布式总线 IO 站&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;/td&gt;
&lt;td&gt;Mini型接线盒&lt;/td&gt;
&lt;td&gt;H450-8-F&lt;/td&gt;
&lt;td&gt;2&lt;/td&gt;
&lt;td&gt;PCS&lt;/td&gt;
&lt;td&gt;SIRON (胜蓝电气)&lt;/td&gt;
&lt;td&gt;机械搬运臂末端快速排线&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;/td&gt;
&lt;td&gt;直线电机&lt;/td&gt;
&lt;td&gt;BPMC06050A1C2&lt;/td&gt;
&lt;td&gt;2&lt;/td&gt;
&lt;td&gt;PCS&lt;/td&gt;
&lt;td&gt;Linkhou (灵猴)&lt;/td&gt;
&lt;td&gt;下料搬运 Y1/Y2 双直线轴高速同步定位&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;/td&gt;
&lt;td&gt;直线电机上的槽型传感器&lt;/td&gt;
&lt;td&gt;PM-Y45&lt;/td&gt;
&lt;td&gt;4&lt;/td&gt;
&lt;td&gt;PCS&lt;/td&gt;
&lt;td&gt;Panasonic (松下)&lt;/td&gt;
&lt;td&gt;双 Y 轴行程极限物理过冲防护&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;/td&gt;
&lt;td&gt;真空一体阀&lt;/td&gt;
&lt;td&gt;ZK2-25EA-A&lt;/td&gt;
&lt;td&gt;4&lt;/td&gt;
&lt;td&gt;PCS&lt;/td&gt;
&lt;td&gt;SMC&lt;/td&gt;
&lt;td&gt;下料滑台气吸爪的吸附操作&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;/td&gt;
&lt;td&gt;伺服电机&lt;/td&gt;
&lt;td&gt;MS1H4-20B30CB&lt;/td&gt;
&lt;td&gt;3&lt;/td&gt;
&lt;td&gt;PCS&lt;/td&gt;
&lt;td&gt;INOVANCE (汇川)&lt;/td&gt;
&lt;td&gt;驱动下料 X 轴、Z 轴升降以及 R 旋转轴&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;/td&gt;
&lt;td&gt;X, Z轴上的槽型光电传感器&lt;/td&gt;
&lt;td&gt;PM-Y45&lt;/td&gt;
&lt;td&gt;3&lt;/td&gt;
&lt;td&gt;PCS&lt;/td&gt;
&lt;td&gt;Panasonic (松下)&lt;/td&gt;
&lt;td&gt;行程原点与物理正负限位&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;/td&gt;
&lt;td&gt;旋转轴上的槽型光电传感器&lt;/td&gt;
&lt;td&gt;PM-T45&lt;/td&gt;
&lt;td&gt;1&lt;/td&gt;
&lt;td&gt;PCS&lt;/td&gt;
&lt;td&gt;Panasonic (松下)&lt;/td&gt;
&lt;td&gt;下料旋转 R 轴的旋转原点传感器&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;/td&gt;
&lt;td&gt;气缸磁性传感器(2线制)&lt;/td&gt;
&lt;td&gt;D-M9B&lt;/td&gt;
&lt;td&gt;4&lt;/td&gt;
&lt;td&gt;PCS&lt;/td&gt;
&lt;td&gt;SMC&lt;/td&gt;
&lt;td&gt;水平气缸气门状态伸长与缩回监测&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;下料翻转&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;远程IO模块 (24I/8O)&lt;/td&gt;
&lt;td&gt;LHS3-2408-XET1&lt;/td&gt;
&lt;td&gt;1&lt;/td&gt;
&lt;td&gt;PCS&lt;/td&gt;
&lt;td&gt;Linkhou (灵猴)&lt;/td&gt;
&lt;td&gt;下料翻转站气阀与限位传感器数据交互&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;/td&gt;
&lt;td&gt;伺服电机&lt;/td&gt;
&lt;td&gt;MS1H4-20B30CB&lt;/td&gt;
&lt;td&gt;1&lt;/td&gt;
&lt;td&gt;PCS&lt;/td&gt;
&lt;td&gt;INOVANCE (汇川)&lt;/td&gt;
&lt;td&gt;驱动翻转 Z 轴升降与 180 度翻转轴&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;/td&gt;
&lt;td&gt;Z轴上的槽型光电传感器&lt;/td&gt;
&lt;td&gt;PM-Y45&lt;/td&gt;
&lt;td&gt;2&lt;/td&gt;
&lt;td&gt;PCS&lt;/td&gt;
&lt;td&gt;Panasonic (松下)&lt;/td&gt;
&lt;td&gt;升降 Z 轴原点及正负极限&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;/td&gt;
&lt;td&gt;R轴上的槽型光电传感器&lt;/td&gt;
&lt;td&gt;PM-T45&lt;/td&gt;
&lt;td&gt;1&lt;/td&gt;
&lt;td&gt;PCS&lt;/td&gt;
&lt;td&gt;Panasonic (松下)&lt;/td&gt;
&lt;td&gt;翻转旋转 R 轴旋转原点限位&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;/td&gt;
&lt;td&gt;Mini型分线接线盒&lt;/td&gt;
&lt;td&gt;H450-8-F&lt;/td&gt;
&lt;td&gt;2&lt;/td&gt;
&lt;td&gt;PCS&lt;/td&gt;
&lt;td&gt;SIRON (胜蓝电气)&lt;/td&gt;
&lt;td&gt;翻转工位活动末端线束快速归拢&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;/td&gt;
&lt;td&gt;真空一体阀&lt;/td&gt;
&lt;td&gt;ZK2-25EA-A&lt;/td&gt;
&lt;td&gt;2&lt;/td&gt;
&lt;td&gt;PCS&lt;/td&gt;
&lt;td&gt;SMC&lt;/td&gt;
&lt;td&gt;夹爪气吸盘真空吸附&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;/td&gt;
&lt;td&gt;气缸磁性传感器(2线制)&lt;/td&gt;
&lt;td&gt;D-M9B&lt;/td&gt;
&lt;td&gt;2&lt;/td&gt;
&lt;td&gt;PCS&lt;/td&gt;
&lt;td&gt;SMC&lt;/td&gt;
&lt;td&gt;垂直导向气缸行程到位控制&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;NG&amp;amp;配对&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;对射型光电传感器&lt;/td&gt;
&lt;td&gt;EX-13EA&lt;/td&gt;
&lt;td&gt;1&lt;/td&gt;
&lt;td&gt;PCS&lt;/td&gt;
&lt;td&gt;Panasonic (松下)&lt;/td&gt;
&lt;td&gt;NG 盒物理到位联锁判定&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;/td&gt;
&lt;td&gt;反射型光电传感器&lt;/td&gt;
&lt;td&gt;EX-14A&lt;/td&gt;
&lt;td&gt;1&lt;/td&gt;
&lt;td&gt;PCS&lt;/td&gt;
&lt;td&gt;Panasonic (松下)&lt;/td&gt;
&lt;td&gt;NG 盒内物料有无监控&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;扫码组件&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;工业扫码枪&lt;/td&gt;
&lt;td&gt;DM262_16MM-COV&lt;/td&gt;
&lt;td&gt;2&lt;/td&gt;
&lt;td&gt;PCS&lt;/td&gt;
&lt;td&gt;Cognex (康耐视)&lt;/td&gt;
&lt;td&gt;载具表面激光印刻二维码高速抓取解析&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;/td&gt;
&lt;td&gt;光源&lt;/td&gt;
&lt;td&gt;-&lt;/td&gt;
&lt;td&gt;1&lt;/td&gt;
&lt;td&gt;PCS&lt;/td&gt;
&lt;td&gt;-&lt;/td&gt;
&lt;td&gt;提供稳定、无闪烁的条码解码补光&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;/td&gt;
&lt;td&gt;气缸磁性传感器(2线制)&lt;/td&gt;
&lt;td&gt;D-M9B&lt;/td&gt;
&lt;td&gt;1&lt;/td&gt;
&lt;td&gt;PCS&lt;/td&gt;
&lt;td&gt;SMC&lt;/td&gt;
&lt;td&gt;扫码支架夹持气缸回缩限位&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;流线1&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;远程IO模块 (24I/8O)&lt;/td&gt;
&lt;td&gt;LHS3-2408-XET1&lt;/td&gt;
&lt;td&gt;1&lt;/td&gt;
&lt;td&gt;PCS&lt;/td&gt;
&lt;td&gt;Linkhou (灵猴)&lt;/td&gt;
&lt;td&gt;线体1段各阻挡与顶升开关信号总线采集&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;/td&gt;
&lt;td&gt;对射型光电传感器&lt;/td&gt;
&lt;td&gt;EX-13EA&lt;/td&gt;
&lt;td&gt;6&lt;/td&gt;
&lt;td&gt;PCS&lt;/td&gt;
&lt;td&gt;Panasonic (松下)&lt;/td&gt;
&lt;td&gt;传送线体上载盘在各点位到位的检测&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;/td&gt;
&lt;td&gt;Mini型分线接线盒&lt;/td&gt;
&lt;td&gt;H450-8-F&lt;/td&gt;
&lt;td&gt;4&lt;/td&gt;
&lt;td&gt;PCS&lt;/td&gt;
&lt;td&gt;SIRON (胜蓝电气)&lt;/td&gt;
&lt;td&gt;沿途 8 个传感器快速并线集线&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;/td&gt;
&lt;td&gt;U型光电传感器&lt;/td&gt;
&lt;td&gt;PM-Y45&lt;/td&gt;
&lt;td&gt;2&lt;/td&gt;
&lt;td&gt;PCS&lt;/td&gt;
&lt;td&gt;Panasonic (松下)&lt;/td&gt;
&lt;td&gt;顶升/阻挡气缸限位，辅助气阀动作&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;/td&gt;
&lt;td&gt;气缸磁性传感器(磁黄)&lt;/td&gt;
&lt;td&gt;D-M9B&lt;/td&gt;
&lt;td&gt;4&lt;/td&gt;
&lt;td&gt;PCS&lt;/td&gt;
&lt;td&gt;SMC&lt;/td&gt;
&lt;td&gt;电磁阻挡挡销气缸到位磁开关&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;/td&gt;
&lt;td&gt;无刷直流减速电机&lt;/td&gt;
&lt;td&gt;25KLD120-220GK-28S&lt;/td&gt;
&lt;td&gt;1&lt;/td&gt;
&lt;td&gt;PCS&lt;/td&gt;
&lt;td&gt;ZD Motor (中大力德)&lt;/td&gt;
&lt;td&gt;线体 1 段输送滚线连续运转动力&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;流线2&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;远程IO模块 (24I/8O)&lt;/td&gt;
&lt;td&gt;LHS3-2408-XET1&lt;/td&gt;
&lt;td&gt;1&lt;/td&gt;
&lt;td&gt;PCS&lt;/td&gt;
&lt;td&gt;Linkhou (灵猴)&lt;/td&gt;
&lt;td&gt;线体2段电磁阀驱动与状态信号总线控制&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;/td&gt;
&lt;td&gt;对射型光电传感器&lt;/td&gt;
&lt;td&gt;EX-13EA&lt;/td&gt;
&lt;td&gt;8&lt;/td&gt;
&lt;td&gt;PCS&lt;/td&gt;
&lt;td&gt;Panasonic (松下)&lt;/td&gt;
&lt;td&gt;检测传送载具在移载过程中的安全防夹&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;/td&gt;
&lt;td&gt;槽型光电传感器 (listed as EX-14A)&lt;/td&gt;
&lt;td&gt;EX-14A&lt;/td&gt;
&lt;td&gt;6&lt;/td&gt;
&lt;td&gt;PCS&lt;/td&gt;
&lt;td&gt;Panasonic (松下)&lt;/td&gt;
&lt;td&gt;流线 2 气缸动作限位光电传感器&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;/td&gt;
&lt;td&gt;无刷直流减速电机&lt;/td&gt;
&lt;td&gt;25KLD120-220GK-28S&lt;/td&gt;
&lt;td&gt;1&lt;/td&gt;
&lt;td&gt;PCS&lt;/td&gt;
&lt;td&gt;ZD Motor (中大力德)&lt;/td&gt;
&lt;td&gt;线体 2 段输送滚线连续运转动力&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;流线3&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;远程IO模块 (24I/8O)&lt;/td&gt;
&lt;td&gt;LHS3-2408-XET1&lt;/td&gt;
&lt;td&gt;1&lt;/td&gt;
&lt;td&gt;PCS&lt;/td&gt;
&lt;td&gt;Linkhou (灵猴)&lt;/td&gt;
&lt;td&gt;流线3回流段过程数据周期采集&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;/td&gt;
&lt;td&gt;对射型光电传感器&lt;/td&gt;
&lt;td&gt;EX-13EB&lt;/td&gt;
&lt;td&gt;8&lt;/td&gt;
&lt;td&gt;PCS&lt;/td&gt;
&lt;td&gt;Panasonic (松下)&lt;/td&gt;
&lt;td&gt;回流段托盘防叠板及到位状态判断&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;/td&gt;
&lt;td&gt;无刷直流减速电机&lt;/td&gt;
&lt;td&gt;25KLD120-220GK-28S&lt;/td&gt;
&lt;td&gt;1&lt;/td&gt;
&lt;td&gt;PCS&lt;/td&gt;
&lt;td&gt;ZD Motor (中大力德)&lt;/td&gt;
&lt;td&gt;线体 3 回流段输送滚线连续运转动力&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;CCD视觉组件 (CCD 1)&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;上CCD相机&lt;/td&gt;
&lt;td&gt;LY-U2000M-19&lt;/td&gt;
&lt;td&gt;1&lt;/td&gt;
&lt;td&gt;PCS&lt;/td&gt;
&lt;td&gt;LUSTER (凌云光)&lt;/td&gt;
&lt;td&gt;采集石墨盘正面对准图像，2000万像素&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;/td&gt;
&lt;td&gt;上CCD镜头&lt;/td&gt;
&lt;td&gt;EG-FA25F-H&lt;/td&gt;
&lt;td&gt;1&lt;/td&gt;
&lt;td&gt;PCS&lt;/td&gt;
&lt;td&gt;LUSTER (凌云光)&lt;/td&gt;
&lt;td&gt;25mm 焦距工业定焦，低畸变对位镜头&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;/td&gt;
&lt;td&gt;上CCD光源&lt;/td&gt;
&lt;td&gt;RBM-SFLK600400Y50-W&lt;/td&gt;
&lt;td&gt;1&lt;/td&gt;
&lt;td&gt;PCS&lt;/td&gt;
&lt;td&gt;RBM&lt;/td&gt;
&lt;td&gt;底部背光源，呈现石墨盘外轮廓阴影&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;/td&gt;
&lt;td&gt;下CCD相机&lt;/td&gt;
&lt;td&gt;LY-U2500M-14&lt;/td&gt;
&lt;td&gt;1&lt;/td&gt;
&lt;td&gt;PCS&lt;/td&gt;
&lt;td&gt;LUSTER (凌云光)&lt;/td&gt;
&lt;td&gt;采集底部对中芯片图像，2500万像素&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;/td&gt;
&lt;td&gt;下CCD镜头&lt;/td&gt;
&lt;td&gt;EG-FA35F-H&lt;/td&gt;
&lt;td&gt;1&lt;/td&gt;
&lt;td&gt;PCS&lt;/td&gt;
&lt;td&gt;LUSTER (凌云光)&lt;/td&gt;
&lt;td&gt;35mm 焦距工业微距，高分辨率镜头&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;/td&gt;
&lt;td&gt;下CCD光源&lt;/td&gt;
&lt;td&gt;RBM-FLK200200Y40-R&lt;/td&gt;
&lt;td&gt;1&lt;/td&gt;
&lt;td&gt;PCS&lt;/td&gt;
&lt;td&gt;RBM&lt;/td&gt;
&lt;td&gt;红色环形光源，高对比呈现引脚极性特征&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;辅助执行&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;总线式阀岛&lt;/td&gt;
&lt;td&gt;-&lt;/td&gt;
&lt;td&gt;4&lt;/td&gt;
&lt;td&gt;PCS&lt;/td&gt;
&lt;td&gt;EMC (亿太诺)&lt;/td&gt;
&lt;td&gt;电磁汇流排，控制气路系统集中换向动作&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;
&lt;hr /&gt;
&lt;p&gt;&lt;em&gt;（本文由上位机软件开发与现场电控硬件工程师联合编制归档。整机 BOM 硬件的品牌与技术选型规格，可直接用于多轴运动控制及分布式控制网络的应用开发设计参考。）&lt;/em&gt;&lt;/p&gt;
</content:encoded></item><item><title>框架详解</title><link>https://meteor-comet.github.io/posts/framework-detailed-guide/</link><guid isPermaLink="true">https://meteor-comet.github.io/posts/framework-detailed-guide/</guid><description>BoTech 工业自动化软件框架开发手册与核心架构指南</description><pubDate>Sat, 06 Jun 2026 00:00:00 GMT</pubDate><content:encoded>&lt;h1&gt;BoTech 工业自动化软件框架开发手册与核心架构指南&lt;/h1&gt;
&lt;p&gt;本手册旨在为开发人员提供关于 BoTech 工业自动化控制软件框架的深度总结。内容涵盖硬件参数配置映射、C# 枚举手动同步 SOP、后台 XML 数据字典架构、自动化工站流程开发 SOP、状态控制逻辑，以及全部常用 API 函数的详细参数、返回值与代码示例。&lt;/p&gt;
&lt;hr /&gt;
&lt;h2&gt;目录&lt;/h2&gt;
&lt;ol&gt;
&lt;li&gt;&lt;a href=&quot;#1-%E8%BD%AF%E4%BB%B6%E6%A1%86%E6%9E%B6%E8%AE%BE%E8%AE%A1%E6%A6%82%E8%BF%B0&quot;&gt;软件框架设计概述&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;#2-ui-%E7%95%8C%E9%9D%A2%E5%8F%82%E6%95%B0%E9%85%8D%E7%BD%AE%E6%98%A0%E5%B0%84%E4%B8%8E%E6%95%B0%E6%8D%AE%E5%BA%93%E6%9E%B6%E6%9E%84&quot;&gt;UI 界面参数配置映射与数据库架构&lt;/a&gt;
&lt;ul&gt;
&lt;li&gt;&lt;a href=&quot;#21-%E7%A1%AC%E4%BB%B6%E5%8F%82%E6%95%B0%E4%B8%8E%E7%B3%BB%E7%BB%9F%E5%8F%82%E6%95%B0%E7%9A%84-excel-%E9%85%8D%E7%BD%AE-%E5%BC%80%E5%8F%91%E7%AC%AC%E4%B8%80%E6%AD%A5&quot;&gt;2.1 硬件参数与系统参数的 Excel 配置 (开发第一步)&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;#22-c-%E6%9E%9A%E4%B8%BE-enumnamecs-%E7%BB%91%E5%AE%9A%E5%85%B3%E7%B3%BB%E4%B8%8E%E6%89%8B%E5%8A%A8%E5%90%8C%E6%AD%A5-sop&quot;&gt;2.2 C# 枚举 (EnumName.cs) 绑定关系与手动同步 SOP&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;#23-excel-%E5%8F%82%E6%95%B0%E9%85%8D%E7%BD%AE%E4%B8%8E-xml-%E6%95%B0%E6%8D%AE%E5%BA%93%E8%BD%AC%E6%8D%A2%E6%98%A0%E5%B0%84%E8%A7%84%E5%88%99&quot;&gt;2.3 Excel 参数配置与 XML 数据库转换映射规则&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;#24-ui-%E7%95%8C%E9%9D%A2%E5%8F%82%E6%95%B0%E6%A0%87%E7%AD%BE%E9%A1%B5-tabpage-%E7%BB%91%E5%AE%9A%E5%85%B3%E7%B3%BB&quot;&gt;2.4 UI 界面参数标签页 (TabPage) 绑定关系&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;#25-hmi-ui-%E8%AF%8A%E6%96%AD%E7%9B%91%E6%8E%A7%E4%B8%8E-io-%E7%A1%AC%E4%BB%B6%E7%9A%84%E4%BA%A4%E4%BA%92%E8%81%94%E7%B3%BB&quot;&gt;2.5 HMI UI 诊断监控与 I/O 硬件的交互联系&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;#26-%E8%BE%93%E5%85%A5%E9%99%90%E5%88%B6%E4%B8%8E%E5%90%88%E6%B3%95%E6%80%A7%E6%A0%A1%E9%AA%8C%E9%80%BB%E8%BE%91&quot;&gt;2.6 输入限制与合法性校验逻辑&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;#27-%E5%8A%9F%E8%83%BD%E4%BD%BF%E8%83%BD%E5%A4%8D%E9%80%89%E6%A1%86%E5%9C%A8%E4%B8%9A%E5%8A%A1%E4%BB%A3%E7%A0%81%E4%B8%AD%E7%9A%84%E8%B7%B3%E6%AD%A5%E4%B8%8E%E5%B1%8F%E8%94%BD%E6%9C%BA%E5%88%B6&quot;&gt;2.7 功能使能复选框在业务代码中的跳步与屏蔽机制&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;#3-%E8%87%AA%E5%8A%A8%E5%8C%96%E6%B5%81%E7%A8%8B%E5%BC%80%E5%8F%91-sop&quot;&gt;自动化流程开发 SOP&lt;/a&gt;
&lt;ul&gt;
&lt;li&gt;&lt;a href=&quot;#31-%E7%BB%A7%E6%89%BF%E5%85%B3%E7%B3%BB%E4%B8%8E%E7%94%9F%E5%91%BD%E5%91%A8%E6%9C%9F%E5%87%BD%E6%95%B0&quot;&gt;3.1 继承关系与生命周期函数&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;#32-%E8%87%AA%E5%8A%A8%E8%BF%90%E8%A1%8C%E7%8A%B6%E6%80%81%E6%9C%BA%E5%BC%80%E5%8F%91%E6%A8%A1%E6%9D%BF&quot;&gt;3.2 自动运行状态机开发模板&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;#33-%E6%AD%A5%E5%BA%8F%E6%8E%A7%E5%88%B6%E4%B8%8E%E6%9B%B4%E6%96%B0%E6%9C%BA%E5%88%B6-setstep&quot;&gt;3.3 步序控制与更新机制 (SetStep)&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;#34-%E8%B6%85%E6%97%B6%E8%AE%A1%E6%97%B6%E5%99%A8%E9%87%8D%E7%BD%AE%E4%B8%8E%E9%98%B2%E8%99%9A%E8%AD%A6%E9%98%B2%E5%91%86%E9%80%BB%E8%BE%91&quot;&gt;3.4 超时计时器重置与防虚警防呆逻辑&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;#35-%E6%B5%81%E6%B0%B4%E7%BA%BFconveyor%E4%B8%8E%E5%B7%A5%E7%AB%99%E7%BB%91%E5%AE%9A%E9%80%BB%E8%BE%91&quot;&gt;3.5 流水线（Conveyor）与工站绑定逻辑&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;#36-%E5%B7%A5%E7%AB%99%E9%97%B4%E4%B8%8E%E5%B7%A5%E7%AB%99%E5%90%8C%E8%BD%B4%E4%BB%BB%E5%8A%A1%E9%97%B4%E7%9A%84%E9%80%9A%E4%BF%A1%E4%B8%8E%E9%A1%BA%E5%BA%8F%E6%8E%A7%E5%88%B6%E9%80%BB%E8%BE%91&quot;&gt;3.6 工站间与工站同轴（任务）间的通信与顺序控制逻辑&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;#37-%E5%85%B8%E5%9E%8B%E5%B7%A5%E5%BA%8F%E5%BC%82%E5%B8%B8%E5%A4%84%E7%90%86%E4%B8%8E%E6%95%85%E9%9A%9C%E6%A8%A1%E6%8B%9F%E8%AE%BE%E8%AE%A1%E4%BB%A5%E6%89%AB%E7%A0%81%E4%B8%8E%E6%89%93%E8%9E%BA%E4%B8%9D%E4%B8%BA%E4%BE%8B&quot;&gt;3.7 典型工序异常处理与故障模拟设计（以扫码与打螺丝为例）&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;#4-%E6%A1%86%E6%9E%B6%E5%B8%B8%E7%94%A8-api-%E5%87%BD%E6%95%B0%E5%8F%82%E8%80%83%E6%89%8B%E5%86%8C&quot;&gt;框架常用 API 函数参考手册&lt;/a&gt;
&lt;ul&gt;
&lt;li&gt;&lt;a href=&quot;#41-%E8%BF%90%E5%8A%A8%E6%8E%A7%E5%88%B6%E7%B1%BB-motion-control&quot;&gt;4.1 运动控制类 (Motion Control)&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;#42-%E7%B3%BB%E7%BB%9F%E8%B6%85%E6%97%B6%E4%B8%8E%E6%97%A5%E5%BF%97-systemutilitylogs&quot;&gt;4.2 系统、超时与日志 (System/Utility/Logs)&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;#43-%E6%96%87%E4%BB%B6%E8%AF%BB%E5%86%99-file-operations&quot;&gt;4.3 文件读写 (File Operations)&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;#44-%E7%BD%91%E5%8F%A3%E4%B8%8E%E4%B8%B2%E5%8F%A3%E9%80%9A%E8%AE%AF-communications&quot;&gt;4.4 网口与串口通讯 (Communications)&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;#45-%E8%BD%AF%E4%BA%A4%E4%BA%92%E4%BF%A1%E5%8F%B7%E9%87%8F%E4%B8%8E%E5%B9%B2%E6%B6%89%E5%8C%BA%E9%98%B2%E6%92%9E-synchronization--concurrency&quot;&gt;4.5 软交互信号量与干涉区防撞 (Synchronization &amp;amp; Concurrency)&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;#46-mfunction-%E6%A0%B8%E5%BF%83%E5%B7%A5%E5%85%B7%E7%B1%BB%E4%B8%8E%E7%B3%BB%E7%BB%9F%E7%8A%B6%E6%80%81%E5%8F%98%E9%87%8F%E8%AF%B4%E6%98%8E&quot;&gt;4.6 mFunction 核心工具类与系统状态变量说明&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;#5-%E6%9C%BA%E6%A2%B0%E8%87%82%E5%9F%BA%E7%B1%BB%E5%BC%80%E5%8F%91%E6%9C%80%E4%BD%B3%E5%AE%9E%E8%B7%B5&quot;&gt;机械臂基类开发最佳实践&lt;/a&gt;
&lt;ul&gt;
&lt;li&gt;&lt;a href=&quot;#51-%E7%BA%BF%E7%A8%8B%E5%AE%89%E5%85%A8%E7%9A%84%E5%AE%89%E5%85%A8%E7%A7%BB%E5%8A%A8%E6%96%B9%E6%B3%95%E8%AE%BE%E8%AE%A1-safemoveto&quot;&gt;5.1 线程安全的安全移动方法设计 (SafeMoveTo)&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;#52-%E6%A1%86%E6%9E%B6%E5%BA%95%E5%B1%82%E5%8E%9F%E7%94%9F%E8%BF%90%E5%8A%A8%E6%8E%A7%E5%88%B6-api-%E8%AF%A6%E8%A7%A3&quot;&gt;5.2 框架底层原生运动控制 API 详解&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;#53-%E4%B8%BA%E4%BB%80%E4%B9%88%E5%B0%81%E8%A3%85-safemoveto-%E8%87%AA%E5%AE%9A%E4%B9%89%E5%AE%89%E5%85%A8%E7%A7%BB%E5%8A%A8%E5%87%BD%E6%95%B0&quot;&gt;5.3 为什么封装 SafeMoveTo 自定义安全移动函数&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;#6-workshare-%E5%AD%90%E5%AF%B9%E8%B1%A1-api-%E5%AE%8C%E6%95%B4%E5%8F%82%E8%80%83&quot;&gt;WorkShare 子对象 API 完整参考&lt;/a&gt;
&lt;ul&gt;
&lt;li&gt;&lt;a href=&quot;#61-mhome--%E5%8D%95%E8%BD%B4%E5%9B%9E%E9%9B%B6&quot;&gt;6.1 mHome — 单轴回零&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;#62-dhome--%E5%A4%9A%E8%BD%B4%E5%90%8C%E6%97%B6%E5%9B%9E%E9%9B%B6&quot;&gt;6.2 dHome — 多轴同时回零&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;#63-pmove--%E4%BD%8D%E7%BD%AE%E8%BF%90%E5%8A%A8&quot;&gt;6.3 pMove — 位置运动&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;#64-smove--%E5%8D%95%E8%BD%B4%E8%BF%90%E5%8A%A8&quot;&gt;6.4 sMove — 单轴运动&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;#65-mmove--%E5%A4%9A%E8%BD%B4%E8%BF%90%E5%8A%A8&quot;&gt;6.5 mMove — 多轴运动&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;#66-mdodi--%E6%95%B0%E5%AD%97io%E7%AD%89%E5%BE%85&quot;&gt;6.6 mDoDi — 数字IO等待&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;#67-mDodis--%E7%AE%80%E5%8C%96%E7%89%88%E6%95%B0%E5%AD%97io&quot;&gt;6.7 mDoDiS — 简化版数字IO&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;#68-msend--tcp%E5%8F%91%E9%80%81%E7%AD%89%E5%BE%85&quot;&gt;6.8 mSend — TCP发送等待&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;#69-mpulseout--%E8%84%89%E5%86%B2%E8%BE%93%E5%87%BA&quot;&gt;6.9 mPulseOut — 脉冲输出&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;#610-mdialog--%E5%AF%B9%E8%AF%9D%E6%A1%86&quot;&gt;6.10 mDialog — 对话框&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;#611-mdicvalue--%E7%AD%89%E5%BE%85%E5%AD%97%E5%85%B8&quot;&gt;6.11 mDicValue — 等待字典&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;#612-motiondll-%E5%BA%95%E5%B1%82-api&quot;&gt;6.12 MotionDll 底层 API&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;#613-mfunctionstate-%E7%B3%BB%E7%BB%9F%E7%8A%B6%E6%80%81%E6%9E%9A%E4%B8%BE&quot;&gt;6.13 mFunction.State 系统状态枚举&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;#614-workshare-%E8%BE%85%E5%8A%A9%E6%96%B9%E6%B3%95&quot;&gt;6.14 WorkShare 辅助方法&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;#615-api-%E9%80%9F%E6%9F%A5%E8%A1%A8workshare-%E5%AD%90%E5%AF%B9%E8%B1%A1&quot;&gt;6.15 API 速查表&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;#7-%E4%BC%A0%E9%80%81%E5%B8%A6conveyor%E9%85%8D%E7%BD%AE%E7%B3%BB%E7%BB%9F%E8%AF%A6%E8%A7%A3&quot;&gt;传送带（Conveyor）配置系统详解&lt;/a&gt;
&lt;ul&gt;
&lt;li&gt;&lt;a href=&quot;#71-%E6%A0%B8%E5%BF%83%E8%AE%BE%E8%AE%A1%E7%90%86%E5%BF%B5&quot;&gt;7.1 核心设计理念&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;#72-conveyorxml-%E9%85%8D%E7%BD%AE%E6%96%87%E4%BB%B6&quot;&gt;7.2 Conveyor.xml 配置文件&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;#73-nconveyor-%E7%8A%B6%E6%80%81%E6%9C%BA%E6%A1%86%E6%9E%B6%E5%86%85%E9%83%A8&quot;&gt;7.3 nConveyor 状态机&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;#74-convevent-%E7%94%A8%E6%88%B7%E5%8F%AF%E7%BC%96%E7%A8%8B%E4%BA%8B%E4%BB%B6a0conveyorscs&quot;&gt;7.4 ConvEvent 用户可编程事件&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;#75-%E5%AE%8C%E6%95%B4%E6%95%B0%E6%8D%AE%E6%B5%81%E7%A4%BA%E4%BE%8B&quot;&gt;7.5 完整数据流示例&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;#76-conveyorxml-%E4%B8%8E-innooutno-%E7%9A%84%E6%98%A0%E5%B0%84%E5%85%B3%E7%B3%BB&quot;&gt;7.6 Conveyor.xml 与 InNo/OutNo 的映射关系&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;#77-%E6%A0%87%E5%87%86%E5%BC%80%E5%8F%91%E6%A8%A1%E5%BC%8F-vs-%E5%BD%93%E5%89%8D%E9%A1%B9%E7%9B%AE&quot;&gt;7.7 标准开发模式 vs 当前项目&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;#78-curstnstatus-%E5%AE%8C%E6%95%B4%E7%8A%B6%E6%80%81%E5%88%97%E8%A1%A8&quot;&gt;7.8 CurStnStatus 完整状态列表&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;#79-conveyordata-%E8%BF%90%E8%A1%8C%E6%97%B6%E5%B1%9E%E6%80%A7&quot;&gt;7.9 ConveyorData 运行时属性&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;#710-%E5%B8%B8%E8%A7%81%E9%97%AE%E9%A2%98&quot;&gt;7.10 常见问题&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;#8-%E6%A0%B8%E5%BF%83-api-%E8%AF%A6%E8%A7%A3%E4%B8%8E%E5%B8%B8%E8%A7%81%E9%97%AE%E9%A2%98&quot;&gt;核心 API 详解与常见问题&lt;/a&gt;
&lt;ul&gt;
&lt;li&gt;&lt;a href=&quot;#81-tasksinteraction-%E8%B7%A8%E7%BA%BF%E7%A8%8B%E9%80%9A%E4%BF%A1%E8%AF%A6%E8%A7%A3&quot;&gt;8.1 TasksInteraction 跨线程通信详解&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;#82-msendwaitdone-%E9%80%9A%E4%BF%A1%E8%AF%A6%E8%A7%A3&quot;&gt;8.2 mSend.WaitDone 通信详解&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;#83-mainconvid-%E8%AF%A6%E8%A7%A3&quot;&gt;8.3 MainConvId 详解&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;#84-tipsdiglogform-%E5%BC%B9%E6%A1%86%E8%AF%A6%E8%A7%A3&quot;&gt;8.4 TipsDiglogForm 弹框详解&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;#85-mdodiwaitdone-%E8%AF%A6%E8%A7%A3&quot;&gt;8.5 mDoDiWaitDone 详解&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;#86-mfunctionovertime-%E8%B6%85%E6%97%B6%E5%88%A4%E6%96%AD&quot;&gt;8.6 mFunction.OverTime 超时判断&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;#87-%E6%9C%BA%E6%A2%B0%E8%BD%B4%E5%B1%8F%E8%94%BD%E6%A8%A1%E5%BC%8F%E8%AF%A6%E8%A7%A3&quot;&gt;8.7 机械轴屏蔽模式详解&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;#88-%E6%89%AB%E7%A0%81%E4%BD%BF%E8%83%BD%E6%A3%80%E6%9F%A5%E9%87%8D%E5%A4%8D%E7%9A%84%E5%8E%9F%E5%9B%A0&quot;&gt;8.8 扫码使能检查重复的原因&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;#9-ui-%E6%8E%A7%E4%BB%B6%E4%B8%8E%E4%BB%A3%E7%A0%81%E7%9A%84%E6%98%A0%E5%B0%84%E5%85%B3%E7%B3%BB&quot;&gt;UI 控件与代码的映射关系&lt;/a&gt;
&lt;ul&gt;
&lt;li&gt;&lt;a href=&quot;#91-%E6%A0%B8%E5%BF%83%E6%A6%82%E5%BF%B5&quot;&gt;9.1 核心概念&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;#92-stationrun--task-%E7%BB%91%E5%AE%9A&quot;&gt;9.2 StationRun → Task 绑定&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;#93-teach--%E8%BD%B4%E8%BF%90%E5%8A%A8%E6%98%A0%E5%B0%84&quot;&gt;9.3 Teach → 轴运动映射&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;#94-cylinder--io-%E6%98%A0%E5%B0%84&quot;&gt;9.4 Cylinder → IO 映射&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;#95-mbtn_out--io-%E6%98%A0%E5%B0%84&quot;&gt;9.5 mBtn_Out → IO 映射&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;#96-%E5%AE%8C%E6%95%B4%E6%98%A0%E5%B0%84%E5%85%B3%E7%B3%BB%E5%9B%BE&quot;&gt;9.6 完整映射关系图&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;/ol&gt;
&lt;h2&gt;1. 软件框架设计概述&lt;/h2&gt;
&lt;p&gt;BoTech 框架是一款基于多线程并发、状态机流控制和点位/参数示教的模块化工业控制系统。其核心架构由以下三层构成：&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;&lt;strong&gt;界面层 (Form/UI)&lt;/strong&gt;：提供以主界面、手动调试界面、参数配置页面和示教界面为核心的 HMI。&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;逻辑控制层 (Tasks)&lt;/strong&gt;：每个独立的物理机构或工位继承自 &lt;code&gt;mWorkShare&lt;/code&gt; 基类，在独立的线程中以状态机形式运行。各工站通过高内聚、低耦合的设计实现协作。&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;硬件抽象与辅助层 (Assist/DLLs)&lt;/strong&gt;：封装运动控制卡（如固高卡等）、数字 I/O 读写、TCP/IP/串口网络通讯、数据库读写、文件存储、日志追踪以及多工站防撞干涉区管理。&lt;/li&gt;
&lt;/ol&gt;
&lt;hr /&gt;
&lt;h2&gt;2. UI 界面参数配置映射与数据库架构&lt;/h2&gt;
&lt;p&gt;在搭建新项目或导入新硬件方案时，&lt;strong&gt;参数配置是绝对的第一步工作&lt;/strong&gt;。此时设备刚完成物理接线，尚未编写业务逻辑，必须先通过 Excel 写入所有硬件元数据，并在 C# 源码中同步对应的枚举字段，以此构建整个工控软件的数据字典与软硬件映射通道。&lt;/p&gt;
&lt;h3&gt;2.1 硬件参数与系统参数的 Excel 配置 (开发第一步)&lt;/h3&gt;
&lt;p&gt;开发人员需要在 &lt;code&gt;D:\BZ-Parameter\RBF\ParXlsx\&lt;/code&gt; 目录下配置以下五个核心 Excel 表格。这些表格定义了整台设备的卡、轴、I/O 以及系统规格。&lt;/p&gt;
&lt;h4&gt;2.1.1 控制卡配置 (&lt;code&gt;CardPar.xlsx&lt;/code&gt;)&lt;/h4&gt;
&lt;p&gt;定义系统中所安装的物理运动控制卡（如固高 GTS 卡等）及扩展板卡的型号与物理地址。&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;strong&gt;主要列定义&lt;/strong&gt;：
&lt;ul&gt;
&lt;li&gt;&lt;strong&gt;编号&lt;/strong&gt;：逻辑 ID（从 0 开始自增），用于软件内存数组分配。&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;卡号&lt;/strong&gt;：物理板卡上拨码开关设定的物理卡号（对应驱动中的 Card ID）。&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;卡名称&lt;/strong&gt;：如 &lt;code&gt;DECAT&lt;/code&gt; (固高主卡)、&lt;code&gt;GENEX&lt;/code&gt; 等。&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;供应商&lt;/strong&gt;：板卡制造厂商，如 &lt;code&gt;固高&lt;/code&gt; 等。&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;起始序号&lt;/strong&gt; 与 &lt;strong&gt;数量&lt;/strong&gt;：该板卡控制的总轴数或 I/O 引脚的起始逻辑地址。&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;主卡&lt;/strong&gt;：布尔值（True/False），设定为主卡后，软件系统启动时将作为主控卡加载。&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;参数路径&lt;/strong&gt;：控制卡底层核心配置文件的相对路径（如 &lt;code&gt;+GTS_Config/gtn_core1.cfg&lt;/code&gt;）。系统初始化时，驱动会自动读取该 CFG 文件进行底层初始化。&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;h4&gt;2.1.2 伺服轴配置 (&lt;code&gt;AxisPar.xlsx&lt;/code&gt;)&lt;/h4&gt;
&lt;p&gt;将 C# 中的逻辑轴映射到控制卡的物理通道上，并进行脉冲当量和加减速规划。&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;strong&gt;主要列定义&lt;/strong&gt;：
&lt;ul&gt;
&lt;li&gt;&lt;strong&gt;编号&lt;/strong&gt;：轴逻辑 ID。&lt;strong&gt;必须与 C# 源码 &lt;code&gt;EnumName.cs&lt;/code&gt; 中 &lt;code&gt;mAxis&lt;/code&gt; 枚举的声明顺序完全保持一致&lt;/strong&gt;。&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;名称&lt;/strong&gt;：轴中文名（如 &lt;code&gt;左X&lt;/code&gt;）。&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;卡号&lt;/strong&gt; / &lt;strong&gt;轴号&lt;/strong&gt;：该轴接在 &lt;code&gt;CardPar.xlsx&lt;/code&gt; 中配置的哪张板卡（卡号）以及哪一个物理轴通道（轴号，通常为 1~8）。&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;脉冲当量计算参数&lt;/strong&gt;（&lt;code&gt;每圈脉冲&lt;/code&gt;、&lt;code&gt;减速比&lt;/code&gt;、&lt;code&gt;导程(mm/°)&lt;/code&gt;）:
用于进行物理单位与脉冲数的自动转换。转换公式为:
$$\text{Pulse Ratio} = \frac{\text{每圈脉冲} \times \text{减速比}}{\text{导程}}$$
示例：伺服电机每圈脉冲为 10000，直连无减速比，丝杠导程为 10 mm，则脉冲比例为 1000 Pulse/mm。当代码调用 &lt;code&gt;MotionAbsMove(轴X, 50, -1)&lt;/code&gt; 移动 50 mm 时，底层驱动会自动发送 50000 脉冲，实现对开发者的物理单位黑盒化。&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;速度规划参数&lt;/strong&gt;（&lt;code&gt;加速度&lt;/code&gt;、&lt;code&gt;减速度&lt;/code&gt;、&lt;code&gt;回零速度(mm/s)&lt;/code&gt;）：
定义轴在执行运动指令时的默认加减速斜率以及回零寻找 Index 信号时的物理速度，保证轴在起停时的平稳度。&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;h4&gt;2.1.3 数字 I/O 映射配置 (&lt;code&gt;Input.xlsx&lt;/code&gt; 与 &lt;code&gt;Output.xlsx&lt;/code&gt;)&lt;/h4&gt;
&lt;p&gt;配置物理传感器（光电开关、安全门、磁簧开关）和物理输出动作（电磁阀、继电器、指示灯）。&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;strong&gt;输入/输出列定义&lt;/strong&gt;：
&lt;ul&gt;
&lt;li&gt;&lt;strong&gt;编号&lt;/strong&gt;：逻辑 ID（从 0 开始自增），必须与 C# &lt;code&gt;EnumName.cs&lt;/code&gt; 中的 &lt;code&gt;InNo&lt;/code&gt; 和 &lt;code&gt;OutNo&lt;/code&gt; 枚举排序完全一致。&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;名称Cn&lt;/strong&gt; / &lt;strong&gt;名称En&lt;/strong&gt;：在中英文界面上显示的信号名称。&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;模块编号&lt;/strong&gt; / &lt;strong&gt;序号&lt;/strong&gt;：指明该 I/O 信号压接在第几张板卡（模块编号）以及哪一个物理引脚（引脚序号，如 Pin 0~15）上。&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;初始状态&lt;/strong&gt;：系统复位上电时控制卡输出的默认电平（通常为 0）。&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;原点序号&lt;/strong&gt; / &lt;strong&gt;动点序号&lt;/strong&gt;（&lt;code&gt;Output.xlsx&lt;/code&gt; 独有）：仅针对双控气缸，用于绑定对应的缩回和伸出物理限位信号（&lt;code&gt;InNo&lt;/code&gt;）。
&lt;em&gt;当调用气缸动作 API &lt;code&gt;mDoDiWaitDone&lt;/code&gt; 时，软件框架根据此映射自动等待对应的传感器信号，如果超时则自动判断为动作未到位报错。&lt;/em&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;h4&gt;2.1.4 系统参数配置 (&lt;code&gt;SysPar.xlsx&lt;/code&gt;)&lt;/h4&gt;
&lt;p&gt;设定整台设备的物理规模限制，用于系统启动时的内存初始化和边界保护。&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;strong&gt;主要参数项&lt;/strong&gt;：
&lt;ul&gt;
&lt;li&gt;&lt;strong&gt;卡数量&lt;/strong&gt;：系统中控制卡及模块的总数。&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;轴数量&lt;/strong&gt;：设备拥有的轴总数。&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;输入点数量&lt;/strong&gt; / &lt;strong&gt;输出点数量&lt;/strong&gt;：数字量 I/O 的最大物理数量规格限制。&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;设置参数数量&lt;/strong&gt;：参数列表 &lt;code&gt;mParList&lt;/code&gt; 的最大容量上限（如 280 个）。&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;急停编号&lt;/strong&gt;：急停按钮所接的 &lt;code&gt;InNo&lt;/code&gt; 输入引脚索引，系统底层急停监控线程将根据此引脚进行高频读取，一旦触发立即拉停所有轴。&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;h4&gt;2.1.5 全局参数表 (&lt;code&gt;ParList.xlsx&lt;/code&gt;)&lt;/h4&gt;
&lt;p&gt;包含用于微调和控制的所有非硬件参数（如微调补偿量、速度、使能复选框开关等）。&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;索引 0 ~ 99、150~179 映射为浮点数/字符串形式的用户参数，对应 &lt;code&gt;UserPar&lt;/code&gt; 枚举。&lt;/li&gt;
&lt;li&gt;索引 100~131 映射为功能使能复选框（布尔开关），对应 &lt;code&gt;FuncChk&lt;/code&gt; 枚举。&lt;/li&gt;
&lt;/ul&gt;
&lt;hr /&gt;
&lt;h3&gt;2.2 C# 枚举 (&lt;code&gt;EnumName.cs&lt;/code&gt;) 绑定关系与手动同步 SOP&lt;/h3&gt;
&lt;p&gt;在 C# 流程开发中，我们绝不能使用裸的物理通道号（如 0, 1, 2）或硬编码变量，而是使用 &lt;code&gt;EnumName.cs&lt;/code&gt; 里的枚举。&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;[!IMPORTANT]
&lt;strong&gt;关于自动“枚举生成”的澄清与同步规范&lt;/strong&gt;：&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;&lt;strong&gt;没有“一键枚举生成”按钮&lt;/strong&gt;：当前 BoTech 框架中，&lt;strong&gt;设置页面没有任何一键“枚举生成”的自动化机制&lt;/strong&gt;。每次对 Excel 配置表（&lt;code&gt;Input.xlsx&lt;/code&gt;, &lt;code&gt;Output.xlsx&lt;/code&gt;, &lt;code&gt;AxisPar.xlsx&lt;/code&gt;, &lt;code&gt;ParList.xlsx&lt;/code&gt;）做出新增、删除或顺序调整时，&lt;strong&gt;必须由开发人员手动修改 C# 源码中的 &lt;code&gt;EnumName.cs&lt;/code&gt;&lt;/strong&gt;。&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;索引严格一致性&lt;/strong&gt;：C# 源码中的枚举值在底层执行时会直接强转为 &lt;code&gt;short&lt;/code&gt; 索引（如 &lt;code&gt;(short)InNo.流线1到位信号&lt;/code&gt;），以去 XML 数据库解析对应的卡号和物理通道。因此，&lt;strong&gt;C# 中的枚举项顺序必须与 Excel 中的“编号”顺序保持 100% 绝对一致&lt;/strong&gt;。如果出现偏离，编译器不会报错，但在自动运行时将直接控制或读取错误的物理引脚，极易引发机械撞击事故！&lt;/li&gt;
&lt;/ol&gt;
&lt;/blockquote&gt;
&lt;h4&gt;2.2.1 详细映射对应关系&lt;/h4&gt;
&lt;p&gt;Excel 与 &lt;code&gt;EnumName.cs&lt;/code&gt; 内枚举项的精确映射如下：&lt;/p&gt;
&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Excel 配置表&lt;/th&gt;
&lt;th&gt;映射 C# 枚举&lt;/th&gt;
&lt;th&gt;声明规则与示例&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;&lt;code&gt;Input.xlsx&lt;/code&gt;&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;&lt;code&gt;InNo&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;按照 &lt;code&gt;Input.xlsx&lt;/code&gt; 编号 0、1、2 顺序依次声明。&amp;lt;br&amp;gt;如 &lt;code&gt;急停信号 = 0&lt;/code&gt;，首个成员必须是它。&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;&lt;code&gt;Output.xlsx&lt;/code&gt;&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;&lt;code&gt;OutNo&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;按照 &lt;code&gt;Output.xlsx&lt;/code&gt; 编号 0、1、2 顺序依次声明。&amp;lt;br&amp;gt;如 &lt;code&gt;五色灯红色 = 0&lt;/code&gt;。&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;&lt;code&gt;AxisPar.xlsx&lt;/code&gt;&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;&lt;code&gt;mAxis&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;逻辑轴配置。由于底层运动卡通道绑定，首项显式指定为 1：&amp;lt;br&amp;gt;&lt;code&gt;右X = 1&lt;/code&gt;, &lt;code&gt;右Y&lt;/code&gt;, &lt;code&gt;右Z&lt;/code&gt; 等。&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;&lt;code&gt;ParList.xlsx&lt;/code&gt; (100~131)&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;&lt;code&gt;FuncChk&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;仅映射 Excel 索引 100~131 处的布尔复选框开关。首项显式声明：&amp;lt;br&amp;gt;&lt;code&gt;Enable_Security_ = 100&lt;/code&gt;。&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;&lt;code&gt;ParList.xlsx&lt;/code&gt; (0~99, 150+)&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;&lt;code&gt;UserPar&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;映射所有常规数值参数。首项显式声明：&amp;lt;br&amp;gt;&lt;code&gt;扫码失败次数 = 0&lt;/code&gt;；并在 150 后显式声明：&amp;lt;br&amp;gt;&lt;code&gt;Machine_runMode = 150&lt;/code&gt;。&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;
&lt;h4&gt;2.2.2 硬件配置与枚举修改的手动同步 SOP 流程&lt;/h4&gt;
&lt;p&gt;在需要添加或修改系统参数时，请务必执行以下规范流程：&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;[步骤1: 修改 Excel] 在 D:\BZ-Parameter\RBF\ParXlsx 修改对应的配置文件 (如 Input.xlsx)
       │
       ▼
[步骤2: 修改 C# 枚举] 打开 C# 项目，修改 AncillaryProject/ParName/ParName/EnumName.cs 对应枚举项
       │
       ▼
[步骤3: 编译类库] 在 VS 中编译 ParName 项目，生成最新的 ParName.dll，并重新编译主程序以更新项目引用
       │
       ▼
[步骤4: 生成/更新 XML] 系统读取 Excel 元数据并写入 ParInput/ParOutput/ParMachine/ParData/SysPar.xml
       │
       ▼
[步骤5: 重启软件生效] 关闭并重新启动软件，底层 DLL 读取新的 XML 数据，此时软硬件完全对应生效！
&lt;/code&gt;&lt;/pre&gt;
&lt;h4&gt;2.2.3 多种参数获取方式&lt;/h4&gt;
&lt;p&gt;框架提供了 &lt;strong&gt;三类&lt;/strong&gt; 常用的全局参数读取方式，适用于不同的开发场景。&lt;/p&gt;
&lt;h5&gt;1. 泛型获取 &lt;code&gt;GetParValue&amp;lt;T&amp;gt;&lt;/code&gt;&lt;/h5&gt;
&lt;pre&gt;&lt;code&gt;// 读取整数参数
int maxRetry = mFunction.GetParValue&amp;lt;int&amp;gt;(UserPar.扫码失败次数);
// 读取字符串参数
string mode = mFunction.GetParValue&amp;lt;string&amp;gt;(UserPar.Machine_runMode);
// 读取浮点参数
double speed = mFunction.GetParValue&amp;lt;double&amp;gt;(UserPar.RobotSpeed);
// 读取 Bool 参数（功能开关）
bool safety = mFunction.GetParValue&amp;lt;bool&amp;gt;(FuncChk.Enable_Security_);
&lt;/code&gt;&lt;/pre&gt;
&lt;ul&gt;
&lt;li&gt;&lt;strong&gt;适用场景&lt;/strong&gt;：一次性读取，类型安全，由框架自动完成类型转换。&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;特性&lt;/strong&gt;：编译期间检查返回类型，防止类型错误。&lt;/li&gt;
&lt;/ul&gt;
&lt;h5&gt;2. 快捷读取 &lt;code&gt;mGlobal&lt;/code&gt; 包装属性&lt;/h5&gt;
&lt;pre&gt;&lt;code&gt;double robotSpeed = mGlobal.ParDbl(UserPar.RobotSpeed);
int delayMs = mGlobal.ParInt(UserPar.电批吸料稳定延时);
string modeStr = mGlobal.ParStr(UserPar.Machine_runMode);
bool enableScan = mGlobal.FuncCheck(FuncChk.启用扫码);  // Bool 快捷读取
&lt;/code&gt;&lt;/pre&gt;
&lt;ul&gt;
&lt;li&gt;&lt;strong&gt;适用场景&lt;/strong&gt;：直接访问全局数组 &lt;code&gt;mParList&lt;/code&gt;，效率最高。&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;最佳实践&lt;/strong&gt;：适合在自动运行循环（AutoRun）或高频轮询的实时控制参数中调用。&lt;/li&gt;
&lt;/ul&gt;
&lt;h5&gt;3. 直接访问全局数组 &lt;code&gt;mParList[]&lt;/code&gt;&lt;/h5&gt;
&lt;pre&gt;&lt;code&gt;// 读取数值
double speed = mParList[(short)UserPar.RobotSpeed].DataDbl;
// 读取 Bool 状态
bool enabled = mParList[(short)FuncChk.Enable_Security_].CheckSts;
// 读取上下限信息（用于 UI 限制）
double up = mParList[(short)UserPar.RobotSpeed].LimitUp;
double down = mParList[(short)UserPar.RobotSpeed].LimitDown;
&lt;/code&gt;&lt;/pre&gt;
&lt;ul&gt;
&lt;li&gt;&lt;strong&gt;适用场景&lt;/strong&gt;：仅在需要访问元数据（如上下限 &lt;code&gt;LimitUp&lt;/code&gt;/&lt;code&gt;LimitDown&lt;/code&gt;、物理单位 &lt;code&gt;Unit&lt;/code&gt;、中文备注 &lt;code&gt;Remark&lt;/code&gt; 等）的场景下使用。&lt;/li&gt;
&lt;/ul&gt;
&lt;hr /&gt;
&lt;h5&gt;4. Bool 类型参数（FuncChk）读取详解&lt;/h5&gt;
&lt;p&gt;&lt;code&gt;FuncChk&lt;/code&gt; 枚举定义了所有功能开关（复选框），对应 Excel 配置表中的&quot;功能使能&quot;列。读取 Bool 参数有三种方式：&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;
&lt;p&gt;&lt;strong&gt;方式1：&lt;code&gt;mGlobal.FuncCheck&lt;/code&gt;（推荐，最简洁）&lt;/strong&gt;&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;// 源码：return mParList[(short)func].CheckSts;
bool enableScan = mGlobal.FuncCheck(FuncChk.启用扫码);
bool enableCCD = mGlobal.FuncCheck(FuncChk.启用CCD);
bool enablePDCA = mGlobal.FuncCheck(FuncChk.启用_PDCA);
bool blockLeft = mGlobal.FuncCheck(FuncChk.屏蔽左轴);
&lt;/code&gt;&lt;/pre&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;&lt;strong&gt;方式2：&lt;code&gt;mFunction.GetParValue&amp;lt;bool&amp;gt;&lt;/code&gt;（泛型，类型安全）&lt;/strong&gt;&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;bool enableScan = mFunction.GetParValue&amp;lt;bool&amp;gt;(FuncChk.启用扫码);
bool blockLeft = mFunction.GetParValue&amp;lt;bool&amp;gt;(FuncChk.屏蔽左轴);
&lt;/code&gt;&lt;/pre&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;&lt;strong&gt;方式3：&lt;code&gt;mParList[].CheckSts&lt;/code&gt;（直接数组访问，最快）&lt;/strong&gt;&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;// 带安全检查的写法（推荐用于属性初始化器或字段声明中）
protected override bool 是否屏蔽 =&amp;gt;
    mParList != null
    &amp;amp;&amp;amp; mParList.Length &amp;gt; (int)FuncChk.屏蔽右轴
    &amp;amp;&amp;amp; mParList[(int)FuncChk.屏蔽右轴].CheckSts;

// 不带安全检查的写法（用于方法内部，确保 mParList 已初始化）
bool enableScan = mParList[(int)FuncChk.启用扫码].CheckSts;
&lt;/code&gt;&lt;/pre&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;hr /&gt;
&lt;blockquote&gt;
&lt;p&gt;[!WARNING]
&lt;strong&gt;参数类型不匹配导致的读取残留值漏洞 (Data vs DataInt)&lt;/strong&gt;&lt;/p&gt;
&lt;p&gt;在 Excel (如 &lt;code&gt;UserPar.xlsx&lt;/code&gt;) 中定义参数时，列 &lt;code&gt;Category&lt;/code&gt; 如果被指定为 &lt;code&gt;D&lt;/code&gt; (即 Double 浮点型)，则用户配置的参数值会存储在 XML 数据库 of &lt;code&gt;&amp;lt;Data&amp;gt;&lt;/code&gt; 节点中。此时，&lt;code&gt;&amp;lt;DataInt&amp;gt;&lt;/code&gt; 节点在 XML 中不会更新，而是保留其默认的初始/残留值 (例如 &lt;code&gt;333&lt;/code&gt; 或 &lt;code&gt;200&lt;/code&gt; 等)。&lt;/p&gt;
&lt;p&gt;如果在 C# 代码中错误地使用 &lt;code&gt;mFunction.GetParValue&amp;lt;int&amp;gt;(UserPar.某参数)&lt;/code&gt; 去读取该参数，系统会因为泛型类型是 &lt;code&gt;int&lt;/code&gt; 而直接返回 &lt;code&gt;&amp;lt;DataInt&amp;gt;&lt;/code&gt; 中的残留默认值，而不是经过正确四舍五入或截断的 &lt;code&gt;&amp;lt;Data&amp;gt;&lt;/code&gt; 值。这会导致读取出来的参数与界面设置完全不符（例如设置了 10 却获取到 7，或者设置了 5 却获取到 333）。&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;正确做法&lt;/strong&gt;：&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;strong&gt;如果 Excel 中参数类型是 &lt;code&gt;D&lt;/code&gt; (Double)&lt;/strong&gt;：必须使用 &lt;code&gt;(int)mFunction.GetParValue&amp;lt;double&amp;gt;(UserPar.某参数)&lt;/code&gt; 先读取为 double，然后再强转为 int。&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;如果 Excel 中参数类型是 &lt;code&gt;I&lt;/code&gt; (Int)&lt;/strong&gt;：可以直接使用 &lt;code&gt;mFunction.GetParValue&amp;lt;int&amp;gt;(UserPar.某参数)&lt;/code&gt;。&lt;/li&gt;
&lt;li&gt;在编写代码前，必须仔细核对 Excel 配置文件中参数的类型定义（&lt;code&gt;Category&lt;/code&gt; 或 &lt;code&gt;&amp;lt;ChkCategory&amp;gt;&lt;/code&gt; 的值）。&lt;/li&gt;
&lt;/ul&gt;
&lt;/blockquote&gt;
&lt;h5&gt;5. &lt;code&gt;mParList&lt;/code&gt; 内存数据结构&lt;/h5&gt;
&lt;p&gt;&lt;code&gt;mParList[index]&lt;/code&gt; 的每个元素包含以下核心字段：&lt;/p&gt;
&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;字段&lt;/th&gt;
&lt;th&gt;类型&lt;/th&gt;
&lt;th&gt;说明&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;DataDbl&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;double&lt;/td&gt;
&lt;td&gt;数值参数值&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;DataInt&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;int&lt;/td&gt;
&lt;td&gt;整数参数值&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;DataStr&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;string&lt;/td&gt;
&lt;td&gt;字符串参数值&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;CheckSts&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;bool&lt;/td&gt;
&lt;td&gt;功能开关状态（对应 FuncChk）&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;LimitUp&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;double&lt;/td&gt;
&lt;td&gt;参数上限&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;LimitDown&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;double&lt;/td&gt;
&lt;td&gt;参数下限&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;Unit&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;string&lt;/td&gt;
&lt;td&gt;单位&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;Remark&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;string&lt;/td&gt;
&lt;td&gt;备注&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;
&lt;hr /&gt;
&lt;h3&gt;2.3 Excel 参数配置与 XML 数据库转换映射规则&lt;/h3&gt;
&lt;p&gt;参数定义的元数据源头位于 Excel 文件，而运行库在运行时会以 XML 文件作为直接持久化数据库（保证无 Office 环境下也能读写）。&lt;/p&gt;
&lt;h4&gt;2.3.1 Excel 参数模板列定义 (&lt;code&gt;ParList.xlsx&lt;/code&gt;)&lt;/h4&gt;
&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;列号&lt;/th&gt;
&lt;th&gt;字段名称&lt;/th&gt;
&lt;th&gt;对应 XML 元素&lt;/th&gt;
&lt;th&gt;作用说明&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;A&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;&lt;code&gt;Index&lt;/code&gt; / &lt;code&gt;ParNo&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;节点索引&lt;/td&gt;
&lt;td&gt;参数在全局数组 &lt;code&gt;mParList&lt;/code&gt; 中的唯一整型索引&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;B&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;&lt;code&gt;NameCh&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;&lt;code&gt;&amp;lt;RemarkCn&amp;gt;&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;在界面 PropertyGrid 显示的中文参数名称&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;C&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;&lt;code&gt;NameEn&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;&lt;code&gt;&amp;lt;RemarkEn&amp;gt;&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;界面切换至英文版时显示的英文参数名称&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;D&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;&lt;code&gt;Category&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;&lt;code&gt;&amp;lt;Category&amp;gt;&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;属性分组名称，PropertyGrid 会依此折叠分类&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;E&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;&lt;code&gt;LimitUp&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;&lt;code&gt;&amp;lt;LimitUp&amp;gt;&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;参数输入的上限值（浮点数）&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;F&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;&lt;code&gt;LimitDown&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;&lt;code&gt;&amp;lt;LimitDown&amp;gt;&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;参数输入的下限值（浮点数）&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;G&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;&lt;code&gt;Unit&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;&lt;code&gt;&amp;lt;Unit&amp;gt;&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;参数物理单位（如 mm, ms, pcs）&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;H-K&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;&lt;code&gt;ChkRemark&lt;/code&gt; 等&lt;/td&gt;
&lt;td&gt;&lt;code&gt;&amp;lt;ChkRemarkCn&amp;gt;&lt;/code&gt; 等&lt;/td&gt;
&lt;td&gt;用于定义复选框的中文名、英文名及复选框分组类别 &lt;code&gt;B&lt;/code&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;
&lt;h4&gt;2.3.2 自动同步生成逻辑 (&lt;code&gt;AutoSetup&lt;/code&gt;)&lt;/h4&gt;
&lt;p&gt;当在参数维护页面执行“AutoSetup”（自动建库）时，框架会读取 &lt;code&gt;ParList.xlsx&lt;/code&gt;，并将每行数据转换为 &lt;code&gt;ParData.xml&lt;/code&gt; 中的结构。如果 XML 中已有数据，则会无损重写元数据，并保持原有参数的当前值（&lt;code&gt;&amp;lt;Data&amp;gt;&lt;/code&gt;）不变。&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;&amp;lt;!-- ParData.xml 单个参数节点结构示例 --&amp;gt;
&amp;lt;mNewPar&amp;gt;
  &amp;lt;RemarkCn&amp;gt;右轴取料X补偿&amp;lt;/RemarkCn&amp;gt;
  &amp;lt;RemarkEn&amp;gt;Right Axis Pick X Offset&amp;lt;/RemarkEn&amp;gt;
  &amp;lt;Category&amp;gt;右机械手参数&amp;lt;/Category&amp;gt;
  &amp;lt;LimitUp&amp;gt;5&amp;lt;/LimitUp&amp;gt;
  &amp;lt;LimitDown&amp;gt;-5&amp;lt;/LimitDown&amp;gt;
  &amp;lt;Unit&amp;gt;mm&amp;lt;/Unit&amp;gt;
  &amp;lt;Data&amp;gt;0.125&amp;lt;/Data&amp;gt;      &amp;lt;!-- 当前浮点值，代码通过 Data 或 GetParValue 获取 --&amp;gt;
  &amp;lt;DataInt&amp;gt;0&amp;lt;/DataInt&amp;gt;    &amp;lt;!-- 整型值 --&amp;gt;
  &amp;lt;DataStr /&amp;gt;             &amp;lt;!-- 字符串值 --&amp;gt;
  &amp;lt;CheckSts&amp;gt;false&amp;lt;/CheckSts&amp;gt; &amp;lt;!-- 复选框状态，仅在 100+ 索引使能参数中生效 --&amp;gt;
  &amp;lt;ChkRemarkCn&amp;gt;备用使能&amp;lt;/ChkRemarkCn&amp;gt;
&amp;lt;/mNewPar&amp;gt;
&lt;/code&gt;&lt;/pre&gt;
&lt;hr /&gt;
&lt;h3&gt;2.4 UI 界面参数标签页 (TabPage) 绑定关系&lt;/h3&gt;
&lt;p&gt;参数配置窗口 &lt;code&gt;Frm_Par&lt;/code&gt; 包含多个 TabPage，它们展示和修改底层同一个全局参数数据源 &lt;code&gt;mFunction.mParList&lt;/code&gt;：&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;  【数据源头】
  Excel 模板 (ParList.xlsx等) 
         │
         │ AutoSetup 自动建库同步
         ▼
  【持久化数据库】
  XML 配置文件 (ParData.xml等)
         ▲
         │ 系统启动读取 / 运行时保存
         ▼
  【内存数据源】
  全局参数数组 (mFunction.mParList等)
         │
         ├─────── 映射 0-49, 150-179 ───────► TabPage 1: PropertyGrid 属性网格
         │
         ├─────── 映射 0-55 ────────────────► TabPage 2: TextBox 文本框组
         │
         └─────── 映射 100-131 ─────────────► TabPage 4: Checkbox 使能页
&lt;/code&gt;&lt;/pre&gt;
&lt;h4&gt;2.4.1 TabPage 1: 属性网格配置 (PropertyGrid)&lt;/h4&gt;
&lt;ul&gt;
&lt;li&gt;&lt;strong&gt;绑定控件&lt;/strong&gt;：由两个 &lt;code&gt;Zcm.PropertyParS&lt;/code&gt; 构成：
&lt;ul&gt;
&lt;li&gt;&lt;code&gt;propertyParS2&lt;/code&gt;：&lt;code&gt;StartIndex = 0&lt;/code&gt;，&lt;code&gt;ParNumber = 50&lt;/code&gt;。绑定内存数组 &lt;code&gt;mParList&lt;/code&gt; 的 &lt;strong&gt;0~49 号参数&lt;/strong&gt;。&lt;/li&gt;
&lt;li&gt;&lt;code&gt;propertyParS1&lt;/code&gt;：&lt;code&gt;StartIndex = 150&lt;/code&gt;，&lt;code&gt;ParNumber = 30&lt;/code&gt;。绑定内存数组 &lt;code&gt;mParList&lt;/code&gt; 的 &lt;strong&gt;150~179 号参数&lt;/strong&gt;。&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;特性&lt;/strong&gt;：该控件提供类似于 PropertyGrid 的界面，自动读取 XML 里的参数分类（Category）将参数折叠展示，并提供中文描述（RemarkCn）、单位（Unit）及当前数值（Data）。&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;权限保护&lt;/strong&gt;：受系统登录机制保护。在未插入读卡器或未通过 &lt;code&gt;Frm_Login&lt;/code&gt; 进行管理员/工程师登录时，&lt;code&gt;Panel_ParList.Enabled&lt;/code&gt; 设为 &lt;code&gt;false&lt;/code&gt;，防止未授权修改。&lt;/li&gt;
&lt;/ul&gt;
&lt;h4&gt;2.4.2 TabPage 2: 文本框组参数配置&lt;/h4&gt;
&lt;ul&gt;
&lt;li&gt;&lt;strong&gt;绑定控件&lt;/strong&gt;：由 7 个 &lt;code&gt;Zcm.UserParS&lt;/code&gt; 控件构成（&lt;code&gt;userParS1&lt;/code&gt; 到 &lt;code&gt;userParS7&lt;/code&gt;），每个控件内部包含 8 个 TextBox 和对应的描述 Label。&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;映射索引&lt;/strong&gt;：
&lt;ul&gt;
&lt;li&gt;&lt;code&gt;userParS1&lt;/code&gt; (0~7), &lt;code&gt;userParS2&lt;/code&gt; (8~15), &lt;code&gt;userParS3&lt;/code&gt; (16~23), &lt;code&gt;userParS4&lt;/code&gt; (24~31), &lt;code&gt;userParS5&lt;/code&gt; (32~39), &lt;code&gt;userParS6&lt;/code&gt; (40~47), &lt;code&gt;userParS7&lt;/code&gt; (48~55)。&lt;/li&gt;
&lt;li&gt;覆盖 &lt;strong&gt;0~55 号参数&lt;/strong&gt;，与 TabPage 1 的前 56 个参数完全对应，但展现形式为 TextBox。&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;h4&gt;2.4.3 TabPage 4: Check Tab (功能使能复选框页)&lt;/h4&gt;
&lt;ul&gt;
&lt;li&gt;&lt;strong&gt;绑定控件&lt;/strong&gt;：由 4 个 &lt;code&gt;Zcm.UserChk&lt;/code&gt; 控件构成（&lt;code&gt;userChk1&lt;/code&gt; 到 &lt;code&gt;userChk4&lt;/code&gt;），每个控件包含 8 个 CheckBox 复选框。&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;映射索引&lt;/strong&gt;：
&lt;ul&gt;
&lt;li&gt;&lt;code&gt;userChk1&lt;/code&gt; (100~107), &lt;code&gt;userChk2&lt;/code&gt; (108~115), &lt;code&gt;userChk3&lt;/code&gt; (116~123), &lt;code&gt;userChk4&lt;/code&gt; (124~131)。&lt;/li&gt;
&lt;li&gt;代表布尔开关，在程序中通过 &lt;code&gt;mParList[Index].CheckSts&lt;/code&gt; 读取状态（&lt;code&gt;true&lt;/code&gt;/&lt;code&gt;false&lt;/code&gt;），对应 &lt;code&gt;FuncChk&lt;/code&gt; 枚举。&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;hr /&gt;
&lt;h3&gt;2.5 HMI UI 诊断监控与 I/O 硬件的交互联系&lt;/h3&gt;
&lt;p&gt;配置完毕的 I/O 映射在软件 UI 界面上有极佳的关联性：&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;&lt;strong&gt;监控界面动态渲染&lt;/strong&gt;：
当进入软件“IO监控”页面时，系统会读取 &lt;code&gt;ParInput.xml&lt;/code&gt; 和 &lt;code&gt;ParOutput.xml&lt;/code&gt;。如果有项的 &lt;code&gt;Name&lt;/code&gt; 不为空，UI 会动态绘制出一个按钮或圆形指示灯。这免去了在界面上手动添加控件的工作。&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;点动控制 (DO 手动调试)&lt;/strong&gt;：
在“手动调试”或“IO监控”界面点击某个输出按钮时，系统会截获该按钮绑定的 &lt;code&gt;OutNo&lt;/code&gt; 逻辑编号。在手动模式下，系统执行 &lt;code&gt;WriteDo(编号, 1)&lt;/code&gt;（底层硬件操作是在配置卡号和序号对应的引脚输出高电平），且按钮变绿。&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;输入反馈 (DI 实时点亮)&lt;/strong&gt;：
系统会在后台开启一个 10ms 级别的扫描线程，高频读取 &lt;code&gt;ParInput.xml&lt;/code&gt; 里配置的所有卡号 and 引脚状态。一旦传感器触发（引脚变高电平），UI 监控上对应的指示灯会点亮成绿色；离开后熄灭。这为电气调试和故障排查提供了极其便捷的可视化支持。&lt;/li&gt;
&lt;/ol&gt;
&lt;hr /&gt;
&lt;h3&gt;2.6 输入限制与合法性校验逻辑&lt;/h3&gt;
&lt;p&gt;在参数页面输入新数值时，UI 控件（如 PropertyGrid 或 TextBox）会自动拦截非法输入：&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;&lt;strong&gt;类型校验&lt;/strong&gt;：仅允许输入与参数类型兼容的数值字符，输入字母会自动过滤或恢复旧值。&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;上下限拦截&lt;/strong&gt;：当输入的值 $V &amp;gt; LimitUp$ 或 $V &amp;lt; LimitDown$ 时，界面会弹出警告对话框，或直接在失去焦点时将数值限制在边界值，拒绝写入 XML，保证设备动作的绝对安全。即：&lt;strong&gt;系统会在输入时硬性进行拦截校验，完全禁止且无法输入超过设定的上下限范围的数值。&lt;/strong&gt;&lt;/li&gt;
&lt;/ol&gt;
&lt;hr /&gt;
&lt;h3&gt;2.7 功能使能复选框在业务代码中的跳步与屏蔽机制&lt;/h3&gt;
&lt;p&gt;在 Check Tab 中配置的布尔开关（&lt;code&gt;FuncChk&lt;/code&gt;）直接参与 &lt;code&gt;Tasks&lt;/code&gt; 中的时序控制。典型的屏蔽策略如下：&lt;/p&gt;
&lt;h4&gt;2.7.1 扫码功能屏蔽 (&lt;code&gt;Enable_scanning_code&lt;/code&gt; 索引 102)&lt;/h4&gt;
&lt;p&gt;在入料扫码站中，若未勾选此功能，则不触发扫码指令，程序自动跳过等待结果状态。&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;case (int)步序.等到位信号:
    if (mGlobal.ReadDi_Bool(InNo.流线1到位信号))
    {
        if (mParList[(int)FuncChk.Enable_scanning_code].CheckSts)
        {
            SetStep(ref StaInfo, (int)步序.电机停扫码, true); // 正常走扫码流程
        }
        else
        {
            AddLog(&quot;扫码使能关闭，跳过扫码，直接进入放行准备&quot;, LogsType.Auto, 20, true);
            SetStep(ref StaInfo, (int)步序.关光源, true); // 跳步
        }
    }
    break;
&lt;/code&gt;&lt;/pre&gt;
&lt;h4&gt;2.7.2 机械手关闭屏蔽 (&lt;code&gt;Block_leftRobot&lt;/code&gt; 索引 116 / &lt;code&gt;Block_rightRobot&lt;/code&gt; 索引 117)&lt;/h4&gt;
&lt;p&gt;在机械手基类的 &lt;code&gt;AutoRun()&lt;/code&gt; 起始位置进行拦截。若被屏蔽，则将所有输出复位，当检测到工作启动交互信号后，立刻返回工作完成标志，既不发生任何物理运动，也不阻塞流水线生产。&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;public override void AutoRun()
{
    if (是否屏蔽) // 从 Block_leftRobot 或 Block_rightRobot 的 CheckSts 获取
    {
        // 1. 安全复位所有物理 DO 输出
        mGlobal.mDoReset(CCD光源触发信号);
        mGlobal.mDoReset(电批吸真空信号);
        mGlobal.mDoReset(电批破真空信号);
        mGlobal.mDoReset(电批启动信号);

        // 2. 检测到握手信号时，直接模拟完成，不执行动作
        if (GetTasksInteraction(启动触发标志, false) == true)
        {
            AddLog(&quot;机械手已屏蔽，跳过拧紧流程，直接发送完成标志&quot;, LogsType.Auto, StaInfo.StepIdx, true);
            SetTasksInteractionTrue(完成标志); // 提前置位工作完成
        }
        SetStep(ref StaInfo, (int)步序.等待启动信号, false);
        return;
    }
    // ... 正常流程 ...
}
&lt;/code&gt;&lt;/pre&gt;
&lt;h4&gt;2.7.3 相机跳步逻辑 (&lt;code&gt;Enable_CCD&lt;/code&gt; 索引 104 - 启用相机功能)&lt;/h4&gt;
&lt;p&gt;用于屏蔽视觉定位，直接以零偏差移至打螺丝点。&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;case (int)步序.等待启动信号:
    if (GetTasksInteraction(启动触发标志, false) == true)
    {
        if (是否启用相机) // 由 Enable_CCD 启用相机复选框控制
        {
            SetStep(ref StaInfo, (int)步序.移至拍照位置, true);
        }
        else
        {
            AddLog(&quot;相机功能被关闭，跳过拍照，直接使用0偏差移至工作点&quot;, LogsType.Auto, 12, true);
            纠偏X = 0 + 补偿X;
            纠偏Y = 0 + 补偿Y;
            纠偏R = 补偿R;
            SetStep(ref StaInfo, (int)步序.移动至电批工作点, true); // 直接去执行拧螺丝
        }
    }
    break;
&lt;/code&gt;&lt;/pre&gt;
&lt;hr /&gt;
&lt;h2&gt;3. 自动化流程开发 SOP&lt;/h2&gt;
&lt;p&gt;所有工站控制类必须遵循本节定义的生命周期函数与状态机规范进行开发。&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;[系统装载] --&amp;gt; Initialize() 注册绑定 -&amp;gt; [复位就绪] --&amp;gt; Homing() 机械初始化 -&amp;gt; Ready() 状态检查 
                                                                             |
[循环执行] &amp;lt;--------------------------------- State = RUNNING &amp;lt;---------------+
   |
   +--&amp;gt; AutoRun() 状态机流控制 (StepIdx) -&amp;gt; SetStep() 转换状态 -&amp;gt; 出现异常 --&amp;gt; State = ALARM
&lt;/code&gt;&lt;/pre&gt;
&lt;h3&gt;3.1 继承关系与生命周期函数&lt;/h3&gt;
&lt;p&gt;每个独立工站必须继承自 &lt;code&gt;mWorkShare&lt;/code&gt;。框架在启动及运行过程中，会依次调用以下生命周期函数：&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;&lt;strong&gt;&lt;code&gt;Initialize()&lt;/code&gt; (初始化阶段)&lt;/strong&gt;：
&lt;ul&gt;
&lt;li&gt;在程序启动装载参数后执行一次。&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;职责&lt;/strong&gt;：设置 &lt;code&gt;TaskID&lt;/code&gt;、向工作管理器 &lt;code&gt;WkManager&lt;/code&gt; 注册当前工站、绑定关联的流水线段、绑定 HMI 状态显示控件、指定涉及的控制轴与关键 I/O 输出映射。&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;&lt;code&gt;Homing()&lt;/code&gt; (复位阶段)&lt;/strong&gt;：
&lt;ul&gt;
&lt;li&gt;在用户点击 UI “Reset” 时，系统创建新线程并发调用所有工站的 &lt;code&gt;Homing&lt;/code&gt; 方法。&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;职责&lt;/strong&gt;：关闭当前工站关联的临时 DO（如真空、气缸触发），设置气缸指示灯及电批安全回缩，检查轴状态是否满足安全位置，最终将 &lt;code&gt;StaHomeOK&lt;/code&gt; 标志设为 &lt;code&gt;true&lt;/code&gt;。&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;&lt;code&gt;Ready()&lt;/code&gt; (运行前检查)&lt;/strong&gt;：
&lt;ul&gt;
&lt;li&gt;当用户在复位完成后点击 “Start” 启动自动运行时执行。&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;职责&lt;/strong&gt;：确认没有急停或报警，确认轴已回零 OK，并最终返回 &lt;code&gt;true&lt;/code&gt;，系统才会将状态机置为 &lt;code&gt;RUNNING&lt;/code&gt;。&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;&lt;code&gt;AutoRun()&lt;/code&gt; (循环运行阶段)&lt;/strong&gt;：
&lt;ul&gt;
&lt;li&gt;&lt;strong&gt;核心执行体&lt;/strong&gt;。当 &lt;code&gt;State == State.RUNNING&lt;/code&gt; 时，后台调度引擎在后台线程中以无延迟的高频 &lt;code&gt;while(true)&lt;/code&gt; 循环调用此方法。&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;职责&lt;/strong&gt;：通过状态机选择结构，判定物理信号与网络指令，驱动硬件动作并流转步序。&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;/ol&gt;
&lt;hr /&gt;
&lt;h3&gt;3.2 自动运行状态机开发模板&lt;/h3&gt;
&lt;p&gt;一个标准的工站逻辑开发结构如下所示：&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;using CoreFunction;
using System;
using System.Threading;
using static CoreFunction.mFunction;
using static ParName.EnumName;

namespace BoTech
{
    public class Task01_DemoStation : mWorkShare
    {
        // 1. 定义工站独立的步骤枚举
        private enum 步序 : int
        {
            启动电机 = 10,
            等到位信号 = 20,
            气缸动作等待 = 30,
            做交互工作 = 40,
            放行退出 = 50,
            异常 = 9000,
        }

        private static Task01_DemoStation mInstance;
        public static Task01_DemoStation Instance =&amp;gt; mInstance ?? (mInstance = new Task01_DemoStation());

        public override void Initialize()
        {
            TaskID = 99; // 唯一ID
            WkManager.BindStation(TaskID, &quot;DemoStation&quot;, this);
            this.BindConv(1, null); // 绑定流水线1
            this.BindStationRun(Frm_Task.Instance.stationRun00); // 绑定界面指示块
            this.SetOutputMaps(new ValueType[] { OutNo.流线1阻挡气缸 }); // 绑定需要监控的输出
            
            _Logs = new LogsHelper.cLogs(TaskName, TaskID);
            base.Initialize();
        }

        public override void Homing()
        {
            base.Homing();
            // 复位物理信号
            mGlobal.mDoSet(OutNo.流线1阻挡气缸);
            StaHomeOK = true;
        }

        public override void AutoRun()
        {
            // 2. 状态机 Step 选择器
            switch (StaInfo.StepIdx)
            {
                case (int)步序.启动电机:
                    // 系统停止或急停时，重置输出，退回等待运行状态
                    if (mFunction.IsSysStop || State == mFunction.State.STOPED)
                    {
                        mGlobal.mDoReset(OutNo.流线1_扫码滚筒电机M0);
                        State = State.WAITRUN;
                        SetStep(ref StaInfo, 0, true);
                        break;
                    }
                    mGlobal.mDoSet(OutNo.流线1_扫码滚筒电机M0);
                    AddLog(&quot;启动电机，等待物料&quot;, LogsType.Auto, (int)步序.启动电机, true);
                    SetStep(ref StaInfo, (int)步序.等到位信号, true);
                    break;

                case (int)步序.等到位信号:
                    if (mGlobal.ReadDi_Bool(InNo.流线1到位信号))
                    {
                        mGlobal.mDoReset(OutNo.流线1_扫码滚筒电机M0);
                        SetStep(ref StaInfo, (int)步序.气缸动作等待, true);
                    }
                    break;

                case (int)步序.气缸动作等待:
                    // 3. 阻塞式动作到位黄金 API
                    mDoDiWaitDone(OutNo.流线1阻挡气缸, 0, InNo.流线1阻挡缩回信号, 1, 10, 3000, true);
                    SetStep(ref StaInfo, (int)步序.做交互工作, true);
                    break;

                case (int)步序.做交互工作:
                    // 重置超时起始时间
                    mFunction.ConveyorData[MainConvId].StartTime = mFunction.GetTickCount();
                    SetTasksInteractionTrue(TasksInteraction.组装允许机械手_标志);
                    
                    // 等待软握手
                    if (WaitTaskInteractionTrue(TasksInteraction.右轴螺丝工作完成_标志, 5000, true, true))
                    {
                        SetStep(ref StaInfo, (int)步序.放行退出, true);
                    }
                    else
                    {
                        SetStep(ref StaInfo, (int)步序.异常, true); // 超时去异常
                    }
                    break;

                case (int)步序.放行退出:
                    mGlobal.mDoSet(OutNo.流线1阻挡气缸);
                    SetStep(ref StaInfo, (int)步序.启动电机, true);
                    break;

                case (int)步序.异常:
                    AddLog(&quot;工站异常发生！&quot;, LogsType.ErrorCode, 9000, true);
                    mGlobal.mDoReset(OutNo.流线1_扫码滚筒电机M0);
                    SetStep(ref StaInfo, 0, true);
                    State = State.ALARM; // 抛出系统报警，红灯亮起，蜂鸣器鸣叫
                    break;
            }
        }
    }
}
&lt;/code&gt;&lt;/pre&gt;
&lt;hr /&gt;
&lt;h3&gt;3.3 步序控制与更新机制 (&lt;code&gt;SetStep&lt;/code&gt;)&lt;/h3&gt;
&lt;ul&gt;
&lt;li&gt;&lt;strong&gt;方法原型&lt;/strong&gt;：&lt;code&gt;protected void SetStep(ref StationInfo StaInfo, int NextStepIdx, bool IsLog = true)&lt;/code&gt;&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;机制&lt;/strong&gt;：
&lt;ul&gt;
&lt;li&gt;该方法接收当前工站的 &lt;code&gt;StaInfo&lt;/code&gt; 引用，将其内部的 &lt;code&gt;StepIdx&lt;/code&gt; 重置为 &lt;code&gt;NextStepIdx&lt;/code&gt;。&lt;/li&gt;
&lt;li&gt;&lt;code&gt;IsLog&lt;/code&gt; 如果为 &lt;code&gt;true&lt;/code&gt;，系统会自动将跳转步序记录到日志流中。&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;必须注意&lt;/strong&gt;：&lt;code&gt;SetStep&lt;/code&gt; 改变步序后，线程会等下一次 &lt;code&gt;AutoRun&lt;/code&gt; 循环触发才进入对应 Case，因此如果有在当前 tick 必须立刻退出的语句，需在其后紧跟 &lt;code&gt;break;&lt;/code&gt; 或 &lt;code&gt;return;&lt;/code&gt;。&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;hr /&gt;
&lt;h3&gt;3.4 超时计时器重置与防虚警防呆逻辑&lt;/h3&gt;
&lt;p&gt;许多误报警都是由于工位等待过程中的耗时与动作时间重叠造成的。&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;strong&gt;错误模式&lt;/strong&gt;：载具一流入工站就设定了 &lt;code&gt;ConveyorData[MainConvId].StartTime&lt;/code&gt;，随后进行侧夹、侧推等一系列动作，等真正打螺丝或扫码开始时，计时器已累积了十几秒，极易在随后的阻塞等待中触发超时（例如扫码设定的 3 秒或螺丝打孔设定的 60 秒限制）。&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;标准重构规范&lt;/strong&gt;：在进入容易发生长延时的步骤（如发送 Socket 扫码指令、发送机械手启动信号）的&lt;strong&gt;前一刻&lt;/strong&gt;，显式重置计时起点：&lt;pre&gt;&lt;code&gt;mFunction.ConveyorData[MainConvId].StartTime = mFunction.GetTickCount();
&lt;/code&gt;&lt;/pre&gt;
这样可以确保超时判定时间（如 &lt;code&gt;OverTime&lt;/code&gt;）纯粹计算该硬件动作本身的响应时间，彻底消除由于物流积压或辅助夹具动作缓慢引起的虚警。&lt;/li&gt;
&lt;/ul&gt;
&lt;h3&gt;3.5 流水线（Conveyor）与工站绑定逻辑&lt;/h3&gt;
&lt;p&gt;BoTech 框架在多工站流线型设备开发中，采用了流水线段（Conveyor Segment）与工站任务（Task）松耦合绑定的设计模式：&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;&lt;strong&gt;流水线配置装载&lt;/strong&gt;：
&lt;ul&gt;
&lt;li&gt;软件启动时，&lt;code&gt;Setup_Load.cs&lt;/code&gt; 会通过 &lt;code&gt;mFunction.ReadXml&lt;/code&gt; 读取 &lt;code&gt;bin\Debug\RBF\Conveyor.xml&lt;/code&gt; 配置文件，将其装载到全局流水线对象数组 &lt;code&gt;mFunction.ConveyorData&lt;/code&gt; 中。&lt;/li&gt;
&lt;li&gt;流水线按照物理分段（段1、段2、段3等）进行逻辑编号管理。&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;工站的流线段绑定&lt;/strong&gt;：
&lt;ul&gt;
&lt;li&gt;每个继承自 &lt;code&gt;mWorkShare&lt;/code&gt; 的工站（如 &lt;code&gt;Task01_入料扫码站&lt;/code&gt;、&lt;code&gt;Task02_螺丝站&lt;/code&gt;）在 &lt;code&gt;Initialize&lt;/code&gt; 方法中，都会显式调用绑定流线函数：&lt;pre&gt;&lt;code&gt;this.BindConv(short ConvID, short[] StateConvIds);
&lt;/code&gt;&lt;/pre&gt;
例如，螺丝工位调用 &lt;code&gt;this.BindConv(2, null)&lt;/code&gt;，表示该工站主动作序列被绑定至 &lt;strong&gt;流线段2&lt;/strong&gt; 上。&lt;/li&gt;
&lt;li&gt;绑定后，工站可以通过继承获得的 &lt;code&gt;MainConvId&lt;/code&gt;（此处为 &lt;code&gt;2&lt;/code&gt;）直观地索引全局流线 &lt;code&gt;mFunction.ConveyorData[MainConvId]&lt;/code&gt; 并管理其状态。&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;流线动作线程与当站处理交互&lt;/strong&gt;：
&lt;ul&gt;
&lt;li&gt;每一段流水线的马达启停、气缸阻挡以及产品流入到位，都由 &lt;code&gt;A0.Conveyors.cs&lt;/code&gt; 中的 &lt;code&gt;nConvEvent&lt;/code&gt; 独立状态机在后台高频轮询控制。&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;物料流入与锁定&lt;/strong&gt;：当产品流入到位后，流水线状态机将该段流线的自定义状态 &lt;code&gt;CustStatus&lt;/code&gt; 修改为 &lt;code&gt;&quot;WAITING_FOR_ASSEMBLY&quot;&lt;/code&gt;（等待装配/工作开始），并在此阻塞等待。&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;工站唤醒与交付&lt;/strong&gt;：工站自身的 &lt;code&gt;AutoRun()&lt;/code&gt; 状态机在检测到位信号和 &lt;code&gt;&quot;WAITING_FOR_ASSEMBLY&quot;&lt;/code&gt; 后，将状态机切入工作流程，将 &lt;code&gt;CustStatus&lt;/code&gt; 设为 &lt;code&gt;&quot;工作中&quot;&lt;/code&gt; 或 &lt;code&gt;&quot;处理中&quot;&lt;/code&gt;。工作完成后，工站将 &lt;code&gt;CustStatus&lt;/code&gt; 修改为 &lt;code&gt;&quot;ASSEMBLY_COMPLETED&quot;&lt;/code&gt;（装配完成/工作完毕）。&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;流出与放行&lt;/strong&gt;：流水线状态机捕获到 &lt;code&gt;&quot;ASSEMBLY_COMPLETED&quot;&lt;/code&gt; 后，自动执行降顶升、缩阻挡动作，并开启滚筒电机将产品放行输送至下一段。&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;/ol&gt;
&lt;h4&gt;3.5.1 流线气缸的去代码化与参数化配置实现&lt;/h4&gt;
&lt;p&gt;在 BoTech 框架中，流水线段的阻挡气缸、顶升气缸等气缸控制，在具体的工站任务中完全实现了&lt;strong&gt;去代码化&lt;/strong&gt;。工位任务不需要直接编写操作气缸的代码（如直接调用 &lt;code&gt;DoSet&lt;/code&gt; 或 &lt;code&gt;DoReset&lt;/code&gt;），而是将这些控制完全委托给&lt;strong&gt;后台流水线状态机&lt;/strong&gt;，并通过 Excel 参数文件进行物理通道的映射绑定。&lt;/p&gt;
&lt;h5&gt;1. 配置映射关系 (以 Conveyor.xlsx 为主)&lt;/h5&gt;
&lt;p&gt;在 &lt;code&gt;ParXlsx\Conveyor.xlsx&lt;/code&gt; 配置中，每一段流水线（Conveyor Segment）都定义了其关联的物理 I/O 通道参数：&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;strong&gt;阻挡气缸输出编号&lt;/strong&gt;：控制流线阻挡气缸电磁阀的全局 DO 索引（在 XML 中序列化为 &lt;code&gt;&amp;lt;阻挡气缸输出编号&amp;gt;&lt;/code&gt;）。&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;阻挡气缸动点/原点信号&lt;/strong&gt;：阻挡气缸反馈感应器的全局 DI 索引（在 XML 中序列化为 &lt;code&gt;&amp;lt;阻挡气缸动点信号&amp;gt;&lt;/code&gt; / &lt;code&gt;&amp;lt;阻挡气缸原点信号&amp;gt;&lt;/code&gt;）。&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;到位感应信号 / 流出感应信号&lt;/strong&gt;：载具到位与流出的全局 DI 索引。&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;本台设备可接收载具 / 下台设备可接收载具&lt;/strong&gt;：用于与前/后机或前后段进行握手通信的 I/O 索引。&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;软件启动时，&lt;code&gt;Conveyor.xlsx&lt;/code&gt; 经转换后生成 &lt;code&gt;Conveyor.xml&lt;/code&gt;，并由框架反序列化装载到全局的流水线内存数组中（框架基类使用 &lt;code&gt;mFunction.ConveyorData[]&lt;/code&gt;，部分项目中本地化为 &lt;code&gt;mFunction.流水线[]&lt;/code&gt;）。&lt;/p&gt;
&lt;h5&gt;2. 后台状态机自动驱动流程&lt;/h5&gt;
&lt;p&gt;后台的流线控制引擎（&lt;code&gt;AutoConv&lt;/code&gt; 状态机类，运行于独立高频扫描线程中）会根据流线的状态步骤（&lt;code&gt;StaStep&lt;/code&gt;）自动进行硬件层面的气缸和电机控制：&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;
&lt;p&gt;&lt;strong&gt;载具流入与锁定&lt;/strong&gt;：
状态机处于 &lt;code&gt;流入开始&lt;/code&gt; 或 &lt;code&gt;到位判断&lt;/code&gt; 时，后台状态机根据配置自动开启输送电机并升起阻挡气缸：&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;DoSet(mFunction.ConveyorData[mStaNum].阻挡气缸输出编号); // 自动动作
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;载具撞上阻挡并触发配置的 &lt;code&gt;到位感应信号&lt;/code&gt; 后，状态机自动将流线状态切为 &lt;code&gt;&quot;当站处理&quot;&lt;/code&gt;，并触发绑定的流线事件委托（触发 &lt;code&gt;A0.Conveyors.cs&lt;/code&gt; 中的 &lt;code&gt;当站处理&lt;/code&gt; 回调）。&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;&lt;strong&gt;工站任务与流线状态握手&lt;/strong&gt;：&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;后台状态机通过 &lt;code&gt;当站处理&lt;/code&gt; 回调，将当前流水线段的自定义状态属性 &lt;code&gt;CustStatus&lt;/code&gt; (部分项目中为 &lt;code&gt;自定状态&lt;/code&gt;) 修改为 &lt;code&gt;&quot;WAITING_FOR_ASSEMBLY&quot;&lt;/code&gt; (等待装配)。&lt;/li&gt;
&lt;li&gt;具体的工位任务进程（如打螺丝站）在其自动运行循环中侦测到流线段的 &lt;code&gt;CustStatus == &quot;WAITING_FOR_ASSEMBLY&quot;&lt;/code&gt; 且到位信号满足时，启动本工位的装配流程，并将该状态置为 &lt;code&gt;&quot;工作中&quot;&lt;/code&gt; (或 &lt;code&gt;&quot;处理中&quot;&lt;/code&gt;)。&lt;/li&gt;
&lt;li&gt;工站任务完成本工位的所有工艺（如打螺丝、相机拍照等）后，&lt;strong&gt;不直接操作流线阻挡气缸&lt;/strong&gt;，而是直接修改流线的属性状态：&lt;pre&gt;&lt;code&gt;// 告诉流线状态机：本站工作已完毕，可以放行
mFunction.ConveyorData[MainConvId].CustStatus = &quot;ASSEMBLY_COMPLETED&quot;; // (或 自定状态 = &quot;装配完成&quot;)
&lt;/code&gt;&lt;/pre&gt;
&lt;/li&gt;
&lt;/ol&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;&lt;strong&gt;放行与复位流出&lt;/strong&gt;：
状态机捕获到 &lt;code&gt;&quot;ASSEMBLY_COMPLETED&quot;&lt;/code&gt; 后，流线状态机切入 &lt;code&gt;流出开始&lt;/code&gt; 步骤，并自动操纵配置 of 阻挡气缸缩回：&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;DoReset(mFunction.ConveyorData[mStaNum].阻挡气缸输出编号); // 自动降阻挡放行
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;待载具完全流出（流出感应信号变红，或下游接收完成）后，后台状态机再次自动将阻挡气缸升起（&lt;code&gt;DoSet&lt;/code&gt;），并清空该流水线段的状态，进入下一个循环。&lt;/p&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;h5&gt;3. 设计优势&lt;/h5&gt;
&lt;ul&gt;
&lt;li&gt;&lt;strong&gt;高内聚低耦合&lt;/strong&gt;：具体的工位任务只关心“工艺什么时候开始（状态被置为等待）”以及“工艺什么时候结束（写入完成状态）”，流线控制细节（气缸反馈、电机速度、防撞控制）完全对工站屏蔽。&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;强安全性与防呆&lt;/strong&gt;：阻挡气缸动作、到位反馈超时检测、马达起停时序均在底层状态机中统一调度，避免了由于每个工站自行编写气缸动作可能导致的时序冲突、气缸误动作或卡载具问题。&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;物理重构免代码修改&lt;/strong&gt;：修改流线段对应的硬件接线（如换个气缸控制阀的 DO 口），仅需在 &lt;code&gt;Conveyor.xlsx&lt;/code&gt; 中修改通道编号即可，无需触动任何逻辑代码。&lt;/li&gt;
&lt;/ul&gt;
&lt;h3&gt;3.6 工站间与工站同轴（任务）间的通信与顺序控制逻辑&lt;/h3&gt;
&lt;p&gt;在自动运行过程中，工站之间的物料移交以及工站与机械轴之间的工作调度，依靠 &lt;strong&gt;流线状态监听&lt;/strong&gt; 与 &lt;strong&gt;任务交互标志（TasksInteraction）&lt;/strong&gt; 来实现有序控制：&lt;/p&gt;
&lt;h4&gt;3.6.1 上下游工站之间的通信与顺序流转（如何判定下游好没好）&lt;/h4&gt;
&lt;p&gt;上游工位在完成本工位的作业后，不能直接放行，必须首先确认&lt;strong&gt;下游工位处于空闲状态&lt;/strong&gt;。以“扫码站（工位1）”与“螺丝站（工位2）”为例，流转和判定顺序如下：&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;&lt;strong&gt;下游空闲判定&lt;/strong&gt;：上游工位1通过直接检查下游工位2流线状态的自定义属性 &lt;code&gt;CustStatus&lt;/code&gt; 来进行通信。若为空值或 &lt;code&gt;null&lt;/code&gt;，代表工位2目前无料且空闲，允许放行：&lt;pre&gt;&lt;code&gt;// 检查工位2的状态属性是否为空，若为空说明工位2目前无料且空闲
if (string.IsNullOrEmpty(mFunction.ConveyorData[2].CustStatus))
{
    // 下游空闲，允许放行！工位1阻挡气缸缩回，电机起转，将载具送出
    SetStep(ref StaInfo, (int)步序.气缸缩回, true);
}
&lt;/code&gt;&lt;/pre&gt;
&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;放行物理动作&lt;/strong&gt;：
&lt;ul&gt;
&lt;li&gt;缩回工位1阻挡气缸：&lt;code&gt;mDoDiWaitDone(OutNo.流线1阻挡气缸, 0, InNo.流线1阻挡缩回信号, 1, 10, 3000, true)&lt;/code&gt;&lt;/li&gt;
&lt;li&gt;开启工位1电机送走产品：&lt;code&gt;mGlobal.mDoSet(OutNo.流线1_扫码滚筒电机M0); mGlobal.mDoSet(OutNo.流线1_扫码滚筒电机M3);&lt;/code&gt;&lt;/li&gt;
&lt;li&gt;等待工位1到位信号消失（载具离开）：&lt;code&gt;if (!mGlobal.ReadDi_Bool(InNo.流线1到位信号))&lt;/code&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;流入状态交接与条码传递&lt;/strong&gt;：
&lt;ul&gt;
&lt;li&gt;当载具完全流出工位1且触发工位2的流入传感器（&lt;code&gt;InNo.流线2流入信号&lt;/code&gt;）时，&lt;code&gt;A0.Conveyors.cs&lt;/code&gt; 中的 &lt;code&gt;ConvEvent.Data_Change&lt;/code&gt; 会被后台线程触发，执行段间数据交接。&lt;/li&gt;
&lt;li&gt;此时，工位1在内存中的条码、测量数据、扫码判定结果等物理信息被自动克隆/移交至工位2的内存缓冲数据结构（&lt;code&gt;mConvData[2]&lt;/code&gt;）中，同时清空工位1的遗留数据（&lt;code&gt;mConvData[1].Clear()&lt;/code&gt;）。&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;提前防撞与提前阻挡&lt;/strong&gt;：
&lt;ul&gt;
&lt;li&gt;在工位1的 &lt;code&gt;等产品到达工位2&lt;/code&gt; 步序中，当检测到产品触发 &lt;code&gt;InNo.流线2流入信号&lt;/code&gt; 且工位1阻挡气缸处于缩回状态时，延迟微调时间（如 350ms）后，工位1&lt;strong&gt;提前升起阻挡气缸&lt;/strong&gt;，确保后续载具不会撞板：&lt;pre&gt;&lt;code&gt;if (mGlobal.ReadDi_Bool(InNo.流线2流入信号) &amp;amp;&amp;amp; MotionDll.ReadDo((short)OutNo.流线1阻挡气缸) == 0)
{
    Thread.Sleep(350); 
    mGlobal.mDoSet(OutNo.流线1阻挡气缸); // 提前伸出阻挡气缸！
}
&lt;/code&gt;&lt;/pre&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;到位唤醒下游&lt;/strong&gt;：
&lt;ul&gt;
&lt;li&gt;载具继续前行至工位2的到位传感器（&lt;code&gt;InNo.流线2到位信号&lt;/code&gt;）时，工位2的主控任务检测到到位信号为 &lt;code&gt;true&lt;/code&gt;。&lt;/li&gt;
&lt;li&gt;工位1检测到工位2到位信号为 &lt;code&gt;true&lt;/code&gt; 之后，确信产品已成功交接：
&lt;ul&gt;
&lt;li&gt;关闭工位1的所有传送电机：&lt;code&gt;mGlobal.mDoReset(OutNo.流线1_扫码滚筒电机M0);&lt;/code&gt;&lt;/li&gt;
&lt;li&gt;复位工位1流线状态为置空释放：&lt;code&gt;mFunction.ConveyorData[MainConvId].CustStatus = &quot;&quot;;&lt;/code&gt;&lt;/li&gt;
&lt;li&gt;返回第一步等待下一次放料循环。&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;工位2主控任务将 &lt;code&gt;ConveyorData[2].CustStatus&lt;/code&gt; 修改为 &lt;code&gt;&quot;工作中&quot;&lt;/code&gt;（表示占位），并关闭滚筒电机、升起顶升和夹具定位产品，随后转入工艺工作（打螺丝）。&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;/ol&gt;
&lt;p&gt;下面是&lt;strong&gt;工站间物料流转与状态握手时序图&lt;/strong&gt;：&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;sequenceDiagram
    autonumber
    participant Station1 as 工位1 (主程序)
    participant Conv1 as 流线1状态机 (后台)
    participant Conv2 as 流线2状态机 (后台)
    participant Station2 as 工位2 (主程序)

    Note over Station1: 扫码完成，CustStatus=&quot;ASSEMBLY_COMPLETED&quot;
    Station1-&amp;gt;&amp;gt;Station1: 轮询检查下游状态：IsNullOrEmpty(mFunction.ConveyorData[2].CustStatus)
    Note over Station2: 初始状态，CustStatus=&quot;&quot; (空闲)
    Station1-&amp;gt;&amp;gt;Station1: 检测到工位2空闲，缩回阻挡，开启电机放行
    Note over Conv1, Conv2: 载具在流线滚筒上向工位2传输
    Station1-&amp;gt;&amp;gt;Station1: 检测到到位信号消失 (流线1到位=0)
    Note over Conv2: 载具到达流线2流入传感器
    Conv2-&amp;gt;&amp;gt;Conv2: 触发 Data_Change，拷贝数据至mConvData[2]，清空mConvData[1]
    Note over Station1: 延时350ms，提前升起阻挡气缸防撞
    Station1-&amp;gt;&amp;gt;Station1: 开启阻挡气缸1
    Note over Conv2: 载具到达流线2到位传感器
    Station1-&amp;gt;&amp;gt;Station1: 检测到流线2到位=1，关闭电机1，置空流线1的 CustStatus=&quot;&quot;
    Note over Station1: 工位1恢复空闲，可接收新料
    Station2-&amp;gt;&amp;gt;Station2: 检测到位信号和WAINTING_FOR_ASSEMBLY，关闭电机2，升起定位夹具
    Station2-&amp;gt;&amp;gt;Station2: 修改流线2状态 CustStatus=&quot;工作中&quot;
    Note over Station2: 工位2开始打螺丝动作
&lt;/code&gt;&lt;/pre&gt;
&lt;h4&gt;3.6.2 工站与机械轴之间的通信（任务协程）&lt;/h4&gt;
&lt;p&gt;对于打螺丝、CCD拍照等包含运动轴组的工站，主工站（如 &lt;code&gt;Task02_螺丝站&lt;/code&gt;）与机械轴（如 &lt;code&gt;Task04_右机械轴&lt;/code&gt;、&lt;code&gt;Task05_左机械轴&lt;/code&gt;）属于独立的两个任务线程，它们通过 &lt;code&gt;TasksInteraction&lt;/code&gt; 全局握手标志位实现同步：&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;&lt;strong&gt;主工站触发机械手工作&lt;/strong&gt;：
&lt;ul&gt;
&lt;li&gt;当载具夹紧定位完毕后，主工站清空历史完成状态，并向机械手广播“允许工作”的标志位：&lt;pre&gt;&lt;code&gt;GetTasksInteraction(TasksInteraction.右轴螺丝工作完成_标志, true); // 清空历史
GetTasksInteraction(TasksInteraction.左轴螺丝工作完成_标志, true); // 清空历史
SetTasksInteractionTrue(TasksInteraction.组装允许机械手_标志);
&lt;/code&gt;&lt;/pre&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;机械手任务响应并锁存&lt;/strong&gt;：
&lt;ul&gt;
&lt;li&gt;机械轴类（继承自 &lt;code&gt;Task_机械轴基类&lt;/code&gt;）在自己的自动循环中配置了 &lt;code&gt;启动触发标志&lt;/code&gt;（即 &lt;code&gt;TasksInteraction.组装允许机械手_标志&lt;/code&gt;）。&lt;/li&gt;
&lt;li&gt;检测到该标志为 &lt;code&gt;true&lt;/code&gt; 后，两轴的任务线程被同步唤醒，脱离等待，前往吸取螺丝或执行视觉纠偏与锁付。&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;防重复触发拦截（清除触发标志）&lt;/strong&gt;：
&lt;ul&gt;
&lt;li&gt;为了防止多轴机械手在完成动作返回时二次触发，主工站检测到左右两轴都已经脱离初始等待步骤（例如 &lt;code&gt;StepIdx &amp;gt;= 20&lt;/code&gt;）且处于工作状态后，会立即将触发标志抹除：&lt;pre&gt;&lt;code&gt;if (!已经清除启动信号 &amp;amp;&amp;amp; 右轴已经启动 &amp;amp;&amp;amp; 左轴已经启动)
{
    SetTasksInteractionFalse(TasksInteraction.组装允许机械手_标志);
    已经清除启动信号 = true;
}
&lt;/code&gt;&lt;/pre&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;工作完成反馈&lt;/strong&gt;：
&lt;ul&gt;
&lt;li&gt;机械轴执行完锁付动作，并在XY轴和Z轴完全退回到避让待机位置（确信物理上完全避让载具和顶升气缸）之后，各自将自己的完成标志置为 &lt;code&gt;true&lt;/code&gt;：&lt;pre&gt;&lt;code&gt;// 右轴任务在其结束步序置位：SetTasksInteractionTrue(TasksInteraction.右轴螺丝工作完成_标志);
// 左轴任务同理置位：SetTasksInteractionTrue(TasksInteraction.左轴螺丝工作完成_标志);
&lt;/code&gt;&lt;/pre&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;主工站汇合与确认&lt;/strong&gt;：
&lt;ul&gt;
&lt;li&gt;主工站以非阻塞形式轮询判断两轴的完成标志。同时开启最大 90 秒打螺丝超时保护监控，防止卡死报警：&lt;pre&gt;&lt;code&gt;if (GetTasksInteraction(TasksInteraction.右轴螺丝工作完成_标志, false) == true &amp;amp;&amp;amp;
    GetTasksInteraction(TasksInteraction.左轴螺丝工作完成_标志, false) == true)
{
    // 自动清除该完成标志位，表示双轴作业顺利结束
    GetTasksInteraction(TasksInteraction.右轴螺丝工作完成_标志, true);
    GetTasksInteraction(TasksInteraction.左轴螺丝工作完成_标志, true);
    // 工位进入等下游空闲放料状态
    SetStep(ref StaInfo, (int)步序.等工位3空闲, true);
}
else if (mFunction.OverTime(mFunction.ConveyorData[MainConvId].StartTime, 90000))
{
    // 超时处理，去异常页报警
    SetStep(ref StaInfo, (int)步序.异常, true);
}
&lt;/code&gt;&lt;/pre&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;/ol&gt;
&lt;p&gt;下面是&lt;strong&gt;工位与机械轴多线程协同握手时序图&lt;/strong&gt;：&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;sequenceDiagram
    autonumber
    participant Master as 螺丝主站 (Task02)
    participant AxisL as 左机械轴 (Task05)
    participant AxisR as 右机械轴 (Task04)

    Note over Master: 载具定位夹紧完毕
    Master-&amp;gt;&amp;gt;Master: 清空左右轴历史完成标志
    Master-&amp;gt;&amp;gt;AxisL: 置位广播信号 组装允许机械手_标志 = true
    Master-&amp;gt;&amp;gt;AxisR: (并发接收) 组装允许机械手_标志 = true
    Note over AxisL: 等待启动信号步序检测到 true
    Note over AxisR: 等待启动信号步序检测到 true
    AxisL-&amp;gt;&amp;gt;AxisL: 启动：前往拍照并打螺丝 (StepIdx=20)
    AxisR-&amp;gt;&amp;gt;AxisR: 启动：前往拍照并打螺丝 (StepIdx=20)
    Note over Master: 检测到左轴和右轴均已进入 StepIdx &amp;gt;= 20
    Master-&amp;gt;&amp;gt;Master: 复位广播信号 组装允许机械手_标志 = false (防二次触发)
    Note over AxisL: 完成螺丝锁付，返回安全待机位置
    AxisL-&amp;gt;&amp;gt;Master: 发送左轴完成信号 LeftAxisDone = true
    Note over AxisR: 完成螺丝锁付，返回安全待机位置
    AxisR-&amp;gt;&amp;gt;Master: 发送右轴完成信号 RightAxisDone = true
    Note over Master: 轮询并检测到 LeftAxisDone=true &amp;amp;&amp;amp; RightAxisDone=true
    Master-&amp;gt;&amp;gt;Master: 消费并自动清除两个完成标志
    Note over Master: 重置定位气缸，进入等下游放行步骤
&lt;/code&gt;&lt;/pre&gt;
&lt;h3&gt;3.7 典型工序异常处理与故障模拟设计（以扫码与打螺丝为例）&lt;/h3&gt;
&lt;p&gt;在大型工控系统中，&lt;strong&gt;异步网络通信与高风险执行单元（如相机纠偏、电批拧紧）是异常和报警最高发的区域&lt;/strong&gt;。为了保证整机连调的流畅性，并对生产制造中的各项 NG（不良）流程进行验证，BoTech 框架推荐采用&lt;strong&gt;通讯与逻辑解耦、全局仿真注入与全面异常隔离&lt;/strong&gt;的设计模式。&lt;/p&gt;
&lt;h4&gt;3.7.1 扫码枪异步通讯与全局仿真&lt;/h4&gt;
&lt;p&gt;在物理扫码枪未连接或调试阶段，传统逻辑往往会因连接超时而陷入阻塞或直接触发停机，极大地定拖慢了现场调试效率。&lt;/p&gt;
&lt;h5&gt;1. 旧数据残留漏洞与解决规范&lt;/h5&gt;
&lt;p&gt;早期的 C# 扫码流程常常采用 &lt;code&gt;有新数据&lt;/code&gt; 等全局静态标志进行读写同步。在高速运行或逻辑跳转复杂的场景中，若未在发送指令前对标志及接收缓冲区进行强制清理，极易发生**“在下一工序开始时，误读取了上一轮遗留的旧条码（Stale Data）”**的严重 bug。&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;推荐的数据同步与清理时序模式&lt;/strong&gt;：&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;strong&gt;触发前重置&lt;/strong&gt;：在向扫码枪发送 &lt;code&gt;ReadCode&lt;/code&gt; 指令前，强制清空接收字符串缓冲区：&lt;pre&gt;&lt;code&gt;接收的数据 = &quot;&quot;; // 显式清除历史接收缓存
&lt;/code&gt;&lt;/pre&gt;
&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;读取即消费&lt;/strong&gt;：在轮询检测到 &lt;code&gt;接收的数据&lt;/code&gt; 变为非空后，&lt;strong&gt;立即锁存并抹除&lt;/strong&gt;：&lt;pre&gt;&lt;code&gt;if (!string.IsNullOrEmpty(接收的数据))
{
    string resp = 接收的数据;
    接收的数据 = &quot;&quot;; // 毁灭性重置，防止下一轮循环二次读取旧数据
    // 开始解析并校验 resp ...
}
&lt;/code&gt;&lt;/pre&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;h5&gt;2. 全局仿真注入（脱机连调支持）&lt;/h5&gt;
&lt;p&gt;如果在自动流程处于在线运行状态，而物理扫码枪断开，BoTech 框架会根据计时器与仿真标志自动切入&lt;strong&gt;全局仿真逻辑&lt;/strong&gt;：&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;如果当前处于虚拟运行（&lt;code&gt;OffLine_VirtualRunMode&lt;/code&gt;）或者&lt;strong&gt;距离发送扫码指令过了 1000ms 物理网口仍无任何返回&lt;/strong&gt;时，软件会自动启动随机条码生成器。&lt;/li&gt;
&lt;li&gt;生成器以 &lt;strong&gt;50% 的权重模拟成功与失败（Scan NG）&lt;/strong&gt;，用以测试整机的缺陷品剔除与气缸分流剔废流程。&lt;/li&gt;
&lt;/ul&gt;
&lt;hr /&gt;
&lt;h4&gt;3.7.2 电批拧紧失败概率模拟与连续故障防护&lt;/h4&gt;
&lt;p&gt;电批拧紧作为核心组装工序，如果气压不稳、螺丝规格不符或螺牙磨损，容易发生滑牙或拧紧 NG。&lt;/p&gt;
&lt;h5&gt;1. 模拟拧紧 NG 与破真空释放&lt;/h5&gt;
&lt;p&gt;在 &lt;code&gt;步序.启动拧紧&lt;/code&gt; 阶段，电批启动信号输出并延时 1.5 秒（模拟螺丝拧紧过程）。完成后，软件会执行 &lt;strong&gt;15% 概率的电批拧紧失败模拟&lt;/strong&gt;。
拧紧失败后，需按照以下防呆时序释放异常螺丝，防止在后续位置发生二次卡料或机械撞击：&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;mGlobal.mDoReset(电批吸真空信号); // 关闭吸真空
mGlobal.mDoSet(电批破真空信号);   // 开启破真空脉冲吹气以放开螺丝
Thread.Sleep(150);
mGlobal.mDoReset(电批破真空信号); // 关闭吹气
已持料 = false;                    // 强制复位内部持料状态
&lt;/code&gt;&lt;/pre&gt;
&lt;h5&gt;2. 连续失败次数超限与原生 TipsDialog 交互&lt;/h5&gt;
&lt;ul&gt;
&lt;li&gt;&lt;strong&gt;故障限额参数&lt;/strong&gt;：软件通过类型安全强转读取 &lt;code&gt;(int)mFunction.GetParValue&amp;lt;double&amp;gt;(UserPar.电批执行失败次数)&lt;/code&gt; 参数来确定允许的最高连续 NG 次数上限。&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;超限提示交互&lt;/strong&gt;：当 &lt;code&gt;当前电批失败次数&lt;/code&gt; 达到该上限时，自动弹出原生对话框 &lt;code&gt;TipsDiglogForm&lt;/code&gt; 提示：
&lt;ul&gt;
&lt;li&gt;&lt;strong&gt;选择“Yes” (重试)&lt;/strong&gt;：重置 &lt;code&gt;当前电批失败次数 = 0&lt;/code&gt;，并命令机械轴退回 &lt;code&gt;移至取料位置&lt;/code&gt; 步骤，重新去供料器吸取新螺丝进行锁付。&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;选择“No” (停止)&lt;/strong&gt;：自动调用整机急停逻辑 &lt;code&gt;Machine.Machine.Instance.Stop(true)&lt;/code&gt;，并将当前轴状态机切换到 &lt;code&gt;State.ALARM&lt;/code&gt; 中断运行，由人工介入排查。&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;hr /&gt;
&lt;h4&gt;3.7.3 网络通信与坐标转换的 try-catch 异常安全防护规范&lt;/h4&gt;
&lt;p&gt;任何与外部硬件（相机、扫码枪、电批控制器）发生套接字（Socket）通信的接口，均存在网线松动、防火墙拦截等引发进程级 Exception 的隐患。此外，转换外部传来的 ASCII 报文（如 &lt;code&gt;double.Parse(parts[2])&lt;/code&gt;）也是高风险操作。&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;BoTech 异常安全编码规范三剑客&lt;/strong&gt;：&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;&lt;strong&gt;网络交互层 Try-Catch&lt;/strong&gt;：
对于所有 &lt;code&gt;SocketDataSend&lt;/code&gt; 和 &lt;code&gt;mSend.WaitDone&lt;/code&gt; 等网口 I/O 动作，必须使用 &lt;code&gt;try-catch&lt;/code&gt; 隔离：&lt;pre&gt;&lt;code&gt;try
{
    isOk = mSend.WaitDone((int)相机端口, 1, 相机发送指令, 0, &quot;&quot;, 5000, true, false);
}
catch (Exception ex)
{
    AddLog($&quot;网络通信发生崩溃异常: {ex.Message}&quot;, LogsType.ErrorCode, StaInfo.StepIdx, true, Color.Red);
    isOk = false; // 降级为网络失败，等待模拟器或重试机制介入
}
&lt;/code&gt;&lt;/pre&gt;
&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;数据解析与转换 Try-Catch&lt;/strong&gt;：
在解析 &lt;code&gt;Split&lt;/code&gt; 数据并转换浮点坐标时，极易因相机传输了乱码或空报文导致崩溃：&lt;pre&gt;&lt;code&gt;try
{
    string[] parts = resp.Split(&apos;,&apos;);
    double offsetX = double.Parse(parts[2]);
    double offsetY = double.Parse(parts[3]);
    // 判定坐标偏移上限并存入纠偏偏差值...
}
catch (Exception ex)
{
    AddLog($&quot;解析相机纠偏报文或校验偏移上限异常: {ex.Message}&quot;, LogsType.ErrorCode, StaInfo.StepIdx, true, Color.Red);
    SetStep(ref StaInfo, (int)步序.拍照重试判定, true); // 优雅重试而绝不发生系统级闪退
}
&lt;/code&gt;&lt;/pre&gt;
&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;动作执行与移动 Try-Catch&lt;/strong&gt;：
伺服轴移动（包含 XY 轴纠偏坐标转换相加）动作，均应封装在带有 &lt;code&gt;try-catch&lt;/code&gt; 保护的 &lt;code&gt;安全移动至&lt;/code&gt;（&lt;code&gt;SafeMoveTo&lt;/code&gt;）方法内。如果 Z 轴未在安全高度、硬件限位触发或计算溢出，软件会捕获异常并返回 &lt;code&gt;false&lt;/code&gt; 以终止后续动作，确保设备人身安全。&lt;/li&gt;
&lt;/ol&gt;
&lt;h2&gt;4. 框架常用 API 函数参考手册&lt;/h2&gt;
&lt;h3&gt;4.1 运动控制类 (Motion Control)&lt;/h3&gt;
&lt;h4&gt;4.1.1 &lt;code&gt;MotionGetDi&lt;/code&gt;&lt;/h4&gt;
&lt;ul&gt;
&lt;li&gt;&lt;strong&gt;功能&lt;/strong&gt;：读取控制卡指定的数字输入（DI）通道电平状态。&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;原型&lt;/strong&gt;：&lt;code&gt;protected bool MotionGetDi(int DiIndex)&lt;/code&gt;&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;参数&lt;/strong&gt;：&lt;code&gt;DiIndex&lt;/code&gt;：控制卡输入通道的全局索引。&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;返回值&lt;/strong&gt;：当该通道有电平输入时返回 &lt;code&gt;true&lt;/code&gt;，无电平输入时返回 &lt;code&gt;false&lt;/code&gt;。&lt;/li&gt;
&lt;/ul&gt;
&lt;h4&gt;4.1.2 &lt;code&gt;MotionGetDo&lt;/code&gt;&lt;/h4&gt;
&lt;ul&gt;
&lt;li&gt;&lt;strong&gt;功能&lt;/strong&gt;：读取当前输出（DO）通道的硬件置位状态。&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;原型&lt;/strong&gt;：&lt;code&gt;protected bool MotionGetDo(int DoIndex)&lt;/code&gt;&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;参数&lt;/strong&gt;：&lt;code&gt;DoIndex&lt;/code&gt;：全局 DO 输出索引。&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;返回值&lt;/strong&gt;：已输出置位时返回 &lt;code&gt;true&lt;/code&gt;，复位状态返回 &lt;code&gt;false&lt;/code&gt;。&lt;/li&gt;
&lt;/ul&gt;
&lt;h4&gt;4.1.3 &lt;code&gt;MotionSetDo&lt;/code&gt;&lt;/h4&gt;
&lt;ul&gt;
&lt;li&gt;&lt;strong&gt;功能&lt;/strong&gt;：写入一个或多个输出通道状态。&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;原型&lt;/strong&gt;：
&lt;ul&gt;
&lt;li&gt;&lt;code&gt;protected void MotionSetDo(int DoIndex, bool sts)&lt;/code&gt;&lt;/li&gt;
&lt;li&gt;&lt;code&gt;protected void MotionSetDo(int[] DoIndex, bool sts)&lt;/code&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;参数&lt;/strong&gt;：&lt;code&gt;DoIndex&lt;/code&gt;：单个通道索引或通道索引数组；&lt;code&gt;sts&lt;/code&gt;：目标电平状态（&lt;code&gt;true&lt;/code&gt; 或 &lt;code&gt;false&lt;/code&gt;）。&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;代码示例&lt;/strong&gt;：&lt;pre&gt;&lt;code&gt;MotionSetDo((int)OutNo.蜂鸣器, true); // 开启蜂鸣
MotionSetDo(new int[] { (int)OutNo.五色灯红色, (int)OutNo.五色灯绿色 }, false); // 并发关闭红绿灯
&lt;/code&gt;&lt;/pre&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;h4&gt;4.1.4 &lt;code&gt;MotionAbsMove&lt;/code&gt; / &lt;code&gt;MotionRelMove&lt;/code&gt;&lt;/h4&gt;
&lt;ul&gt;
&lt;li&gt;&lt;strong&gt;功能&lt;/strong&gt;：驱动单轴或多轴以绝对坐标或相对位移开始运动。该方法是&lt;strong&gt;非阻塞的&lt;/strong&gt;，启动指令下发后立即返回。&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;原型&lt;/strong&gt;：
&lt;ul&gt;
&lt;li&gt;&lt;code&gt;protected bool MotionAbsMove(int AxisID, double Position, double Vel)&lt;/code&gt;&lt;/li&gt;
&lt;li&gt;&lt;code&gt;protected bool MotionAbsMove(int[] AxisID, double[] Position, double[] Vel)&lt;/code&gt;&lt;/li&gt;
&lt;li&gt;&lt;code&gt;protected bool MotionRelMove(int AxisID, double Dist, double Vel)&lt;/code&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;参数&lt;/strong&gt;：
&lt;ul&gt;
&lt;li&gt;&lt;code&gt;AxisID&lt;/code&gt;：目标轴号（单个或数组）。&lt;/li&gt;
&lt;li&gt;&lt;code&gt;Position&lt;/code&gt; / &lt;code&gt;Dist&lt;/code&gt;：目标绝对坐标（mm）或移动距离（mm）。&lt;/li&gt;
&lt;li&gt;&lt;code&gt;Vel&lt;/code&gt;：运动速度（mm/s），输入 &lt;code&gt;-1&lt;/code&gt; 则采用系统配置的默认运行速度。&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;返回值&lt;/strong&gt;：指令发送成功返回 &lt;code&gt;true&lt;/code&gt;，驱动报错返回 &lt;code&gt;false&lt;/code&gt;。&lt;/li&gt;
&lt;/ul&gt;
&lt;h4&gt;4.1.5 &lt;code&gt;MotionWaitMoveDone&lt;/code&gt;&lt;/h4&gt;
&lt;ul&gt;
&lt;li&gt;&lt;strong&gt;功能&lt;/strong&gt;：阻塞当前线程，等待指定的一个或多个轴到达目标坐标或运动静止，直到超时。&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;原型&lt;/strong&gt;：
&lt;ul&gt;
&lt;li&gt;&lt;code&gt;protected bool MotionWaitMoveDone(int AxisId, int timeout = -1)&lt;/code&gt;&lt;/li&gt;
&lt;li&gt;&lt;code&gt;protected bool MotionWaitMoveDone(int[] AxisId, double[] targetpos, int timeout = -1)&lt;/code&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;参数&lt;/strong&gt;：
&lt;ul&gt;
&lt;li&gt;&lt;code&gt;AxisId&lt;/code&gt;：等待的轴号。&lt;/li&gt;
&lt;li&gt;&lt;code&gt;targetpos&lt;/code&gt;：目标坐标对比数组（若不传入此参数，则判定轴停止运行即完成）。&lt;/li&gt;
&lt;li&gt;&lt;code&gt;timeout&lt;/code&gt;：最大等待毫秒数，&lt;code&gt;-1&lt;/code&gt; 为无限等待。&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;返回值&lt;/strong&gt;：到达或静止返回 &lt;code&gt;true&lt;/code&gt;；超时返回 &lt;code&gt;false&lt;/code&gt;。&lt;/li&gt;
&lt;/ul&gt;
&lt;h4&gt;4.1.6 &lt;code&gt;MotionAbsMoveAndDone&lt;/code&gt; / &lt;code&gt;MotionRelMoveAndDone&lt;/code&gt;&lt;/h4&gt;
&lt;ul&gt;
&lt;li&gt;&lt;strong&gt;功能&lt;/strong&gt;：单轴或多轴运动并阻塞等待其到位，是 &lt;code&gt;MotionAbsMove&lt;/code&gt; 与 &lt;code&gt;MotionWaitMoveDone&lt;/code&gt; 的高度封装。&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;原型&lt;/strong&gt;：&lt;code&gt;protected bool MotionAbsMoveAndDone(int AxisID, double Position, double Vel, int timeout = -1)&lt;/code&gt;&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;返回值&lt;/strong&gt;：在超时范围内成功运动并到位返回 &lt;code&gt;true&lt;/code&gt;，任意轴超时或失败返回 &lt;code&gt;false&lt;/code&gt;。&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;代码示例&lt;/strong&gt;：&lt;pre&gt;&lt;code&gt;// 抬升 Z 轴至 0.0 安全高度，设定 5000ms 超时
if (!MotionAbsMoveAndDone((short)mAxis.左Z, 0.0, -1, 5000))
{
    AddLog(&quot;Z轴安全抬升失败，紧急停机！&quot;, LogsType.ErrorCode, StaInfo.StepIdx, true);
    SetStep(ref StaInfo, (int)步序.异常, true);
}
&lt;/code&gt;&lt;/pre&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;h4&gt;4.1.7 &lt;code&gt;MotionWaitDi&lt;/code&gt; / &lt;code&gt;MotionWaitDo&lt;/code&gt;&lt;/h4&gt;
&lt;ul&gt;
&lt;li&gt;&lt;strong&gt;功能&lt;/strong&gt;：阻塞线程并等待特定的输入（DI）或输出（DO）状态转为设定状态。&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;原型&lt;/strong&gt;：&lt;code&gt;protected bool MotionWaitDi(int diId, bool isOn, int timeout = -1, bool TimeoutToBeContinue = false)&lt;/code&gt;&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;参数&lt;/strong&gt;：
&lt;ul&gt;
&lt;li&gt;&lt;code&gt;diId&lt;/code&gt;：待检测的 I/O 全局索引。&lt;/li&gt;
&lt;li&gt;&lt;code&gt;isOn&lt;/code&gt;：期待的目标状态（&lt;code&gt;true&lt;/code&gt;/&lt;code&gt;false&lt;/code&gt;）。&lt;/li&gt;
&lt;li&gt;&lt;code&gt;timeout&lt;/code&gt;：等待超时时间（ms）。&lt;/li&gt;
&lt;li&gt;&lt;code&gt;TimeoutToBeContinue&lt;/code&gt;：&lt;strong&gt;关键参数&lt;/strong&gt;。
&lt;ul&gt;
&lt;li&gt;设为 &lt;code&gt;false&lt;/code&gt; 时：若超时，系统将&lt;strong&gt;弹出带有“重试(Retry)”和“取消(Cancel)”的对话框&lt;/strong&gt;。用户点击重试会再次等待，点击取消则返回 &lt;code&gt;false&lt;/code&gt; 触发报警。&lt;/li&gt;
&lt;li&gt;设为 &lt;code&gt;true&lt;/code&gt; 时：若超时，程序&lt;strong&gt;不弹窗，直接返回 &lt;code&gt;false&lt;/code&gt; 并执行下一行代码&lt;/strong&gt;，交由程序员在代码中做流转决策。&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;h4&gt;4.1.8 &lt;code&gt;MotionGoHomeAndDone&lt;/code&gt;&lt;/h4&gt;
&lt;ul&gt;
&lt;li&gt;&lt;strong&gt;功能&lt;/strong&gt;：驱动指定轴回零，并阻塞等待直至回零成功或超时。&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;原型&lt;/strong&gt;：&lt;code&gt;protected bool MotionGoHomeAndDone(int AxisId, int timeout = -1)&lt;/code&gt;&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;返回值&lt;/strong&gt;：回零成功返回 &lt;code&gt;true&lt;/code&gt;，超时或报错返回 &lt;code&gt;false&lt;/code&gt;。&lt;/li&gt;
&lt;/ul&gt;
&lt;h4&gt;4.1.9 &lt;code&gt;mDoDiWaitDone&lt;/code&gt;&lt;/h4&gt;
&lt;ul&gt;
&lt;li&gt;&lt;strong&gt;功能&lt;/strong&gt;：框架中最常用的&lt;strong&gt;气缸一体化动作等待方法&lt;/strong&gt;，将“写输出”与“等反馈”合并。&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;原型&lt;/strong&gt;：
&lt;ul&gt;
&lt;li&gt;&lt;code&gt;public void mDoDiWaitDone(OutNo OutNum, short state, InNo InNum, short state1, short DelayTime, short Timeout, bool Pop_up_message = false)&lt;/code&gt;&lt;/li&gt;
&lt;li&gt;&lt;code&gt;public void mDoDiWaitDone(InNo InNum, short state1, short DelayTime, short Timeout, bool Pop_up_message = false)&lt;/code&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;参数&lt;/strong&gt;：
&lt;ul&gt;
&lt;li&gt;&lt;code&gt;OutNum&lt;/code&gt;：要驱动的电磁阀 DO。&lt;/li&gt;
&lt;li&gt;&lt;code&gt;state&lt;/code&gt;：电磁阀输出电平（&lt;code&gt;1&lt;/code&gt; 伸出，&lt;code&gt;0&lt;/code&gt; 缩回）。&lt;/li&gt;
&lt;li&gt;&lt;code&gt;InNum&lt;/code&gt;：气缸到位磁簧开关 DI。&lt;/li&gt;
&lt;li&gt;&lt;code&gt;state1&lt;/code&gt;：期待的磁簧开关状态（&lt;code&gt;1&lt;/code&gt; 触发到位，&lt;code&gt;0&lt;/code&gt; 离开到位）。&lt;/li&gt;
&lt;li&gt;&lt;code&gt;DelayTime&lt;/code&gt;：到位后的额外稳定延时（ms）。&lt;/li&gt;
&lt;li&gt;&lt;code&gt;Timeout&lt;/code&gt;：最大等待时间（ms）。&lt;/li&gt;
&lt;li&gt;&lt;code&gt;Pop_up_message&lt;/code&gt;：若为 &lt;code&gt;true&lt;/code&gt;，超时后会在界面弹出“Retry/Cancel”重试对话框；若为 &lt;code&gt;false&lt;/code&gt; 且超时，则直接抛出异常终止程序。&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;代码示例&lt;/strong&gt;：&lt;pre&gt;&lt;code&gt;// 将流线1阻挡气缸复位为 0，并同步等待阻挡缩回信号变为 1。如果超过 3000ms 未到位，弹窗提示用户
mDoDiWaitDone(OutNo.流线1阻挡气缸, 0, InNo.流线1阻挡缩回信号, 1, 10, 3000, true);
&lt;/code&gt;&lt;/pre&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;hr /&gt;
&lt;h3&gt;4.2 系统、超时与日志 (System/Utility/Logs)&lt;/h3&gt;
&lt;h4&gt;4.2.1 &lt;code&gt;mFunction.GetTickCount()&lt;/code&gt;&lt;/h4&gt;
&lt;ul&gt;
&lt;li&gt;&lt;strong&gt;功能&lt;/strong&gt;：获取高精度系统计时器当前的 Tick 数值（自系统启动以来的毫秒数，常用于精确超时和节拍测算）。&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;原型&lt;/strong&gt;：&lt;code&gt;public static long GetTickCount()&lt;/code&gt;&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;返回值&lt;/strong&gt;：&lt;code&gt;long&lt;/code&gt; 类型的毫秒时间戳。&lt;/li&gt;
&lt;/ul&gt;
&lt;h4&gt;4.2.2 &lt;code&gt;mFunction.OverTime&lt;/code&gt;&lt;/h4&gt;
&lt;ul&gt;
&lt;li&gt;&lt;strong&gt;功能&lt;/strong&gt;：判定给定时间戳是否已超出限定时长。&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;原型&lt;/strong&gt;：&lt;code&gt;public static bool OverTime(long StartTime, int SleepTime)&lt;/code&gt;&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;参数&lt;/strong&gt;：&lt;code&gt;StartTime&lt;/code&gt;：起始 Tick 值；&lt;code&gt;SleepTime&lt;/code&gt;：限定的超时时间（ms）。&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;返回值&lt;/strong&gt;：已超时返回 &lt;code&gt;true&lt;/code&gt;，未超时返回 &lt;code&gt;false&lt;/code&gt;。&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;代码示例&lt;/strong&gt;：&lt;pre&gt;&lt;code&gt;long myTimer = mFunction.GetTickCount();
// ... 执行某操作 ...
if (mFunction.OverTime(myTimer, 5000))
{
    // 耗时超过 5 秒，进行超时处理
}
&lt;/code&gt;&lt;/pre&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;h4&gt;4.2.3 &lt;code&gt;mFunction.Sleep&lt;/code&gt;&lt;/h4&gt;
&lt;ul&gt;
&lt;li&gt;&lt;strong&gt;功能&lt;/strong&gt;：让当前工作线程进入休眠状态，以释放 CPU 资源。&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;原型&lt;/strong&gt;：&lt;code&gt;public static bool Sleep(int DT)&lt;/code&gt;&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;参数&lt;/strong&gt;：&lt;code&gt;DT&lt;/code&gt;：挂起的毫秒数。&lt;/li&gt;
&lt;/ul&gt;
&lt;h4&gt;4.2.4 &lt;code&gt;AddLog&lt;/code&gt;&lt;/h4&gt;
&lt;ul&gt;
&lt;li&gt;&lt;strong&gt;功能&lt;/strong&gt;：在日志系统中记录事件。支持写入本地硬盘、显示在运行界面的动态日志窗口。&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;原型&lt;/strong&gt;：&lt;code&gt;public string AddLog(string MsgStr, LogsType model = LogsType.Logs, int StepNo = 0, bool dn_UI_Show = false, Color _color = default(Color))&lt;/code&gt;&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;参数&lt;/strong&gt;：
&lt;ul&gt;
&lt;li&gt;&lt;code&gt;MsgStr&lt;/code&gt;：日志记录的内容字符串。&lt;/li&gt;
&lt;li&gt;&lt;code&gt;model&lt;/code&gt;：日志类别枚举（&lt;code&gt;LogsType&lt;/code&gt;），如 &lt;code&gt;LogsType.Auto&lt;/code&gt;（自动流程日志）、&lt;code&gt;LogsType.CCD&lt;/code&gt;（相机通讯）、&lt;code&gt;LogsType.Barcode&lt;/code&gt;（扫码枪数据）、&lt;code&gt;LogsType.Home&lt;/code&gt;（回零复位日志）等。&lt;/li&gt;
&lt;li&gt;&lt;code&gt;StepNo&lt;/code&gt;：当前状态机步骤，便于在日志中定位逻辑步骤。&lt;/li&gt;
&lt;li&gt;&lt;code&gt;dn_UI_Show&lt;/code&gt;：为 &lt;code&gt;true&lt;/code&gt; 时，该条日志会同步推送到主画面的日志 ListBox，使用户直观可见。&lt;/li&gt;
&lt;li&gt;&lt;code&gt;_color&lt;/code&gt;：指定该行在主界面显示的文本颜色。&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;返回值&lt;/strong&gt;：格式化后的完整日志行字符串。&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;代码示例&lt;/strong&gt;：&lt;pre&gt;&lt;code&gt;AddLog(&quot;螺丝站：两轴已全部启动，清除触发信号&quot;, LogsType.Auto, StaInfo.StepIdx, true, Color.ForestGreen);
&lt;/code&gt;&lt;/pre&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;h4&gt;4.2.5 &lt;code&gt;AddAlarmCenter&lt;/code&gt; / &lt;code&gt;AddTipCentert&lt;/code&gt;&lt;/h4&gt;
&lt;ul&gt;
&lt;li&gt;&lt;strong&gt;功能&lt;/strong&gt;：中断运行并在主界面中心弹出一个阻塞的交互窗口。&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;原型&lt;/strong&gt;：
&lt;ul&gt;
&lt;li&gt;&lt;code&gt;public AlarmCenter.mDialogResult AddAlarmCenter(string MsgStr, bool isWaitOne = true, string btnOKText = &quot;Continue&quot;, string btnCancelText = &quot;Cancel&quot;, string btnIgnoreText = &quot;&quot;, bool isBuzzer = true)&lt;/code&gt;&lt;/li&gt;
&lt;li&gt;&lt;code&gt;public AlarmCenter.mDialogResult AddTipCentert(string MsgStr, string changeWithoutTran = &quot;&quot;, bool isWaitOne = true, string btnOKText = &quot;Continue&quot;, string btnCancelText = &quot;Cancel&quot;, bool isBuzzer = false)&lt;/code&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;参数说明&lt;/strong&gt;：
&lt;ul&gt;
&lt;li&gt;&lt;code&gt;MsgStr&lt;/code&gt;：弹窗内展示的异常报警信息。&lt;/li&gt;
&lt;li&gt;&lt;code&gt;isWaitOne&lt;/code&gt;：为 &lt;code&gt;true&lt;/code&gt; 时将彻底阻塞当前工站线程，直至用户做出按钮点击反馈。&lt;/li&gt;
&lt;li&gt;&lt;code&gt;btnOKText&lt;/code&gt; / &lt;code&gt;btnCancelText&lt;/code&gt;：两个响应按钮的自定义文言（通常为 Continue 与 Cancel）。&lt;/li&gt;
&lt;li&gt;&lt;code&gt;isBuzzer&lt;/code&gt;：是否同步亮红灯并启动物理蜂鸣器。&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;返回值&lt;/strong&gt;：&lt;code&gt;AlarmCenter.mDialogResult.OK&lt;/code&gt; (对应 Continue) 或 &lt;code&gt;AlarmCenter.mDialogResult.Cancel&lt;/code&gt; (对应 Cancel)。&lt;/li&gt;
&lt;/ul&gt;
&lt;hr /&gt;
&lt;h3&gt;4.3 文件读写 (File Operations)&lt;/h3&gt;
&lt;h4&gt;4.3.1 INI 配置文件读写&lt;/h4&gt;
&lt;ul&gt;
&lt;li&gt;&lt;strong&gt;原型&lt;/strong&gt;：
&lt;ul&gt;
&lt;li&gt;&lt;code&gt;public static void SetIniS(string SectionName, string KeyWord, string ValStr, string FileName)&lt;/code&gt;&lt;/li&gt;
&lt;li&gt;&lt;code&gt;public static void SetIniN(string SectionName, string KeyWord, double ValInt, string FileName)&lt;/code&gt;&lt;/li&gt;
&lt;li&gt;&lt;code&gt;public static string GetIniS(string SectionName, string KeyWord, string DefString, string FileName)&lt;/code&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;说明&lt;/strong&gt;：向路径 &lt;code&gt;FileName&lt;/code&gt; 写入或读取标准 &lt;code&gt;[Section]&lt;/code&gt; 下的键值。读取时，若键不存在则返回默认值 &lt;code&gt;DefString&lt;/code&gt;。&lt;/li&gt;
&lt;/ul&gt;
&lt;h4&gt;4.3.2 XML 数据读写&lt;/h4&gt;
&lt;ul&gt;
&lt;li&gt;&lt;strong&gt;原型&lt;/strong&gt;：
&lt;ul&gt;
&lt;li&gt;&lt;code&gt;public static void ReadXml&amp;lt;T&amp;gt;(string XmlFileName, ref T ReadData)&lt;/code&gt;&lt;/li&gt;
&lt;li&gt;&lt;code&gt;public static void WriteXml&amp;lt;T&amp;gt;(string XmlFileName, ref T WriteData)&lt;/code&gt;&lt;/li&gt;
&lt;li&gt;&lt;code&gt;public static void Read2DXml&amp;lt;T&amp;gt;(string XmlFileName, ref T[,] mDataTmp)&lt;/code&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;说明&lt;/strong&gt;：利用 XML 序列化器对指定对象 &lt;code&gt;ReadData&lt;/code&gt;/&lt;code&gt;WriteData&lt;/code&gt;（可以是简单结构体、包含属性的参数数组或二维数组）进行快速保存和加载。&lt;/li&gt;
&lt;/ul&gt;
&lt;h4&gt;4.3.3 CSV 与 TXT 文件写入&lt;/h4&gt;
&lt;ul&gt;
&lt;li&gt;&lt;strong&gt;原型&lt;/strong&gt;：
&lt;ul&gt;
&lt;li&gt;&lt;code&gt;public void WriteCsvFile(string FilePathName, string Savedata)&lt;/code&gt;&lt;/li&gt;
&lt;li&gt;&lt;code&gt;public void WriteDattxt(string Filename, string WriteData)&lt;/code&gt;&lt;/li&gt;
&lt;li&gt;&lt;code&gt;public string ReadDattxt(string Filename)&lt;/code&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;说明&lt;/strong&gt;：用于配置 PDCA 记录、生产报表及标定数据的快捷文件写入。&lt;code&gt;WriteCsvFile&lt;/code&gt; 会自动创建目录并以追加方式（Append）写入一行 CSV 格式字符串。&lt;/li&gt;
&lt;/ul&gt;
&lt;hr /&gt;
&lt;h3&gt;4.4 网口与串口通讯 (Communications)&lt;/h3&gt;
&lt;p&gt;BoTech 框架底层集成了基于以太网套接字 (Socket Client) 的网络通讯组件，用于与相机 (CCD)、扫码枪 (Scanner)、RFID 读写器及其他外部智能设备进行双向网络报文交互。&lt;/p&gt;
&lt;h4&gt;4.4.1 Socket 客户端发送数据&lt;/h4&gt;
&lt;ul&gt;
&lt;li&gt;&lt;strong&gt;方法&lt;/strong&gt;：&lt;code&gt;TcpIP[Index].SendData(string DataStr)&lt;/code&gt;&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;参数&lt;/strong&gt;：
&lt;ul&gt;
&lt;li&gt;&lt;code&gt;Index&lt;/code&gt;：网口的逻辑映射编号（对应 &lt;code&gt;TCPIP_Port&lt;/code&gt; 枚举）。&lt;/li&gt;
&lt;li&gt;&lt;code&gt;DataStr&lt;/code&gt;：需要发送给服务器的字符串报文。&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;返回值&lt;/strong&gt;：若成功送入发送缓冲区则返回 &lt;code&gt;true&lt;/code&gt;，否则返回 &lt;code&gt;false&lt;/code&gt;。&lt;/li&gt;
&lt;/ul&gt;
&lt;h4&gt;4.4.2 检查是否收到新数据与读取机制&lt;/h4&gt;
&lt;p&gt;在后台套接字接收线程收到数据后，会将标志置位。工站可以通过以下 API 进行检查与消费：&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;strong&gt;&lt;code&gt;TcpInfo[Index].Received&lt;/code&gt;&lt;/strong&gt;：只读布尔值。当网络物理链路收到新报文且未被读取时返回 &lt;code&gt;true&lt;/code&gt;。&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;&lt;code&gt;TcpInfo[Index].Data&lt;/code&gt;&lt;/strong&gt;：读取并清空缓冲区内完整的网口数据。
&lt;blockquote&gt;
&lt;p&gt;[!IMPORTANT]
&lt;strong&gt;毁灭性读取机制&lt;/strong&gt;：
读取 &lt;code&gt;TcpInfo[Index].Data&lt;/code&gt; 属性会触发其 Getter，该操作在将接收数据字符串返回的同时，会&lt;strong&gt;立即清空底层接收缓冲区并将 &lt;code&gt;Received&lt;/code&gt; 标志原子复位为 &lt;code&gt;false&lt;/code&gt;&lt;/strong&gt;。
因此，同一循环中&lt;strong&gt;不可重复读取该属性&lt;/strong&gt;（第二次读取将获得空字符串 &lt;code&gt;&quot;&quot;&lt;/code&gt;）。如果需要多次使用接收到的数据，必须在首次读取时用局部变量锁存（如 &lt;code&gt;string resp = TcpInfo[Index].Data;&lt;/code&gt;）。&lt;/p&gt;
&lt;/blockquote&gt;
&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;&lt;code&gt;TcpInfo[Index].Open&lt;/code&gt;&lt;/strong&gt;：检查网口连接状态（连接建立为 &lt;code&gt;true&lt;/code&gt;，断开为 &lt;code&gt;false&lt;/code&gt;）。&lt;/li&gt;
&lt;/ul&gt;
&lt;h4&gt;4.4.3 TCP 双向应答最佳实践示例&lt;/h4&gt;
&lt;p&gt;在 AutoRun 状态机中，典型的双向应答（Request-Response）模式如下：&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;case (int)步序.发送拍照指令:
    // 重新记录起点时间，避免累加之前动作的时间导致超时错误
    mFunction.ConveyorData[MainConvId].StartTime = mFunction.GetTickCount(); 
    AddLog(&quot;右机械轴：向相机发送拍照指令&quot;, LogsType.CCD, StaInfo.StepIdx, true);
    
    // 发送报文
    if (TcpIp_Communication.SocketDataSend(TCPIP_Port.右CCD, &quot;RScrew&quot;))
    {
        SetStep(ref StaInfo, (int)步序.等相机数据, true);
    }
    else
    {
        SetStep(ref StaInfo, (int)步序.异常, true);
    }
    break;

case (int)步序.等相机数据:
    // 1. 判断是否收到回复
    if (mFunction.TcpInfo[(short)TCPIP_Port.右CCD].Received)
    {
        // 2. 局部变量读取并锁存，同时复位 Received 标志并清除接收区
        string resp = mFunction.TcpInfo[(short)TCPIP_Port.右CCD].Data;
        AddLog($&quot;收到相机回复: {resp}&quot;, LogsType.CCD, StaInfo.StepIdx, true);
        
        // 3. 解析相机纠偏数据
        if (resp.StartsWith(&quot;OK&quot;))
        {
            // 解析并赋值纠偏 X, Y, R
            SetStep(ref StaInfo, (int)步序.平移对位, true);
        }
        else
        {
            SetStep(ref StaInfo, (int)步序.异常, true);
        }
    }
    // 4. 超时监控（3秒）
    else if (mFunction.OverTime(mFunction.ConveyorData[MainConvId].StartTime, 3000))
    {
        AddLog(&quot;等待相机数据超时！&quot;, LogsType.CCD, StaInfo.StepIdx, true);
        SetStep(ref StaInfo, (int)步序.异常, true);
    }
    break;
&lt;/code&gt;&lt;/pre&gt;
&lt;h4&gt;4.4.4 框架内网口通信的两种实现方式（对比）&lt;/h4&gt;
&lt;p&gt;在 BoTech 软件框架中，接收以太网报文有两种经典开发模式，开发者应根据并发场景进行合理选型。&lt;/p&gt;
&lt;h5&gt;方式一：直接在 AutoRun 状态机中轮询接收（直接访问模式）&lt;/h5&gt;
&lt;ul&gt;
&lt;li&gt;&lt;strong&gt;工作原理&lt;/strong&gt;：
直接在工站的 &lt;code&gt;AutoRun()&lt;/code&gt; 状态机步序中，使用 &lt;code&gt;if (mFunction.TcpInfo[Index].Received)&lt;/code&gt; 轮询底层网络接收缓冲区。当判定为 &lt;code&gt;true&lt;/code&gt; 后，直接读取 &lt;code&gt;string resp = mFunction.TcpInfo[Index].Data&lt;/code&gt; 获取响应。&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;典型代码&lt;/strong&gt;：&lt;pre&gt;&lt;code&gt;if (mFunction.TcpInfo[(short)TCPIP_Port.扫描].Received)
{
    string resp = mFunction.TcpInfo[(short)TCPIP_Port.扫描].Data; // 毁灭性读取
    // 处理扫码数据
}
&lt;/code&gt;&lt;/pre&gt;
&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;适用场景&lt;/strong&gt;：
&lt;strong&gt;单端口单工站独占&lt;/strong&gt;场景。例如，扫码枪网口只与 &lt;code&gt;Task01_入料扫码站&lt;/code&gt; 发生交互，无其他工站线程介入读取该端口。&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;优缺点&lt;/strong&gt;：
&lt;ul&gt;
&lt;li&gt;&lt;strong&gt;优点&lt;/strong&gt;：简单直接，逻辑高度内聚，无需在其他网络配置文件中注册转发。&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;缺点&lt;/strong&gt;：由于 &lt;code&gt;.Data&lt;/code&gt; 的毁灭性读取特性，如果有两个并发线程（如主站和辅轴）同时轮询 &lt;code&gt;TcpInfo[Index].Received&lt;/code&gt;，一旦数据到达，其中一个线程读取了 &lt;code&gt;.Data&lt;/code&gt;，另一个线程就会读取到空字符串 &lt;code&gt;&quot;&quot;&lt;/code&gt;，从而导致数据丢失或逻辑失效。&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;h5&gt;方式二：基于全局事件路由与静态/实例字段（回调分发模式，推荐）&lt;/h5&gt;
&lt;ul&gt;
&lt;li&gt;&lt;strong&gt;工作原理&lt;/strong&gt;：
网络底层接收事件与工站时序线程解耦。在系统初始化时，框架通过 &lt;code&gt;mFunction.TcpIP[i].mDataRec += mTcpData;&lt;/code&gt; 注册全局接收事件回调。
当任意网口收到数据时，回调线程立即进入辅助类 &lt;code&gt;2.TcpIp.cs&lt;/code&gt;（通常称为 &lt;code&gt;TcpIpcs&lt;/code&gt; 文件）的 &lt;code&gt;mTcpData(short Index)&lt;/code&gt; 方法：
在回调中，读取数据、置位 &lt;code&gt;Received = false&lt;/code&gt;，然后根据端口索引直接将数据&lt;strong&gt;路由并推送&lt;/strong&gt;给目标工站的成员属性中。&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;回调路由代码 (&lt;code&gt;2.TcpIp.cs&lt;/code&gt;)&lt;/strong&gt;：&lt;pre&gt;&lt;code&gt;public static void mTcpData(short Index)
{
    if (TcpInfo[Index].Received) 
    {
        string data = mFunction.TcpInfo[Index].Data; // 拦截并读取数据，重置缓冲区
        TcpInfo[Index].Received = false; 

        // 根据网口逻辑端口 Index 进行数据分发
        switch (Index)
        {
            case 1: // 扫码枪端口
                Task01_入料扫码站.接收的数据 = data;
                break;
            case 2: // 右轴CCD端口
                Task04_右机械轴.Instance.相机接收数据 = data;
                Task04_右机械轴.Instance.有新数据 = true;
                break;
            case 3: // 左轴CCD端口
                Task05_左机械轴.Instance.相机接收数据 = data;
                Task05_左机械轴.Instance.有新数据 = true;
                break;
        }
    }
}
&lt;/code&gt;&lt;/pre&gt;
&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;工位状态机消费代码 (&lt;code&gt;Task01_入料扫码站.cs&lt;/code&gt;)&lt;/strong&gt;：&lt;pre&gt;&lt;code&gt;case (int)步序.等扫码结果:
    if (!string.IsNullOrEmpty(接收的数据))
    {
        string resp = 接收的数据; // 消费分发过来的数据
        接收的数据 = &quot;&quot;;           // 立即清空，防止下个循环重复读取
        AddLog($&quot;扫码成功: {resp}&quot;, LogsType.Barcode, StaInfo.StepIdx, true);
        // 处理业务...
    }
    break;
&lt;/code&gt;&lt;/pre&gt;
&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;适用场景&lt;/strong&gt;：
存在多轴/多线程并发交互、一包数据多处监听，或在后台需要对报文进行统一的断包、心跳过滤、CRC校验的复杂通讯场景。&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;优缺点&lt;/strong&gt;：
&lt;ul&gt;
&lt;li&gt;&lt;strong&gt;优点&lt;/strong&gt;：网口 I/O 线程与状态机时序线程彻底解耦；多线程并发读取工站成员属性安全无冲突；利于底层统一维护。&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;缺点&lt;/strong&gt;：开发人员需同时修改 &lt;code&gt;2.TcpIp.cs&lt;/code&gt; 路由分发器和对应的工位类成员，稍微增加了代码维护点。&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;对比维度&lt;/th&gt;
&lt;th&gt;方式一：直接在 AutoRun 轮询&lt;/th&gt;
&lt;th&gt;方式二：在 2.TcpIp.cs 回调分发&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;调用位置&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;工站的 &lt;code&gt;AutoRun()&lt;/code&gt; 状态机内&lt;/td&gt;
&lt;td&gt;&lt;code&gt;2.TcpIp.cs&lt;/code&gt; 静态方法 &lt;code&gt;mTcpData&lt;/code&gt; 路由推送&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;线程归属&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;工站自身的时序线程&lt;/td&gt;
&lt;td&gt;Socket 异步监听后台接收线程&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;数据读取方式&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;主动拉取：直接调用 &lt;code&gt;TcpInfo[Index].Data&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;被动分发：由回调写入工位静态字段 &lt;code&gt;接收的数据&lt;/code&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;并发安全性&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;&lt;strong&gt;极低&lt;/strong&gt;。多线程并发读取会导致缓冲区清空，产生竞争丢失。&lt;/td&gt;
&lt;td&gt;&lt;strong&gt;极高&lt;/strong&gt;。数据固化为静态属性，可供多线程安全读取。&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;代码耦合度&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;高。网络交互时序紧密耦合在自动步骤中。&lt;/td&gt;
&lt;td&gt;低。网络接收与时序解耦，通信异常不阻塞主流程。&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;最佳实践&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;扫码枪单向请求、流程线性的简单工位。&lt;/td&gt;
&lt;td&gt;左右双轴并发纠偏对位、多相机协作的复杂工位。&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;
&lt;hr /&gt;
&lt;h3&gt;4.5 软交互信号量与干涉区防撞 (Synchronization &amp;amp; Concurrency)&lt;/h3&gt;
&lt;h4&gt;4.5.1 干涉区互斥锁 (Interference Zone)&lt;/h4&gt;
&lt;p&gt;多台机械轴或机构的活动范围在物理上存在交叠时，为了防止碰撞，必须在进入该交叠空域前申请干涉锁。&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;strong&gt;&lt;code&gt;EnterInterferenceZone(InterferenceZone id, int ThreadId = 0)&lt;/code&gt;&lt;/strong&gt;
&lt;ul&gt;
&lt;li&gt;&lt;strong&gt;机制&lt;/strong&gt;：阻塞申请。如果当前干涉区 &lt;code&gt;id&lt;/code&gt; 已经被其他工站线程占用，此方法将挂起当前工站线程，直到占用者退出。&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;&lt;code&gt;ExitInterferenceZone(InterferenceZone id, int threadId = 0)&lt;/code&gt;&lt;/strong&gt;
&lt;ul&gt;
&lt;li&gt;&lt;strong&gt;机制&lt;/strong&gt;：释放对干涉区 &lt;code&gt;id&lt;/code&gt; 的占用，允许其他处于等待队列中的工站线程进入。&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;代码示例&lt;/strong&gt;：&lt;pre&gt;&lt;code&gt;case (int)步序.移动至电批工作点:
    // 1. 申请进入组装干涉区 1
    EnterInterferenceZone(InterferenceZone.Assembly_Interference_Zone1);
    
    // 2. 申请成功后，安全移动至电批下压工作点
    bool arWork = SafeMoveTo(示教电批执行位置, 电批点位索引, 0.0, 纠偏X, 纠偏Y, 纠偏R, 15000);
    if (arWork)
    {
        SetStep(ref StaInfo, (int)步序.启动拧紧, true);
    }
    else
    {
        // 移动失败必须在退出前释放干涉区，防止死锁
        ExitInterferenceZone(InterferenceZone.Assembly_Interference_Zone1);
        SetStep(ref StaInfo, (int)步序.异常, true);
    }
    break;

case (int)步序.安全返回:
    // 3. 抬起 Z 轴，机械臂完全退出干涉空域后，释放占用
    if (SafeMoveTo(示教待机位置, 待机点位索引, 0.0, 0.0, 0.0, 0.0, 15000))
    {
        ExitInterferenceZone(InterferenceZone.Assembly_Interference_Zone1);
        SetTasksInteractionTrue(完成标志);
        SetStep(ref StaInfo, (int)步序.移至取料位置, true);
    }
    break;
&lt;/code&gt;&lt;/pre&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;h4&gt;4.5.2 工站间软交互信号量 (TasksInteraction)&lt;/h4&gt;
&lt;p&gt;用于工站线程之间的软握手和事件同步，避免因线程竞争导致的逻辑混乱。&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;strong&gt;&lt;code&gt;SetTasksInteractionTrue(Enum id)&lt;/code&gt;&lt;/strong&gt;：将指定的交互信号置为 &lt;code&gt;true&lt;/code&gt;。&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;&lt;code&gt;SetTasksInteractionFalse(Enum id)&lt;/code&gt;&lt;/strong&gt;：将指定的交互信号置为 &lt;code&gt;false&lt;/code&gt;。&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;&lt;code&gt;GetTasksInteraction(Enum id, bool isAutoClear = false)&lt;/code&gt;&lt;/strong&gt;：
&lt;ul&gt;
&lt;li&gt;读取交互信号的当前布尔值。&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;参数 &lt;code&gt;isAutoClear&lt;/code&gt;&lt;/strong&gt;：如果为 &lt;code&gt;true&lt;/code&gt;，会在成功读取到 &lt;code&gt;true&lt;/code&gt; 状态之后，&lt;strong&gt;自动将该交互信号复位为 &lt;code&gt;false&lt;/code&gt;&lt;/strong&gt;。这极大简化了手动清除的工作，能有效避免残留信号导致的多轮空跑。&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;&lt;code&gt;WaitTaskInteractionTrue(Enum id, int nTimeOut = -1, bool bTimeOutShowDialog = true, bool isAutoClear = false)&lt;/code&gt;&lt;/strong&gt;：
&lt;ul&gt;
&lt;li&gt;阻塞当前线程，等待指定交互信号变为 &lt;code&gt;true&lt;/code&gt;。&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;参数 &lt;code&gt;bTimeOutShowDialog&lt;/code&gt;&lt;/strong&gt;：超时是否弹窗重试。&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;参数 &lt;code&gt;isAutoClear&lt;/code&gt;&lt;/strong&gt;：为 &lt;code&gt;true&lt;/code&gt; 时，等待成功后自动复位该标志。&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;&lt;code&gt;WaitAllTaskInteractionTrue(Enum[] ids, int nTimeOut = -1, bool bTimeOutShowDialog = true, bool isAutoClear = false)&lt;/code&gt;&lt;/strong&gt;：
&lt;ul&gt;
&lt;li&gt;阻塞等待数组内&lt;strong&gt;所有的&lt;/strong&gt;交互信号均变为 &lt;code&gt;true&lt;/code&gt; 时才返回。&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;h4&gt;4.5.3 TasksInteraction 软交互信号量状态详解与使用指南&lt;/h4&gt;
&lt;p&gt;&lt;code&gt;TasksInteraction&lt;/code&gt; 是 BoTech 框架中实现跨线程、跨任务（Task）数据同步与多轴协同握手的核心软信号量。&lt;/p&gt;
&lt;h5&gt;1. 底层存储与工作原理&lt;/h5&gt;
&lt;ul&gt;
&lt;li&gt;&lt;strong&gt;定义位置&lt;/strong&gt;：所有交互信号均声明于 &lt;code&gt;4.Assist/6.mEnum.cs&lt;/code&gt; 的 &lt;code&gt;public enum TasksInteraction&lt;/code&gt; 枚举中。&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;寄存器机制&lt;/strong&gt;：系统启动后，内存中开辟了一段 &lt;code&gt;bool?&lt;/code&gt;（Nullable Boolean）类型的寄存器数组。当执行 &lt;code&gt;SetTasksInteractionTrue(id)&lt;/code&gt; 或 &lt;code&gt;GetTasksInteraction(id)&lt;/code&gt; 时，框架使用 &lt;code&gt;Convert.ToInt32(id)&lt;/code&gt; 对枚举值进行整型强转，直接作为寄存器数组的物理索引，保证了在高频多线程轮询下的无锁高性能访问。&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;框架级变动日志&lt;/strong&gt;：为了便于流程死锁排查，每当调用 &lt;code&gt;SetTasksInteractionTrue&lt;/code&gt; 或 &lt;code&gt;SetTasksInteractionFalse&lt;/code&gt; 使得信号值变更时，底层会&lt;strong&gt;自动调用 &lt;code&gt;AddLog&lt;/code&gt;&lt;/strong&gt; 输出带时间戳的变动日志，高亮显示在主界面的“运行日志”窗口中（例如：“&lt;em&gt;线程交互变量: 组装允许机械手_标志, set value is true&lt;/em&gt;”）。&lt;/li&gt;
&lt;/ul&gt;
&lt;h5&gt;2. 交互 API 的参数细节与核心防呆规范&lt;/h5&gt;
&lt;p&gt;在 &lt;code&gt;mWorkShare&lt;/code&gt; 业务逻辑开发中，应严格遵守以下 API 调用规则：&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;&lt;strong&gt;&lt;code&gt;GetTasksInteraction(Enum id, bool isAutoClear)&lt;/code&gt;&lt;/strong&gt;
&lt;ul&gt;
&lt;li&gt;用于自动运行主流程中的非阻塞条件判定。&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;&lt;code&gt;isAutoClear = true&lt;/code&gt; 的重要性&lt;/strong&gt;：当读取信号状态为 &lt;code&gt;true&lt;/code&gt; 且该方法返回 &lt;code&gt;true&lt;/code&gt; 时，系统在同一个原子操作内将该交互寄存器&lt;strong&gt;复位为 &lt;code&gt;false&lt;/code&gt;&lt;/strong&gt;。这能有效阻断信号的持续粘连，防止状态机在进入下一次循环时被残留信号误触发，从而发生“连续空跑”的严重工艺事故。&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;&lt;code&gt;WaitTaskInteractionTrue(Enum id, int nTimeOut, bool bTimeOutShowDialog, bool isAutoClear)&lt;/code&gt;&lt;/strong&gt;
&lt;ul&gt;
&lt;li&gt;用于辅轴或机械手线程的同步阻塞等待。&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;&lt;code&gt;bTimeOutShowDialog&lt;/code&gt; 选型&lt;/strong&gt;：
&lt;ul&gt;
&lt;li&gt;若设为 &lt;code&gt;true&lt;/code&gt;（默认），当等待超时后，前台 UI 会弹出一个带有“重试/取消”的强交互对话框。线程将挂起等待人工干预，非常适合于安全要求高的物理对位阶段。&lt;/li&gt;
&lt;li&gt;若设为 &lt;code&gt;false&lt;/code&gt;，超时后不弹窗，直接向调用者返回 &lt;code&gt;false&lt;/code&gt;。状态机可以捕获该返回值并跳转至 &lt;code&gt;步序.异常&lt;/code&gt; 进行气缸自动缩回及故障停机逻辑。&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;/ol&gt;
&lt;h5&gt;3. 经典业务场景：多轴主从协同握手设计（打螺丝站时序分析）&lt;/h5&gt;
&lt;p&gt;以 &lt;code&gt;Task02_螺丝站&lt;/code&gt;（主工站）与 &lt;code&gt;Task04_右机械轴&lt;/code&gt; / &lt;code&gt;Task05_左机械轴&lt;/code&gt;（辅轴任务）为例，标准的多轴协同握手流程如下：&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;strong&gt;第一阶段：主站复位与就位&lt;/strong&gt;
系统启动或复位（&lt;code&gt;Homing&lt;/code&gt;）时，主工站必须主动将所有握手信号初始化为 &lt;code&gt;false&lt;/code&gt;，清空一切历史状态：&lt;pre&gt;&lt;code&gt;SetTasksInteractionFalse(TasksInteraction.组装允许机械手_标志);
SetTasksInteractionFalse(TasksInteraction.右轴螺丝工作完成_标志);
SetTasksInteractionFalse(TasksInteraction.左轴螺丝工作完成_标志);
&lt;/code&gt;&lt;/pre&gt;
&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;第二阶段：主站异步广播与防二次触发&lt;/strong&gt;
当产品定位夹紧后，主工站进入 &lt;code&gt;做螺丝工作&lt;/code&gt; 步序。它首先以 &lt;strong&gt;&lt;code&gt;isAutoClear = true&lt;/code&gt;&lt;/strong&gt; 消费清除历史残留信号，然后广播 &lt;code&gt;true&lt;/code&gt; 信号启动左右双轴：&lt;pre&gt;&lt;code&gt;GetTasksInteraction(TasksInteraction.右轴螺丝工作完成_标志, true); // 清空历史残留
GetTasksInteraction(TasksInteraction.左轴螺丝工作完成_标志, true);
SetTasksInteractionTrue(TasksInteraction.组装允许机械手_标志);  // 广播启动信号
&lt;/code&gt;&lt;/pre&gt;
&lt;strong&gt;防二次触发锁&lt;/strong&gt;：一旦检测到左、右轴均已读取信号并脱离了等待状态（例如 StepIdx &amp;gt;= 20），主工站必须&lt;strong&gt;立即&lt;/strong&gt;执行：&lt;pre&gt;&lt;code&gt;SetTasksInteractionFalse(TasksInteraction.组装允许机械手_标志); // 清除启动信号
&lt;/code&gt;&lt;/pre&gt;
如果不及时清除该启动信号，那么当左、右轴执行完毕并返回到等待原点时，检测到该启动信号依然为 &lt;code&gt;true&lt;/code&gt;，会再次被错误启动，造成二次拧紧的事故。&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;第三阶段：辅轴独立动作与完成置位&lt;/strong&gt;
左、右机械轴在各自的 &lt;code&gt;AutoRun()&lt;/code&gt; 步骤中，轮询检查 &lt;code&gt;启动触发标志&lt;/code&gt;（即绑定的 &lt;code&gt;组装允许机械手_标志&lt;/code&gt;）：&lt;pre&gt;&lt;code&gt;if (GetTasksInteraction(启动触发标志, false) == true)
{
    SetStep(ref StaInfo, (int)步序.前往拍照对位, true); // 触发启动
}
&lt;/code&gt;&lt;/pre&gt;
轴动作完成后，各自退出物理干涉区并回到避让高度，然后将自己的完成标志置为 &lt;code&gt;true&lt;/code&gt;：&lt;pre&gt;&lt;code&gt;SetTasksInteractionTrue(完成标志); // 左轴置位左标志，右轴置位右标志
&lt;/code&gt;&lt;/pre&gt;
&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;第四阶段：主站双轴汇合确认&lt;/strong&gt;
主工站在 &lt;code&gt;AutoRun&lt;/code&gt; 中使用非阻塞轮询等待两轴的完成标志，并增加 90 秒最大工作时限保护：&lt;pre&gt;&lt;code&gt;if (GetTasksInteraction(TasksInteraction.右轴螺丝工作完成_标志, false) == true &amp;amp;&amp;amp;
    GetTasksInteraction(TasksInteraction.左轴螺丝工作完成_标志, false) == true)
{
    // 双方均已完成，使用 isAutoClear = true 消费并清除两个完成标志
    GetTasksInteraction(TasksInteraction.右轴螺丝工作完成_标志, true);
    GetTasksInteraction(TasksInteraction.左轴螺丝工作完成_标志, true);
    SetStep(ref StaInfo, (int)步序.等工位3空闲, true); // 进入放行判断
}
else if (mFunction.OverTime(mFunction.ConveyorData[MainConvId].StartTime, 90000))
{
    AddLog(&quot;主站等待双轴螺丝工作超时异常！&quot;, LogsType.Auto, StaInfo.StepIdx, true);
    SetStep(ref StaInfo, (int)步序.异常, true);
}
&lt;/code&gt;&lt;/pre&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;h5&gt;4. 典型交互信号与业务用途说明&lt;/h5&gt;
&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;交互信号枚举值&lt;/th&gt;
&lt;th&gt;触发源 (置为 true)&lt;/th&gt;
&lt;th&gt;消费源 (判定并置为 false)&lt;/th&gt;
&lt;th&gt;业务协同物理目的&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;&lt;code&gt;组装允许机械手_标志&lt;/code&gt;&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;组装螺丝主工站（载具到位夹紧）&lt;/td&gt;
&lt;td&gt;左机械轴、右机械轴&lt;/td&gt;
&lt;td&gt;广播启动信号，通知左右两个打螺丝轴同步脱离等待步序，前往吸取螺丝并拧紧。&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;&lt;code&gt;右轴螺丝工作完成_标志&lt;/code&gt;&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;右机械轴（拧紧完毕且回退安全原点）&lt;/td&gt;
&lt;td&gt;组装螺丝主工站&lt;/td&gt;
&lt;td&gt;反馈右轴动作结束。主工站轮询此标志，确信右轴已完全退出工作干涉区。&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;&lt;code&gt;左轴螺丝工作完成_标志&lt;/code&gt;&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;左机械轴（拧紧完毕且回退安全原点）&lt;/td&gt;
&lt;td&gt;组装螺丝主工站&lt;/td&gt;
&lt;td&gt;反馈左轴动作结束。主工站轮询此标志，确信左轴已完全退出工作干涉区。&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;
&lt;hr /&gt;
&lt;h3&gt;4.6 mFunction 核心工具类与系统状态变量说明&lt;/h3&gt;
&lt;p&gt;&lt;code&gt;mFunction&lt;/code&gt; 是整个 BoTech 软件框架的系统枢纽与静态公共工具类，它统一管理着系统运行状态、参数配置、网络套接字以及底层轴数据结构。在开发工站控制类和辅助逻辑时，它是最常调用的底层类。&lt;/p&gt;
&lt;h5&gt;1. 核心全局状态属性&lt;/h5&gt;
&lt;ul&gt;
&lt;li&gt;
&lt;p&gt;&lt;strong&gt;&lt;code&gt;mFunction.SysState&lt;/code&gt;&lt;/strong&gt;：&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;strong&gt;数据类型&lt;/strong&gt;：&lt;code&gt;mFunction.State&lt;/code&gt; 枚举。&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;作用&lt;/strong&gt;：表示整机系统的当前运行状态。包含：
&lt;ul&gt;
&lt;li&gt;&lt;code&gt;State.RUNNING&lt;/code&gt;：自动运行中。&lt;/li&gt;
&lt;li&gt;&lt;code&gt;State.STOPED&lt;/code&gt;：系统已停止。&lt;/li&gt;
&lt;li&gt;&lt;code&gt;State.ALARM&lt;/code&gt;：当前存在系统报警，三色灯红灯亮，蜂鸣器叫。&lt;/li&gt;
&lt;li&gt;&lt;code&gt;State.WAITRESET&lt;/code&gt;：等待复位。&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;控制逻辑&lt;/strong&gt;：
在工位自动循环线程中，必须在步序开始处实时判断系统是否停止或进入报警。例如：&lt;pre&gt;&lt;code&gt;if ((mFunction.IsSysStop || State == mFunction.State.STOPED) &amp;amp;&amp;amp; mFunction.ConveyorData[MainConvId].StepIdx == 0)
{
    State = mFunction.State.WAITRUN;
    SetStep(ref StaInfo, 0, true);
    break;
}
&lt;/code&gt;&lt;/pre&gt;
一旦 &lt;code&gt;SysState&lt;/code&gt; 变为 &lt;code&gt;State.ALARM&lt;/code&gt;，三色灯红灯会闪烁，蜂鸣器会鸣叫，软件框架后台监控线程会接管所有运动轴发出急停（Stop）指令。&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;&lt;strong&gt;&lt;code&gt;mFunction.LanguageState&lt;/code&gt;&lt;/strong&gt;：&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;strong&gt;作用&lt;/strong&gt;：系统当前设定的显示语言。如 &lt;code&gt;LanguageSet.CHN&lt;/code&gt; (中文)、&lt;code&gt;LanguageSet.ENG&lt;/code&gt; (英文)。在日志输出和提示对话框中，可依此判定加载对应的文字。&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;&lt;strong&gt;&lt;code&gt;mFunction.ConveyorData&lt;/code&gt;&lt;/strong&gt;：&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;strong&gt;数据类型&lt;/strong&gt;：&lt;code&gt;Conveyor&lt;/code&gt; 数组（容量通常为 25 段）。&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;作用&lt;/strong&gt;：流水线状态数组。每一段流线都对应一个数据结构，保存着该流水线段的状态信息，包含：
&lt;ul&gt;
&lt;li&gt;&lt;code&gt;MotorRun&lt;/code&gt;：滚筒电机工作状态（是否正在运行）。&lt;/li&gt;
&lt;li&gt;&lt;code&gt;ProdPres&lt;/code&gt;：产品存在物理传感器信号。&lt;/li&gt;
&lt;li&gt;&lt;code&gt;StepIdx&lt;/code&gt; / &lt;code&gt;SubStepIdx&lt;/code&gt;：段内部步骤索引。&lt;/li&gt;
&lt;li&gt;&lt;code&gt;CustStatus&lt;/code&gt;：工站自定义状态属性（如 &lt;code&gt;&quot;工作中&quot;&lt;/code&gt;、&lt;code&gt;&quot;处理中&quot;&lt;/code&gt;、&lt;code&gt;&quot;&quot;&lt;/code&gt;）。&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;绑定机制&lt;/strong&gt;：工站在 &lt;code&gt;Initialize()&lt;/code&gt; 时，调用 &lt;code&gt;this.BindConv(short ConvID, short[] StateConvIds)&lt;/code&gt;。框架会将工站的 &lt;code&gt;MainConvId&lt;/code&gt; 设为绑定的流线段 ID，将 &lt;code&gt;StateConvIds&lt;/code&gt; 设为依赖的关联流线 ID 数组。工位在运行时会监听 &lt;code&gt;ConveyorData[MainConvId]&lt;/code&gt; 的数据来唤醒本站流程。&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;&lt;strong&gt;&lt;code&gt;mFunction.TcpInfo&lt;/code&gt; / &lt;code&gt;mFunction.TcpIP&lt;/code&gt;&lt;/strong&gt;：&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;strong&gt;作用&lt;/strong&gt;：全局网口连接及缓冲数组。&lt;code&gt;TcpInfo[Index]&lt;/code&gt; 存储接收缓冲区与状态，&lt;code&gt;TcpIP[Index]&lt;/code&gt; 存储 Socket Client 通信实例。&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;&lt;strong&gt;&lt;code&gt;mFunction.AxisIndex&lt;/code&gt;&lt;/strong&gt;：&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;strong&gt;作用&lt;/strong&gt;：保存着由 &lt;code&gt;AxisPar.xlsx&lt;/code&gt; 配置表中读入的各个轴的轴号、所属卡号、每圈脉冲数、导程及减速比等基本元数据。&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;h5&gt;2. 常用全局工具方法 API&lt;/h5&gt;
&lt;ul&gt;
&lt;li&gt;
&lt;p&gt;&lt;strong&gt;&lt;code&gt;mFunction.GetParValue&amp;lt;T&amp;gt;(Enum id)&lt;/code&gt;&lt;/strong&gt;：&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;strong&gt;功能&lt;/strong&gt;：类型安全地读取指定参数的值。&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;类型支持&lt;/strong&gt;：&lt;code&gt;double&lt;/code&gt;（浮点数参数）、&lt;code&gt;int&lt;/code&gt;（整型参数）、&lt;code&gt;string&lt;/code&gt;（字符类型，如工作模式）、&lt;code&gt;bool&lt;/code&gt;（复选框参数）。&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;代码示例&lt;/strong&gt;：&lt;pre&gt;&lt;code&gt;// 读取扫码失败次数参数
int maxRetry = mFunction.GetParValue&amp;lt;int&amp;gt;(UserPar.扫码失败次数);
// 判断是否在虚拟仿真运行模式下
if (mFunction.GetParValue&amp;lt;string&amp;gt;(UserPar.Machine_runMode) == &quot;OffLine_VirtualRun&quot;) { ... }
&lt;/code&gt;&lt;/pre&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;blockquote&gt;
&lt;p&gt;[!TIP]
&lt;strong&gt;延时参数化与拍率优化最佳实践&lt;/strong&gt;：
为了便于在设备调试阶段微调气缸响应、光源稳定及动作时间，本框架将常用的硬编码 &lt;code&gt;Thread.Sleep&lt;/code&gt; 延时全部抽象为 &lt;code&gt;UserPar&lt;/code&gt; 并在 Excel 中配置，以便在线修改：&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;strong&gt;&lt;code&gt;扫码光源稳定延时&lt;/code&gt;&lt;/strong&gt; (&lt;code&gt;UserPar.扫码光源稳定延时&lt;/code&gt; / 默认 &lt;code&gt;500ms&lt;/code&gt;，上限 &lt;code&gt;2000ms&lt;/code&gt;, 下限 &lt;code&gt;0ms&lt;/code&gt;)&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;&lt;code&gt;CCD光源稳定延时&lt;/code&gt;&lt;/strong&gt; (&lt;code&gt;UserPar.CCD光源稳定延时&lt;/code&gt; / 默认 &lt;code&gt;1000ms&lt;/code&gt;，上限 &lt;code&gt;3000ms&lt;/code&gt;, 下限 &lt;code&gt;0ms&lt;/code&gt;)&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;&lt;code&gt;电批吸料稳定延时&lt;/code&gt;&lt;/strong&gt; (&lt;code&gt;UserPar.电批吸料稳定延时&lt;/code&gt; / 默认 &lt;code&gt;200ms&lt;/code&gt;，上限 &lt;code&gt;1000ms&lt;/code&gt;, 下限 &lt;code&gt;50ms&lt;/code&gt;)
通过上述延时参数的上下限管控（例如：电批吸料稳定延时限制最低 &lt;code&gt;50ms&lt;/code&gt; 以保证吸附稳定），既保证了机构响应拍率，又实现了防呆保护。&lt;/li&gt;
&lt;/ul&gt;
&lt;/blockquote&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;&lt;strong&gt;&lt;code&gt;mFunction.GetTickCount()&lt;/code&gt;&lt;/strong&gt;：&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;strong&gt;功能&lt;/strong&gt;：获取高精度自系统启动以来的毫秒时间戳（Tick 数值），主要用于超时计算。&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;&lt;strong&gt;&lt;code&gt;mFunction.OverTime(long StartTime, int SleepTime)&lt;/code&gt;&lt;/strong&gt;：&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;strong&gt;功能&lt;/strong&gt;：判定从指定的 &lt;code&gt;StartTime&lt;/code&gt; 刻度开始，耗时是否已经超过了 &lt;code&gt;SleepTime&lt;/code&gt;（毫秒）。&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;注意要点&lt;/strong&gt;：在自动状态机（AutoRun）的多线程高频循环（Tick）中，&lt;strong&gt;严禁使用 &lt;code&gt;Thread.Sleep&lt;/code&gt; 进行硬等待&lt;/strong&gt;。因为 &lt;code&gt;Thread.Sleep&lt;/code&gt; 会挂起当前的工位时序线程，导致系统对急停、光幕遮挡等防呆信号的响应产生延迟。必须使用 &lt;code&gt;GetTickCount&lt;/code&gt; 记录起点，并在后续步序中用 &lt;code&gt;OverTime&lt;/code&gt; 进行非阻塞时间跨度判断：&lt;pre&gt;&lt;code&gt;case (int)步序.等待气缸伸出:
    if (mGlobal.ReadDi_Bool(InNo.气缸伸出限位))
    {
        SetStep(ref StaInfo, (int)步序.下一动作, true);
    }
    else if (mFunction.OverTime(mFunction.ConveyorData[MainConvId].StartTime, 3000))
    {
        AddLog(&quot;气缸伸出超时！&quot;, LogsType.Alarm, StaInfo.StepIdx, true);
        SetStep(ref StaInfo, (int)步序.异常, true);
    }
    break;
&lt;/code&gt;&lt;/pre&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;&lt;strong&gt;&lt;code&gt;mFunction.ReadXml&amp;lt;T&amp;gt;(string XmlFileName, ref T ReadData)&lt;/code&gt;&lt;/strong&gt;：&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;strong&gt;功能&lt;/strong&gt;：从指定磁盘路径读取并反序列化 XML 文件至泛型对象中，用于加载持久化的非易失数据字典。&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;&lt;strong&gt;&lt;code&gt;mFunction.WriteXml&amp;lt;T&amp;gt;(string XmlFileName, ref T WriteData)&lt;/code&gt;&lt;/strong&gt;：&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;strong&gt;功能&lt;/strong&gt;：将泛型对象序列化并写入指定硬盘路径的 XML 文件。&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;hr /&gt;
&lt;h2&gt;5. 机械臂基类开发最佳实践&lt;/h2&gt;
&lt;p&gt;在多轴模组（如 X/Y/Z 三轴直角坐标机械臂）开发中，若在运行状态下频繁调用 &lt;code&gt;pMove.WaitDone()&lt;/code&gt; 或 &lt;code&gt;mDoDi&lt;/code&gt; 进行手动轮询，容易产生以下问题：&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;工站内由于气缸动作、相机响应和轴动作互相穿插，会导致流程代码冗长、嵌套复杂。&lt;/li&gt;
&lt;li&gt;缺乏安全防呆：如果在 X/Y 轴大行程移动时 Z 轴未处于安全高度，极易引发机械碰撞。&lt;/li&gt;
&lt;/ol&gt;
&lt;h3&gt;5.1 线程安全的安全移动方法设计 (&lt;code&gt;SafeMoveTo&lt;/code&gt;)&lt;/h3&gt;
&lt;p&gt;在 &lt;code&gt;Task_机械轴基类&lt;/code&gt; 中，封装了专为三轴机械手设计的 &lt;code&gt;SafeMoveTo&lt;/code&gt; 方法，能够确保 XY 动作在大行程定位时 Z 轴处于安全高度之上：&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;protected bool SafeMoveTo(ValueType stationId, int pointIdx, double safeZ = 0.0, double offsetX = 0.0, double offsetY = 0.0, double offsetR = 0.0, int timeout = 15000)
{
    try
    {
        // 1. 从本地数据库获取目标点位坐标信息
        PosInfo targetPos = GetPosInfo(stationId, pointIdx);
        double targetX = targetPos.X + offsetX;
        double targetY = targetPos.Y + offsetY;

        AddLog($&quot;[SafeMove] XY轴定位目标: X={targetX:F3}, Y={targetY:F3}&quot;, LogsType.Auto, StaInfo.StepIdx, false);

        // 2. 先安全抬起 Z 轴到 safeZ (例如 0.0)
        if (!MotionAbsMoveAndDone(轴Z, safeZ, -1, timeout))
        {
            AddLog(&quot;Z轴上升至安全高度超时！&quot;, LogsType.Auto, StaInfo.StepIdx, true);
            return false;
        }

        // 3. 异步并发移动 X, Y 轴，大幅缩短移动用时
        MotionAbsMove(轴X, targetX, -1);
        MotionAbsMove(轴Y, targetY, -1);

        // 4. 同步阻塞等待 X, Y 轴均到达目标坐标
        bool xyDone = MotionWaitMoveDone(new int[] { 轴X, 轴Y }, new double[] { targetX, targetY }, timeout);
        if (!xyDone)
        {
            AddLog(&quot;XY轴平移移动超时！&quot;, LogsType.Auto, StaInfo.StepIdx, true);
            return false;
        }

        // 5. 确保 XY 轴就位后，Z 轴下降至目标执行高度
        if (!MotionAbsMoveAndDone(轴Z, targetPos.Z, -1, timeout))
        {
            AddLog(&quot;Z轴下降至目标坐标超时！&quot;, LogsType.Auto, StaInfo.StepIdx, true);
            return false;
        }

        AddLog(&quot;[SafeMove] 已平稳到达目标点位&quot;, LogsType.Auto, StaInfo.StepIdx, false);
        return true;
    }
    catch (Exception ex)
    {
        AddLog($&quot;[SafeMove] 捕获异常: {ex.Message}&quot;, LogsType.Auto, StaInfo.StepIdx, true);
        return false;
    }
}
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;通过调用 &lt;code&gt;SafeMoveTo&lt;/code&gt;，可以将原本需要 5~6 个步骤的轴控制时序合并为单一步骤调用，极大简化了自动流程的开发，提升了代码的健壮性。&lt;/p&gt;
&lt;h3&gt;5.2 框架底层原生运动控制 API 详解&lt;/h3&gt;
&lt;p&gt;在框架底层的 &lt;code&gt;Zcm.Moving&lt;/code&gt; 类（即工站中的 &lt;code&gt;pMove&lt;/code&gt; 对象）中，提供了若干套底层的阻塞或非阻塞的运动控制 API。我们在开发高阶机械轴任务时，应当根据不同的使用场景（如：常规点位对位、离线对位、带纠偏偏置对位等）灵活选择使用。&lt;/p&gt;
&lt;h4&gt;5.2.1 原生阻塞型运动 API (&lt;code&gt;WaitDone&lt;/code&gt;)&lt;/h4&gt;
&lt;p&gt;&lt;code&gt;WaitDone&lt;/code&gt; 系列方法会同步阻塞当前工站线程，直到轴运动就位或检测到运动超时报警。&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;
&lt;p&gt;&lt;strong&gt;多轴示教点联动（带 Z 轴安全高度）&lt;/strong&gt;&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;public bool WaitDone(
    int StationNum, 
    int PointNum, 
    bool MultiAxisSync, 
    double ZLiftHeight, 
    int PosDelayTime, 
    int MaxWaitTime, 
    double LowSpeedApproachDist = 0.0, 
    double LowSpeedApproachSpeed = 0.0, 
    double LowSpeedLiftDist = 0.0, 
    double LowSpeedLiftSpeed = 0.0
)
&lt;/code&gt;&lt;/pre&gt;
&lt;ul&gt;
&lt;li&gt;&lt;strong&gt;使用场景&lt;/strong&gt;：从当前位置安全移动 to 另一个示教点（例如：从取料位置移动至拍照位置）。&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;运行逻辑&lt;/strong&gt;：
&lt;ol&gt;
&lt;li&gt;若 &lt;code&gt;MultiAxisSync&lt;/code&gt; 为 &lt;code&gt;true&lt;/code&gt;，且 &lt;code&gt;ZLiftHeight&lt;/code&gt;（安全抬升高度）设置合理（如 0.0），Z 轴会首先快速上升至 &lt;code&gt;ZLiftHeight&lt;/code&gt;；&lt;/li&gt;
&lt;li&gt;X, Y 等平面轴联动，同步移至目标示教点的 XY 坐标；&lt;/li&gt;
&lt;li&gt;待 XY 轴完全就位后，Z 轴再次下降至该示教点的目标 Z 轴坐标就位。&lt;/li&gt;
&lt;/ol&gt;
&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;优点&lt;/strong&gt;：单行调用，自带防碰撞和 Z 轴优先抬高逻辑。&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;局限&lt;/strong&gt;：&lt;strong&gt;不支持实时坐标偏置参数（如相机纠偏的 OffsetX/OffsetY）&lt;/strong&gt;。&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;&lt;strong&gt;逻辑单轴绝对运动&lt;/strong&gt;&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;public bool WaitDone(ValueType AxisNum, double TargetPos, double Speed, int PosDelayTime, int MaxWaitTime)
&lt;/code&gt;&lt;/pre&gt;
&lt;ul&gt;
&lt;li&gt;&lt;strong&gt;使用场景&lt;/strong&gt;：在知道某个单轴目标坐标时单独移动该轴（例如：Z 轴单独回零后的安全抬升）。&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;运行逻辑&lt;/strong&gt;：控制 &lt;code&gt;AxisNum&lt;/code&gt; 指定的轴以 &lt;code&gt;Speed&lt;/code&gt;（通常传 -1 使用参数配置速度）移动至 &lt;code&gt;TargetPos&lt;/code&gt;，并最多等待 &lt;code&gt;MaxWaitTime&lt;/code&gt; 毫秒就位。&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;/ol&gt;
&lt;h4&gt;5.2.2 原生非阻塞型运动 API (&lt;code&gt;StaXYMove&lt;/code&gt; 系列)&lt;/h4&gt;
&lt;p&gt;如果您需要在示教点的基础坐标上增加相机的对位偏置，且希望自行控制多轴联动的时序，可以使用 &lt;code&gt;StaXYMove&lt;/code&gt; 等异步控制 API。&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;strong&gt;双轴平移异步偏置运动&lt;/strong&gt;：
&lt;code&gt;public bool StaXYMove(ValueType StaID, ValueType PosIndex, double Vel, double OffsetX, double OffsetY)&lt;/code&gt;&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;三轴联移异步偏置运动&lt;/strong&gt;：
&lt;code&gt;public bool StaXYRMove(ValueType StaID, ValueType PosIndex, double Vel, double OffsetX, double OffsetY, double OffsetR)&lt;/code&gt;
&lt;blockquote&gt;
&lt;p&gt;[!NOTE]
这些方法仅负责异步下发 XY(R) 轴的定位指令并叠加偏置，并不会自动执行 Z 轴防撞抬起，也不会同步阻塞等待轴到位。&lt;/p&gt;
&lt;/blockquote&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;hr /&gt;
&lt;h3&gt;5.3 为什么封装 &lt;code&gt;SafeMoveTo&lt;/code&gt; 自定义安全移动函数&lt;/h3&gt;
&lt;p&gt;如 5.2 节所述，底层的 &lt;code&gt;pMove.WaitDone&lt;/code&gt; 存在设计上的“互斥性”：&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;&lt;strong&gt;多轴示教点联动 &lt;code&gt;WaitDone&lt;/code&gt;&lt;/strong&gt; 能够安全自动地处理 Z 轴升降，但&lt;strong&gt;不支持纠偏偏移量&lt;/strong&gt;；&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;非阻塞的 &lt;code&gt;StaXYRMove&lt;/code&gt;&lt;/strong&gt; 支持纠偏偏移量，但&lt;strong&gt;不支持安全 Z 轴防撞和到位检测&lt;/strong&gt;；&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;单轴 &lt;code&gt;WaitDone&lt;/code&gt;&lt;/strong&gt; 支持纠偏坐标，但需要连续编写 4 步同步时序，流程非常繁琐。&lt;/li&gt;
&lt;/ol&gt;
&lt;p&gt;为了合并这一缺陷，我们在 &lt;code&gt;Task_机械轴基类&lt;/code&gt; 中封装了 &lt;code&gt;SafeMoveTo&lt;/code&gt; 方法。它将”Z轴安全上升 -&amp;gt; XY轴纠偏联移（支持 Offset） -&amp;gt; Z轴下降到位”的 4 步过程，通过单行代码进行了底层安全封装，既避免了 XY 轴在大行程移动时撞击治具，又简化了自动流程的实现。&lt;/p&gt;
&lt;hr /&gt;
&lt;h2&gt;6. WorkShare 子对象 API 完整参考&lt;/h2&gt;
&lt;p&gt;WorkShare 基类包含多个子对象，每个子对象提供一组专用 API。这些是工位开发中最常用的方法，全部来源于 DLL 说明书。&lt;/p&gt;
&lt;h3&gt;6.1 mHome — 单轴回零&lt;/h3&gt;
&lt;p&gt;&lt;strong&gt;类型：&lt;/strong&gt; &lt;code&gt;ZHome&lt;/code&gt;
&lt;strong&gt;用途：&lt;/strong&gt; 控制单个轴的回零操作&lt;/p&gt;
&lt;h4&gt;WaitDone&lt;/h4&gt;
&lt;pre&gt;&lt;code&gt;mHome.WaitDone(short axisId)
&lt;/code&gt;&lt;/pre&gt;
&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;参数&lt;/th&gt;
&lt;th&gt;类型&lt;/th&gt;
&lt;th&gt;说明&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;axisId&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;short&lt;/td&gt;
&lt;td&gt;轴编号（对应 &lt;code&gt;mAxis&lt;/code&gt; 枚举）&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;
&lt;p&gt;&lt;strong&gt;阻塞方法&lt;/strong&gt;，执行后线程会等待轴回零完成。&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;使用示例：&lt;/strong&gt;&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;mHome.WaitDone(mAxis.右Z);
if (mHome.RunSts)
{
    AddLog(“右Z轴回零OK”, LogsType.Home);
}
else
{
    AddLog(“右Z轴回零失败”, LogsType.Home);
}
&lt;/code&gt;&lt;/pre&gt;
&lt;h4&gt;RunSts&lt;/h4&gt;
&lt;pre&gt;&lt;code&gt;bool mHome.RunSts
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;回零结果。&lt;code&gt;true&lt;/code&gt; = 回零成功，&lt;code&gt;false&lt;/code&gt; = 回零失败。在 &lt;code&gt;WaitDone&lt;/code&gt; 返回后读取。&lt;/p&gt;
&lt;hr /&gt;
&lt;h3&gt;6.2 dHome — 多轴同时回零&lt;/h3&gt;
&lt;p&gt;&lt;strong&gt;类型：&lt;/strong&gt; &lt;code&gt;DHome&lt;/code&gt;
&lt;strong&gt;用途：&lt;/strong&gt; 控制多个轴同时回零&lt;/p&gt;
&lt;h4&gt;WaitDone&lt;/h4&gt;
&lt;pre&gt;&lt;code&gt;dHome.WaitDone(short[] axisIds, double[] speeds)
&lt;/code&gt;&lt;/pre&gt;
&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;参数&lt;/th&gt;
&lt;th&gt;类型&lt;/th&gt;
&lt;th&gt;说明&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;axisIds&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;short[]&lt;/td&gt;
&lt;td&gt;轴编号数组&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;speeds&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;double[]&lt;/td&gt;
&lt;td&gt;各轴回零速度，-1 表示使用默认速度&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;
&lt;p&gt;&lt;strong&gt;阻塞方法&lt;/strong&gt;，所有轴回零完成后返回。&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;使用示例：&lt;/strong&gt;&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;dHome.WaitDone(
    new short[] { (short)mAxis.右X, (short)mAxis.右Y },
    new double[] { -1, -1 }
);
if (dHome.RunSts)
{
    AddLog(“右XY轴回零OK”, LogsType.Home);
}
&lt;/code&gt;&lt;/pre&gt;
&lt;h4&gt;RunSts&lt;/h4&gt;
&lt;pre&gt;&lt;code&gt;bool dHome.RunSts
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;回零结果。&lt;code&gt;true&lt;/code&gt; = 全部成功，&lt;code&gt;false&lt;/code&gt; = 有失败。&lt;/p&gt;
&lt;hr /&gt;
&lt;h3&gt;6.3 pMove — 位置运动&lt;/h3&gt;
&lt;p&gt;&lt;strong&gt;类型：&lt;/strong&gt; &lt;code&gt;Moving&lt;/code&gt;
&lt;strong&gt;用途：&lt;/strong&gt; 控制轴移动到指定位置（支持点位移动和直接坐标移动）&lt;/p&gt;
&lt;h4&gt;WaitDone（10参数完整版 — 多轴示教点联动）&lt;/h4&gt;
&lt;pre&gt;&lt;code&gt;pMove.WaitDone(
    int StationNum,                 // 工位编号
    int PointNum,                   // 点位编号
    bool MultiAxisSync,             // 是否多轴联动
    double ZLiftHeight,             // Z轴安全高度(mm)
    int PosDelayTime,               // 到位延时(ms)
    int MaxWaitTime,                // 超时(ms)
    double LowSpeedApproachDist,    // 低速趋近距离(mm)
    double LowSpeedApproachSpeed,   // 低速趋近速度(mm/s)
    double LowSpeedLiftDist,        // 低速抬升距离(mm)
    double LowSpeedLiftSpeed        // 低速抬升速度(mm/s)
)
&lt;/code&gt;&lt;/pre&gt;
&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;参数&lt;/th&gt;
&lt;th&gt;类型&lt;/th&gt;
&lt;th&gt;说明&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;StationNum&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;int&lt;/td&gt;
&lt;td&gt;工站编号（对应 &lt;code&gt;mTeachN&lt;/code&gt; 枚举）&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;PointNum&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;int&lt;/td&gt;
&lt;td&gt;点位编号（对应 &lt;code&gt;ePx&lt;/code&gt; 枚举）&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;MultiAxisSync&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;bool&lt;/td&gt;
&lt;td&gt;&lt;code&gt;true&lt;/code&gt;=多轴联动（Z先升→XY移动→Z降），&lt;code&gt;false&lt;/code&gt;=单轴独立移动&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;ZLiftHeight&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;double&lt;/td&gt;
&lt;td&gt;Z轴安全抬升高度（mm），仅 &lt;code&gt;MultiAxisSync=true&lt;/code&gt; 时生效&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;PosDelayTime&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;int&lt;/td&gt;
&lt;td&gt;到位后延时（ms）&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;MaxWaitTime&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;int&lt;/td&gt;
&lt;td&gt;最大等待时间（ms）&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;LowSpeedApproachDist&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;double&lt;/td&gt;
&lt;td&gt;低速趋近距离（mm），到达目标前最后一段距离降速&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;LowSpeedApproachSpeed&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;double&lt;/td&gt;
&lt;td&gt;低速趋近速度（mm/s）&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;LowSpeedLiftDist&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;double&lt;/td&gt;
&lt;td&gt;低速抬升距离（mm）&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;LowSpeedLiftSpeed&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;double&lt;/td&gt;
&lt;td&gt;低速抬升速度（mm/s）&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;
&lt;p&gt;&lt;strong&gt;运行逻辑（MultiAxisSync=true 时）：&lt;/strong&gt;&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;Z 轴先上升至 &lt;code&gt;ZLiftHeight&lt;/code&gt;（安全高度）&lt;/li&gt;
&lt;li&gt;X、Y 轴联动移至目标点位&lt;/li&gt;
&lt;li&gt;XY 到位后，Z 轴下降至目标 Z 坐标&lt;/li&gt;
&lt;/ol&gt;
&lt;p&gt;&lt;strong&gt;使用示例：&lt;/strong&gt;&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;// 多轴联动，Z轴先升到0mm，到位延时10ms，超时15秒
pMove.WaitDone(
    (int)mTeachN.Sta_右待机位置,
    (int)ePx.P0_待机位置,
    true,            // 多轴联动
    0.0,             // Z轴安全高度
    10,              // 到位延时10ms
    15000,           // 超时15秒
    0.0, 0.0, 0.0, 0.0  // 低速趋近参数（0=不使用）
);

// 单轴独立移动（MultiAxisSync=false）
pMove.WaitDone(
    (int)mTeachN.Sta_右待机位置,
    (int)ePx.P0_待机位置,
    false,           // 不联动
    0.0, 10, 15000,
    0.0, 0.0, 0.0, 0.0
);
&lt;/code&gt;&lt;/pre&gt;
&lt;h4&gt;WaitDone（5参数版 — 单轴绝对运动）&lt;/h4&gt;
&lt;pre&gt;&lt;code&gt;pMove.WaitDone(
    ValueType AxisNum,      // 轴编号
    double TargetPos,       // 目标位置(mm)
    double Speed,           // 速度(mm/s)，-1=使用参数配置速度
    int PosDelayTime,       // 到位延时(ms)
    int MaxWaitTime         // 超时(ms)
)
&lt;/code&gt;&lt;/pre&gt;
&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;参数&lt;/th&gt;
&lt;th&gt;类型&lt;/th&gt;
&lt;th&gt;说明&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;AxisNum&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;ValueType&lt;/td&gt;
&lt;td&gt;轴编号&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;TargetPos&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;double&lt;/td&gt;
&lt;td&gt;目标位置（mm）&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;Speed&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;double&lt;/td&gt;
&lt;td&gt;速度（mm/s），&lt;strong&gt;-1=使用参数配置速度&lt;/strong&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;PosDelayTime&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;int&lt;/td&gt;
&lt;td&gt;到位延时（ms）&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;MaxWaitTime&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;int&lt;/td&gt;
&lt;td&gt;最大等待时间（ms）&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;
&lt;p&gt;&lt;strong&gt;使用示例：&lt;/strong&gt;&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;// Z轴移动到等待位，使用默认速度，超时60秒
pMove.WaitDone(上料Z轴, 上料Z_P0等待位.Z, -1, 10, 60000);

// X轴移动到工作位
pMove.WaitDone(搬运X轴, 搬运X_P2工作位.X, -1, 10, 60000);
&lt;/code&gt;&lt;/pre&gt;
&lt;h4&gt;Pause&lt;/h4&gt;
&lt;pre&gt;&lt;code&gt;bool pMove.Pause
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;暂停标志。设为 &lt;code&gt;true&lt;/code&gt; 暂停运动，&lt;code&gt;false&lt;/code&gt; 恢复。在 &lt;code&gt;Homing()&lt;/code&gt; 中通常设为 &lt;code&gt;false&lt;/code&gt;：&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;this.pMove.Pause = false;
&lt;/code&gt;&lt;/pre&gt;
&lt;h4&gt;Pause&lt;/h4&gt;
&lt;pre&gt;&lt;code&gt;bool pMove.Pause
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;暂停标志。设为 &lt;code&gt;true&lt;/code&gt; 暂停运动，&lt;code&gt;false&lt;/code&gt; 恢复。在 &lt;code&gt;Homing()&lt;/code&gt; 中通常设为 &lt;code&gt;false&lt;/code&gt;：&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;this.pMove.Pause = false;
&lt;/code&gt;&lt;/pre&gt;
&lt;hr /&gt;
&lt;h3&gt;6.4 sMove — 单轴运动（非阻塞）&lt;/h3&gt;
&lt;p&gt;&lt;strong&gt;类型：&lt;/strong&gt; &lt;code&gt;OneAxis&lt;/code&gt;
&lt;strong&gt;用途：&lt;/strong&gt; 单轴运动控制（发送指令，不等待到位）&lt;/p&gt;
&lt;h4&gt;常用方法&lt;/h4&gt;
&lt;pre&gt;&lt;code&gt;// 绝对位置移动（发送指令，不阻塞）
sMove.AbsMove(short axisIndex, double position, double speed)

// 停止
sMove.Stop(short axisIndex)
&lt;/code&gt;&lt;/pre&gt;
&lt;hr /&gt;
&lt;h3&gt;6.5 mMove — 多轴运动（非阻塞）&lt;/h3&gt;
&lt;p&gt;&lt;strong&gt;类型：&lt;/strong&gt; &lt;code&gt;MutiAxis&lt;/code&gt;
&lt;strong&gt;用途：&lt;/strong&gt; 多轴联动控制（发送指令，不等待到位）&lt;/p&gt;
&lt;h4&gt;常用方法&lt;/h4&gt;
&lt;pre&gt;&lt;code&gt;// 多轴同时移动（发送指令，不阻塞）
mMove.AbsMove(short[] axisIndexes, double[] positions, double[] speeds)
&lt;/code&gt;&lt;/pre&gt;
&lt;hr /&gt;
&lt;h3&gt;6.6 mDoDi — 数字IO等待&lt;/h3&gt;
&lt;p&gt;&lt;strong&gt;类型：&lt;/strong&gt; &lt;code&gt;DoAndDi&lt;/code&gt;
&lt;strong&gt;用途：&lt;/strong&gt; 设置输出并等待输入条件（阻塞方法）&lt;/p&gt;
&lt;h4&gt;WaitDone（设置输出 + 等待输入）— 7参数版&lt;/h4&gt;
&lt;pre&gt;&lt;code&gt;mDoDi.WaitDone(
    ValueType OutNum,       // 输出点序号
    short nState,           // 输出点目标状态：1=ON, 0=OFF
    ValueType InNum,        // 输入点序号
    short nState1,          // 输入点目标状态：1=ON, 0=OFF
    short DelayTime,        // 到位后延时(ms)
    short TimeOut,          // 超时(ms)，-1=无限等待
    bool Pop_up_message     // 超时是否弹框
)
&lt;/code&gt;&lt;/pre&gt;
&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;参数&lt;/th&gt;
&lt;th&gt;类型&lt;/th&gt;
&lt;th&gt;说明&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;OutNum&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;ValueType&lt;/td&gt;
&lt;td&gt;输出端口号（&lt;code&gt;OutNo&lt;/code&gt; 枚举）&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;nState&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;short&lt;/td&gt;
&lt;td&gt;输出目标状态：&lt;strong&gt;1=ON, 0=OFF&lt;/strong&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;InNum&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;ValueType&lt;/td&gt;
&lt;td&gt;输入端口号（&lt;code&gt;InNo&lt;/code&gt; 枚举）&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;nState1&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;short&lt;/td&gt;
&lt;td&gt;输入目标状态：&lt;strong&gt;1=ON, 0=OFF&lt;/strong&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;DelayTime&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;short&lt;/td&gt;
&lt;td&gt;到位后延时（ms）&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;TimeOut&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;short&lt;/td&gt;
&lt;td&gt;超时（ms），&lt;strong&gt;-1=无限等待&lt;/strong&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;Pop_up_message&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;bool&lt;/td&gt;
&lt;td&gt;超时是否弹框提示&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;
&lt;p&gt;&lt;strong&gt;阻塞方法&lt;/strong&gt;，先设置输出，然后等待输入达到期望状态。&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;使用示例：&lt;/strong&gt;&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;// 设置气缸伸出（输出ON），等待伸出信号（输入ON）
mDoDi.WaitDone(
    OutNo.流线2阻挡气缸, 1,        // 输出：气缸伸出
    InNo.流线2阻挡伸出信号, 1,      // 等待：伸出信号亮
    10, 3000, true                  // 延时10ms，超时3秒，超时弹框
);

// 设置气缸缩回（输出OFF），等待缩回信号（输入ON）
mDoDi.WaitDone(
    OutNo.流线2阻挡气缸, 0,        // 输出：气缸缩回
    InNo.流线2阻挡缩回信号, 1,      // 等待：缩回信号亮
    10, 3000, true
);
&lt;/code&gt;&lt;/pre&gt;
&lt;h4&gt;WaitDone（仅等待输入）— 4参数版&lt;/h4&gt;
&lt;pre&gt;&lt;code&gt;mDoDi.WaitDone(
    ValueType InNum,        // 输入点序号
    short nState1,          // 输入点目标状态：1=ON, 0=OFF
    short DelayTime,        // 到位后延时(ms)
    short TimeOut,          // 超时(ms)
    bool Pop_up_message     // 超时是否弹框
)
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;&lt;strong&gt;使用示例：&lt;/strong&gt;&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;// 等待信号消失
mDoDi.WaitDone(
    InNo.搬运层料盘有无感应, 0,    // 等待信号消失
    10, 5000, true                  // 延时10ms，超时5秒
);
&lt;/code&gt;&lt;/pre&gt;
&lt;h4&gt;mAction&lt;/h4&gt;
&lt;pre&gt;&lt;code&gt;event Action&amp;lt;string, string&amp;gt; mDoDi.mAction
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;错误回调事件。当 &lt;code&gt;WaitDone&lt;/code&gt; 超时时触发。在 &lt;code&gt;Initialize()&lt;/code&gt; 中注册：&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;mDoDi.mAction += Err;
&lt;/code&gt;&lt;/pre&gt;
&lt;hr /&gt;
&lt;h3&gt;6.7 mDoDiS — 简化版数字IO&lt;/h3&gt;
&lt;p&gt;&lt;strong&gt;类型：&lt;/strong&gt; &lt;code&gt;DoAndDiS&lt;/code&gt;
&lt;strong&gt;用途：&lt;/strong&gt; 简化的 IO 控制，支持输出脉冲和批量操作&lt;/p&gt;
&lt;h4&gt;WaitDone — 7参数版（设置输出 + 等待输入）&lt;/h4&gt;
&lt;pre&gt;&lt;code&gt;mDoDiS.WaitDone(
    ValueType OutNum,       // 输出点序号
    short nState,           // 输出点目标状态：1=ON, 0=OFF
    ValueType InNum,        // 输入点序号
    short nState1,          // 输入点目标状态：1=ON, 0=OFF
    short DelayTime,        // 到位后延时(ms)
    short TimeOut,          // 超时(ms)
    bool Pop_up_message     // 超时是否弹框
)
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;功能与 &lt;code&gt;mDoDi.WaitDone&lt;/code&gt; 7参数版相同。&lt;/p&gt;
&lt;h4&gt;WaitDone — 4参数版（仅等待输入）&lt;/h4&gt;
&lt;pre&gt;&lt;code&gt;mDoDiS.WaitDone(
    ValueType InNum,        // 输入点序号
    short nState1,          // 输入点目标状态：1=ON, 0=OFF
    short DelayTime,        // 到位后延时(ms)
    short TimeOut,          // 超时(ms)
    bool Pop_up_message     // 超时是否弹框
)
&lt;/code&gt;&lt;/pre&gt;
&lt;h4&gt;mAction&lt;/h4&gt;
&lt;pre&gt;&lt;code&gt;event Action&amp;lt;string, string&amp;gt; mDoDiS.mAction
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;错误回调事件，用法同 &lt;code&gt;mDoDi.mAction&lt;/code&gt;。&lt;/p&gt;
&lt;hr /&gt;
&lt;h3&gt;6.8 mSend — TCP 发送等待&lt;/h3&gt;
&lt;p&gt;&lt;strong&gt;类型：&lt;/strong&gt; &lt;code&gt;DataSend&lt;/code&gt;
&lt;strong&gt;用途：&lt;/strong&gt; 发送 TCP/串口数据并等待响应（阻塞方法）&lt;/p&gt;
&lt;h4&gt;WaitDone&lt;/h4&gt;
&lt;pre&gt;&lt;code&gt;mSend.WaitDone(
    int mPortIndex,         // 端口号
    int sendType,           // 发送类型
    string SendData,        // 发送数据
    int recvType,           // 接收类型
    string recvStr,         // 匹配字符串
    int nTimeOut,           // 超时(ms)
    bool bTimeOutShowDialog,// 超时是否弹框
    bool nShowLog           // 是否显示日志
)
&lt;/code&gt;&lt;/pre&gt;
&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;参数&lt;/th&gt;
&lt;th&gt;类型&lt;/th&gt;
&lt;th&gt;说明&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;mPortIndex&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;int&lt;/td&gt;
&lt;td&gt;端口号（对应 &lt;code&gt;TCPIP_Port&lt;/code&gt; 枚举或串口编号）&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;sendType&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;int&lt;/td&gt;
&lt;td&gt;&lt;strong&gt;发送类型：0=字节发送, 1=字符串发送&lt;/strong&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;SendData&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;string&lt;/td&gt;
&lt;td&gt;发送的数据内容&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;recvType&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;int&lt;/td&gt;
&lt;td&gt;&lt;strong&gt;接收类型：0=字节接收, 1=字符串接收&lt;/strong&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;recvStr&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;string&lt;/td&gt;
&lt;td&gt;匹配字符串（空字符串=接收任何响应）&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;nTimeOut&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;int&lt;/td&gt;
&lt;td&gt;超时（ms），&lt;strong&gt;-1=无限等待&lt;/strong&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;bTimeOutShowDialog&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;bool&lt;/td&gt;
&lt;td&gt;超时是否弹框提示&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;nShowLog&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;bool&lt;/td&gt;
&lt;td&gt;是否在界面显示日志&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;
&lt;p&gt;&lt;strong&gt;使用示例：&lt;/strong&gt;&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;// 发送字符串指令，等待字符串响应
mSend.WaitDone(
    (int)TCPIP_Port.扫描,  // 端口
    1,                       // sendType: 1=字符串发送
    “ReadCode”,              // 发送数据
    1,                       // recvType: 1=字符串接收
    “”,                      // 匹配字符串（空=任意）
    5000,                    // 超时5秒
    true,                    // 超时弹框
    false                    // 不显示日志
);

// 发送字符串，等待以特定前缀开头的响应
mSend.WaitDone(
    (int)TCPIP_Port.扫描, 1, “ReadCode”, 1, “ReadCode,OK,”, 5000, true, false
);
&lt;/code&gt;&lt;/pre&gt;
&lt;h4&gt;mAction&lt;/h4&gt;
&lt;pre&gt;&lt;code&gt;event Action&amp;lt;string, string&amp;gt; mSend.mAction
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;错误回调事件。在 &lt;code&gt;Initialize()&lt;/code&gt; 中注册：&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;mSend.mAction += Err;
&lt;/code&gt;&lt;/pre&gt;
&lt;h4&gt;mAction&lt;/h4&gt;
&lt;pre&gt;&lt;code&gt;event Action&amp;lt;string, string&amp;gt; mSend.mAction
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;错误回调事件。在 &lt;code&gt;Initialize()&lt;/code&gt; 中注册：&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;mSend.mAction += Err;
&lt;/code&gt;&lt;/pre&gt;
&lt;hr /&gt;
&lt;h3&gt;6.9 mPulseOut — 脉冲输出&lt;/h3&gt;
&lt;p&gt;&lt;strong&gt;类型：&lt;/strong&gt; &lt;code&gt;PulseOut&lt;/code&gt;
&lt;strong&gt;用途：&lt;/strong&gt; 输出指定时长的脉冲信号（阻塞方法）&lt;/p&gt;
&lt;h4&gt;Send&lt;/h4&gt;
&lt;pre&gt;&lt;code&gt;mPulseOut.Send(
    ValueType index,    // 输出端口号
    int nValue,         // 输出状态：1=ON, 0=OFF
    int nDelayTime      // 脉冲持续时间(ms)
)
&lt;/code&gt;&lt;/pre&gt;
&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;参数&lt;/th&gt;
&lt;th&gt;类型&lt;/th&gt;
&lt;th&gt;说明&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;index&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;ValueType&lt;/td&gt;
&lt;td&gt;输出端口号（&lt;code&gt;OutNo&lt;/code&gt; 枚举）&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;nValue&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;int&lt;/td&gt;
&lt;td&gt;输出状态：&lt;strong&gt;1=ON, 0=OFF&lt;/strong&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;nDelayTime&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;int&lt;/td&gt;
&lt;td&gt;脉冲持续时间（ms）&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;
&lt;p&gt;&lt;strong&gt;使用示例：&lt;/strong&gt;&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;// 输出100ms的ON脉冲
mPulseOut.Send(OutNo.蜂鸣器, 1, 100);
&lt;/code&gt;&lt;/pre&gt;
&lt;hr /&gt;
&lt;h3&gt;6.11 mDialog — 对话框&lt;/h3&gt;
&lt;p&gt;&lt;strong&gt;类型：&lt;/strong&gt; &lt;code&gt;Dialog&lt;/code&gt;
&lt;strong&gt;用途：&lt;/strong&gt; 在工位线程中弹出对话框&lt;/p&gt;
&lt;hr /&gt;
&lt;h3&gt;6.12 mDicValue — 等待字典&lt;/h3&gt;
&lt;p&gt;&lt;strong&gt;类型：&lt;/strong&gt; &lt;code&gt;WaitDic&lt;/code&gt;
&lt;strong&gt;用途：&lt;/strong&gt; 基于字典的条件等待&lt;/p&gt;
&lt;hr /&gt;
&lt;h3&gt;6.13 MotionDll 底层 API&lt;/h3&gt;
&lt;p&gt;&lt;strong&gt;命名空间：&lt;/strong&gt; &lt;code&gt;MotionFunction&lt;/code&gt;
&lt;strong&gt;类：&lt;/strong&gt; &lt;code&gt;static class MotionDll&lt;/code&gt;&lt;/p&gt;
&lt;h4&gt;IO 操作&lt;/h4&gt;
&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;方法&lt;/th&gt;
&lt;th&gt;签名&lt;/th&gt;
&lt;th&gt;说明&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;ReadDi&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;&lt;code&gt;int ReadDi(short Index)&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;读取数字输入（返回 0 或 1）&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;ReadDiT&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;&lt;code&gt;bool ReadDiT(short Index)&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;读取 DI，信号存在时返回 true&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;ReadDiF&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;&lt;code&gt;bool ReadDiF(short Index)&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;读取 DI，信号不存在时返回 true&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;ReadDo&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;&lt;code&gt;int ReadDo(short Index)&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;读取数字输出状态&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;WriteDo&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;&lt;code&gt;bool WriteDo(short Index, short Value)&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;写入数字输出&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;DoSet&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;&lt;code&gt;bool DoSet(ValueType Index)&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;设置输出 ON&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;DoReset&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;&lt;code&gt;bool DoReset(ValueType Index)&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;设置输出 OFF&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;WriteDoPls&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;&lt;code&gt;void WriteDoPls(ValueType Index, short Value, int WaitTime)&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;输出脉冲信号&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;
&lt;h4&gt;单轴运动&lt;/h4&gt;
&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;方法&lt;/th&gt;
&lt;th&gt;签名&lt;/th&gt;
&lt;th&gt;说明&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;AbsMotion&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;&lt;code&gt;void AbsMotion(ValueType mCardNum, ValueType mAxis, double Position, double Vel)&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;绝对位置移动（mm, mm/s）&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;AbsMove&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;&lt;code&gt;void AbsMove(axisIndex, position, speed)&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;绝对移动&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;AxisMove&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;&lt;code&gt;void AxisMove(short mAxisIndex, double Position, double Vel)&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;发送运动指令（不等待）&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;AxisMoveAndStop&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;&lt;code&gt;void AxisMoveAndStop(short mAxisIndex, double Position, double Speed, int WaitTime)&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;移动并等待到位&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;StopMove&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;&lt;code&gt;void StopMove(ValueType AxisID, double Speed)&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;停止轴&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;
&lt;h4&gt;工站级运动（阻塞）&lt;/h4&gt;
&lt;pre&gt;&lt;code&gt;// 绝对移动并等待到位（工站级）
MotionDll.MotionAbsMoveAndDone(short axisIndex, double position, double speed, int timeout)
&lt;/code&gt;&lt;/pre&gt;
&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;参数&lt;/th&gt;
&lt;th&gt;类型&lt;/th&gt;
&lt;th&gt;说明&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;axisIndex&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;short&lt;/td&gt;
&lt;td&gt;轴编号&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;position&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;double&lt;/td&gt;
&lt;td&gt;目标位置（mm）&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;speed&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;double&lt;/td&gt;
&lt;td&gt;速度（mm/s），-1=使用配置速度&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;timeout&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;int&lt;/td&gt;
&lt;td&gt;超时（ms）&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;
&lt;p&gt;&lt;strong&gt;使用示例：&lt;/strong&gt;&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;// Z轴移动到安全高度
bool ok = MotionDll.MotionAbsMoveAndDone(轴Z, 0.0, -1, 5000);
if (!ok) { /* 超时处理 */ }
&lt;/code&gt;&lt;/pre&gt;
&lt;h4&gt;工站级运动（非阻塞）&lt;/h4&gt;
&lt;pre&gt;&lt;code&gt;// 绝对移动（不等待到位）
MotionDll.MotionAbsMove(short axisIndex, double position, double speed)
&lt;/code&gt;&lt;/pre&gt;
&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;参数&lt;/th&gt;
&lt;th&gt;类型&lt;/th&gt;
&lt;th&gt;说明&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;axisIndex&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;short&lt;/td&gt;
&lt;td&gt;轴编号&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;position&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;double&lt;/td&gt;
&lt;td&gt;目标位置（mm）&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;speed&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;double&lt;/td&gt;
&lt;td&gt;速度（mm/s），-1=使用配置速度&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;
&lt;h4&gt;多轴到位等待&lt;/h4&gt;
&lt;pre&gt;&lt;code&gt;// 等待多个轴同时到位
bool MotionWaitMoveDone(int[] axisIndexes, double[] targetPositions, int timeout)
&lt;/code&gt;&lt;/pre&gt;
&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;参数&lt;/th&gt;
&lt;th&gt;类型&lt;/th&gt;
&lt;th&gt;说明&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;axisIndexes&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;int[]&lt;/td&gt;
&lt;td&gt;轴编号数组&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;targetPositions&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;double[]&lt;/td&gt;
&lt;td&gt;目标位置数组（mm）&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;timeout&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;int&lt;/td&gt;
&lt;td&gt;超时（ms）&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;
&lt;p&gt;&lt;strong&gt;使用示例：&lt;/strong&gt;&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;// 先异步发送XY移动指令
MotionDll.MotionAbsMove(轴X, targetX, -1);
MotionDll.MotionAbsMove(轴Y, targetY, -1);
// 再同步等待XY都到位
bool ok = MotionDll.MotionWaitMoveDone(new int[]{轴X, 轴Y}, new double[]{targetX, targetY}, 15000);
&lt;/code&gt;&lt;/pre&gt;
&lt;h4&gt;到位检测&lt;/h4&gt;
&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;方法&lt;/th&gt;
&lt;th&gt;签名&lt;/th&gt;
&lt;th&gt;说明&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;ZSPD&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;&lt;code&gt;bool ZSPD(ValueType AxisID)&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;轴到位检查&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;ZSPD&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;&lt;code&gt;bool ZSPD(short CardNum, short Axis, double InPosDist)&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;带误差范围的到位检查&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;SatZSPD&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;&lt;code&gt;bool SatZSPD(short StationID)&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;工站所有轴到位检查&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;
&lt;h4&gt;编码器&lt;/h4&gt;
&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;方法&lt;/th&gt;
&lt;th&gt;签名&lt;/th&gt;
&lt;th&gt;说明&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;GetEncMm&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;&lt;code&gt;double GetEncMm(int axisIndex)&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;获取编码器位置（mm）&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;
&lt;h4&gt;配置&lt;/h4&gt;
&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;方法&lt;/th&gt;
&lt;th&gt;签名&lt;/th&gt;
&lt;th&gt;说明&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;SetSpeedRatio&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;&lt;code&gt;void SetSpeedRatio(double Ratio, bool AllSpeedDn = false)&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;设置全局速度比例（0-1）&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;SetAcc&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;&lt;code&gt;void SetAcc(short AxisID, double Acc, double Dec)&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;设置加减速（m/s），-1=系统默认&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;CardLoad&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;&lt;code&gt;int CardLoad(int, short)&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;卡初始化&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;GetCardPar&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;&lt;code&gt;void GetCardPar(...)&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;传递卡参数&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;GetAxisPar&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;&lt;code&gt;void GetAxisPar(...)&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;传递轴参数&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;
&lt;h4&gt;属性&lt;/h4&gt;
&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;属性&lt;/th&gt;
&lt;th&gt;类型&lt;/th&gt;
&lt;th&gt;说明&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;VirtualMode&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;bool&lt;/td&gt;
&lt;td&gt;离线调试模式（true 时到位检查直接返回 true）&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;mGEN&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;mGEN&lt;/td&gt;
&lt;td&gt;Googol EtherCAT 卡接口&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;mGTN&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;nGTN&lt;/td&gt;
&lt;td&gt;Googol GTN 卡接口&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;
&lt;hr /&gt;
&lt;h3&gt;6.14 mFunction.State 系统状态枚举&lt;/h3&gt;
&lt;pre&gt;&lt;code&gt;public enum State
{
    NONE,           // 无状态
    WAITRESET,      // 等待复位
    RESETTING,      // 复位中
    WAITRUN,        // 等待运行
    RUNNING,        // 运行中
    PAUSE,          // 暂停
    STOPED,         // 停止
    ALARM,          // 报警
    MANUAL,         // 手动模式
}
&lt;/code&gt;&lt;/pre&gt;
&lt;hr /&gt;
&lt;h3&gt;6.15 WorkShare 辅助方法&lt;/h3&gt;
&lt;h4&gt;SetDoBit / ResetDoBit&lt;/h4&gt;
&lt;pre&gt;&lt;code&gt;void SetDoBit(ValueType Index)    // 设置输出ON（暂停时阻塞）
void ResetDoBit(ValueType Index)  // 设置输出OFF（暂停时阻塞）
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;与 &lt;code&gt;mGlobal.mDoSet&lt;/code&gt;/&lt;code&gt;mDoReset&lt;/code&gt; 的区别：&lt;strong&gt;这两个方法在系统暂停时会阻塞&lt;/strong&gt;，直到暂停恢复后才执行。适用于需要暂停安全保护的场景。&lt;/p&gt;
&lt;h4&gt;GetPosInfo&lt;/h4&gt;
&lt;pre&gt;&lt;code&gt;mFunction.PosInfo GetPosInfo(ValueType StaId, ValueType PosIndex)
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;获取系统点位数据。&lt;code&gt;StaId&lt;/code&gt; = 工站编号，&lt;code&gt;PosIndex&lt;/code&gt; = 点位编号。&lt;/p&gt;
&lt;h4&gt;GetPosData&lt;/h4&gt;
&lt;pre&gt;&lt;code&gt;double[] GetPosData(ValueType StaId, ValueType PosIndex)
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;获取系统点位数据（返回 double 数组）。&lt;/p&gt;
&lt;hr /&gt;
&lt;h3&gt;6.16 API 速查表（WorkShare 子对象）&lt;/h3&gt;
&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;API&lt;/th&gt;
&lt;th&gt;说明&lt;/th&gt;
&lt;th&gt;阻塞？&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;mHome.WaitDone(axisId)&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;单轴回零&lt;/td&gt;
&lt;td&gt;✅&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;mHome.RunSts&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;回零结果&lt;/td&gt;
&lt;td&gt;—&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;dHome.WaitDone(axisIds[], speeds[])&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;多轴同时回零&lt;/td&gt;
&lt;td&gt;✅&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;dHome.RunSts&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;回零结果&lt;/td&gt;
&lt;td&gt;—&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;pMove.WaitDone(stationId, posIndex, ...)&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;点位移动（带Z轴安全高度）&lt;/td&gt;
&lt;td&gt;✅&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;pMove.WaitDone(axisId, position, speed, ...)&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;直接坐标移动&lt;/td&gt;
&lt;td&gt;✅&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;pMove.Pause&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;暂停/恢复运动&lt;/td&gt;
&lt;td&gt;—&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;mDoDi.WaitDone(outNum, outState, inNum, inState, ...)&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;设置输出+等待输入&lt;/td&gt;
&lt;td&gt;✅&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;mDoDi.WaitDone(inNum, inState, ...)&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;仅等待输入&lt;/td&gt;
&lt;td&gt;✅&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;mDoDi.mAction += Err&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;注册错误回调&lt;/td&gt;
&lt;td&gt;—&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;mSend.WaitDone(port, sendType, data, ...)&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;TCP发送+等待响应&lt;/td&gt;
&lt;td&gt;✅&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;mSend.mAction += Err&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;注册错误回调&lt;/td&gt;
&lt;td&gt;—&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;SetTasksInteractionTrue(id)&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;设置交互标志为 true&lt;/td&gt;
&lt;td&gt;❌&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;SetTasksInteractionFalse(id)&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;设置交互标志为 false&lt;/td&gt;
&lt;td&gt;❌&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;GetTasksInteraction(id, autoClear)&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;读取交互标志&lt;/td&gt;
&lt;td&gt;❌&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;WaitTaskInteractionTrue(id, timeout, ...)&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;等待标志变为 true&lt;/td&gt;
&lt;td&gt;✅&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;WaitAllTaskInteractionTrue(ids, timeout, ...)&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;等待所有标志为 true&lt;/td&gt;
&lt;td&gt;✅&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;WaitAnyTaskInteractionTrue(ids, timeout, ...)&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;等待任一标志为 true&lt;/td&gt;
&lt;td&gt;✅&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;MotionDll.ReadDi(index)&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;读取数字输入&lt;/td&gt;
&lt;td&gt;❌&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;MotionDll.DoSet(index)&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;设置输出 ON&lt;/td&gt;
&lt;td&gt;❌&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;MotionDll.DoReset(index)&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;设置输出 OFF&lt;/td&gt;
&lt;td&gt;❌&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;MotionDll.AbsMove(axis, pos, speed)&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;绝对移动&lt;/td&gt;
&lt;td&gt;❌&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;MotionDll.StopMove(axis, speed)&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;停止轴&lt;/td&gt;
&lt;td&gt;❌&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;MotionDll.ZSPD(axisId)&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;到位检查&lt;/td&gt;
&lt;td&gt;❌&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;MotionDll.GetEncMm(axisIndex)&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;读取编码器位置&lt;/td&gt;
&lt;td&gt;❌&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;
&lt;hr /&gt;
&lt;h2&gt;7. 传送带（Conveyor）配置系统详解&lt;/h2&gt;
&lt;h3&gt;7.1 核心设计理念&lt;/h3&gt;
&lt;p&gt;在 BoTech 框架中，&lt;strong&gt;传送带的 IO 控制（电机、气缸、传感器）应该配置在传送带框架中，而不是写在 Task 代码里&lt;/strong&gt;。&lt;/p&gt;
&lt;p&gt;标准开发模式：&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;┌─────────────────────────────────────────────────────────┐
│                    Conveyor.xml                           │
│  配置：电机IO、气缸IO、传感器IO、速度、延时、上下游关系       │
└─────────────────────────────────────────────────────────┘
         │
         ↓
┌─────────────────────────────────────────────────────────┐
│               nConveyor 状态机（框架内部）                  │
│  自动执行：启动电机 → 等传感器 → 控制气缸 → 停止电机        │
│  自动处理：流入、到位、减速、流出、阻挡气缸伸缩               │
│  自动处理：异常检测（超时弹框）                              │
└─────────────────────────────────────────────────────────┘
         │
         ↓ CurStnStatus 状态变化
┌─────────────────────────────────────────────────────────┐
│               ConvEvent（A0.Conveyors.cs）                 │
│  ConvRun() 根据 CurStnStatus 分发到：                      │
│    → Data_Change()        数据交换                         │
│    → HandleCurrentStation() 当站处理（等工位完成）           │
│    → Start_Send()         电机启动                         │
│    → Stop_Send()          电机停止                         │
│    → Reduce_Speed()       减速                             │
│    → ReLoadCarrier()      载具重载                         │
│    → 异常处理              弹框重试/取消                     │
└─────────────────────────────────────────────────────────┘
         │
         ↓ CustStatus 握手
┌─────────────────────────────────────────────────────────┐
│               Task（5.Tasks/Task0X_xxx.cs）                │
│  只负责：                                                 │
│    1. 等待 CustStatus == &quot;WAITING_FOR_ASSEMBLY&quot;           │
│    2. 做工位业务（扫码/锁螺丝/出料）                        │
│    3. 设置 CustStatus = &quot;ASSEMBLY_COMPLETED&quot;              │
│  不负责：电机、气缸、传感器（由传送带框架自动控制）           │
└─────────────────────────────────────────────────────────┘
&lt;/code&gt;&lt;/pre&gt;
&lt;h3&gt;7.2 Conveyor.xml 配置文件&lt;/h3&gt;
&lt;p&gt;&lt;strong&gt;路径：&lt;/strong&gt; &lt;code&gt;BZ-Parameter/RBF/Conveyor.xml&lt;/code&gt;&lt;/p&gt;
&lt;p&gt;每个 &lt;code&gt;&amp;lt;Conveyor&amp;gt;&lt;/code&gt; 节点代表一条传送带段，包含完整的 IO 和运动配置。&lt;/p&gt;
&lt;h4&gt;完整字段说明&lt;/h4&gt;
&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;字段&lt;/th&gt;
&lt;th&gt;类型&lt;/th&gt;
&lt;th&gt;说明&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;IO编号&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;string&lt;/td&gt;
&lt;td&gt;电机IO端口编号（逗号分隔）&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;IO控制&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;bool&lt;/td&gt;
&lt;td&gt;&lt;code&gt;true&lt;/code&gt;=IO控制电机，&lt;code&gt;false&lt;/code&gt;=轴控制电机&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;工作速度&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;double&lt;/td&gt;
&lt;td&gt;工作时电机速度&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;流出速度&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;double&lt;/td&gt;
&lt;td&gt;流出时电机速度&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;减速速度&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;double&lt;/td&gt;
&lt;td&gt;减速时电机速度&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;允许同步流入&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;bool&lt;/td&gt;
&lt;td&gt;是否允许与前站同步流入&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;同步流入延时&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;int&lt;/td&gt;
&lt;td&gt;同步流入延时(ms)&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;上一段流线编号&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;int&lt;/td&gt;
&lt;td&gt;前站传送带编号（-1=无）&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;下一段流线编号&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;int&lt;/td&gt;
&lt;td&gt;后站传送带编号（-1=无）&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;阻挡气缸输出编号&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;int&lt;/td&gt;
&lt;td&gt;阻挡气缸输出IO（-1=无气缸）&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;阻挡气缸原点信号&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;int&lt;/td&gt;
&lt;td&gt;气缸缩回到位信号（-1=无）&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;阻挡气缸动点信号&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;int&lt;/td&gt;
&lt;td&gt;气缸伸到位信号（-1=无）&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;起始感应信号&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;int&lt;/td&gt;
&lt;td&gt;流入传感器信号（-1=无）&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;末端减速信号&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;int&lt;/td&gt;
&lt;td&gt;减速传感器信号（-1=无）&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;到位感应信号&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;int&lt;/td&gt;
&lt;td&gt;到位传感器信号（-1=无）&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;流出感应信号&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;int&lt;/td&gt;
&lt;td&gt;流出传感器信号（-1=无）&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;马达轴编号&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;int&lt;/td&gt;
&lt;td&gt;电机轴编号（IO控制时为0）&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;马达启动信号&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;int&lt;/td&gt;
&lt;td&gt;电机启动IO（IO控制时为0）&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;顶升延时&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;int&lt;/td&gt;
&lt;td&gt;顶升延时(ms)&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;到位延时&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;int&lt;/td&gt;
&lt;td&gt;到位后延时(ms)&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;方向调转&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;bool&lt;/td&gt;
&lt;td&gt;电机方向调转&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;
&lt;h4&gt;传送带1 实际配置示例&lt;/h4&gt;
&lt;pre&gt;&lt;code&gt;&amp;lt;Conveyor&amp;gt;
    &amp;lt;IO编号&amp;gt;40,41,42&amp;lt;/IO编号&amp;gt;
    &amp;lt;IO控制&amp;gt;true&amp;lt;/IO控制&amp;gt;
    &amp;lt;工作速度&amp;gt;500&amp;lt;/工作速度&amp;gt;
    &amp;lt;流出速度&amp;gt;500&amp;lt;/流出速度&amp;gt;
    &amp;lt;减速速度&amp;gt;50&amp;lt;/减速速度&amp;gt;
    &amp;lt;上一段流线编号&amp;gt;-1&amp;lt;/上一段流线编号&amp;gt;     &amp;lt;!-- 没有前站 --&amp;gt;
    &amp;lt;下一段流线编号&amp;gt;2&amp;lt;/下一段流线编号&amp;gt;      &amp;lt;!-- 后站是传送带2 --&amp;gt;
    &amp;lt;阻挡气缸输出编号&amp;gt;62&amp;lt;/阻挡气缸输出编号&amp;gt;
    &amp;lt;阻挡气缸原点信号&amp;gt;-1&amp;lt;/阻挡气缸原点信号&amp;gt;
    &amp;lt;阻挡气缸动点信号&amp;gt;73&amp;lt;/阻挡气缸动点信号&amp;gt;
    &amp;lt;到位感应信号&amp;gt;74&amp;lt;/到位感应信号&amp;gt;
    &amp;lt;起始感应信号&amp;gt;-1&amp;lt;/起始感应信号&amp;gt;
    &amp;lt;流出感应信号&amp;gt;-1&amp;lt;/流出感应信号&amp;gt;
    &amp;lt;到位延时&amp;gt;1000&amp;lt;/到位延时&amp;gt;
&amp;lt;/Conveyor&amp;gt;
&lt;/code&gt;&lt;/pre&gt;
&lt;hr /&gt;
&lt;h3&gt;7.3 nConveyor 状态机（框架内部）&lt;/h3&gt;
&lt;p&gt;&lt;code&gt;nConveyor&lt;/code&gt; 是框架提供的传送带状态机，运行在独立线程上。根据 &lt;code&gt;Conveyor.xml&lt;/code&gt; 的配置自动控制电机、气缸和传感器。&lt;/p&gt;
&lt;h4&gt;状态机步进&lt;/h4&gt;
&lt;pre&gt;&lt;code&gt;StepIdx=10   FlowInStart          启动电机，等待产品流入
StepIdx=20   AtPositionCheck      等待到位传感器 + 阻挡气缸
StepIdx=30   DataExchange         从前一站复制产品数据
StepIdx=40   AtPositionClassify   到位分类，停止电机
StepIdx=70   WaitProcessing       通知工位开始工作
StepIdx=80   ProcessingComplete   工位说&quot;做完了&quot;
StepIdx=100  FlowOutStart         启动流出电机
StepIdx=110  ObstacleRetract      阻挡气缸缩回
StepIdx=150  FlowOutEnd           等待产品流走
StepIdx=160  LoopCheck            计算CT，回到10
&lt;/code&gt;&lt;/pre&gt;
&lt;h4&gt;nConveyor 自动控制的 IO&lt;/h4&gt;
&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;步进&lt;/th&gt;
&lt;th&gt;自动控制&lt;/th&gt;
&lt;th&gt;依据配置&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;StepIdx=10&lt;/td&gt;
&lt;td&gt;启动电机&lt;/td&gt;
&lt;td&gt;&lt;code&gt;工作速度&lt;/code&gt;、&lt;code&gt;IO编号&lt;/code&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;StepIdx=20&lt;/td&gt;
&lt;td&gt;等传感器&lt;/td&gt;
&lt;td&gt;&lt;code&gt;到位感应信号&lt;/code&gt;、&lt;code&gt;阻挡气缸动点信号&lt;/code&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;StepIdx=40&lt;/td&gt;
&lt;td&gt;停止电机&lt;/td&gt;
&lt;td&gt;&lt;code&gt;Stop_Send()&lt;/code&gt; 回调&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;StepIdx=100&lt;/td&gt;
&lt;td&gt;启动流出电机&lt;/td&gt;
&lt;td&gt;&lt;code&gt;流出速度&lt;/code&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;StepIdx=110&lt;/td&gt;
&lt;td&gt;缩回阻挡气缸&lt;/td&gt;
&lt;td&gt;&lt;code&gt;阻挡气缸输出编号&lt;/code&gt;、&lt;code&gt;阻挡气缸原点信号&lt;/code&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;StepIdx=150&lt;/td&gt;
&lt;td&gt;停止电机&lt;/td&gt;
&lt;td&gt;&lt;code&gt;Stop_Send()&lt;/code&gt; 回调&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;
&lt;hr /&gt;
&lt;h3&gt;7.4 ConvEvent 用户可编程事件（A0.Conveyors.cs）&lt;/h3&gt;
&lt;p&gt;&lt;code&gt;ConvEvent&lt;/code&gt; 是用户可编程的事件处理器。当 &lt;code&gt;nConveyor&lt;/code&gt; 状态机的状态发生变化时，通过 &lt;code&gt;mEvent&lt;/code&gt; 委托调用 &lt;code&gt;ConvRun()&lt;/code&gt;。&lt;/p&gt;
&lt;h4&gt;ConvRun 主调度器&lt;/h4&gt;
&lt;pre&gt;&lt;code&gt;public void ConvRun(short ConvID)
{
    if (CurStnStatus == &quot;PRODUCT_ARRIVED&quot;)              → Data_Change()
    if (CurStnStatus == &quot;CURRENT_STATION_PROCESSING&quot;)   → HandleCurrentStation()
    if (CurStnStatus == &quot;CARRIER_RELOAD&quot;)               → ReLoadCarrier()
    if (CurStnStatus == &quot;START_TRANSFER&quot;)               → Start_Send()
    if (CurStnStatus == &quot;STOP_TRANSFER&quot;)                → Stop_Send()
    if (CurStnStatus == &quot;START_DECELERATING&quot;)           → Reduce_Speed()
    if (CurStnStatus == &quot;ObstacleRetract_ERROR&quot;)        → 异常弹框
    if (CurStnStatus == &quot;ObstacleLifting_ERROR&quot;)        → 异常弹框
    if (CurStnStatus == &quot;FLOWOUT_ERROR&quot;)                → 异常弹框
    if (CurStnStatus == &quot;RECEIVING_ERROR&quot;)              → 异常弹框
    if (CurStnStatus == &quot;FlowIn_ERROR&quot;)                 → 异常弹框
}
&lt;/code&gt;&lt;/pre&gt;
&lt;h4&gt;HandleCurrentStation — 当站处理（核心）&lt;/h4&gt;
&lt;p&gt;连接传送带框架和 Task 代码的桥梁：&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;public static bool HandleCurrentStation(int StaNum)
{
    switch (StaNum)
    {
        case 1:  // 传送带1
            if (SubStepIdx == 10)
            {
                ConvData.Clear();                              // 清空旧数据
                CustStatus = &quot;WAITING_FOR_ASSEMBLY&quot;;           // ← 通知工位
                SubStepIdx = 20;
            }
            if (CustStatus == &quot;ASSEMBLY_COMPLETED&quot;)            // ← 工位完成
            {
                return true;                                   // 告诉框架：可以流走
            }
            break;
    }
    return false;
}
&lt;/code&gt;&lt;/pre&gt;
&lt;h4&gt;Start_Send / Stop_Send — 电机控制&lt;/h4&gt;
&lt;pre&gt;&lt;code&gt;// 电机启动（框架在 StepIdx=100 时调用）
private static bool Start_Send(int ConvID) { return true; }

// 电机停止（框架在 StepIdx=40/150 时调用）
// 关键：传送带成对共用电机（1-2、3-4、5-6、7-8）
private static bool Stop_Send(int ConvID)
{
    switch (ConvID)
    {
        case 1: case 2:
            if (ConveyorData[1].MotorRun | ConveyorData[2].MotorRun)
                return true;  // 配对还在跑，不停
            return true;
        case 3: case 4:
            if (ConveyorData[3].MotorRun | ConveyorData[4].MotorRun)
                return true;
            return true;
    }
    return true;
}
&lt;/code&gt;&lt;/pre&gt;
&lt;hr /&gt;
&lt;h3&gt;7.5 完整数据流示例&lt;/h3&gt;
&lt;p&gt;以传送带1为例，产品从流入到流出的完整流程：&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;传送带框架（nConveyor）           ConvEvent                    Task01
    │                              │                           │
    │ StepIdx=10: 启动电机          │                           │
    │ 等待产品流入                   │                           │
    │                              │                           │
    │ 产品到达 → 到位传感器亮        │                           │
    │ StepIdx=20: 等到位+气缸       │                           │
    │                              │                           │
    │ StepIdx=30: 数据交换          │                           │
    │ CurStnStatus=&quot;PRODUCT_ARRIVED&quot;│                           │
    │                              │→ ConvRun → Data_Change()  │
    │                              │  清空前站数据               │
    │                              │                           │
    │ StepIdx=40: 停止电机          │                           │
    │ CurStnStatus=&quot;CURRENT_STATION&quot;│                           │
    │       _PROCESSING             │                           │
    │                              │→ ConvRun →                │
    │                              │  HandleCurrentStation()   │
    │                              │  CustStatus=              │
    │                              │  &quot;WAITING_FOR_ASSEMBLY&quot;  ─│─→ 检测到信号
    │                              │                           │  开始扫码
    │                              │                           │  做工作...
    │                              │                           │  完成
    │                              │                           │  CustStatus=
    │                              │  检测到 CustStatus  ←─────│─  &quot;ASSEMBLY_COMPLETED&quot;
    │                              │  == &quot;ASSEMBLY_COMPLETED&quot;  │
    │                              │  return true              │
    │                              │                           │
    │ StepIdx=100: 启动流出电机     │                           │
    │ CurStnStatus=&quot;START_TRANSFER&quot;│                           │
    │                              │→ ConvRun → Start_Send()   │
    │                              │                           │
    │ StepIdx=110: 气缸缩回         │                           │
    │ 产品流出                      │                           │
    │                              │                           │
    │ StepIdx=150: 停止电机         │                           │
    │ CurStnStatus=&quot;STOP_TRANSFER&quot; │                           │
    │                              │→ ConvRun → Stop_Send()    │
    │                              │                           │
    │ StepIdx=160: 回到 StepIdx=10  │                           │
    │                              │                           │
    │ 等待下一个产品...              │                           │
&lt;/code&gt;&lt;/pre&gt;
&lt;hr /&gt;
&lt;h3&gt;7.6 Conveyor.xml 与 InNo/OutNo 的映射关系&lt;/h3&gt;
&lt;p&gt;Conveyor.xml 中的信号编号&lt;strong&gt;直接对应&lt;/strong&gt; &lt;code&gt;EnumName.cs&lt;/code&gt; 中的 &lt;code&gt;InNo&lt;/code&gt; 和 &lt;code&gt;OutNo&lt;/code&gt; 枚举值。&lt;/p&gt;
&lt;h4&gt;各传送带映射&lt;/h4&gt;
&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;传送带&lt;/th&gt;
&lt;th&gt;&lt;code&gt;阻挡气缸输出编号&lt;/code&gt;&lt;/th&gt;
&lt;th&gt;&lt;code&gt;阻挡气缸动点信号&lt;/code&gt;&lt;/th&gt;
&lt;th&gt;&lt;code&gt;到位感应信号&lt;/code&gt;&lt;/th&gt;
&lt;th&gt;&lt;code&gt;上一站&lt;/code&gt;&lt;/th&gt;
&lt;th&gt;&lt;code&gt;下一站&lt;/code&gt;&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;1&lt;/td&gt;
&lt;td&gt;62&lt;/td&gt;
&lt;td&gt;73&lt;/td&gt;
&lt;td&gt;74&lt;/td&gt;
&lt;td&gt;-1&lt;/td&gt;
&lt;td&gt;2&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;2&lt;/td&gt;
&lt;td&gt;64&lt;/td&gt;
&lt;td&gt;77&lt;/td&gt;
&lt;td&gt;78&lt;/td&gt;
&lt;td&gt;1&lt;/td&gt;
&lt;td&gt;3&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;3&lt;/td&gt;
&lt;td&gt;-1&lt;/td&gt;
&lt;td&gt;-1&lt;/td&gt;
&lt;td&gt;83&lt;/td&gt;
&lt;td&gt;2&lt;/td&gt;
&lt;td&gt;-1&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;
&lt;hr /&gt;
&lt;h3&gt;7.7 标准开发模式 vs 当前项目&lt;/h3&gt;
&lt;h4&gt;标准模式（推荐）&lt;/h4&gt;
&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;层级&lt;/th&gt;
&lt;th&gt;职责&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;Conveyor.xml&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;配置电机IO、气缸IO、传感器IO、速度、延时、上下游关系&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;A0.Conveyors.cs&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;HandleCurrentStation: 设置 CustStatus 等工位完成；Start_Send/Stop_Send: 共用电机处理&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;Task 代码&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;只检查 CustStatus，只做业务逻辑，不直接控制电机/气缸&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;
&lt;p&gt;&lt;strong&gt;优点：&lt;/strong&gt; 框架自动处理电机启停、气缸伸缩、传感器等待、异常检测。Task 代码简洁。&lt;/p&gt;
&lt;h4&gt;当前项目模式&lt;/h4&gt;
&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;层级&lt;/th&gt;
&lt;th&gt;职责&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;Task 代码&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;直接控制电机、气缸、检查传感器、处理超时&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;
&lt;p&gt;&lt;strong&gt;缺点：&lt;/strong&gt; Task 代码复杂，IO 操作在等待循环中导致日志刷屏、UI 卡死。&lt;/p&gt;
&lt;hr /&gt;
&lt;h3&gt;7.8 CurStnStatus 完整状态列表&lt;/h3&gt;
&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;状态字符串&lt;/th&gt;
&lt;th&gt;中文&lt;/th&gt;
&lt;th&gt;设置者&lt;/th&gt;
&lt;th&gt;说明&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;&quot;PRODUCT_ARRIVED&quot;&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;产品到位&lt;/td&gt;
&lt;td&gt;nConveyor&lt;/td&gt;
&lt;td&gt;产品到达工位&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;&quot;SWAP_COMPLETED&quot;&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;交换完成&lt;/td&gt;
&lt;td&gt;ConvEvent&lt;/td&gt;
&lt;td&gt;数据交换完成&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;&quot;CURRENT_STATION_PROCESSING&quot;&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;当站处理&lt;/td&gt;
&lt;td&gt;nConveyor&lt;/td&gt;
&lt;td&gt;通知 ConvEvent 开始处理&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;&quot;PROCESSING_COMPLETED&quot;&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;处理完成&lt;/td&gt;
&lt;td&gt;ConvEvent&lt;/td&gt;
&lt;td&gt;工位完成&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;&quot;CARRIER_RELOAD&quot;&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;载具重载&lt;/td&gt;
&lt;td&gt;nConveyor&lt;/td&gt;
&lt;td&gt;需要载具重载&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;&quot;RELOAD_COMPLETED&quot;&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;重载完成&lt;/td&gt;
&lt;td&gt;ConvEvent&lt;/td&gt;
&lt;td&gt;重载完成&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;&quot;START_TRANSFER&quot;&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;开始传送&lt;/td&gt;
&lt;td&gt;nConveyor&lt;/td&gt;
&lt;td&gt;通知启动电机&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;&quot;STOP_TRANSFER&quot;&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;停止传送&lt;/td&gt;
&lt;td&gt;nConveyor&lt;/td&gt;
&lt;td&gt;通知停止电机&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;&quot;START_DECELERATING&quot;&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;开始减速&lt;/td&gt;
&lt;td&gt;nConveyor&lt;/td&gt;
&lt;td&gt;通知减速&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;&quot;DONE&quot;&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;完成&lt;/td&gt;
&lt;td&gt;nConveyor&lt;/td&gt;
&lt;td&gt;流程完成&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;&quot;RESTART&quot;&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;重新开始&lt;/td&gt;
&lt;td&gt;HandleCurrentStation&lt;/td&gt;
&lt;td&gt;流线复位&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;&quot;ObstacleRetract_ERROR&quot;&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;阻挡缩回异常&lt;/td&gt;
&lt;td&gt;nConveyor&lt;/td&gt;
&lt;td&gt;气缸缩回超时&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;&quot;ObstacleLifting_ERROR&quot;&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;阻挡伸出异常&lt;/td&gt;
&lt;td&gt;nConveyor&lt;/td&gt;
&lt;td&gt;气缸伸出超时&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;&quot;FLOWOUT_ERROR&quot;&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;流出异常&lt;/td&gt;
&lt;td&gt;nConveyor&lt;/td&gt;
&lt;td&gt;流出超时&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;&quot;RECEIVING_ERROR&quot;&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;接收异常&lt;/td&gt;
&lt;td&gt;nConveyor&lt;/td&gt;
&lt;td&gt;接收超时&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;&quot;FlowIn_ERROR&quot;&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;流入异常&lt;/td&gt;
&lt;td&gt;nConveyor&lt;/td&gt;
&lt;td&gt;流入超时&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;
&lt;hr /&gt;
&lt;h3&gt;7.9 ConveyorData 运行时属性&lt;/h3&gt;
&lt;p&gt;&lt;code&gt;mFunction.ConveyorData[i]&lt;/code&gt; 的运行时属性：&lt;/p&gt;
&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;属性&lt;/th&gt;
&lt;th&gt;类型&lt;/th&gt;
&lt;th&gt;说明&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;StepIdx&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;int&lt;/td&gt;
&lt;td&gt;状态机当前步进（0=停, 10=流入, 70=等待工位, 160=循环）&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;SubStepIdx&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;int&lt;/td&gt;
&lt;td&gt;子步进（给 HandleCurrentStation 用）&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;CurStnStatus&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;string&lt;/td&gt;
&lt;td&gt;当前工位状态（框架设置）&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;CustStatus&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;string&lt;/td&gt;
&lt;td&gt;自定义状态（工位间通信）&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;ProdPres&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;string&lt;/td&gt;
&lt;td&gt;产品存在：&quot;HAS&quot; / &quot;无&quot;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;ProdStatus&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;string&lt;/td&gt;
&lt;td&gt;产品状态：&quot;OK&quot; / &quot;NG&quot;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;MotorRun&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;bool&lt;/td&gt;
&lt;td&gt;电机是否在转&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;PrevFLNum&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;short&lt;/td&gt;
&lt;td&gt;前站编号（-1=无）&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;NextFLNum&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;short&lt;/td&gt;
&lt;td&gt;后站编号（-1=无）&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;IOControl&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;bool&lt;/td&gt;
&lt;td&gt;IO控制模式&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;BlockCylOutputNum&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;int&lt;/td&gt;
&lt;td&gt;阻挡气缸输出编号&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;PosSensorSig&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;int&lt;/td&gt;
&lt;td&gt;到位传感器信号编号&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;
&lt;hr /&gt;
&lt;h3&gt;7.10 常见问题&lt;/h3&gt;
&lt;p&gt;&lt;strong&gt;Q: 为什么 Task 里的电机控制不生效？&lt;/strong&gt;
A: 如果 Conveyor.xml 中配置了 &lt;code&gt;IO控制=true&lt;/code&gt;，框架会自动控制电机。Task 里的 &lt;code&gt;mDoSet&lt;/code&gt;/&lt;code&gt;mDoReset&lt;/code&gt; 会被覆盖。&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;Q: 传送带1和2为什么会同时停？&lt;/strong&gt;
A: 因为 &lt;code&gt;Stop_Send&lt;/code&gt; 中 1-2、3-4 成对共用电机。停止一条时会检查配对是否还在运行。&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;Q: Conveyor.xml 中的数字和 InNo/OutNo 枚举值不一样？&lt;/strong&gt;
A: Conveyor.xml 用的是 IO 卡的&lt;strong&gt;物理端口号&lt;/strong&gt;，InNo/OutNo 是&lt;strong&gt;逻辑索引&lt;/strong&gt;。通过 &lt;code&gt;ParInput.xml&lt;/code&gt;/&lt;code&gt;ParOutput.xml&lt;/code&gt; 映射。&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;Q: 如何让传送带不停电机？&lt;/strong&gt;
A: 在 &lt;code&gt;Stop_Send&lt;/code&gt; 中设置 &lt;code&gt;mConvSts[ConvID].StepStop = true&lt;/code&gt;，框架不会执行停止代码。&lt;/p&gt;
&lt;hr /&gt;
&lt;h2&gt;8. 核心 API 详解与常见问题&lt;/h2&gt;
&lt;h3&gt;8.1 TasksInteraction 跨线程通信详解&lt;/h3&gt;
&lt;h4&gt;底层原理&lt;/h4&gt;
&lt;p&gt;&lt;code&gt;TasksInteraction&lt;/code&gt; 的本质是一个&lt;strong&gt;全局线程安全寄存器表&lt;/strong&gt;，由 &lt;code&gt;SystemMgr&lt;/code&gt; 单例管理：&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;SystemMgr（全局单例，内存中）
    │
    ├─ 寄存器[0] → bool?  组装允许机械手_标志
    ├─ 寄存器[1] → bool?  右轴螺丝工作完成_标志
    ├─ 寄存器[2] → bool?  左轴螺丝工作完成_标志
    └─ ...
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;每个寄存器可以存三个值：&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;code&gt;null&lt;/code&gt; — 从未设置过&lt;/li&gt;
&lt;li&gt;&lt;code&gt;true&lt;/code&gt; — 被设为 true&lt;/li&gt;
&lt;li&gt;&lt;code&gt;false&lt;/code&gt; — 被设为 false&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;枚举值通过 &lt;code&gt;Convert.ToInt32(id)&lt;/code&gt; 转为 int，直接作为寄存器数组的物理索引。&lt;/p&gt;
&lt;h4&gt;为什么不用普通 bool 变量？&lt;/h4&gt;
&lt;pre&gt;&lt;code&gt;// ❌ 普通 bool — 跨线程不安全
public static bool 右轴完成 = false;

// Task02（线程A）写入
右轴完成 = true;

// Task04（线程B）读取
if (右轴完成) { ... }  // 可能读到旧值！CPU缓存、编译器优化可能导致不可见
&lt;/code&gt;&lt;/pre&gt;
&lt;pre&gt;&lt;code&gt;// ✅ TasksInteraction — 线程安全
SetTasksInteractionTrue(TasksInteraction.右轴螺丝工作完成_标志);   // 线程安全写入
GetTasksInteraction(TasksInteraction.右轴螺丝工作完成_标志);        // 线程安全读取
&lt;/code&gt;&lt;/pre&gt;
&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;对比项&lt;/th&gt;
&lt;th&gt;普通 bool&lt;/th&gt;
&lt;th&gt;TasksInteraction&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;线程安全&lt;/td&gt;
&lt;td&gt;❌ 不安全&lt;/td&gt;
&lt;td&gt;✅ 有锁保护&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;跨线程可见&lt;/td&gt;
&lt;td&gt;❌ 可能读到旧值&lt;/td&gt;
&lt;td&gt;✅ 保证最新值&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;超时等待&lt;/td&gt;
&lt;td&gt;❌ 需自己写循环&lt;/td&gt;
&lt;td&gt;✅ 内置 Wait + 超时 + 弹框&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;自动日志&lt;/td&gt;
&lt;td&gt;❌ 无&lt;/td&gt;
&lt;td&gt;✅ 值变化自动记录&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;适用场景&lt;/td&gt;
&lt;td&gt;同一线程内&lt;/td&gt;
&lt;td&gt;跨线程工位间通信&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;
&lt;h4&gt;SetTasksInteractionTrue&lt;/h4&gt;
&lt;pre&gt;&lt;code&gt;void SetTasksInteractionTrue(Enum id)
&lt;/code&gt;&lt;/pre&gt;
&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;参数&lt;/th&gt;
&lt;th&gt;类型&lt;/th&gt;
&lt;th&gt;说明&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;id&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Enum&lt;/td&gt;
&lt;td&gt;标志枚举值&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;
&lt;p&gt;把指定寄存器设为 &lt;code&gt;true&lt;/code&gt;。值变化时自动写日志。&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;SetTasksInteractionTrue(TasksInteraction.左轴螺丝工作完成_标志);
// 日志：线程交互变量: 左轴螺丝工作完成_标志, set value is true
&lt;/code&gt;&lt;/pre&gt;
&lt;h4&gt;SetTasksInteractionFalse&lt;/h4&gt;
&lt;pre&gt;&lt;code&gt;void SetTasksInteractionFalse(Enum id)
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;把指定寄存器设为 &lt;code&gt;false&lt;/code&gt;。&lt;/p&gt;
&lt;h4&gt;GetTasksInteraction&lt;/h4&gt;
&lt;pre&gt;&lt;code&gt;bool? GetTasksInteraction(Enum id, bool isAutoClear = false)
&lt;/code&gt;&lt;/pre&gt;
&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;参数&lt;/th&gt;
&lt;th&gt;类型&lt;/th&gt;
&lt;th&gt;默认值&lt;/th&gt;
&lt;th&gt;说明&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;id&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Enum&lt;/td&gt;
&lt;td&gt;—&lt;/td&gt;
&lt;td&gt;标志枚举值&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;isAutoClear&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;bool&lt;/td&gt;
&lt;td&gt;&lt;code&gt;false&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;读取后是否自动清空&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;
&lt;p&gt;返回 &lt;code&gt;null&lt;/code&gt;（未设置）、&lt;code&gt;true&lt;/code&gt; 或 &lt;code&gt;false&lt;/code&gt;。&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;// 读取但不清空
bool? result = GetTasksInteraction(TasksInteraction.右轴完成_标志, false);

// 读取后自动清空（一次性信号）
bool? result = GetTasksInteraction(TasksInteraction.右轴完成_标志, true);
&lt;/code&gt;&lt;/pre&gt;
&lt;h4&gt;WaitTaskInteractionTrue&lt;/h4&gt;
&lt;pre&gt;&lt;code&gt;bool WaitTaskInteractionTrue(Enum id, int nTimeOut = -1, bool bTimeOutShowDialog = true, bool isAutoClear = false)
&lt;/code&gt;&lt;/pre&gt;
&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;参数&lt;/th&gt;
&lt;th&gt;类型&lt;/th&gt;
&lt;th&gt;默认值&lt;/th&gt;
&lt;th&gt;说明&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;id&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Enum&lt;/td&gt;
&lt;td&gt;—&lt;/td&gt;
&lt;td&gt;标志枚举值&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;nTimeOut&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;int&lt;/td&gt;
&lt;td&gt;&lt;code&gt;-1&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;超时(ms)，&lt;strong&gt;-1=永远等待&lt;/strong&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;bTimeOutShowDialog&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;bool&lt;/td&gt;
&lt;td&gt;&lt;code&gt;true&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;超时是否弹框&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;isAutoClear&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;bool&lt;/td&gt;
&lt;td&gt;&lt;code&gt;false&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;成功后是否自动清空&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;
&lt;p&gt;&lt;strong&gt;阻塞方法&lt;/strong&gt;，每 10ms 检查一次。暂停时计时器停止。&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;bool ok = WaitTaskInteractionTrue(
    TasksInteraction.左轴螺丝工作完成_标志,
    5000,    // 5秒超时
    true,    // 超时弹框
    true     // 成功后自动清空
);
&lt;/code&gt;&lt;/pre&gt;
&lt;h4&gt;WaitAllTaskInteractionTrue&lt;/h4&gt;
&lt;pre&gt;&lt;code&gt;bool WaitAllTaskInteractionTrue(Enum[] ids, int nTimeOut = -1, bool bTimeOutShowDialog = true, bool isAutoClear = false)
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;等待所有标志&lt;strong&gt;同时&lt;/strong&gt;为 true。&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;bool ok = WaitAllTaskInteractionTrue(
    new Enum[] {
        TasksInteraction.右轴螺丝工作完成_标志,
        TasksInteraction.左轴螺丝工作完成_标志
    },
    5000, true, false
);
&lt;/code&gt;&lt;/pre&gt;
&lt;h4&gt;WaitAnyTaskInteractionTrue&lt;/h4&gt;
&lt;pre&gt;&lt;code&gt;bool WaitAnyTaskInteractionTrue(Enum[] ids, out Enum[] completes, int nTimeOut = -1, bool bTimeOutShowDialog = true, bool isAutoClear = false)
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;等待&lt;strong&gt;任一&lt;/strong&gt;标志为 true。&lt;code&gt;completes&lt;/code&gt; 输出满足条件的标志。&lt;/p&gt;
&lt;hr /&gt;
&lt;h3&gt;8.2 mSend.WaitDone 通信详解&lt;/h3&gt;
&lt;pre&gt;&lt;code&gt;bool mSend.WaitDone(int mPortIndex, int sendType, string SendData, int recvType, string recvStr, int nTimeOut, bool bTimeOutShowDialog, bool nShowLog)
&lt;/code&gt;&lt;/pre&gt;
&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;#&lt;/th&gt;
&lt;th&gt;参数&lt;/th&gt;
&lt;th&gt;类型&lt;/th&gt;
&lt;th&gt;说明&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;1&lt;/td&gt;
&lt;td&gt;&lt;code&gt;mPortIndex&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;int&lt;/td&gt;
&lt;td&gt;端口号（&lt;code&gt;TCPIP_Port&lt;/code&gt; 枚举或串口编号）&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;2&lt;/td&gt;
&lt;td&gt;&lt;code&gt;sendType&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;int&lt;/td&gt;
&lt;td&gt;&lt;strong&gt;0=字节发送, 1=字符串发送&lt;/strong&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;3&lt;/td&gt;
&lt;td&gt;&lt;code&gt;SendData&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;string&lt;/td&gt;
&lt;td&gt;发送的数据&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;4&lt;/td&gt;
&lt;td&gt;&lt;code&gt;recvType&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;int&lt;/td&gt;
&lt;td&gt;&lt;strong&gt;0=字节接收, 1=字符串接收&lt;/strong&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;5&lt;/td&gt;
&lt;td&gt;&lt;code&gt;recvStr&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;string&lt;/td&gt;
&lt;td&gt;匹配字符串（空=接收任何响应）&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;6&lt;/td&gt;
&lt;td&gt;&lt;code&gt;nTimeOut&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;int&lt;/td&gt;
&lt;td&gt;超时(ms)，&lt;strong&gt;-1=无限等待&lt;/strong&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;7&lt;/td&gt;
&lt;td&gt;&lt;code&gt;bTimeOutShowDialog&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;bool&lt;/td&gt;
&lt;td&gt;超时是否弹框&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;8&lt;/td&gt;
&lt;td&gt;&lt;code&gt;nShowLog&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;bool&lt;/td&gt;
&lt;td&gt;是否显示日志&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;
&lt;h4&gt;recvStr 匹配规则&lt;/h4&gt;
&lt;p&gt;框架对接收到的数据做 &lt;strong&gt;前缀匹配（StartsWith）&lt;/strong&gt;：&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;// 接收到的数据
string receivedData = &quot;ReadCode,OK,SN12345678&quot;;

// 匹配检查
receivedData.StartsWith(&quot;ReadCode,OK,&quot;)  // ✅ 匹配成功
&lt;/code&gt;&lt;/pre&gt;
&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;recvStr 值&lt;/th&gt;
&lt;th&gt;行为&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;&quot;&quot;&lt;/code&gt; (空)&lt;/td&gt;
&lt;td&gt;接收任何响应都算成功&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;&quot;ReadCode,OK,&quot;&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;响应必须以这个字符串开头才算成功&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;
&lt;p&gt;不匹配的数据会被忽略，继续等下一条响应，直到超时。&lt;/p&gt;
&lt;h4&gt;使用示例&lt;/h4&gt;
&lt;pre&gt;&lt;code&gt;// 扫码枪通信
bool ok = mSend.WaitDone(
    (int)TCPIP_Port.扫描,  // 端口
    1,                       // 字符串发送
    &quot;ReadCode&quot;,              // 发送数据
    0,                       // 字节接收
    &quot;&quot;,                      // 接收任何响应
    5000,                    // 超时5秒
    true,                    // 超时弹框
    false                    // 不显示日志
);
&lt;/code&gt;&lt;/pre&gt;
&lt;hr /&gt;
&lt;h3&gt;8.3 MainConvId 详解&lt;/h3&gt;
&lt;p&gt;&lt;code&gt;MainConvId&lt;/code&gt; 是基类 &lt;code&gt;WorkShare&lt;/code&gt; 的属性，在 &lt;code&gt;Initialize()&lt;/code&gt; 中通过 &lt;code&gt;BindConv()&lt;/code&gt; 设置：&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;public override void Initialize()
{
    this.BindConv(1, null);  // MainConvId = 1
    // ...
}
&lt;/code&gt;&lt;/pre&gt;
&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;工位&lt;/th&gt;
&lt;th&gt;BindConv&lt;/th&gt;
&lt;th&gt;MainConvId&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;Task01&lt;/td&gt;
&lt;td&gt;&lt;code&gt;BindConv(1, null)&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;1&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Task02&lt;/td&gt;
&lt;td&gt;&lt;code&gt;BindConv(2, null)&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;2&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Task03&lt;/td&gt;
&lt;td&gt;&lt;code&gt;BindConv(3, null)&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;3&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;
&lt;p&gt;使用 &lt;code&gt;MainConvId&lt;/code&gt; 而不是写死数字，保证代码通用：&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;// ✅ 通用写法
mFunction.ConveyorData[MainConvId].StartTime

// ❌ 写死数字
mFunction.ConveyorData[1].StartTime
&lt;/code&gt;&lt;/pre&gt;
&lt;hr /&gt;
&lt;h3&gt;8.4 TipsDiglogForm 弹框详解&lt;/h3&gt;
&lt;pre&gt;&lt;code&gt;AlarmCenter.XAlarmRecord.Instance.TipsDiglogForm(
    int taskId, string taskName, string message,
    string changeWithoutTran, bool isWaitOne,
    string btnOKText, string btnCancelText
)
&lt;/code&gt;&lt;/pre&gt;
&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;#&lt;/th&gt;
&lt;th&gt;参数&lt;/th&gt;
&lt;th&gt;类型&lt;/th&gt;
&lt;th&gt;说明&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;1&lt;/td&gt;
&lt;td&gt;&lt;code&gt;taskId&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;int&lt;/td&gt;
&lt;td&gt;工位ID（用于日志）&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;2&lt;/td&gt;
&lt;td&gt;&lt;code&gt;taskName&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;string&lt;/td&gt;
&lt;td&gt;工位名称（用于日志）&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;3&lt;/td&gt;
&lt;td&gt;&lt;code&gt;message&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;string&lt;/td&gt;
&lt;td&gt;弹框消息&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;4&lt;/td&gt;
&lt;td&gt;&lt;code&gt;changeWithoutTran&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;string&lt;/td&gt;
&lt;td&gt;不翻译的变量（多语言用，通常 &lt;code&gt;&quot;&quot;&lt;/code&gt;）&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;5&lt;/td&gt;
&lt;td&gt;&lt;code&gt;isWaitOne&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;bool&lt;/td&gt;
&lt;td&gt;是否阻塞等待用户点击&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;6&lt;/td&gt;
&lt;td&gt;&lt;code&gt;btnOKText&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;string&lt;/td&gt;
&lt;td&gt;确定按钮文字&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;7&lt;/td&gt;
&lt;td&gt;&lt;code&gt;btnCancelText&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;string&lt;/td&gt;
&lt;td&gt;取消按钮文字（可选）&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;
&lt;p&gt;返回 &lt;code&gt;AlarmCenter.mDialogResult.OK&lt;/code&gt; 或 &lt;code&gt;AlarmCenter.mDialogResult.Cancel&lt;/code&gt;。&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;if (AlarmCenter.XAlarmRecord.Instance.TipsDiglogForm(
    (int)this.TaskID, this.TaskName,
    &quot;是否重试？&quot;, &quot;&quot;, true, &quot;Yes&quot;, &quot;No&quot;) == AlarmCenter.mDialogResult.OK)
{
    // 用户点了 Yes
}
&lt;/code&gt;&lt;/pre&gt;
&lt;hr /&gt;
&lt;h3&gt;8.5 mDoDiWaitDone 详解&lt;/h3&gt;
&lt;pre&gt;&lt;code&gt;mDoDi.WaitDone(outNum, outState, inNum, inState, delayTime, timeout, popUpMessage)
&lt;/code&gt;&lt;/pre&gt;
&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;#&lt;/th&gt;
&lt;th&gt;参数&lt;/th&gt;
&lt;th&gt;类型&lt;/th&gt;
&lt;th&gt;说明&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;1&lt;/td&gt;
&lt;td&gt;&lt;code&gt;outNum&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;ValueType&lt;/td&gt;
&lt;td&gt;输出端口号（&lt;code&gt;OutNo&lt;/code&gt; 枚举）&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;2&lt;/td&gt;
&lt;td&gt;&lt;code&gt;outState&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;short&lt;/td&gt;
&lt;td&gt;输出状态：&lt;strong&gt;1=ON, 0=OFF&lt;/strong&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;3&lt;/td&gt;
&lt;td&gt;&lt;code&gt;inNum&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;ValueType&lt;/td&gt;
&lt;td&gt;输入端口号（&lt;code&gt;InNo&lt;/code&gt; 枚举）&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;4&lt;/td&gt;
&lt;td&gt;&lt;code&gt;inState&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;short&lt;/td&gt;
&lt;td&gt;期望输入状态：&lt;strong&gt;1=ON, 0=OFF&lt;/strong&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;5&lt;/td&gt;
&lt;td&gt;&lt;code&gt;delayTime&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;short&lt;/td&gt;
&lt;td&gt;到位后延时(ms)&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;6&lt;/td&gt;
&lt;td&gt;&lt;code&gt;timeout&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;short&lt;/td&gt;
&lt;td&gt;超时(ms)，&lt;strong&gt;-1=无限等待&lt;/strong&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;7&lt;/td&gt;
&lt;td&gt;&lt;code&gt;popUpMessage&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;bool&lt;/td&gt;
&lt;td&gt;超时是否弹框&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;
&lt;p&gt;&lt;strong&gt;阻塞方法&lt;/strong&gt;，先设置输出，然后等待输入达到期望状态。&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;// 气缸伸出，等伸出信号
mDoDiWaitDone(OutNo.流线1阻挡气缸, 1, InNo.流线1阻挡伸出信号, 1, 10, 3000, true);

// 气缸缩回，等缩回信号
mDoDiWaitDone(OutNo.流线1阻挡气缸, 0, InNo.流线1阻挡缩回信号, 1, 10, 3000, true);
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;&lt;strong&gt;注意：&lt;/strong&gt; &lt;code&gt;mDoDiWaitDone&lt;/code&gt; 是阻塞方法，在 AutoRun 中使用会导致线程阻塞。仅在 Homing 或非循环步骤中使用。AutoRun 中用 &lt;code&gt;mDoSet&lt;/code&gt; + 下一步 &lt;code&gt;ReadDi_Bool&lt;/code&gt; 替代。&lt;/p&gt;
&lt;hr /&gt;
&lt;h3&gt;8.6 mFunction.OverTime 超时判断&lt;/h3&gt;
&lt;pre&gt;&lt;code&gt;bool mFunction.OverTime(long StartTime, int SleepTime)
&lt;/code&gt;&lt;/pre&gt;
&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;参数&lt;/th&gt;
&lt;th&gt;类型&lt;/th&gt;
&lt;th&gt;说明&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;StartTime&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;long&lt;/td&gt;
&lt;td&gt;起始时间戳（&lt;code&gt;GetTickCount()&lt;/code&gt; 获取）&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;SleepTime&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;int&lt;/td&gt;
&lt;td&gt;超时时间(ms)&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;
&lt;p&gt;&lt;strong&gt;非阻塞方法&lt;/strong&gt;，判断从 &lt;code&gt;StartTime&lt;/code&gt; 开始是否已超过 &lt;code&gt;SleepTime&lt;/code&gt; 毫秒。&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;// 记录起点
mFunction.ConveyorData[MainConvId].StartTime = mFunction.GetTickCount();

// 后续步骤中检查（每10ms检查一次）
if (mFunction.OverTime(mFunction.ConveyorData[MainConvId].StartTime, 5000))
{
    // 5秒超时
}
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;&lt;strong&gt;替代 Thread.Sleep 的非阻塞写法：&lt;/strong&gt;&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;// ❌ 阻塞
Thread.Sleep(3000);

// ✅ 非阻塞
StartTime = GetTickCount();
// ... 下一步 ...
if (OverTime(StartTime, 3000)) { /* 3秒到了 */ }
&lt;/code&gt;&lt;/pre&gt;
&lt;hr /&gt;
&lt;h3&gt;8.7 机械轴屏蔽模式详解&lt;/h3&gt;
&lt;pre&gt;&lt;code&gt;if (是否屏蔽)
{
    已持料 = false;
    mGlobal.mDoReset(CCD光源触发信号);
    mGlobal.mDoReset(电批吸真空信号);
    mGlobal.mDoReset(电批破真空信号);
    mGlobal.mDoReset(电批启动信号);

    if (GetTasksInteraction(启动触发标志, false) == true)
    {
        SetTasksInteractionTrue(完成标志);  // 直接回复&quot;完成&quot;
    }
    SetStep(ref StaInfo, (int)步序.等待启动信号, false);
    return;  // 不执行后面的 switch
}
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;&lt;strong&gt;执行流程：&lt;/strong&gt;&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;屏蔽状态（每10ms）：
    清除所有输出 → 收到启动信号？→ 直接回复&quot;完成&quot;
    → SetStep(等待启动信号) → return（不执行switch）

取消屏蔽后：
    是否屏蔽=false → 不进if块 → 执行switch
    → StepIdx 已经是&quot;等待启动信号&quot; → 等产品到位 → 正常工作
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;&lt;strong&gt;为什么 SetStep(等待启动信号)？&lt;/strong&gt; 确保取消屏蔽后，轴从&quot;等产品到位&quot;这个安全状态开始，而不是从&quot;拧紧到一半&quot;或&quot;移动中&quot;继续。&lt;/p&gt;
&lt;hr /&gt;
&lt;h3&gt;8.8 扫码使能检查重复的原因&lt;/h3&gt;
&lt;pre&gt;&lt;code&gt;// Step10: 检查到位信号
if (mGlobal.ReadDi_Bool(InNo.流线1到位信号))
{
    // 产品已经在位（机器重启场景）
    if (启用扫码) { SetStep(电机停扫码); }
    else { SetStep(关光源); }
}

// Step20: 等到位信号
if (WaitDi(InNo.流线1到位信号, 1))
{
    // 产品刚到达（正常流程）
    if (启用扫码) { SetStep(电机停扫码); }
    else { SetStep(关光源); }
}
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;&lt;strong&gt;Step10 处理&quot;产品已在位&quot;（机器重启），Step20 处理&quot;产品刚到达&quot;（正常流程）。两个场景都需要检查扫码使能，所以写了两遍。&lt;/strong&gt;&lt;/p&gt;
&lt;hr /&gt;
&lt;h2&gt;9. UI 控件与代码的映射关系&lt;/h2&gt;
&lt;h3&gt;9.1 核心概念&lt;/h3&gt;
&lt;p&gt;&lt;img src=&quot;/images/in-post/framework-guide/manual_test_control.png&quot; alt=&quot;手动测试控件面板&quot; /&gt;&lt;/p&gt;
&lt;p&gt;UI 控件和 Task 代码之间的桥梁是&lt;strong&gt;枚举映射&lt;/strong&gt;。控件通过枚举值（&lt;code&gt;OutNo&lt;/code&gt;、&lt;code&gt;InNo&lt;/code&gt;、&lt;code&gt;mAxis&lt;/code&gt;、&lt;code&gt;Task_ID&lt;/code&gt;）与代码关联。&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;UI 控件（设计时配置）          Task 代码（运行时调用）
    │                              │
    ├─ StationRun.StationID ──────→ Task_ID 枚举
    ├─ Teach.StationID ──────────→ 工站编号属性
    ├─ Cylinder.OutputIndex ─────→ OutNo 枚举
    ├─ Cylinder.MovingPointNumber → InNo 枚举
    └─ mBtn_Out.OutputIndex ─────→ OutNo 枚举
&lt;/code&gt;&lt;/pre&gt;
&lt;hr /&gt;
&lt;h3&gt;9.2 StationRun → Task 绑定&lt;/h3&gt;
&lt;p&gt;&lt;img src=&quot;/images/in-post/framework-guide/stationRun_control.png&quot; alt=&quot;StationRun 工站启停控件&quot; /&gt;&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;映射关系：&lt;/strong&gt; &lt;code&gt;StationRun.StationID&lt;/code&gt; = &lt;code&gt;Task_ID&lt;/code&gt; 枚举值&lt;/p&gt;
&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;UI 控件&lt;/th&gt;
&lt;th&gt;StationID&lt;/th&gt;
&lt;th&gt;Task 代码&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;stationRun00&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;1&lt;/td&gt;
&lt;td&gt;&lt;code&gt;Task01_入料扫码站&lt;/code&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;stationRun01&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;2&lt;/td&gt;
&lt;td&gt;&lt;code&gt;Task02_螺丝站&lt;/code&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;stationRun02&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;3&lt;/td&gt;
&lt;td&gt;&lt;code&gt;Task03_出料站&lt;/code&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;
&lt;p&gt;&lt;strong&gt;代码绑定（Initialize 中）：&lt;/strong&gt;&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;public override void Initialize()
{
    TaskID = (short)Task_ID.Task01_入料扫码站;
    WkManager.BindStation((short)Task_ID.Task01_入料扫码站, &quot;名称&quot;, this);
    this.BindStationRun(Frm_Task.Instance.stationRun00);  // ← 绑定UI控件
    // ...
}
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;&lt;strong&gt;调用链：&lt;/strong&gt; 用户点击 StationRun.Start() → 框架找到对应 Task → 调用 Ready() → 调用 ManualMode(&quot;模式名&quot;)&lt;/p&gt;
&lt;hr /&gt;
&lt;h3&gt;9.3 Teach → 轴运动映射&lt;/h3&gt;
&lt;p&gt;&lt;img src=&quot;/images/in-post/framework-guide/teach_control.png&quot; alt=&quot;Teach 轴示教控件&quot; /&gt;&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;映射关系：&lt;/strong&gt; &lt;code&gt;Teach.StationID&lt;/code&gt; = 代码中的 &lt;code&gt;工站编号&lt;/code&gt; 属性&lt;/p&gt;
&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;UI 控件&lt;/th&gt;
&lt;th&gt;StationID&lt;/th&gt;
&lt;th&gt;代码属性&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;teach_右轴&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;1&lt;/td&gt;
&lt;td&gt;&lt;code&gt;工站编号 =&amp;gt; 示教工站ID.右取料模组&lt;/code&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;teach_左轴&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;2&lt;/td&gt;
&lt;td&gt;&lt;code&gt;工站编号 =&amp;gt; 示教工站ID.左取料模组&lt;/code&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;
&lt;p&gt;&lt;strong&gt;代码中的使用：&lt;/strong&gt;&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;// 基类定义
protected abstract ValueType 工站编号 { get; }

// 子类实现（映射到 Teach 控件的 StationID）
protected override ValueType 工站编号 =&amp;gt; 示教工站ID.右取料模组;

// 使用时：工站编号 + 点位索引 → 获取坐标 → 移动轴
PosInfo pos = GetPosInfo(工站编号, 取料点位索引);
pMove.WaitDone(轴X, pos.X, -1, 10, 15000);
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;&lt;strong&gt;映射链：&lt;/strong&gt; &lt;code&gt;teach_右轴.StationID&lt;/code&gt; → &lt;code&gt;示教工站ID.右取料模组&lt;/code&gt; → &lt;code&gt;工站编号&lt;/code&gt; 属性 → &lt;code&gt;GetPosInfo()&lt;/code&gt; 读取坐标&lt;/p&gt;
&lt;hr /&gt;
&lt;h3&gt;9.4 Cylinder → IO 映射&lt;/h3&gt;
&lt;p&gt;&lt;img src=&quot;/images/in-post/framework-guide/cylinder_control.png&quot; alt=&quot;Cylinder 气缸控件&quot; /&gt;&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;映射关系：&lt;/strong&gt; &lt;code&gt;Cylinder.OutputIndex&lt;/code&gt; = &lt;code&gt;OutNo&lt;/code&gt; 枚举值，&lt;code&gt;MovingPointNumber&lt;/code&gt;/&lt;code&gt;HomePointNumber&lt;/code&gt; = &lt;code&gt;InNo&lt;/code&gt; 枚举值&lt;/p&gt;
&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;UI 控件&lt;/th&gt;
&lt;th&gt;OutputIndex&lt;/th&gt;
&lt;th&gt;MovingPointNumber&lt;/th&gt;
&lt;th&gt;HomePointNumber&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;流线1阻挡气缸&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;80&lt;/td&gt;
&lt;td&gt;68&lt;/td&gt;
&lt;td&gt;69&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;
&lt;p&gt;&lt;strong&gt;代码中的对应：&lt;/strong&gt;&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;// UI 控件配置
流线1阻挡气缸.OutputIndex = &quot;80&quot;;        // = OutNo.流线1阻挡气缸
流线1阻挡气缸.MovingPointNumber = 68;    // = InNo.流线1阻挡伸出信号
流线1阻挡气缸.HomePointNumber = 69;      // = InNo.流线1阻挡缩回信号

// Task 代码中使用
mGlobal.mDoSet(OutNo.流线1阻挡气缸);                          // 伸出
mGlobal.mDoReset(OutNo.流线1阻挡气缸);                        // 缩回
mGlobal.ReadDi_Bool(InNo.流线1阻挡伸出信号);                   // 读伸出状态
mGlobal.ReadDi_Bool(InNo.流线1阻挡缩回信号);                   // 读缩回状态
mDoDiWaitDone(OutNo.流线1阻挡气缸, 1, InNo.流线1阻挡伸出信号, 1, 10, 3000, true);  // 伸出并等待
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;&lt;strong&gt;映射链：&lt;/strong&gt; &lt;code&gt;Cylinder.OutputIndex&lt;/code&gt; → &lt;code&gt;OutNo&lt;/code&gt; 枚举 → &lt;code&gt;mDoSet&lt;/code&gt;/&lt;code&gt;mDoReset&lt;/code&gt;&lt;/p&gt;
&lt;hr /&gt;
&lt;h3&gt;9.5 mBtn_Out → IO 映射&lt;/h3&gt;
&lt;p&gt;&lt;img src=&quot;/images/in-post/framework-guide/mbtn_out_control.png&quot; alt=&quot;mBtn_Out 输出按钮控件&quot; /&gt;&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;映射关系：&lt;/strong&gt; &lt;code&gt;mBtn_Out.OutputIndex&lt;/code&gt; = &lt;code&gt;OutNo&lt;/code&gt; 枚举值&lt;/p&gt;
&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;UI 控件&lt;/th&gt;
&lt;th&gt;OutputIndex&lt;/th&gt;
&lt;th&gt;代码对应&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;mBtn_右电批启动&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;56&lt;/td&gt;
&lt;td&gt;&lt;code&gt;OutNo.右电批Start&lt;/code&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;mBtn_右电批reset&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;57&lt;/td&gt;
&lt;td&gt;&lt;code&gt;OutNo.右电批Reset&lt;/code&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;mBtn_右吸真空&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;60&lt;/td&gt;
&lt;td&gt;&lt;code&gt;OutNo.右电批吸真空&lt;/code&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;mBtn_右破真空&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;61&lt;/td&gt;
&lt;td&gt;&lt;code&gt;OutNo.右电批破真空&lt;/code&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;
&lt;p&gt;&lt;strong&gt;代码中的对应：&lt;/strong&gt;&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;// UI 控件配置
mBtn_右电批启动.OutputIndex = 56;     // = OutNo.右电批Start

// Task 代码中使用
mGlobal.mDoSet(OutNo.右电批Start);    // 启动
mGlobal.mDoReset(OutNo.右电批Start);  // 停止
&lt;/code&gt;&lt;/pre&gt;
&lt;hr /&gt;
&lt;h3&gt;9.6 完整映射关系图&lt;/h3&gt;
&lt;p&gt;:::carousel
&lt;img src=&quot;/images/in-post/framework-guide/conveyor_auto_control.png&quot; alt=&quot;Conveyor 传送带自动控制面板&quot; /&gt;
&amp;lt;!-- slide --&amp;gt;
&lt;img src=&quot;/images/in-post/framework-guide/conv_status_control.png&quot; alt=&quot;Conveyor 状态控制面板&quot; /&gt;
:::&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;┌─────────────────────────────────────────────────────────────┐
│                        UI 控件层                             │
│                                                              │
│  StationRun        Teach           Cylinder      mBtn_Out   │
│  (stationRun00)    (teach_右轴)    (流线1阻挡气缸) (mBtn_右电批)│
│  StationID=1       StationID=1     OutputIndex=80  Index=56  │
│       │                │           MovingPt=68                │
│       │                │           HomePt=69                  │
└───────┼────────────────┼───────────────┼──────────────┼──────┘
        │                │               │              │
        │    ┌───────────┘               │              │
        │    │                           │              │
┌───────┼────┼───────────────────────────┼──────────────┼──────┐
│       │    │          Task 代码层       │              │      │
│       │    │                           │              │      │
│  Initialize()      工站编号属性         OutNo枚举      OutNo  │
│  BindStationRun()  GetPosInfo()        mDoSet()       枚举   │
│  ManualMode()      pMove.WaitDone()    mDoReset()           │
│       │    │               │               │              │  │
└───────┼────┼───────────────┼───────────────┼──────────────┘
        │    │               │               │
        │    │               │               │
┌───────┼────┼───────────────┼───────────────┼──────────────────┐
│       │    │          底层 API 层          │                  │
│       │    │                               │                  │
│  WkManager    MotionDll              MotionDll.DoSet/DoReset │
│  .BindStation .AbsMove()             MotionDll.ReadDi()      │
│  .TaskStart() .WaitDone()                                  │
└──────────────────────────────────────────────────────────────┘
&lt;/code&gt;&lt;/pre&gt;
</content:encoded></item><item><title>Google Antigravity 代理使用</title><link>https://meteor-comet.github.io/posts/antigravity-proxy-guide/</link><guid isPermaLink="true">https://meteor-comet.github.io/posts/antigravity-proxy-guide/</guid><description>Gemini 3 IDE登录与使用问题完整解决方案</description><pubDate>Fri, 28 Nov 2025 00:00:00 GMT</pubDate><content:encoded>&lt;h1&gt;Google Antigravity 代理指南&lt;/h1&gt;
&lt;h2&gt;前言&lt;/h2&gt;
&lt;p&gt;谷歌的Gemini 3 发布了，很强大。而且这次他们还发布了IDE - Antigravity。但对于中国用户来说，又遇到了老生常谈的问题，登录不上，使用不了。本文介绍我个人解决 Google Antigravity 无法登录以及其他问题的经验。&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;/images/in-post/antigravity-preview.svg&quot; alt=&quot;Antigravity IDE 界面&quot; /&gt;&lt;/p&gt;
&lt;h2&gt;常见问题&lt;/h2&gt;
&lt;p&gt;从我个人使用过程来看，主要是下面几个地方会卡住：&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;&lt;strong&gt;登录谷歌账号的时候&lt;/strong&gt;，显示成功但返回不了 Antigravity，软件上无法点击下一步。&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;成功登录之后&lt;/strong&gt;，软件上显示 &quot;Setting up your account&quot;，一直转圈。&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;模型加载阶段&lt;/strong&gt;，一直卡在 &quot;Loading models&quot;，模型加载不出来。&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;使用过程中&lt;/strong&gt;，本来用着好好的，提示账号 is not eligible。&lt;/li&gt;
&lt;/ol&gt;
&lt;h2&gt;解决方案一：代理模式调整&lt;/h2&gt;
&lt;p&gt;这些问题的核心问题还是在于代理。以下是我亲测有效的解决办法：&lt;/p&gt;
&lt;h3&gt;1. 登录授权卡住问题&lt;/h3&gt;
&lt;p&gt;&lt;strong&gt;问题描述&lt;/strong&gt;：刚打开 Antigravity，根据提示一步步设置，然后要求登录 Google 账号的时候，就显示已经成功授权，点击返回 Antigravity，但是就是没反应。&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;解决办法&lt;/strong&gt;：打开你代理客户端的 &lt;strong&gt;TUN 模式（虚拟网卡）&lt;/strong&gt;，然后选择&lt;strong&gt;非香港节点&lt;/strong&gt;。我当时使用大哥云的新加坡节点通过了这步。&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;重要提示&lt;/strong&gt;：这一步其实也没有要求你账号的区域，我香港地区的账号一样通过了授权。&lt;/p&gt;
&lt;h3&gt;2. 账号设置转圈问题&lt;/h3&gt;
&lt;p&gt;&lt;strong&gt;问题描述&lt;/strong&gt;：通过授权返回 Antigravity 时，就一直在 &quot;Setting up your account&quot; 这里转圈。我转了一个多小时还是没成。&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;解决办法&lt;/strong&gt;：切换节点或者机场。我直接睡觉了，今天上午换了龙猫云的新加坡节点就过去了。&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;社区经验&lt;/strong&gt;：我在 Linux Do 上看到有人说他今天早上重新登录，香港节点垃圾节点+香港的地区账号+规则代理也通过了。所以可能也是运气问题。&lt;/p&gt;
&lt;h3&gt;3. 账号资格问题&lt;/h3&gt;
&lt;p&gt;这一步你可能会遇到两个报错：&lt;/p&gt;
&lt;h4&gt;报错一：地区限制&lt;/h4&gt;
&lt;pre&gt;&lt;code&gt;Your current account is not eligible for Antigravity, because it is not
currently available in your location.
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;&lt;strong&gt;解决办法&lt;/strong&gt;：账号资格这块，就去改账号地区或者重新去注册一个美国区的Google账号。&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;更改地区步骤&lt;/strong&gt;：&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;查看地区：&lt;a href=&quot;https://policies.google.com/terms&quot;&gt;https://policies.google.com/terms&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;切换地区：&lt;a href=&quot;https://policies.google.com/country-association-form&quot;&gt;https://policies.google.com/country-association-form&lt;/a&gt;&lt;/li&gt;
&lt;/ol&gt;
&lt;p&gt;&lt;strong&gt;注意&lt;/strong&gt;：不过也有很多人被卡在修改这步了，被拒了。&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;重要发现&lt;/strong&gt;：但是我分别用一个免费账号和Pro账号做了试验，如果你的账号本身有 Gemini Pro 的话，好像就不会遇到报错。&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;P.S. 之前白嫖的一年 Gemini Pro 物超所值的感觉啊。&lt;/p&gt;
&lt;/blockquote&gt;
&lt;h4&gt;报错二：账号不符合要求&lt;/h4&gt;
&lt;pre&gt;&lt;code&gt;Your current account is not eligible for Antigravity. Try signing in with another personal Google account.
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;&lt;strong&gt;解决办法&lt;/strong&gt;：先去下个工具：&lt;a href=&quot;https://github.com/lbjlaq/Antigravity-Manager&quot;&gt;Antigravity-Manager&lt;/a&gt;&lt;/p&gt;
&lt;p&gt;登录你的账号，然后按照工具中的提示操作。&lt;/p&gt;
&lt;h2&gt;解决方案二：使用 Proxifier&lt;/h2&gt;
&lt;p&gt;补充一个方法，这几天在网上看到的。除了直接开 TUN 模式之外，还可以使用 Proxifier，这是一个代理客户端。搭配梯子使用，它能让 Antigravity 也乖乖得走代理。&lt;/p&gt;
&lt;h3&gt;下载并激活 Proxifier&lt;/h3&gt;
&lt;ol&gt;
&lt;li&gt;
&lt;p&gt;&lt;strong&gt;下载安装 Proxifier&lt;/strong&gt;：访问官网下载并安装&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;&lt;strong&gt;软件激活&lt;/strong&gt;：这个软件可以试用，我在网上找到了别人逆向出来的Proxifier激活码。（源：Github）&lt;/p&gt;
&lt;/li&gt;
&lt;/ol&gt;
&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;版本&lt;/th&gt;
&lt;th&gt;激活码&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;Windows安装版&lt;/td&gt;
&lt;td&gt;CLOT5-J3GYK-VGPYE-BDPMN-WKWMU&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Windows便携版&lt;/td&gt;
&lt;td&gt;NY8VX-Z2NH2-TFXWY-IL5YC-GARRM&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Mac&lt;/td&gt;
&lt;td&gt;57J8Z-D2QD5-A37WU-LEG4E-43WYH&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;
&lt;h3&gt;配置代理服务器&lt;/h3&gt;
&lt;ol&gt;
&lt;li&gt;点击菜单 &lt;strong&gt;Profile &amp;gt; Proxy Servers&lt;/strong&gt;&lt;/li&gt;
&lt;li&gt;填写配置信息：
&lt;ul&gt;
&lt;li&gt;&lt;strong&gt;Address&lt;/strong&gt;：填写 &lt;code&gt;127.0.0.1&lt;/code&gt;&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Port&lt;/strong&gt;：填写你自己的端口，Clash一般默认是 &lt;code&gt;7890&lt;/code&gt;&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Protocol&lt;/strong&gt;：协议选 &lt;code&gt;Socks 5&lt;/code&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;完成后点击 &lt;strong&gt;OK&lt;/strong&gt; 回到主界面&lt;/li&gt;
&lt;/ol&gt;
&lt;h3&gt;给 Antigravity 设置规则&lt;/h3&gt;
&lt;ol&gt;
&lt;li&gt;点击 &lt;strong&gt;Profile &amp;gt; Proxification Rules&lt;/strong&gt;&lt;/li&gt;
&lt;li&gt;点击 &lt;strong&gt;Add&lt;/strong&gt; 添加规则：
&lt;ul&gt;
&lt;li&gt;&lt;strong&gt;Name&lt;/strong&gt;：随便填，比如 &quot;Antigravity&quot;&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Applications&lt;/strong&gt;：填 &lt;code&gt;antigravity.exe; language_server_windows_x64.exe&lt;/code&gt; 或者你点 Browse 自己选取&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Target host&lt;/strong&gt; 和 &lt;strong&gt;Target ports&lt;/strong&gt;：不用填&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Action&lt;/strong&gt;：选择 &lt;code&gt;Proxy SOCKS5 127.0.0.1&lt;/code&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;点击 &lt;strong&gt;OK&lt;/strong&gt; 保存&lt;/li&gt;
&lt;/ol&gt;
&lt;p&gt;然后梯子就继续选择系统代理就行了。&lt;/p&gt;
&lt;h2&gt;总结&lt;/h2&gt;
&lt;p&gt;总结一下，使用 Google Antigravity 的时候：&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;&lt;strong&gt;确保你开启了TUN模式&lt;/strong&gt;。至于规则还是全局无所谓。或者搭配使用 Proxifier。&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;使用非香港节点&lt;/strong&gt;。建议多尝试一些节点。&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;最好是有 Gemini Pro&lt;/strong&gt;，我觉得遇到的玄学问题会少一些，而且有 Pro的话，你的免费配额也会更多一些。&lt;/li&gt;
&lt;/ol&gt;
&lt;h2&gt;常见问题排查清单&lt;/h2&gt;
&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;问题&lt;/th&gt;
&lt;th&gt;可能原因&lt;/th&gt;
&lt;th&gt;解决方案&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;登录后无法返回 Antigravity&lt;/td&gt;
&lt;td&gt;代理不支持 OAuth 回调&lt;/td&gt;
&lt;td&gt;开启 TUN 模式&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Setting up your account 一直转圈&lt;/td&gt;
&lt;td&gt;节点速度太慢或被限制&lt;/td&gt;
&lt;td&gt;更换节点&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Loading models 卡住&lt;/td&gt;
&lt;td&gt;网络连接不稳定&lt;/td&gt;
&lt;td&gt;检查代理连接&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;提示账号不可用&lt;/td&gt;
&lt;td&gt;账号地区限制&lt;/td&gt;
&lt;td&gt;更改账号地区或使用新账号&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;
&lt;h2&gt;快速开始：3步搞定&lt;/h2&gt;
&lt;p&gt;如果你不想看那么多细节，可以直接按以下步骤操作：&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;&lt;strong&gt;确保代理开启 TUN 模式&lt;/strong&gt;，选择新加坡或美国节点&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;打开 Antigravity&lt;/strong&gt;，正常登录 Google 账号&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;耐心等待&lt;/strong&gt;，如果某个步骤卡住超过 10 分钟，尝试重启软件或更换节点&lt;/li&gt;
&lt;/ol&gt;
&lt;h2&gt;TUN 模式配置指南&lt;/h2&gt;
&lt;h3&gt;Clash 开启 TUN 模式&lt;/h3&gt;
&lt;p&gt;如果你使用的是 Clash 作为代理客户端，按以下步骤开启 TUN 模式：&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;打开 Clash 配置界面&lt;/li&gt;
&lt;li&gt;找到 &quot;TUN&quot; 或 &quot;虚拟网卡&quot; 设置&lt;/li&gt;
&lt;li&gt;开启 &quot;Enable TUN Device&quot; 或类似选项&lt;/li&gt;
&lt;li&gt;重启 Clash 使设置生效&lt;/li&gt;
&lt;/ol&gt;
&lt;p&gt;&lt;strong&gt;注意&lt;/strong&gt;：开启 TUN 模式可能需要管理员权限。&lt;/p&gt;
&lt;h3&gt;其他代理客户端&lt;/h3&gt;
&lt;ul&gt;
&lt;li&gt;&lt;strong&gt;Clash for Windows&lt;/strong&gt;：设置 &amp;gt; 系统 &amp;gt; 开启 &quot;TUN 模式&quot;&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;ClashX Pro&lt;/strong&gt;（Mac）：菜单栏 &amp;gt; TUN &amp;gt; 开启&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;V2RayN&lt;/strong&gt;：需要额外配置，建议参考官方文档&lt;/li&gt;
&lt;/ul&gt;
&lt;h2&gt;常见问题补充&lt;/h2&gt;
&lt;h3&gt;模型加载失败怎么办？&lt;/h3&gt;
&lt;p&gt;如果一直卡在 &quot;Loading models&quot;：&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;首先检查你的网络连接是否稳定&lt;/li&gt;
&lt;li&gt;尝试更换节点（有时候当前节点被限速了）&lt;/li&gt;
&lt;li&gt;确保你的代理连接是持续的，没有断开&lt;/li&gt;
&lt;li&gt;如果是第一次使用，模型需要下载，需要等待较长时间&lt;/li&gt;
&lt;/ol&gt;
&lt;h3&gt;为什么建议使用 Gemini Pro？&lt;/h3&gt;
&lt;ol&gt;
&lt;li&gt;&lt;strong&gt;减少报错&lt;/strong&gt;：有 Pro 的账号遇到资格问题的概率更低&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;更多配额&lt;/strong&gt;：Pro 账号的免费使用额度更多&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;优先访问&lt;/strong&gt;：可能有更好的服务器资源&lt;/li&gt;
&lt;/ol&gt;
&lt;h3&gt;如何检查我的账号是否有 Gemini Pro？&lt;/h3&gt;
&lt;ol&gt;
&lt;li&gt;访问 &lt;a href=&quot;https://gemini.google.com/&quot;&gt;Gemini 官网&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;登录你的 Google 账号&lt;/li&gt;
&lt;li&gt;查看是否有 &quot;Gemini Pro&quot; 或类似标识&lt;/li&gt;
&lt;/ol&gt;
&lt;h2&gt;附录：推荐节点配置&lt;/h2&gt;
&lt;p&gt;根据社区反馈，以下配置成功率较高：&lt;/p&gt;
&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;提供商&lt;/th&gt;
&lt;th&gt;推荐节点&lt;/th&gt;
&lt;th&gt;测试结果&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;大哥云&lt;/td&gt;
&lt;td&gt;新加坡&lt;/td&gt;
&lt;td&gt;✅ 成功&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;龙猫云&lt;/td&gt;
&lt;td&gt;新加坡&lt;/td&gt;
&lt;td&gt;✅ 成功&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;其他机场&lt;/td&gt;
&lt;td&gt;美国西部&lt;/td&gt;
&lt;td&gt;⚠️ 部分成功&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;其他机场&lt;/td&gt;
&lt;td&gt;美国东部&lt;/td&gt;
&lt;td&gt;⚠️ 部分成功&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;其他机场&lt;/td&gt;
&lt;td&gt;日本&lt;/td&gt;
&lt;td&gt;❌ 失败率较高&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;其他机场&lt;/td&gt;
&lt;td&gt;韩国&lt;/td&gt;
&lt;td&gt;❌ 失败率较高&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;
&lt;h2&gt;注意事项&lt;/h2&gt;
&lt;ol&gt;
&lt;li&gt;&lt;strong&gt;不要频繁更换账号&lt;/strong&gt;：Google 可能会检测到异常行为&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;保持代理稳定&lt;/strong&gt;：避免在使用过程中频繁切换节点&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;耐心等待&lt;/strong&gt;：首次设置可能需要较长时间，不要轻易放弃&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;备份重要数据&lt;/strong&gt;：在尝试各种方法前，确保你的数据安全&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;尊重用户协议&lt;/strong&gt;：合理使用 Antigravity，遵守 Google 的服务条款&lt;/li&gt;
&lt;/ol&gt;
&lt;p&gt;祝大家都能顺利使用 Antigravity！有问题欢迎在评论区交流。&lt;/p&gt;
&lt;h2&gt;参考资料&lt;/h2&gt;
&lt;ul&gt;
&lt;li&gt;&lt;a href=&quot;https://github.com/lbjlaq/Antigravity-Manager&quot;&gt;Antigravity-Manager GitHub&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;https://policies.google.com/country-association-form&quot;&gt;Google 地区设置&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;https://www.proxifier.com/&quot;&gt;Proxifier 官网&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;https://linux.do/&quot;&gt;Linux Do 社区讨论&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;
</content:encoded></item><item><title>Avalonia全景指南：ReactiveUI精讲与Prism企业级架构深度扩展</title><link>https://meteor-comet.github.io/posts/avalonia-prism-framework/</link><guid isPermaLink="true">https://meteor-comet.github.io/posts/avalonia-prism-framework/</guid><description>从属性绑定到模块化注入：一万字超长干货解析Avalonia生态核心API与所有应用场景</description><pubDate>Mon, 10 Mar 2025 00:00:00 GMT</pubDate><content:encoded>&lt;h1&gt;Avalonia 全景指南：ReactiveUI精讲与Prism企业级架构深度扩展&lt;/h1&gt;
&lt;p&gt;本文将提供一份超越官方基础文档的“万字级长文”实战解析。
我们将把跨平台明星框架 &lt;strong&gt;Avalonia UI&lt;/strong&gt; 的核心驱动引擎分为两部分进行极致拆解：
第一部分，深入剖析 Avalonia 官方首推的响应式 MVVM 框架 &lt;strong&gt;ReactiveUI&lt;/strong&gt; 的所有核心 API 及在复杂前端场景中的应用；
第二部分，探讨面对几百个复杂页面与外部插件系统的大型商业应用时，如何引入 &lt;strong&gt;Prism (Prism.DryIoc.Avalonia)&lt;/strong&gt; 作为扩展底座，完成视图路由、依赖注入、跨窗口弹层以及拓展模块化 (Modularity) 机制。&lt;/p&gt;
&lt;hr /&gt;
&lt;h2&gt;目录&lt;/h2&gt;
&lt;ul&gt;
&lt;li&gt;&lt;a href=&quot;#%E4%B8%80-%E4%B8%BA%E4%BB%80%E4%B9%88-avalonia-%E5%AE%98%E6%96%B9%E5%8E%9F%E7%94%9F%E6%8B%A5%E6%8A%B1-reactiveui&quot;&gt;一、 为什么 Avalonia 官方原生拥抱 ReactiveUI？&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;#%E4%BA%8C-reactiveui-%E6%A0%B8%E5%BF%83%E5%AE%9E%E6%88%98%E4%BB%8E%E5%B1%9E%E6%80%A7%E5%88%B0%E5%91%BD%E4%BB%A4%E7%9A%84%E5%85%A8%E6%99%AF-api&quot;&gt;二、 ReactiveUI 核心实战：从属性到命令的全景 API&lt;/a&gt;
&lt;ul&gt;
&lt;li&gt;&lt;a href=&quot;#21-%E5%93%8D%E5%BA%94%E5%BC%8F%E5%AF%B9%E8%B1%A1%E5%9F%BA%E7%9F%B3reactiveobject-%E4%B8%8E%E5%BA%95%E5%B1%82%E6%9C%BA%E5%88%B6&quot;&gt;2.1 响应式对象基石：ReactiveObject 与底层机制&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;#22-reactivecommand-%E5%BC%82%E6%AD%A5%E5%91%BD%E4%BB%A4%E6%B7%B1%E5%BA%A6%E5%89%96%E6%9E%90&quot;&gt;2.2 ReactiveCommand 异步命令深度剖析&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;#23-observableaspropertyhelper-oaph%E5%8F%AA%E8%AF%BB%E6%B4%BE%E7%94%9F%E5%B1%9E%E6%80%A7%E7%9A%84%E6%8E%A8%E8%8D%90%E6%96%B9%E6%A1%88&quot;&gt;2.3 ObservableAsPropertyHelper (OAPH)：只读派生属性的推荐方案&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;#24-%E7%A5%9E%E7%BA%A7%E7%9B%91%E5%90%AC%E4%B8%8E%E6%95%B0%E6%8D%AE%E6%B5%81%E8%BD%AC%E5%8F%98%E5%B9%BBwhenanyvalue-%E6%A0%B8%E5%BF%83%E5%8E%9F%E7%90%86&quot;&gt;2.4 神级监听与数据流转变幻：WhenAnyValue 核心原理&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;#25-reactiveui-%E7%BB%93%E5%90%88-dynamicdata-%E8%BF%9B%E8%A1%8C%E5%B7%A8%E9%87%8F%E9%9B%86%E5%90%88%E7%AE%A1%E7%90%86&quot;&gt;2.5 ReactiveUI 结合 DynamicData 进行巨量集合管理&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;#%E4%B8%89-%E8%BF%9B%E9%98%B6%E6%89%A9%E5%B1%95avalonia-%E4%B8%8E-prism-%E6%A1%86%E6%9E%B6%E7%9A%84%E5%BE%AE%E6%A0%B8%E6%9E%B6%E6%9E%84%E6%90%AD%E5%BB%BA&quot;&gt;三、 进阶扩展：Avalonia 与 Prism 框架的微核架构搭建&lt;/a&gt;
&lt;ul&gt;
&lt;li&gt;&lt;a href=&quot;#31-prism-in-avalonia%E5%AE%9A%E4%BD%8D%E4%B8%8E%E6%A0%B8%E5%BF%83%E4%BB%B7%E5%80%BC&quot;&gt;3.1 Prism in Avalonia：定位与核心价值&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;#32-%E5%90%AF%E5%8A%A8%E6%8E%A5%E7%AE%A1%E4%B8%8E%E4%BE%9D%E8%B5%96%E6%B3%A8%E5%85%A5%E5%BA%95%E5%BA%A7-dryioc-api&quot;&gt;3.2 启动接管与依赖注入底座 (DryIoc) API&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;#33-%E5%8C%BA%E5%9F%9F%E7%AE%A1%E7%90%86%E5%99%A8-regionmanager-%E6%B7%B1%E5%BA%A6%E8%AE%B2%E8%A7%A3&quot;&gt;3.3 区域管理器 (RegionManager) 深度讲解&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;#34-%E8%B7%AF%E7%94%B1%E7%B3%BB%E7%BB%9F-navigation-%E7%9A%84%E5%85%A8%E5%9C%BA%E6%99%AF%E4%BD%BF%E7%94%A8%E6%9C%BA%E5%88%B6&quot;&gt;3.4 路由系统 (Navigation) 的全场景使用机制&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;#35-%E7%BB%9D%E5%AF%B9%E8%A7%A3%E8%80%A6%E7%9A%84%E5%BC%B9%E5%B1%82%E6%9C%8D%E5%8A%A1-dialogservice&quot;&gt;3.5 绝对解耦的弹层服务 (DialogService)&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;#36-%E4%BA%8B%E4%BB%B6%E8%81%9A%E5%90%88%E5%99%A8-eventaggregator-%E7%9A%84%E6%9E%81%E8%87%B4%E8%BF%90%E7%94%A8&quot;&gt;3.6 事件聚合器 (EventAggregator) 的极致运用&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;#37-%E8%BF%9B%E9%98%B6%E6%9E%B6%E6%9E%84%E8%A7%A3%E8%80%A6%E6%A8%A1%E5%9D%97%E5%8C%96%E5%BA%94%E7%94%A8%E6%A0%91-modularity&quot;&gt;3.7 进阶架构解耦：模块化应用树 (Modularity)&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;#%E5%9B%9B-%E6%80%BB%E7%BB%93%E4%BB%8E%E8%BD%BB%E9%87%8F%E8%B7%A8%E5%B9%B3%E5%8F%B0%E5%88%B0%E9%87%8D%E5%B7%A5%E4%B8%9A%E5%B7%A8%E6%9E%84%E7%9A%84%E8%BF%9B%E9%98%B6%E6%B3%95%E5%88%99&quot;&gt;四、 总结：从轻量跨平台到重工业巨构的进阶法则&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;hr /&gt;
&lt;h2&gt;一、 为什么 Avalonia 官方原生拥抱 ReactiveUI？&lt;/h2&gt;
&lt;p&gt;传统 WPF 或旧版 UWP 开发者习惯了手写无尽的 &lt;code&gt;INotifyPropertyChanged&lt;/code&gt;，并在命令里穿插 &lt;code&gt;CanExecute&lt;/code&gt; 的判断，然后在后台 Task 任务中与 &lt;code&gt;Dispatcher.Invoke&lt;/code&gt; 搏斗。
当你面对类似“如何实现打字时如果停顿0.5秒才发网络请求”、“如何监控5个独立输入框的数据决定提交按钮的可用性”、“如何在后台任务报错时拦截处理界面，并且在加载时按钮转圈禁用”等需求时，你会发现传统 MVVM 写出的代码像一座包含大量 &lt;code&gt;if/else&lt;/code&gt; 的屎山。&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;ReactiveUI 解决的痛点：&lt;/strong&gt;
它是基于 &lt;strong&gt;函数响应式编程 (FRP)&lt;/strong&gt; 和 Rx.NET 的大成之作。&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;&lt;strong&gt;统一数据流 (Everything is a Stream)&lt;/strong&gt;：用户的点击操作、计时器、属性的变化事件，全都可以被抽象为 &lt;code&gt;IObservable&amp;lt;T&amp;gt;&lt;/code&gt; 热流。&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Avalonia 的底层契合&lt;/strong&gt;：Avalonia 的依赖属性与附加属性本身就暴露了 &lt;code&gt;GetObservable&lt;/code&gt; 和 &lt;code&gt;Bind&lt;/code&gt; API，完美呼应 ReactiveUI，让你在 View 和 ViewModel 层中实现 100% 连贯的流式开发。&lt;/li&gt;
&lt;/ol&gt;
&lt;hr /&gt;
&lt;h2&gt;二、 ReactiveUI 核心实战：从属性到命令的全景 API&lt;/h2&gt;
&lt;p&gt;想要打通 Avalonia 的任督二脉，必须掌握 ReactiveUI 最核心的四大板块。请先引入 &lt;code&gt;Avalonia.ReactiveUI&lt;/code&gt; NuGet 库。&lt;/p&gt;
&lt;h3&gt;2.1 响应式对象基石：ReactiveObject 与底层机制&lt;/h3&gt;
&lt;p&gt;它是替代 &lt;code&gt;INotifyPropertyChanged&lt;/code&gt; 的绝对核心。所有的 ViewModel 都必须继承它。&lt;/p&gt;
&lt;h4&gt;A. 经典写法 API: &lt;code&gt;RaiseAndSetIfChanged&lt;/code&gt;&lt;/h4&gt;
&lt;p&gt;这个方法不仅帮我们抛出了通知，它内部还做了一次 &lt;code&gt;EqualityComparer&amp;lt;T&amp;gt;.Default.Equals&lt;/code&gt;。如果新赋的值和旧值一致，它会直接掐断赋值和通知，天然节省内存并避免死循环更新：&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;using ReactiveUI;

public class UserViewModel : ReactiveObject
{
    private string _userName;
    public string UserName
    {
        get =&amp;gt; _userName;
        // API: bool RaiseAndSetIfChanged&amp;lt;TObj, TRet&amp;gt;(ref TRet backingField, TRet newValue)
        // 只有值改变才会返回 True，并对外广播 [PropertyChanged] 信号
        set =&amp;gt; this.RaiseAndSetIfChanged(ref _userName, value);
    }
}
&lt;/code&gt;&lt;/pre&gt;
&lt;h4&gt;B. 现代写法：结合 Source Generators &lt;code&gt;[Reactive]&lt;/code&gt; 宏特性&lt;/h4&gt;
&lt;p&gt;如果在极为庞大的表单页面，手写几十个 private backing field 是要命的。ReactiveUI 推荐安装基于源生成器的附加包 &lt;code&gt;ReactiveUI.Fody&lt;/code&gt; 构建代码织入。
引用后，所有的属性只需要一行代码：&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;public class UserViewModel : ReactiveObject
{
    // 在编译时，Fody 会全自动将其转化为带 RaiseAndSetIfChanged 的完整形态！
    [Reactive] public string UserName { get; set; }
    [Reactive] public int Age { get; set; }
    [Reactive] public bool IsVip { get; set; }
}
&lt;/code&gt;&lt;/pre&gt;
&lt;h3&gt;2.2 ReactiveCommand 异步命令深度剖析&lt;/h3&gt;
&lt;p&gt;&lt;code&gt;ReactiveCommand&lt;/code&gt; 是对传统 &lt;code&gt;ICommand&lt;/code&gt; 的降维打击。它将命令彻底分解为了两个层面：“何时能执行”、“执行内部流状态监控”。&lt;/p&gt;
&lt;h4&gt;A. ReactiveCommand 的创建 API 体系&lt;/h4&gt;
&lt;p&gt;它不能直接 &lt;code&gt;new&lt;/code&gt; 出来，所有命令通过 &lt;code&gt;ReactiveCommand.CreateXXX&lt;/code&gt; 静态工厂构建：&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;strong&gt;&lt;code&gt;Create(Action execute, IObservable&amp;lt;bool&amp;gt; canExecute)&lt;/code&gt;&lt;/strong&gt;：创建没有参数也没有返回值的同步动作。&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;&lt;code&gt;CreateFromTask(Func&amp;lt;Task&amp;gt; execute, IObservable&amp;lt;bool&amp;gt; canExecute)&lt;/code&gt;&lt;/strong&gt;：专治各种异步 Web API 请求！它不仅帮你把异常吃掉发给特定流，它还会在执行 Task 时，“锁死” 这个命令，让前台按钮自动 Disable（防重复点击），等 Task 结束后重新解开！&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;&lt;code&gt;CreateFromObservable&lt;/code&gt;&lt;/strong&gt;：针对高级玩家，直接抛出/接收 Rx 数据流。&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;&lt;strong&gt;全场景使用范例：带条件的复杂登陆命令&lt;/strong&gt;&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;public class LoginViewModel : ReactiveObject
{
    // 第一个参数是输入(TParam)，第二个参数是该任务完成的返回值(TResult)
    public ReactiveCommand&amp;lt;Unit, bool&amp;gt; LoginCmd { get; }

    [Reactive] public string Account { get; set; }
    [Reactive] public string Password { get; set; }

    public LoginViewModel()
    {
        // 构建 IObservable&amp;lt;bool&amp;gt; 控制流，后面章节详细解释 WhenAnyValue
        var isInputValid = this.WhenAnyValue(
            vm =&amp;gt; vm.Account,
            vm =&amp;gt; vm.Password,
            (acc, pwd) =&amp;gt; !string.IsNullOrEmpty(acc) &amp;amp;&amp;amp; pwd?.Length &amp;gt;= 6);

        // 绑定流，如果 input 不 valid，命令不可用；并且点击时进入防抖等待
        LoginCmd = ReactiveCommand.CreateFromTask&amp;lt;Unit, bool&amp;gt;(async _ =&amp;gt;
        {
            await Task.Delay(2000); // 假装请求 API 网络阻塞
            return Account == &quot;admin&quot; &amp;amp;&amp;amp; Password == &quot;123456&quot;;
        }, isInputValid);

        // 高级 API 1: 执行状态监控，实时更新给 UI 比如“大马猴加载圈”！
        LoginCmd.IsExecuting.Subscribe(isRunning =&amp;gt; 
        {
            Console.WriteLine(isRunning ? &quot;网络请求中，按钮已自动禁用...&quot; : &quot;请求结束！&quot;);
        });

        // 高级 API 2: 接管错误流（防止 Task 崩溃炸掉整个应用）
        LoginCmd.ThrownExceptions.Subscribe(ex =&amp;gt; 
        {
            Console.WriteLine($&quot;糟糕，崩溃了被我拦截了: {ex.Message}&quot;);
        });
    }
}
&lt;/code&gt;&lt;/pre&gt;
&lt;h3&gt;2.3 ObservableAsPropertyHelper (OAPH)：只读派生属性的推荐方案&lt;/h3&gt;
&lt;p&gt;传统 MVVM 让人痛苦的场景：&lt;code&gt;FirstName&lt;/code&gt; 和 &lt;code&gt;LastName&lt;/code&gt;，你要一个只读的前台属性 &lt;code&gt;FullName&lt;/code&gt;。在以往，你必须分别监听 FirstName 和 LastName，手写 &lt;code&gt;PropertyChanged(&quot;FullName&quot;)&lt;/code&gt;，逻辑丑陋。&lt;/p&gt;
&lt;p&gt;ReactiveUI 提供了 &lt;strong&gt;OAPH：基于流派生只读属性&lt;/strong&gt; 的核心方法：&lt;code&gt;ToProperty()&lt;/code&gt;。&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;public class ProfileViewModel : ReactiveObject
{
    [Reactive] public string FirstName { get; set; }
    [Reactive] public string LastName { get; set; }

    // OAPH 专门用来包装只读依赖属性
    private readonly ObservableAsPropertyHelper&amp;lt;string&amp;gt; _fullNameOaph;
    public string FullName =&amp;gt; _fullNameOaph.Value;

    public ProfileViewModel()
    {
        // 将两者的流合并后，直接转为依赖属性挂载到 FullName 上
        // 只要源头属性发生任何变故，这个流会自动往下奔涌刷新！
        _fullNameOaph = this.WhenAnyValue(
                x =&amp;gt; x.FirstName, 
                x =&amp;gt; x.LastName, 
                (first, last) =&amp;gt; $&quot;{first} {last}&quot;)
            .ToProperty(this, x =&amp;gt; x.FullName);
    }
}
&lt;/code&gt;&lt;/pre&gt;
&lt;h3&gt;2.4 神级监听与数据流转变幻：WhenAnyValue 核心原理&lt;/h3&gt;
&lt;p&gt;&lt;code&gt;WhenAnyValue&lt;/code&gt; 是你在业务逻辑层面写得最多、最依赖的核心 API。它的作用是：&lt;strong&gt;把类实例普通属性的值变化，包装成为一条源源不断的 Rx 观察流水线&lt;/strong&gt;。&lt;/p&gt;
&lt;p&gt;并且由于它返回的类型是原生的 &lt;code&gt;IObservable&amp;lt;T&amp;gt;&lt;/code&gt;，所以你可以大胆且随意地使用 System.Reactive 自带的所有 LINQ 高级操作符！&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;超全链路高频查询场景实战（包括过滤、防抖、安全处理截断）：&lt;/strong&gt;&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;public SearchViewModel()
{
    this.WhenAnyValue(x =&amp;gt; x.SearchKeyword)
        // 1. 数据清洗：如果输入是 null 或纯空格，直接剪断这条水流，不往下走了
        .Where(keyword =&amp;gt; !string.IsNullOrWhiteSpace(keyword)) 
        
        // 2. 高频防抖：当用户正在疯狂按键盘键盘时暂停，等停止键入半秒(500ms)后才释放水流
        .Throttle(TimeSpan.FromMilliseconds(500)) 
        
        // 3. 值去重：如果拦截后的词，跟上一次查的词完全一样（可能是复制粘贴或撤销），滤除不搜！
        .DistinctUntilChanged() 
        
        // 4. 调用 API 并转化为结果流。使用 SelectMany 把异步任务展平压入主线！
        .SelectMany(async term =&amp;gt; await ApiService.SearchAsync(term))
        
        // 5. 跨线程转接：极其重要！！上面的网络请求处于后台线程，直接赋值前端必崩。
        // 用 ObserveOn 把后续的流处理强行拉回到 Avalonia 的 UI 主线程。
        .ObserveOn(RxApp.MainThreadScheduler) 
        
        // 6. 水流终点端：执行最终动作或者赋值
        .Subscribe(searchResults =&amp;gt; 
        {
            this.ResultList = searchResults;
        });
}
&lt;/code&gt;&lt;/pre&gt;
&lt;h3&gt;2.5 ReactiveUI 结合 DynamicData 进行巨量集合管理&lt;/h3&gt;
&lt;p&gt;在 Avalonia 原生中依然保留了 &lt;code&gt;ObservableCollection&lt;/code&gt;，但它是一个极其危险和低效率的集合组件（你对它排序、批量新增一万条数据时，它会不断散发 &lt;code&gt;CollectionChanged&lt;/code&gt; 甚至直接让渲染引擎瘫痪）。
ReactiveUI 极度推崇搭配 &lt;strong&gt;&lt;code&gt;DynamicData&lt;/code&gt;&lt;/strong&gt; 库处理海量数据容器交互。&lt;/p&gt;
&lt;p&gt;**核心原理：**使用底层集合（SourceList 或 SourceCache）存数据，通过 Connect() 取条数据流，进行 Sort/Filter ，最后 Bind() 进一份 UI 专用给前端看！&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;using DynamicData;
using DynamicData.Binding; // 包含 Bind() 

public class DataViewModel : ReactiveObject
{
    // 系统内部的线程安全底层库
    private readonly SourceList&amp;lt;UserEntity&amp;gt; _usersSource = new SourceList&amp;lt;UserEntity&amp;gt;();

    // 给 Avalonia 绑定的 UI 展示列表！完全只读。
    private readonly ReadOnlyObservableCollection&amp;lt;UserEntity&amp;gt; _usersView;
    public ReadOnlyObservableCollection&amp;lt;UserEntity&amp;gt; UsersView =&amp;gt; _usersView;

    public DataViewModel()
    {
        // 将底层核心连接成管道流，处理后抛给 UI
        var subscription = _usersSource.Connect()
            // 在这层管道做条件筛选
            .Filter(user =&amp;gt; user.Age &amp;gt;= 18)
            // 在管道里按 Name 进行排序
            .Sort(SortExpressionComparer&amp;lt;UserEntity&amp;gt;.Ascending(u =&amp;gt; u.Name))
            // 确保同步给前端这一刻必须回到 UI 线程！
            .ObserveOn(RxApp.MainThreadScheduler)
            // 将过滤后的只读镜像绑定给界面变量！
            .Bind(out _usersView)
            .Subscribe(); 
        
        // 数据添加：
        // 执行 AddRange，界面不会像源生 OC 那样刷新 100 遍界面，而是内部打平高效刷新 1 次！
        _usersSource.AddRange(new [] { new UserEntity{Name=&quot;A&quot;, Age=20}, new UserEntity{Name=&quot;B&quot;} });
    }
}
&lt;/code&gt;&lt;/pre&gt;
&lt;hr /&gt;
&lt;h2&gt;三、 进阶扩展：Avalonia 与 Prism 框架的微核架构搭建&lt;/h2&gt;
&lt;p&gt;对于常规工具型甚至小型应用，上面的 &lt;code&gt;ReactiveUI&lt;/code&gt; 已经完全可以让你写出性能爆炸的跨平台客户端。但&lt;strong&gt;真正的巨头系统&lt;/strong&gt;（比如一套庞大的银行管理软件，左侧各种导航树、几十个弹弹窗、上百个各自独立的插件程序集互斥存在），仅有响应流还远远不够。&lt;/p&gt;
&lt;p&gt;这就是老牌王者 &lt;strong&gt;Prism&lt;/strong&gt;（通过社区衍生开源版本 &lt;code&gt;Prism.DryIoc.Avalonia&lt;/code&gt;）出现的使命！当两者合并，你将会获得：&lt;strong&gt;ReactiveUI 主导细胞活性与属性计算响应&lt;/strong&gt; + &lt;strong&gt;Prism 主导骨骼框架、生命周期路由拆解与外挂插拔！&lt;/strong&gt;&lt;/p&gt;
&lt;h3&gt;3.1 Prism in Avalonia：定位与核心价值&lt;/h3&gt;
&lt;p&gt;在双剑合璧架构中，Prism 的使用绝不污染 ReactiveUI。Prism 的任务极专一：&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;&lt;strong&gt;彻底分离 UI 代码和加载机制&lt;/strong&gt;（Modularity 模块化技术）。&lt;/li&gt;
&lt;li&gt;把大屏幕切成方格子，往格子里丢模块页面（Region 划分与 View Injection）。&lt;/li&gt;
&lt;li&gt;让 ViewModelA 在不认识 ViewModelB 的情况下可以呼叫/传递参数给它（EventAggregator 与 Navigation 参数）。&lt;/li&gt;
&lt;/ol&gt;
&lt;h3&gt;3.2 启动接管与依赖注入底座 (DryIoc) API&lt;/h3&gt;
&lt;p&gt;Prism 在 Avalonia 中的引入步骤极度统一：从原本普通的 Application 接盘。&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;修改 &lt;code&gt;App.axaml&lt;/code&gt; 基类头：&lt;/strong&gt;&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;&amp;lt;!-- xmlns:prism=&quot;&quot; 引入基类命名空间 --&amp;gt;
&amp;lt;prism:PrismApplication xmlns=&quot;https://github.com/avaloniaui&quot;
                        xmlns:prism=&quot;http://prismlibrary.com/&quot;
                        x:Class=&quot;MyPrismApp.App&quot;&amp;gt;
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;&lt;strong&gt;修改 &lt;code&gt;App.axaml.cs&lt;/code&gt;（核心 API 使用解析）：&lt;/strong&gt;
所有的模块系统、依赖单例必须在复写方法中集中处理。&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;public partial class App : PrismApplication
{
    // 必须要被调用，以初始化 Avalonia 原生系统底座
    public override void Initialize()
    {
        AvaloniaXamlLoader.Load(this);
        base.Initialize(); // 由 Prism 包接管启动构建
    }

    // Prism 的启动核心：定义应用的 Shell(最外的大壳子主窗体)
    protected override AvaloniaObject CreateShell()
    {
        // 必须使用 IoC 容器产生主窗口。这让该窗口依赖注入进来的 VM 或服务全自动解析出来。
        return Container.Resolve&amp;lt;MainWindow&amp;gt;();
    }

    // Prism 的重头戏：所有的依赖解耦在此集中配置
    protected override void RegisterTypes(IContainerRegistry containerRegistry)
    {
        // 核心API：RegisterSingleton (一直存活单例对象)
        containerRegistry.RegisterSingleton&amp;lt;IGlobalSettings, GlobalSettings&amp;gt;();
        
        // 核心API：Register (瞬态对象，每次要的时候都 New 一个全新的)
        containerRegistry.Register&amp;lt;IHeavyCalculator, HeavyCalculator&amp;gt;();

        // 核心API：RegisterForNavigation 注册可以在大纲窗体中来回推入替换的可跳转“视图页面”。
        // 当你不传第二个参数时，默认叫页面本来的名字(如 &quot;UserListView&quot;)作为跳转秘钥。
        containerRegistry.RegisterForNavigation&amp;lt;UserListView, UserListViewModel&amp;gt;(&quot;UsersPage&quot;);
    }
}
&lt;/code&gt;&lt;/pre&gt;
&lt;h3&gt;3.3 区域管理器 (RegionManager) 深度讲解&lt;/h3&gt;
&lt;p&gt;区域 &lt;code&gt;Region&lt;/code&gt; 是 Prism 将视图分离出去的神奇容器。你可以在 MainWindow 等母窗体里挖坑。&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;在 XAML 里挖坑并起名 API:&lt;/strong&gt;
在 Avalonia 中，一般拿 &lt;code&gt;ContentControl&lt;/code&gt; (单页面容器) 或者 &lt;code&gt;ItemsControl&lt;/code&gt; (多重集合页，例如 TabControl 的内核) 作为区域适配器。&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;&amp;lt;Window ... xmlns:prism=&quot;http://prismlibrary.com/&quot;&amp;gt;
    &amp;lt;SplitView IsPaneOpen=&quot;True&quot; DisplayMode=&quot;Inline&quot;&amp;gt;
        &amp;lt;SplitView.Pane&amp;gt;
            &amp;lt;!-- 挖出名为&quot;MenuRegion&quot;的坑！ --&amp;gt;
            &amp;lt;ContentControl prism:RegionManager.RegionName=&quot;MenuRegion&quot; /&amp;gt;
        &amp;lt;/SplitView.Pane&amp;gt;

        &amp;lt;SplitView.Content&amp;gt;
            &amp;lt;!-- 挖出右侧主展示区，名叫&quot;MainRegion&quot; --&amp;gt;
            &amp;lt;TransitioningContentControl prism:RegionManager.RegionName=&quot;MainRegion&quot; /&amp;gt;
        &amp;lt;/SplitView.Content&amp;gt;
    &amp;lt;/SplitView&amp;gt;
&amp;lt;/Window&amp;gt;
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;&lt;strong&gt;后台操作的 API：向坑内注射子视图（View Injection）&lt;/strong&gt;
你只需要拿到接口类型是 &lt;code&gt;IRegionManager&lt;/code&gt; 的对象（比如 ViewModel 构建时由构造函数拿），随后发射即可定点挂载：&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;public class MainWindowViewModel : ReactiveObject
{
    public MainWindowViewModel(IRegionManager regionManager)
    {
        // API1: 直接粗暴在坑位里挂载一次这个对象，通常于启动时挂载左侧菜单面板。不维护历史。
        regionManager.RegisterViewWithRegion(&quot;MenuRegion&quot;, typeof(LeftMenuView));
    }
}
&lt;/code&gt;&lt;/pre&gt;
&lt;h3&gt;3.4 路由系统 (Navigation) 的全场景使用机制&lt;/h3&gt;
&lt;p&gt;上面的方法通常解决静态区域。想要像网页浏览器一样在多个后台列表组件之间互跳、前进后退并传值，必须使用大发明的 &lt;code&gt;Navigation&lt;/code&gt; 流：&lt;/p&gt;
&lt;h4&gt;A. 基础跳转 API:&lt;code&gt;RequestNavigate&lt;/code&gt; 与 &lt;code&gt;NavigationParameters&lt;/code&gt;&lt;/h4&gt;
&lt;pre&gt;&lt;code&gt;// 在侧边栏导航控件的 VM 中我们拿到参数：
var parameters = new NavigationParameters
{
    { &quot;DepartmentId&quot;, 1024 },
    { &quot;RoleInfo&quot;, new RoleSession{ Name=&quot;Admin&quot; } } // 支持传递复杂实体对象类型！
};

// 指定大主干网 &quot;MainRegion&quot;，向内部推送名叫 &quot;UsersPage&quot; 的用户列表，同时抛掷出降落伞参数包！
_regionManager.RequestNavigate(&quot;MainRegion&quot;, &quot;UsersPage&quot;, parameters);
&lt;/code&gt;&lt;/pre&gt;
&lt;h4&gt;B. 拦截与响应：&lt;code&gt;INavigationAware&lt;/code&gt; 全生命周期&lt;/h4&gt;
&lt;p&gt;被跳转到的 &lt;code&gt;UserListViewModel&lt;/code&gt; 需要感知发生了跳转事件。此时因为它是底层继承了 &lt;code&gt;ReactiveObject&lt;/code&gt; 的 ReactiveUI 模型，毫不冲突，再叠加一个接口 &lt;code&gt;INavigationAware&lt;/code&gt; 即可。&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;public class UserListViewModel : ReactiveObject, INavigationAware
{
    // API 1: 新页面被推挤落地后执行！处理上个跳转地发来的包袱。
    public void OnNavigatedTo(NavigationContext navigationContext)
    {
        // 从上下文中检索获取强类型参数
        if (navigationContext.Parameters.TryGetValue&amp;lt;int&amp;gt;(&quot;DepartmentId&quot;, out var depId))
        {
           LoadDataReactiveMethod(depId);
        }
    }

    // API 2: 设置是否复用本页生命周期
    // 如果返回 True：以后只要跳进你的页面，不需要走容器重新 new 构造函数，继续用这旧壳子；这就做了大页面的缓存效果。
    // 如果返回 False：每次按导航栏，只要路由进来就生成一个新的类对象替换。
    public bool IsNavigationTarget(NavigationContext navigationContext) =&amp;gt; true;

    // API 3: 从当前页面要离开，前往别的地方的时候触发调用，可写暂存等逻辑。
    public void OnNavigatedFrom(NavigationContext navigationContext) { }
}
&lt;/code&gt;&lt;/pre&gt;
&lt;h4&gt;C. 路由守卫拦截网防手滑 API：&lt;code&gt;IConfirmNavigationRequest&lt;/code&gt;&lt;/h4&gt;
&lt;p&gt;如果用户在当前表单填写了大量内容或者网络没有保存的时候点了返回或者切换，你需要拦截下该路由！只需要继承自带的守卫：&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;public class FormViewModel : ReactiveObject, IConfirmNavigationRequest
{
    // 相较于上面的Aware，额外增加一个必须实现的打断方法：
    public void ConfirmNavigationRequest(NavigationContext context, Action&amp;lt;bool&amp;gt; callback)
    {
        bool allowed = ShowUnsavedChangesDialogAndCheck();
        
        // 这决定了你的路由！回调 false，这辈子也别想切走；回调 true 立即允许放行！
        callback(allowed);
    }
}
&lt;/code&gt;&lt;/pre&gt;
&lt;h4&gt;D. 后退与历史管理 API：&lt;code&gt;IRegionNavigationJournal&lt;/code&gt;&lt;/h4&gt;
&lt;p&gt;Prism 会自建浏览器模型，它内置在 上下文中。&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;public void OnNavigatedTo(NavigationContext context)
{
    // 把日记本拿到，赋值给你的私有变量或者绑给按钮
    var journal = context.NavigationService.Journal;
    
    // API 大全：
    bool test1 = journal.CanGoBack; // 当前上方有没有回退页？
    bool test2 = journal.CanGoForward; // 能不能前进栈堆？
    journal.GoBack(); // 执行后退！回退上个组件！
    journal.Clear(); // 核心：用户一旦登入成功进系统，把外面的所有登录页等历史一巴掌全部清除不留痕。
}
&lt;/code&gt;&lt;/pre&gt;
&lt;h3&gt;3.5 绝对解耦的弹层服务 (DialogService)&lt;/h3&gt;
&lt;p&gt;如果在一个大型框架里你在普通业务内写 &lt;code&gt;new Window().ShowDialog()&lt;/code&gt; 这就成了严重破坏封装和跨平台的灾难代码（在 WebAssembly 里会更惨重）。
必须用 Prism 控制抽象依赖：&lt;code&gt;IDialogService&lt;/code&gt;。&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;步骤1：注册弹窗组件&lt;/strong&gt;
在 &lt;code&gt;App.axaml.cs&lt;/code&gt; 下面不使用 RegisterForNavigation 而是使用专有的：&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;containerRegistry.RegisterDialog&amp;lt;WarnAlertView, WarnAlertViewModel&amp;gt;(&quot;WarnAlert&quot;);
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;&lt;strong&gt;步骤2：ViewModel 必须实现弹窗接口 &lt;code&gt;IDialogAware&lt;/code&gt;&lt;/strong&gt;&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;public class WarnAlertViewModel : ReactiveObject, IDialogAware
{
    public string Title =&amp;gt; &quot;危险弹层&quot;;

    // 这个动作事件是由 Prism 派发的关闭触发钩子，当你要关掉自己时必须 Invoke 告诉它结果。
    public event Action&amp;lt;IDialogResult&amp;gt; RequestClose;

    // 接参数弹开窗：
    public void OnDialogOpened(IDialogParameters parameters) { }
    
    public bool CanCloseDialog() =&amp;gt; true;
    public void OnDialogClosed() { }

    // 你可以用 ReactiveCommand 调用它来手动提交结束整个弹窗的归宿
    public void HandleCloseMeLogic()
    {
        // 送出返回包裹对象：ButtonResult(OK/Cancel...) 以及 DialogParameters 回传结果给召唤你的底层系统。
        var res = new DialogResult(ButtonResult.OK);
        RequestClose?.Invoke(res);
    }
}
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;&lt;strong&gt;步骤3：普通页面执行呼叫：&lt;/strong&gt;&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;// _dialog 注入拿来的 IDialogService
_dialog.ShowDialog(&quot;WarnAlert&quot;, new DialogParameters{ {&quot;msg&quot;,&quot;断网拉！&quot;} }, result =&amp;gt; 
{
    if(result.Result == ButtonResult.OK) { 
        // 别人按了关闭弹窗并且确认了！
    }
});
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;&lt;em&gt;(注：另外还有一个 &lt;code&gt;Show(…)&lt;/code&gt; API 用在展示无模式、可以继续跟后边背景互动的提示弹出层)&lt;/em&gt;。&lt;/p&gt;
&lt;h3&gt;3.6 事件聚合器 (EventAggregator) 的极致运用&lt;/h3&gt;
&lt;p&gt;有些界面处于两重天完全隔接的情况下（你在播放音乐控件点击了切歌，全局桌面底部的歌词栏目需要换字）。
请不用乱绑静态变量，而是靠 EventAggregator 全局集线器发包：&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;1.定义一个广播频道类（基于 &lt;code&gt;PubSubEvent&amp;lt;类型&amp;gt;&lt;/code&gt;）：&lt;/strong&gt;&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;public class SongChangedEvent : PubSubEvent&amp;lt;SongModel&amp;gt; { }
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;&lt;strong&gt;2. 在底座（例如播放控制器）无脑发推广播 API：&lt;/strong&gt;&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;// _eventAggr 注入的 IEventAggregator
_eventAggr.GetEvent&amp;lt;SongChangedEvent&amp;gt;().Publish(new SongModel(id:112));
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;&lt;strong&gt;3. 在不认识彼此的接收页面（侧栏歌词等）收听雷达 API：&lt;/strong&gt;&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;_eventAggr.GetEvent&amp;lt;SongChangedEvent&amp;gt;().Subscribe(
    payload =&amp;gt; { /* 开始改变前台歌词状态更新 ViewModel... */ },
    
    // 高级 API 选项1：由于可能发起线程是在后端查询库里，通知你直接切换在 Avalonia 界面层执行处理：
    ThreadOption.UIThread,
    
    // 高级 API 选项2：是否保留对订阅者的强引用？一般选 false 做弱引用，不怕页面死循环销毁不了。
    false,
    
    // 高级 API 选项3：只有发生什么样的特指内容你才关心并继续拦截动作？过滤管道！
    song =&amp;gt; song.Duration &amp;gt; 60 // 过滤掉小于 60 秒的数据
);
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;&lt;em&gt;(注意：也可以使用 ReactiveUI 提供的系统内核即 &lt;code&gt;MessageBus.Current.SendMessage(...)&lt;/code&gt;。在多半情境下两者可平替，但基于 Prism 生态大型开发大家更偏向于 &lt;code&gt;EventAggregator&lt;/code&gt;)&lt;/em&gt;。&lt;/p&gt;
&lt;h3&gt;3.7 进阶架构解耦：模块化应用树 (Modularity)&lt;/h3&gt;
&lt;p&gt;大型 Avalonia 甚至可以允许一个上层的团队完全看不到底层的代码。我们可以将其拆给单独独立在外的类库 (DLL) 以便插件式热插拔，这就构建了插拔式微内核架构系统。&lt;/p&gt;
&lt;p&gt;比如有个类库项目：&lt;code&gt;CometApp.FinanceModule.dll&lt;/code&gt; 负责财务功能。&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;模块类创建与接管 API：&lt;/strong&gt;
在这个庞大 DLL 内新建一个实现接口： &lt;code&gt;IModule&lt;/code&gt; 的首脑类：&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;using Prism.Ioc;
using Prism.Modularity;
using Prism.Regions;

public class FinanceModule : IModule
{
    // A. 第一个生命周期触发：该 DLL 的地皮在此向主系统统揽地登记自己的子服务和内部单独导航的窗口！
    public void RegisterTypes(IContainerRegistry containerRegistry)
    {
        containerRegistry.RegisterForNavigation&amp;lt;InvoiceView&amp;gt;(&quot;InvoiceBoard&quot;);
        containerRegistry.RegisterSingleton&amp;lt;ITaxApiService, TaxApiService&amp;gt;();
    }

    // B. 第二个初始化生命周期触发：已经准备就绪时；用于干啥？
    // 用于拿总装的 RegionManager 神不知鬼不觉把 DLL 内专属私有按钮页面塞进全局菜单的坑！！
    public void OnInitialized(IContainerProvider containerProvider)
    {
        var regions = containerProvider.Resolve&amp;lt;IRegionManager&amp;gt;();
        
        // 全世界的挂载主程序甚至不用这个界面长啥样，模块自动去主界面侧边栏里安插了自己的财务专用图标与列表！
        regions.RegisterViewWithRegion(&quot;GlobalSidebarRegion&quot;, typeof(FinanceIconBtnView));
    }
}
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;&lt;strong&gt;宿主挂载调用 API (ModuleCatalog)：&lt;/strong&gt;
在宿主程序的 &lt;code&gt;App.axaml.cs&lt;/code&gt; 配置该模块如何挂进系统运转树中：&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;protected override void ConfigureModuleCatalog(IModuleCatalog moduleCatalog)
{
    // 方式1：强引用（主项目在代码里直接 Add Reference 依赖了这个分库）
    moduleCatalog.AddModule&amp;lt;FinanceModule&amp;gt;();
}

// 方式2的黑科技API：基于动态抛目录无引用的极致加载！！！
// 主程序不写死引用代码！所有的团队自己编译了自己的 .dll 后直接甩到一个 &quot;Plugins&quot; 物理文件夹就能直接接管激活功能！！
protected override IModuleCatalog CreateModuleCatalog()
{
    // 此目录扫描将在启动期载入插件加载入一切路由及模块类库中提供的资源分配！
    return new DirectoryModuleCatalog() { ModulePath = @&quot;.\Plugins&quot; };
}
&lt;/code&gt;&lt;/pre&gt;
&lt;hr /&gt;
&lt;h2&gt;四、 总结：从轻量跨平台到重工业巨构的进阶法则&lt;/h2&gt;
&lt;p&gt;Avalonia 是一款极高灵活度的生态霸主。在此体系下：&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;strong&gt;基础阶段工具型应用选型：&lt;/strong&gt; 只需搭载 &lt;strong&gt;ReactiveUI 生态&lt;/strong&gt; (&lt;code&gt;ReactiveObject，ReactiveCommand，Subscribe，Bind&lt;/code&gt;) 辅以基础的内置窗体调用。它天生拥抱 Avalonia 系统中的所有附加绑定结构，能够极短平快地生成极为清晰数据流的纯 C# 系统，大幅度避免多线程污染和状态通知垃圾逻辑堆砌。&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;中大型进阶或商业工业级项目架构选型：&lt;/strong&gt; 以 &lt;strong&gt;Prism 极其成熟的企业级标准&lt;/strong&gt;为“架构骨干”，统筹着 &lt;code&gt;Modularity&lt;/code&gt; (分拆 DLL 跨组协同微前端) 的生杀大权，调动 &lt;code&gt;RegionManger&lt;/code&gt; 将模块嵌入各个位置，然后依赖 &lt;code&gt;DialogService&lt;/code&gt; 断开各方层级硬弹窗纠缠；在所有 ViewModel 当中使用 &lt;strong&gt;ReactiveUI 当作计算心脏或处理灵魂&lt;/strong&gt;，发挥一切高吞吐量及 IO 控制的 Rx 功能。这是在 2026 后大型 .NET 跨平台客户端中最完美的满分教科书方案解！&lt;/li&gt;
&lt;/ul&gt;
</content:encoded></item><item><title>ASP.NET Core 企业级开发全栈手册：从核心架构到万能 HttpClient 实战</title><link>https://meteor-comet.github.io/posts/asp-net-notes/</link><guid isPermaLink="true">https://meteor-comet.github.io/posts/asp-net-notes/</guid><description>深度拆解 IActionResult 全景字典、Minimal API 高级策略、以及 HttpClient 通用请求引擎 SendAsync 深度解析</description><pubDate>Fri, 15 Nov 2024 00:00:00 GMT</pubDate><content:encoded>&lt;h1&gt;ASP.NET Core 企业级 Web API 深度全景指南&lt;/h1&gt;
&lt;p&gt;在现代工业物联网（IIoT）与企业级应用中，ASP.NET Core 凭借其卓越的性能与灵活的架构，已成为构建“服务端大脑”的首选。本文将作为一份高密度的技术参考手册，从启动基石到底层通信，全方位解析 Web API 的核心机制与生产实践。&lt;/p&gt;
&lt;hr /&gt;
&lt;h3&gt;文章目录&lt;/h3&gt;
&lt;ul&gt;
&lt;li&gt;&lt;a href=&quot;#%E4%B8%80-%E6%9E%B6%E6%9E%84%E5%9F%BA%E7%9F%B3%E5%90%AF%E5%8A%A8%E5%BC%95%E5%AF%BCdi-%E4%B8%8E%E7%AE%A1%E9%81%93%E6%B5%81%E8%BD%AC&quot;&gt;一、 架构基石：启动引导、DI 与管道流转&lt;/a&gt;
&lt;ul&gt;
&lt;li&gt;&lt;a href=&quot;#11-%E5%90%AF%E5%8A%A8%E7%94%9F%E5%91%BD%E5%91%A8%E6%9C%9Fbuilder-%E4%B8%8E-app-%E7%9A%84%E8%81%8C%E8%B4%A3%E5%88%86%E7%95%8C&quot;&gt;1.1 启动生命周期：Builder 与 App 的职责分界&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;#12-%E6%B3%A8%E5%85%A5%E4%B9%8B%E9%AD%82-di%E7%94%9F%E5%91%BD%E5%91%A8%E6%9C%9F%E4%B8%8E%E5%8D%95%E4%BE%8B%E6%8D%95%E8%8E%B7%E9%97%AE%E9%A2%98&quot;&gt;1.2 注入之魂 (DI)：生命周期与单例捕获问题&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;#13-%E6%89%A7%E8%A1%8C%E9%93%BE%E6%9D%A1%E4%B8%AD%E9%97%B4%E4%BB%B6-middleware-%E4%B8%8E-%E8%BF%87%E6%BB%A4%E5%99%A8-filter-%E7%9A%84%E7%BB%88%E6%9E%81%E5%AF%B9%E6%AF%94&quot;&gt;1.3 执行链条：中间件 (Middleware) 与 过滤器 (Filter) 的终极对比&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;#%E4%BA%8C-%E8%BF%94%E5%9B%9E%E5%80%BC%E5%85%A8%E6%99%AF%E5%A4%A7%E8%BE%9E%E5%85%B8iactionresult-%E4%B8%8E-iresult-%E6%B7%B1%E5%BA%A6%E5%8F%82%E8%80%83&quot;&gt;二、 返回值全景大辞典：IActionResult 与 IResult 深度参考&lt;/a&gt;
&lt;ul&gt;
&lt;li&gt;&lt;a href=&quot;#21-%E6%88%90%E5%8A%9F%E7%B3%BB%E5%88%97-success---2xx&quot;&gt;2.1 成功系列 (Success - 2xx)&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;#22-%E5%AE%A2%E6%88%B7%E7%AB%AF%E9%94%99%E8%AF%AF%E7%B3%BB%E5%88%97-client-error---4xx&quot;&gt;2.2 客户端错误系列 (Client Error - 4xx)&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;#23-%E6%9C%8D%E5%8A%A1%E5%99%A8%E9%94%99%E8%AF%AF%E7%B3%BB%E5%88%97-server-error---5xx&quot;&gt;2.3 服务器错误系列 (Server Error - 5xx)&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;#24-%E7%89%B9%E6%AE%8A%E8%B5%84%E6%BA%90%E5%A4%84%E7%90%86%E9%87%8D%E5%AE%9A%E5%90%91%E6%B5%81%E5%BC%8F%E6%96%87%E4%BB%B6%E4%B8%8E%E7%89%A9%E7%90%86%E8%B7%AF%E5%BE%84&quot;&gt;2.4 特殊资源处理：重定向、流式文件与物理路径&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;#%E4%B8%89-%E8%B7%AF%E7%94%B1%E4%B8%8E%E7%BB%88%E7%82%B9%E6%98%A0%E5%B0%84minimal-api-%E4%B8%8E-controller-%E6%B7%B1%E5%BA%A6%E5%AE%9E%E6%88%98&quot;&gt;三、 路由与终点映射：Minimal API 与 Controller 深度实战&lt;/a&gt;
&lt;ul&gt;
&lt;li&gt;&lt;a href=&quot;#31-%E8%B7%AF%E7%94%B1%E7%BA%A6%E6%9D%9F%E5%88%86%E5%8F%91%E5%B1%82%E7%9A%84%E9%80%BB%E8%BE%91%E7%86%94%E6%96%AD%E5%99%A8&quot;&gt;3.1 路由约束：分发层的“逻辑熔断器”&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;#32-controller-%E5%B1%9E%E6%80%A7%E8%B7%AF%E7%94%B1-attribute-routing&quot;&gt;3.2 Controller 属性路由 (Attribute Routing)&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;#33-%E6%B7%B1%E5%BA%A6%E6%A8%A1%E5%9E%8B%E7%BB%91%E5%AE%9Afrom-%E6%8C%87%E5%AE%9A%E5%8F%82%E6%95%B0%E6%9D%A5%E6%BA%90&quot;&gt;3.3 深度模型绑定：[From...] 指定参数来源&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;#%E5%9B%9B-%E4%BC%81%E4%B8%9A%E7%BA%A7%E5%9F%BA%E7%A1%80%E8%AE%BE%E6%96%BD%E5%90%8E%E5%8F%B0%E4%BB%BB%E5%8A%A1%E5%AE%89%E5%85%A8%E4%B8%8E%E6%80%A7%E8%83%BD&quot;&gt;四、 企业级基础设施：后台任务、安全与性能&lt;/a&gt;
&lt;ul&gt;
&lt;li&gt;&lt;a href=&quot;#41-%E6%8C%81%E7%BB%AD%E6%9C%8D%E5%8A%A1backgroundservice-%E5%B7%A5%E4%B8%9A%E6%95%B0%E6%8D%AE%E8%BD%AE%E8%AF%A2&quot;&gt;4.1 持续服务：BackgroundService 工业数据轮询&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;#42-%E9%9B%B6%E5%8F%8D%E5%B0%84%E4%BC%98%E5%8C%96json-source-generators&quot;&gt;4.2 零反射优化：JSON Source Generators&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;#%E4%BA%94-%E7%94%9F%E4%BA%A7%E9%83%A8%E7%BD%B2iis-%E6%89%98%E7%AE%A1%E4%B8%8E%E7%94%9F%E4%BA%A7%E7%8E%AF%E5%A2%83%E8%B0%83%E4%BC%98&quot;&gt;五、 生产部署：IIS 托管与生产环境调优&lt;/a&gt;
&lt;ul&gt;
&lt;li&gt;&lt;a href=&quot;#51-iis-%E6%89%98%E7%AE%A1in-process-%E6%A8%A1%E5%BC%8F%E4%B8%8E%E7%AB%99%E7%82%B9%E8%B5%8B%E6%9D%83&quot;&gt;5.1 IIS 托管：In-Process 模式与站点赋权&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;#%E5%85%AD-%E8%B7%A8%E7%AB%AF%E8%81%94%E5%8A%A8httpclient-%E4%B8%87%E8%83%BD%E8%AF%B7%E6%B1%82%E5%85%A8%E6%8C%87%E5%8D%97&quot;&gt;六、 跨端联动：HttpClient 万能请求全指南&lt;/a&gt;
&lt;ul&gt;
&lt;li&gt;&lt;a href=&quot;#61-%E7%94%9F%E5%91%BD%E5%91%A8%E6%9C%9Fhttpclientfactory-%E8%B5%84%E6%BA%90%E6%B1%A0%E5%8C%96&quot;&gt;6.1 生命周期：HttpClientFactory 资源池化&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;#62-%E8%B0%93%E8%AF%8D%E8%AF%B7%E6%B1%82%E7%9F%A9%E9%98%B5get-post-put-delete-patch&quot;&gt;6.2 谓词请求矩阵：GET, POST, PUT, DELETE, PATCH&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;#63-%E6%A0%B8%E5%BF%83%E5%BC%95%E6%93%8Esendasync-%E4%B8%8E-httprequestmessage-%E6%89%8B%E5%8A%A8%E6%9E%84%E9%80%A0&quot;&gt;6.3 核心引擎：SendAsync 与 HttpRequestMessage 手动构造&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;#64-%E5%86%85%E5%AE%B9%E8%BD%BD%E4%BD%93httpcontent-%E5%AD%97%E5%85%B8-json-multipart-stream&quot;&gt;6.4 内容载体：HttpContent 字典 (JSON, Multipart, Stream)&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;#65-%E5%BC%B9%E6%80%A7%E6%8E%A7%E5%88%B6cancellationtoken-%E4%B8%8E-completionoption-%E5%86%85%E5%AD%98%E4%BC%98%E5%8C%96&quot;&gt;6.5 弹性控制：CancellationToken 与 CompletionOption 内存优化&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;hr /&gt;
&lt;h2&gt;一、 架构基石：启动引导、DI 与管道流转&lt;/h2&gt;
&lt;h3&gt;1.1 启动生命周期：Builder 与 App 的职责分界&lt;/h3&gt;
&lt;p&gt;ASP.NET Core 通过 &lt;code&gt;WebApplicationBuilder&lt;/code&gt; 实现了配置与管道的严格解耦：&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;var builder = WebApplication.CreateBuilder(args);

// --- [服务注册阶段] ：填充容器 (Service Collection) ---
builder.Services.AddControllers(); 
builder.Services.AddSingleton&amp;lt;IDataHub, DeviceDataHub&amp;gt;(); // 全局设备总线

var app = builder.Build(); // [分水岭]：从此往后无法追加新服务

// --- [管道配置阶段] ：中间件编排 (Middleware Pipeline) ---
app.UseAuthentication(); 
app.UseAuthorization();
app.MapControllers(); 

app.Run();
&lt;/code&gt;&lt;/pre&gt;
&lt;h3&gt;1.2 注入之魂 (DI)：生命周期与单例捕获问题&lt;/h3&gt;
&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;生命周期&lt;/th&gt;
&lt;th&gt;描述&lt;/th&gt;
&lt;th&gt;生产实战建议&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;Singleton&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;应用级唯一&lt;/td&gt;
&lt;td&gt;&lt;strong&gt;工业场景&lt;/strong&gt;：串口管理器、TCP 连接池、PLC 镜像缓存。&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;Scoped&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;请求级唯一&lt;/td&gt;
&lt;td&gt;&lt;strong&gt;业务场景&lt;/strong&gt;：DbContext、操作员会话信息。&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;Transient&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;瞬时创建&lt;/td&gt;
&lt;td&gt;&lt;strong&gt;工具场景&lt;/strong&gt;：无状态的数值转换算法、非共享逻辑。&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;
&lt;blockquote&gt;
&lt;p&gt;&lt;strong&gt;[重要警告]&lt;/strong&gt;：禁止在 &lt;strong&gt;Singleton&lt;/strong&gt; 服务中构造注入 &lt;strong&gt;Scoped&lt;/strong&gt; 服务。正确做法是注入 &lt;code&gt;IServiceScopeFactory&lt;/code&gt; 并在方法内手动创建 Scope。&lt;/p&gt;
&lt;/blockquote&gt;
&lt;h3&gt;1.3 执行链条：中间件 (Middleware) 与 过滤器 (Filter) 的终极对比&lt;/h3&gt;
&lt;ul&gt;
&lt;li&gt;&lt;strong&gt;Middleware&lt;/strong&gt;：控制请求如何进入系统，处理跨端逻辑（如跨域、异常捕获、静态文件）。&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Filter&lt;/strong&gt;：属于 Action 逻辑的一部分，能访问接口上下文，用于业务级权限、参数验证。&lt;/li&gt;
&lt;/ul&gt;
&lt;hr /&gt;
&lt;h2&gt;二、 返回值全景大辞典：IActionResult 与 IResult 深度参考&lt;/h2&gt;
&lt;h3&gt;2.1 成功系列 (Success - 2xx)&lt;/h3&gt;
&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Helper (Controller)&lt;/th&gt;
&lt;th&gt;状态码&lt;/th&gt;
&lt;th&gt;业务语境&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;Ok(obj)&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;200&lt;/td&gt;
&lt;td&gt;请求成功并返回数据。&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;Created(uri, obj)&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;201&lt;/td&gt;
&lt;td&gt;&lt;strong&gt;资源创建成功&lt;/strong&gt;，返回 Location 地址。&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;Accepted()&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;202&lt;/td&gt;
&lt;td&gt;任务已接受但未处理完成（高能耗任务）。&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;NoContent()&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;204&lt;/td&gt;
&lt;td&gt;请求成功但无返回（Update/Delete）。&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;
&lt;h3&gt;2.2 客户端错误系列 (Client Error - 4xx)&lt;/h3&gt;
&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Helper (Controller)&lt;/th&gt;
&lt;th&gt;状态码&lt;/th&gt;
&lt;th&gt;业务语境&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;BadRequest(ms)&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;400&lt;/td&gt;
&lt;td&gt;&lt;strong&gt;参数校验失败&lt;/strong&gt;（模型校验）。&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;Unauthorized()&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;401&lt;/td&gt;
&lt;td&gt;&lt;strong&gt;身份验证缺失&lt;/strong&gt;或凭证过期。&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;Forbidden()&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;403&lt;/td&gt;
&lt;td&gt;&lt;strong&gt;权限不足&lt;/strong&gt;（角色不匹配）。&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;NotFound()&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;404&lt;/td&gt;
&lt;td&gt;寻址失败，资源不存在。&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;Conflict()&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;409&lt;/td&gt;
&lt;td&gt;&lt;strong&gt;资源冲突&lt;/strong&gt;（唯一性重复/并发冲突）。&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;UnprocessableEntity()&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;422&lt;/td&gt;
&lt;td&gt;&lt;strong&gt;业务语义错误&lt;/strong&gt;（如非法逻辑判断）。&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;
&lt;h3&gt;2.3 服务器错误系列 (Server Error - 5xx)&lt;/h3&gt;
&lt;ul&gt;
&lt;li&gt;&lt;strong&gt;Problem (500)&lt;/strong&gt;：统一的后端故障响应。推荐封装 &lt;code&gt;ProblemDetails&lt;/code&gt; 返回码。&lt;/li&gt;
&lt;/ul&gt;
&lt;hr /&gt;
&lt;h2&gt;三、 路由与终点映射：Minimal API 与 Controller 深度实战&lt;/h2&gt;
&lt;p&gt;路由系统是 Web API 的“神经中枢”，负责将 HTTP 请求精确分发至逻辑端点。ASP.NET Core 支持 Minimal API 的“编程式”映射与 Controller 的“声明式”特性映射。&lt;/p&gt;
&lt;h3&gt;3.1 路由约束：分发层的“逻辑熔断器”&lt;/h3&gt;
&lt;p&gt;路由约束允许在请求进入业务层之前就拦截非法输入。&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;// Minimal API 约束
app.MapGet(&quot;/dev/{id:int:min(1000)}&quot;, (int id) =&amp;gt; ...); 
app.MapGet(&quot;/data/{protocol:regex(^(modbus|mqtt)$)}&quot;, ...);

// Controller 属性约束
[HttpGet(&quot;{id:guid}&quot;)] // 仅匹配 Guid 格式
public IActionResult GetById(Guid id) =&amp;gt; ...;
&lt;/code&gt;&lt;/pre&gt;
&lt;h3&gt;3.2 Controller 属性路由 (Attribute Routing)&lt;/h3&gt;
&lt;p&gt;在企业级控制器开发中，属性路由是管理复杂 API 路径的标准做法。&lt;/p&gt;
&lt;h4&gt;A. 层次化路由与 Token 替换&lt;/h4&gt;
&lt;p&gt;利用 &lt;code&gt;[controller]&lt;/code&gt; 和 &lt;code&gt;[action]&lt;/code&gt; 占位符，可以实现代码重命名的自动适应：&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;[ApiController]
[Route(&quot;api/v1/[controller]&quot;)] // 类级别：api/v1/Device
public class DeviceController : ControllerBase
{
    // 最终路由：GET api/v1/Device/Status
    [HttpGet(&quot;[action]&quot;)] 
    public IActionResult Status() =&amp;gt; Ok(&quot;Online&quot;);

    // 最终路由：GET api/v1/Device/101
    [HttpGet(&quot;{id:int}&quot;)] 
    public IActionResult GetById(int id) =&amp;gt; Ok();
}
&lt;/code&gt;&lt;/pre&gt;
&lt;h4&gt;B. 多路径映射与默认值&lt;/h4&gt;
&lt;p&gt;单个 Action 可以响应多个路由模板，并灵活配置默认参数：&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;[HttpGet(&quot;info/{id?}&quot;)]         // id 为可选参数
[HttpGet(&quot;find/{id=DEFAULT}&quot;)] // id 缺省时为 &quot;DEFAULT&quot;
public IActionResult GetInfo(string id) =&amp;gt; Ok(id);
&lt;/code&gt;&lt;/pre&gt;
&lt;h3&gt;3.3 深度模型绑定：[From...] 指定参数来源&lt;/h3&gt;
&lt;p&gt;当模型结构复杂或存在参数冲突时，必须显式指定数据来源：&lt;/p&gt;
&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;注解&lt;/th&gt;
&lt;th&gt;提取范围&lt;/th&gt;
&lt;th&gt;工业实战场景&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;[FromRoute]&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;URL 路径变量&lt;/td&gt;
&lt;td&gt;获取特定传感器 ID&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;[FromQuery]&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;URL 查询字符串&lt;/td&gt;
&lt;td&gt;分页、过滤条件、排序依据&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;[FromBody]&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;请求报文体 (JSON)&lt;/td&gt;
&lt;td&gt;提交复杂的配置对象、上报批次数据&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;[FromHeader]&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;HTTP 请求头&lt;/td&gt;
&lt;td&gt;提取独立设备鉴权 Key 或版本标签&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;[FromForm]&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;表单数据&lt;/td&gt;
&lt;td&gt;传统表单提交（较少用于纯 API）&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;
&lt;hr /&gt;
&lt;h2&gt;四、 企业级基础设施：后台任务、安全与性能&lt;/h2&gt;
&lt;h3&gt;4.1 持续服务：BackgroundService 工业数据轮询&lt;/h3&gt;
&lt;pre&gt;&lt;code&gt;public class PollingService : BackgroundService {
    protected override async Task ExecuteAsync(CancellationToken ct) {
        while (!ct.IsCancellationRequested) {
            // 工业数据采集逻辑...
            await Task.Delay(1000, ct); 
        }
    }
}
&lt;/code&gt;&lt;/pre&gt;
&lt;h3&gt;4.2 零反射优化：JSON Source Generators&lt;/h3&gt;
&lt;p&gt;通过 AOT 兼容的生成器消灭运行时反射压力。&lt;/p&gt;
&lt;hr /&gt;
&lt;h2&gt;五、 生产部署：IIS 托管与生产环境调优&lt;/h2&gt;
&lt;h3&gt;5.1 IIS 托管：In-Process 模式与站点赋权&lt;/h3&gt;
&lt;ol&gt;
&lt;li&gt;&lt;strong&gt;Hosting Bundle&lt;/strong&gt;：确保 Windows Server 已安装运行时组件。&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;In-Process&lt;/strong&gt;：&lt;code&gt;web.config&lt;/code&gt; 中确保 &lt;code&gt;hostingModel=&quot;inprocess&quot;&lt;/code&gt; 以获最高吞吐。&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;权限&lt;/strong&gt;：必须赋予 &lt;code&gt;IIS AppPool\池名称&lt;/code&gt; 对物理路径的读写执行权。&lt;/li&gt;
&lt;/ol&gt;
&lt;hr /&gt;
&lt;h2&gt;六、 跨端联动：HttpClient 万能请求全指南&lt;/h2&gt;
&lt;p&gt;上位机（C# / WinForms）与服务端的联动中，&lt;code&gt;HttpClient&lt;/code&gt; 的调用广度决定了系统的集成能力。&lt;/p&gt;
&lt;h3&gt;6.1 生命周期：HttpClientFactory 资源池化&lt;/h3&gt;
&lt;p&gt;&lt;strong&gt;核心准则&lt;/strong&gt;：严禁 &lt;code&gt;new HttpClient()&lt;/code&gt;。必须通过 &lt;code&gt;IHttpClientFactory&lt;/code&gt; 管理 &lt;code&gt;HttpMessageHandler&lt;/code&gt; 周期，防止 &lt;code&gt;TIME_WAIT&lt;/code&gt; 连接耗尽导致网络瘫痪。&lt;/p&gt;
&lt;h3&gt;6.2 谓词请求矩阵：GET, POST, PUT, DELETE, PATCH&lt;/h3&gt;
&lt;p&gt;针对不同的业务动作，应选择匹配的语义化方法：&lt;/p&gt;
&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;方法&lt;/th&gt;
&lt;th&gt;SDK 对应&lt;/th&gt;
&lt;th&gt;幂等性&lt;/th&gt;
&lt;th&gt;安全性&lt;/th&gt;
&lt;th&gt;实战场景&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;GET&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;&lt;code&gt;GetAsync&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;✅&lt;/td&gt;
&lt;td&gt;✅&lt;/td&gt;
&lt;td&gt;获取设备状态、查询报表、拉取分页数据。&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;POST&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;&lt;code&gt;PostAsync&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;❌&lt;/td&gt;
&lt;td&gt;❌&lt;/td&gt;
&lt;td&gt;&lt;strong&gt;注册新设备&lt;/strong&gt;、提交控制指令、上传图片。&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;PUT&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;&lt;code&gt;PutAsync&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;✅&lt;/td&gt;
&lt;td&gt;❌&lt;/td&gt;
&lt;td&gt;&lt;strong&gt;全量修改&lt;/strong&gt;已有设备的配置信息（覆盖请求）。&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;DELETE&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;&lt;code&gt;DeleteAsync&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;✅&lt;/td&gt;
&lt;td&gt;❌&lt;/td&gt;
&lt;td&gt;根据 ID 删除特定传感器记录。&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;PATCH&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;&lt;code&gt;PatchAsync&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;❌&lt;/td&gt;
&lt;td&gt;❌&lt;/td&gt;
&lt;td&gt;&lt;strong&gt;增量修改&lt;/strong&gt;（仅修改设备的部分字段，如仅关闭报警器）。&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;
&lt;h3&gt;6.3 核心引擎：SendAsync 与 HttpRequestMessage 手动构造&lt;/h3&gt;
&lt;p&gt;所有的辅助方法（如 &lt;code&gt;GetAsync&lt;/code&gt;）底层最终都调用了 &lt;code&gt;SendAsync&lt;/code&gt;。当你需要极高自由度时，必须学会手动构造请求对象。&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;var request = new HttpRequestMessage(HttpMethod.Post, &quot;https://api/device/1&quot;);

// 1. 注入动态 Header (请求级独立头)
request.Headers.Add(&quot;X-Action-Type&quot;, &quot;Reset&quot;);
request.Headers.Add(&quot;Authorization&quot;, $&quot;Bearer {token}&quot;);

// 2. 注入内容载体
request.Content = new StringContent(&quot;{\&quot;cmd\&quot;:\&quot;reboot\&quot;}&quot;, Encoding.UTF8, &quot;application/json&quot;);

// 3. 全能发送 (支持所有自定义配置)
var response = await _client.SendAsync(request);
&lt;/code&gt;&lt;/pre&gt;
&lt;h3&gt;6.4 内容载体：HttpContent 深度解析 (JSON, Multipart, Stream)&lt;/h3&gt;
&lt;p&gt;在 &lt;code&gt;PostAsync&lt;/code&gt; 或 &lt;code&gt;SendAsync&lt;/code&gt; 中，&lt;code&gt;HttpContent&lt;/code&gt; 决定了报文实体的格式。正确配置参数（如 Encoding 和 MediaType）是服务端能否解析成功的关键。&lt;/p&gt;
&lt;h4&gt;A. JsonContent —— 现代 API 的标准载体&lt;/h4&gt;
&lt;p&gt;自 .NET 5 引入，它内置了 &lt;code&gt;System.Text.Json&lt;/code&gt; 序列化，性能优于手动序列化。&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;// 自动将对象序列化为 JSON 并设置 Content-Type: application/json; charset=utf-8
var content = JsonContent.Create(new { DeviceId = 101, Value = 25.4 });
&lt;/code&gt;&lt;/pre&gt;
&lt;h4&gt;B. StringContent —— 底层控制与兼容性&lt;/h4&gt;
&lt;p&gt;当你需要完全控制原始字符串（如自定义加密报文）时使用。&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;strong&gt;参数解释&lt;/strong&gt;：
&lt;ul&gt;
&lt;li&gt;&lt;code&gt;content&lt;/code&gt;: 原始字符串。&lt;/li&gt;
&lt;li&gt;&lt;code&gt;encoding&lt;/code&gt;: 字符编码（强烈建议 &lt;code&gt;Encoding.UTF8&lt;/code&gt;）。&lt;/li&gt;
&lt;li&gt;&lt;code&gt;mediaType&lt;/code&gt;: 告知服务端如何解析（如 &lt;code&gt;&quot;application/json&quot;&lt;/code&gt;、&lt;code&gt;&quot;text/plain&quot;&lt;/code&gt;）。&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;pre&gt;&lt;code&gt;var content = new StringContent(&quot;{\&quot;raw\&quot;:\&quot;data\&quot;}&quot;, Encoding.UTF8, &quot;application/json&quot;);
&lt;/code&gt;&lt;/pre&gt;
&lt;h4&gt;C. MultipartFormDataContent —— 混合上报实战&lt;/h4&gt;
&lt;p&gt;常用于&lt;strong&gt;图文并茂&lt;/strong&gt;的数据上报，例如：发送一个传感器 JSON 状态，并附带一张现场抓拍的图片。&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;using var multipart = new MultipartFormDataContent();

// 1. 添加普通字段 (类似表单 name=&quot;metadata&quot;)
multipart.Add(new StringContent(&quot;{\&quot;room\&quot;:\&quot;lab\&quot;}&quot;), &quot;metadata&quot;);

// 2. 添加文件流 (参数: HttpContent, 参数名, 文件名)
var fileContent = new StreamContent(File.OpenRead(&quot;photo.jpg&quot;));
fileContent.Headers.ContentType = new MediaTypeHeaderValue(&quot;image/jpeg&quot;);
multipart.Add(fileContent, &quot;device_image&quot;, &quot;capture.jpg&quot;);

await _client.PostAsync(url, multipart);
&lt;/code&gt;&lt;/pre&gt;
&lt;h4&gt;D. StreamContent —— 大数据量与 Zero-Allocation&lt;/h4&gt;
&lt;p&gt;在上位机导出 GB 级日志或上传大量传感数据时，严禁先读入内存。&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;strong&gt;实战&lt;/strong&gt;：直接将底层 &lt;code&gt;FileStream&lt;/code&gt; 或 &lt;code&gt;NetworkStream&lt;/code&gt; 包装后发出，实现高效流式传输。&lt;/li&gt;
&lt;/ul&gt;
&lt;pre&gt;&lt;code&gt;using var fs = File.OpenRead(&quot;large_data.bin&quot;);
var content = new StreamContent(fs); // 直接从磁盘读取并在网络流中发送，内存占用极低
&lt;/code&gt;&lt;/pre&gt;
&lt;h3&gt;6.5 弹性控制：CancellationToken 与 CompletionOption 内存优化&lt;/h3&gt;
&lt;p&gt;在不稳定的工业网络或内存受限环境中，以下参数是系统的“保险丝”：&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;strong&gt;CancellationToken (取消令牌)&lt;/strong&gt;：&lt;pre&gt;&lt;code&gt;var cts = new CancellationTokenSource(TimeSpan.FromSeconds(5)); // 设置 5 秒强制超时
await _client.GetAsync(url, cts.Token); // 若 5 秒未响应，自动抛出 TaskCanceledException
&lt;/code&gt;&lt;/pre&gt;
&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;HttpCompletionOption.ResponseHeadersRead&lt;/strong&gt;：
默认 &lt;code&gt;HttpClient&lt;/code&gt; 会把整个 Body 缓存到内存才返回。对于大数据量请求，设置此参数可以让代码在&lt;strong&gt;读完 Header 后立即返回&lt;/strong&gt;，随后通过 &lt;code&gt;ReadAsStreamAsync&lt;/code&gt; 直接操作流，避免瞬间将 GB 级数据撑爆内存。&lt;/li&gt;
&lt;/ul&gt;
&lt;hr /&gt;
&lt;blockquote&gt;
&lt;p&gt;&lt;strong&gt;结语&lt;/strong&gt;：
掌握了 &lt;strong&gt;SendAsync&lt;/strong&gt; 与 &lt;strong&gt;HttpRequestMessage&lt;/strong&gt; 的精髓，你便解锁了 &lt;code&gt;HttpClient&lt;/code&gt; 的全部上限。无论是复杂的 Header 校验还是大文件的流式上报，这一套全方位链路都能确保你在工业应用开发中精准控制每一帧数据。&lt;/p&gt;
&lt;/blockquote&gt;
</content:encoded></item><item><title>工业控制核心：S7.Net 深度指南与固高、雷赛运动控制卡全景实战</title><link>https://meteor-comet.github.io/posts/industrial-control-s7net-motion-cards/</link><guid isPermaLink="true">https://meteor-comet.github.io/posts/industrial-control-s7net-motion-cards/</guid><description>Siemens PLC 通信协议解析与多轴运动控制底层 API、参数配置及应用场景全解</description><pubDate>Sun, 20 Oct 2024 00:00:00 GMT</pubDate><content:encoded>&lt;h1&gt;工业控制核心：S7.Net 与固高/雷赛运动控制卡全景实战&lt;/h1&gt;
&lt;p&gt;在现代上位机开发（无论是老牌的 WinForm 还是现代化的 WPF）中，工控软件工程师往往需要面对两大类核心硬件：&lt;strong&gt;PLC（可编程逻辑控制器，主要管逻辑与低速IO）&lt;/strong&gt; 与 &lt;strong&gt;运动控制卡（主要管伺服电机的高速、高精度位移）&lt;/strong&gt;。&lt;/p&gt;
&lt;p&gt;本文将摒弃空洞的理论，直接切入核心：为你全景展现如何通过 C# 与 西门子 PLC（借助 S7netplus）、固高科技 (Googol) 控制卡、雷赛智能 (Leadshine) 控制卡进行深度通讯与控制。每一段代码、每一个 API 我们都会掰开揉碎地讲解其参数含义与使用流程。&lt;/p&gt;
&lt;hr /&gt;
&lt;h2&gt;1. S7netplus (S7.Net) 深度实战指南&lt;/h2&gt;
&lt;p&gt;&lt;strong&gt;S7netplus&lt;/strong&gt; 是开源的 C# 西门子通讯库。相比昂贵的 Kepware 或庞大的 PROFINET 官方库，它直接通过 TCP 建立 S7 协议的 Socket 连接，极其轻量。&lt;/p&gt;
&lt;h3&gt;1.1 核心连接 API 与参数解析&lt;/h3&gt;
&lt;p&gt;在 WinForm 或 WPF 启动时，我们决不能在 UI 主线程（比如 &lt;code&gt;Form_Load&lt;/code&gt;）里直接去连接 PLC，因为 Socket 连接可能面临网络不通而导致长达几秒的阻塞（超时），这会让整个界面白屏假死。&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;【API 详解】&lt;/strong&gt;&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;code&gt;new Plc(CpuType cpu, string ip, short rack, short slot)&lt;/code&gt;: 构造函数。
&lt;ul&gt;
&lt;li&gt;&lt;code&gt;cpu&lt;/code&gt;: 目标型号枚举（&lt;code&gt;S71200&lt;/code&gt;, &lt;code&gt;S71500&lt;/code&gt;, &lt;code&gt;S7300&lt;/code&gt;, &lt;code&gt;S7200Smart&lt;/code&gt;等）。&lt;/li&gt;
&lt;li&gt;&lt;code&gt;ip&lt;/code&gt;: 设备的局域网 IPv4 地址。&lt;/li&gt;
&lt;li&gt;&lt;code&gt;rack&lt;/code&gt;: 机架号。绝大多数单背板系统填 &lt;code&gt;0&lt;/code&gt;。&lt;/li&gt;
&lt;li&gt;&lt;code&gt;slot&lt;/code&gt;: 插槽号。S7-1200/1500 的 CPU 通常在槽 &lt;code&gt;1&lt;/code&gt;，S7-300 在槽 &lt;code&gt;2&lt;/code&gt;。&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;&lt;code&gt;plc.OpenAsync()&lt;/code&gt;: 异步打开连接。这不会阻塞主线程。&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;&lt;strong&gt;【WPF / WinForm 异步连接场景示例】&lt;/strong&gt;&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;using S7.Net;
using System.Threading.Tasks;
using System.Windows; // WPF 示例

public partial class MainWindow : Window
{
    private Plc _siemensPlc;

    public MainWindow()
    {
        InitializeComponent();
        // 绑定窗口加载事件
        this.Loaded += MainWindow_Loaded; 
    }

    private async void MainWindow_Loaded(object sender, RoutedEventArgs e)
    {
        // UI 显示提示语
        lblStatus.Text = &quot;正在连接设备...&quot;;
        
        // 初始化对象
        _siemensPlc = new Plc(CpuType.S71200, &quot;192.168.0.10&quot;, 0, 1);

        try
        {
            // 使用 await 异步等待连接，避免界面假死
            await _siemensPlc.OpenAsync();
            
            if(_siemensPlc.IsConnected)
            {
                lblStatus.Text = &quot;PLC 连接成功！&quot;;
                lblStatus.Foreground = System.Windows.Media.Brushes.Green;
                
                // 连接成功后，开启后台轮询数据的任务
                StartPollingData();
            }
        }
        catch (Exception ex)
        {
            lblStatus.Text = &quot;连接失败，请检查网线！&quot;;
            lblStatus.Foreground = System.Windows.Media.Brushes.Red;
        }
    }
}
&lt;/code&gt;&lt;/pre&gt;
&lt;blockquote&gt;
&lt;p&gt;&lt;strong&gt;🔥 避坑指南 (博图配置)&lt;/strong&gt;：针对 1200/1500，必须在博图中勾选 &lt;strong&gt;“允许从远程伙伴使用 PUT/GET 通信访问”&lt;/strong&gt;，且要访问的 DB 块必须&lt;strong&gt;取消勾选“优化的块访问”&lt;/strong&gt;。&lt;/p&gt;
&lt;/blockquote&gt;
&lt;h3&gt;1.2 数据读写流与内存切片映射&lt;/h3&gt;
&lt;p&gt;S7.Net 支持绝对地址寻址，语法规则类似于 &lt;code&gt;DB1.DBX0.1&lt;/code&gt;, &lt;code&gt;MD20&lt;/code&gt;, &lt;code&gt;Q0.0&lt;/code&gt;。
然而，在实际上位机应用中（如 SCADA 看板），我们需要实时刷新几十上百个数据。千万&lt;strong&gt;不要使用&lt;/strong&gt;多次 &lt;code&gt;plc.Read(&quot;DB1...&quot;)&lt;/code&gt;，因为每一次调用都会触发一次 TCP 握手和 S7 报文收发，极易造成网络风暴。&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;正确使用流程（大块读取 + 内存切片）&lt;/strong&gt;：&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;【API 详解】&lt;/strong&gt;&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;code&gt;ReadBytes(DataType dataType, int db, int startByteAdr, int count)&lt;/code&gt;: 一次性读取整块字节流。
&lt;ul&gt;
&lt;li&gt;&lt;code&gt;dataType&lt;/code&gt;: 数据区枚举。常用 &lt;code&gt;DataType.DataBlock&lt;/code&gt; (DB块), &lt;code&gt;DataType.Memory&lt;/code&gt; (M区)。&lt;/li&gt;
&lt;li&gt;&lt;code&gt;db&lt;/code&gt;: DB块编号。读 DB10 就填 10（如果是非DB区此参数填0）。&lt;/li&gt;
&lt;li&gt;&lt;code&gt;startByteAdr&lt;/code&gt;: 起始字节偏移量。&lt;/li&gt;
&lt;li&gt;&lt;code&gt;count&lt;/code&gt;: 要读取的总字节数。&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;&lt;strong&gt;【WPF 实时数据轮询场景示例】&lt;/strong&gt;&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;using S7.Net;
using S7.Net.Types; // 需要用到辅助转换类

// 开启一个后台死循环任务，用于监控产线数据
private void StartPollingData()
{
    Task.Run(async () =&amp;gt;
    {
        while (true)
        {
            try
            {
                // 一次性读取 DB10 中，从偏移量 0 开始的 100 字节数据
                byte[] buffer = _siemensPlc.ReadBytes(DataType.DataBlock, 10, 0, 100);

                // --- 开始在 C# 内存中高速解析 ---
                
                // 1. 解析 Bool (位): 假设需求是读取 DB10.DBX0.2 (系统启动信号)
                // SelectBit(2) 表示取第一个字节 (buffer[0]) 的第 2 个比特位
                bool isSystemRunning = buffer[0].SelectBit(2); 

                // 2. 解析 Int16 (短整型): 假设需求是读取 DB10.DBW2 (气缸压力值)
                // Skip(2).Take(2) 截取从字节 2 开始的连续 2 个字节
                // FromByteArray 负责处理西门子（大端）与 Windows（小端）的字节序翻转
                short airPressure = Int.FromByteArray(buffer.Skip(2).Take(2).ToArray());

                // 3. 解析 Float/Real (单精度浮点数): 假设需求读取 DB10.DBD4 (熔炉温度)
                // 截取连续 4 个字节，转换为浮点数
                float temperature = Real.FromByteArray(buffer.Skip(4).Take(4).ToArray());

                // --- 切换回 UI 线程刷新界面 ---
                Application.Current.Dispatcher.Invoke(() =&amp;gt; 
                {
                    chkRunning.IsChecked = isSystemRunning;
                    txtPressure.Text = airPressure.ToString() + &quot; Mpa&quot;;
                    txtTemp.Text = temperature.ToString(&quot;F2&quot;) + &quot; ℃&quot;;
                });
            }
            catch(Exception)
            {
                // 通讯中断处理，可在此重连
            }

            // 严禁无限光速轮询，必须休眠，保护网卡与 CPU
            await Task.Delay(100); 
        }
    });
}
&lt;/code&gt;&lt;/pre&gt;
&lt;hr /&gt;
&lt;h2&gt;2. 固高科技 (Googol) 运动控制卡实战&lt;/h2&gt;
&lt;p&gt;固高控制卡是国内运控领域的元老，动态链接库通常为 &lt;code&gt;gts.dll&lt;/code&gt;。它的 API 风格非常偏向底层和 C 语言（全是返回 &lt;code&gt;short&lt;/code&gt; 类型的错误码，0 代表成功，非 0 代表故障）。&lt;/p&gt;
&lt;h3&gt;2.1 核心初始化与伺服上电流程&lt;/h3&gt;
&lt;p&gt;固高卡强依赖于外部配置文件（&lt;code&gt;.cfg&lt;/code&gt;）。机电工程师在装配好机器后，会用固高提供的 MCT2008 辅助软件，把电机的极性、脉冲当量、限位传感器等配置好，并导出一个 &lt;code&gt;.cfg&lt;/code&gt; 文件供 C# 上位机调用。&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;【API 详解】&lt;/strong&gt;&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;code&gt;GT_Open(short channel, short param)&lt;/code&gt;: 打开板卡底层驱动。channel 常填 0（指控第一张卡）。&lt;/li&gt;
&lt;li&gt;&lt;code&gt;GT_Reset()&lt;/code&gt;: 给板卡的 DSP 芯片发一个复位信号，清空内部一切残留的运行状态。&lt;/li&gt;
&lt;li&gt;&lt;code&gt;GT_LoadConfig(string pFile)&lt;/code&gt;: 将配置好的 cfg 文件刷入板卡内部寄存器。&lt;/li&gt;
&lt;li&gt;&lt;code&gt;GT_ClrSts(short axis, short count)&lt;/code&gt;: 清除指定轴上的错误状态（比如刚才碰到了急停、或者掉电报警）。&lt;/li&gt;
&lt;li&gt;&lt;code&gt;GT_AxisOn(short axis)&lt;/code&gt;: 给驱动器发送 Servo On（伺服使能）信号。驱动器听到后就会抱死电机轴，不允许手掰。&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;&lt;strong&gt;【WinForm 窗口加载初始化场景示例】&lt;/strong&gt;&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;using gts;

private void FormMain_Load(object sender, EventArgs e)
{
    // 1. 尝试打开板卡，如果返回不是 0，说明驱动没装好或板卡没插稳
    short rtn = mc.GT_Open(0, 1);
    if (rtn != 0) 
    {
        MessageBox.Show(&quot;PCIe总线未检测到固高控制卡！错误码：&quot; + rtn);
        return;
    }

    // 2. 深度复位
    mc.GT_Reset();

    // 3. 加载存放在程序根目录的机器配置文件
    rtn = mc.GT_LoadConfig(&quot;MachineConfig.cfg&quot;);
    if(rtn != 0)
    {
        MessageBox.Show(&quot;配置文件加载失败，请确认 MachineConfig.cfg 是否存在！&quot;);
        return;
    }

    // 4. 假设我们的设备是一个三轴系统 (X=1, Y=2, Z=3)
    // 我们需要循环清除每个轴的报警，并开启使能
    for (short axis = 1; axis &amp;lt;= 3; axis++)
    {
        mc.GT_ClrSts(1, axis); // 清除轴状态
        mc.GT_AxisOn(axis);    // 伺服通电，听到“哒”的一声继电器响，电机锁死
    }

    MessageBox.Show(&quot;机台底层伺服初始化完成，等待运动指令。&quot;);
}
&lt;/code&gt;&lt;/pre&gt;
&lt;h3&gt;2.2 Jog 模式 (点动：用于手动调试面板)&lt;/h3&gt;
&lt;p&gt;&lt;strong&gt;场景描述&lt;/strong&gt;：在调试阶段，工人需要按住界面上的“X+”按钮，电机就开始转；松开按钮，电机就停。这叫做 Jog 模式。&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;【API 详解】&lt;/strong&gt;&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;code&gt;GT_PrfJog(short axis)&lt;/code&gt;: 告知板卡，这个轴接下来的运动属于 Jog 模式。&lt;/li&gt;
&lt;li&gt;&lt;code&gt;GT_SetJogPrm(short axis, ref TJogPrm prm)&lt;/code&gt;: 配置点动的加速度和减速度。&lt;/li&gt;
&lt;li&gt;&lt;code&gt;GT_SetVel(short axis, double vel)&lt;/code&gt;: 设置点动的方向与速度（正数为正转，负数为反转）。&lt;/li&gt;
&lt;li&gt;&lt;code&gt;GT_Update(int mask)&lt;/code&gt;: 【极度重要】固高卡的大多数设置指令只是放在缓冲区，必须调用 Update 发送掩码，才会真正起跑！掩码采用位运算：轴1是 &lt;code&gt;1&amp;lt;&amp;lt;0&lt;/code&gt;(1)，轴2是 &lt;code&gt;1&amp;lt;&amp;lt;1&lt;/code&gt;(2)，轴3是 &lt;code&gt;1&amp;lt;&amp;lt;2&lt;/code&gt;(4)。&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;&lt;strong&gt;【WPF 键盘点动与安全失焦防护场景示例】&lt;/strong&gt;&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;&lt;strong&gt;🔥 避坑指南 (极度危险的 MouseDown 陷阱)&lt;/strong&gt;：
很多新手喜欢用鼠标的 &lt;code&gt;MouseDown&lt;/code&gt; 和 &lt;code&gt;MouseUp&lt;/code&gt; 来做 Jog。但如果在鼠标按下期间，&lt;strong&gt;窗口被其他软件覆盖、弹出了弹窗导致焦点丢失，或者鼠标拖到窗体外松开，MouseUp 事件将永远不会触发&lt;/strong&gt;，机器轴会像脱缰的野马一样直接撞向机械硬限位！
工业级标准做法是：使用 &lt;code&gt;PreviewKeyDown&lt;/code&gt; / &lt;code&gt;PreviewKeyUp&lt;/code&gt;（原生支持物理按键操作）配合窗口或按钮的 &lt;code&gt;LostFocus&lt;/code&gt; 事件，一旦失去焦点必须强制发送停轴指令。&lt;/p&gt;
&lt;/blockquote&gt;
&lt;pre&gt;&lt;code&gt;// 键盘/鼠标【按下】事件 (开始正向转动)
private void btnXPositive_PreviewKeyDown(object sender, KeyEventArgs e)
{
    // 拦截长按键盘时产生的连续重复触发信号
    if (e.IsRepeat) return; 

    short axisX = 1; // 假设 X 轴映射在物理轴 1
    
    // 1. 设定为点动模式
    mc.GT_PrfJog(axisX);
    
    // 2. 配置点动参数
    mc.TJogPrm jogPrm;
    mc.GT_GetJogPrm(axisX, out jogPrm);
    // 【TJogPrm 结构体参数详解】
    // acc (double): 加速度，单位通常为 pulse/ms^2 (脉冲每平方毫秒)
    // dec (double): 减速度，单位为 pulse/ms^2
    // smooth (double): 平滑系数(0~1)，用于在加减速拐点做 S 型曲线平滑，值越大越平滑但跟随滞后越长
    jogPrm.acc = 0.5; 
    jogPrm.dec = 0.5; 
    jogPrm.smooth = 0.5; // 可选的平滑过渡
    mc.GT_SetJogPrm(axisX, ref jogPrm);

    // 3. 设定恒定速度 (假设 100 脉冲/毫秒，正数代表正向跑)
    mc.GT_SetVel(axisX, 100);

    // 4. 触发物理运行！(1 &amp;lt;&amp;lt; (1-1) 即 1)
    mc.GT_Update(1 &amp;lt;&amp;lt; (axisX - 1)); 
}

// 键盘【抬起】事件 (要求平滑停止)
private void btnXPositive_PreviewKeyUp(object sender, KeyEventArgs e)
{
    StopJogAxis(1);
}

// 【保命核心】控件或窗口失去焦点时，无条件强制刹车！
private void btnXPositive_LostFocus(object sender, RoutedEventArgs e)
{
    StopJogAxis(1); // 哪怕没有触发 KeyUp，失焦也立刻停机
}

// 独立的平滑停止函数
private void StopJogAxis(short axis)
{
    // 在 Jog 模式下，将目标速度设为 0 并 Update，电机就会根据设定的 dec (减速度) 完美平滑刹车
    mc.GT_SetVel(axis, 0);
    mc.GT_Update(1 &amp;lt;&amp;lt; (axis - 1));
}
&lt;/code&gt;&lt;/pre&gt;
&lt;h3&gt;2.3 Trap 模式 (点位定位：最常用的绝对/相对运动)&lt;/h3&gt;
&lt;p&gt;&lt;strong&gt;场景描述&lt;/strong&gt;：这是工控中最核心的模式。机台要从当前工位“走到”绝对坐标 50000 的位置，要求起步有加速，中途匀速，快到终点时自动减速刹车（呈现梯形速度曲线）。&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;【API 与参数详解】&lt;/strong&gt;&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;code&gt;GT_PrfTrap(short axis)&lt;/code&gt;: 设定轴为梯形点位模式。&lt;/li&gt;
&lt;li&gt;&lt;code&gt;GT_SetTrapPrm(short axis, ref TTrapPrm prm)&lt;/code&gt;: 配置梯形曲线的核心参数。
&lt;ul&gt;
&lt;li&gt;&lt;strong&gt;【TTrapPrm 结构体详解】&lt;/strong&gt;:
&lt;ul&gt;
&lt;li&gt;&lt;code&gt;acc&lt;/code&gt; (double): 加速度 (pulse/ms^2)，决定起步有多猛。&lt;/li&gt;
&lt;li&gt;&lt;code&gt;dec&lt;/code&gt; (double): 减速度 (pulse/ms^2)，决定刹车有多急。&lt;/li&gt;
&lt;li&gt;&lt;code&gt;smoothTime&lt;/code&gt; (short): 平滑时间 (ms)。极其重要！如果不设置，电机会在加速瞬间产生极其刺耳的“嘎噔”异响。设为 10~50ms 可在加减速拐点形成 S 曲线，大幅度保护机械寿命。&lt;/li&gt;
&lt;li&gt;&lt;code&gt;velStart&lt;/code&gt; (double): 起步初速，通常设为 0 以防刚性冲击。&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;&lt;code&gt;GT_SetPos(short axis, int pos)&lt;/code&gt;: 设定目标位置的绝对刻度（或者相对刻度，取决于底层配置）。&lt;/li&gt;
&lt;li&gt;&lt;code&gt;GT_SetVel(short axis, double vel)&lt;/code&gt;: 设定此段路程的最高巡航速度。&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;&lt;strong&gt;【梯形点位移动场景示例】&lt;/strong&gt;&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;public void MoveToAbsolutePosition(short axis, int targetPos)
{
    // 1. 设置该轴为梯形点位定位模式
    mc.GT_PrfTrap(axis);

    // 2. 灌入梯形结构体参数
    mc.TTrapPrm trapPrm;
    mc.GT_GetTrapPrm(axis, out trapPrm); // 先获取底层默认参数
    trapPrm.acc = 1.0;          // 设定加速度
    trapPrm.dec = 1.0;          // 设定减速度
    trapPrm.smoothTime = 25;    // 设定 25ms 的 S 型平滑过度，让起步如丝般顺滑
    mc.GT_SetTrapPrm(axis, ref trapPrm);

    // 3. 压入终点坐标与速度
    mc.GT_SetPos(axis, targetPos);
    mc.GT_SetVel(axis, 50.0); // 巡航速度设定为 50 pulse/ms

    // 4. 呼叫底层起跑
    mc.GT_Update(1 &amp;lt;&amp;lt; (axis - 1));
}
&lt;/code&gt;&lt;/pre&gt;
&lt;h3&gt;2.4 PT 模式 (Position-Time 位置-时间模式)&lt;/h3&gt;
&lt;p&gt;&lt;strong&gt;场景描述&lt;/strong&gt;：当传统的 Trap 模式无法满足需求（例如：你需要让电机的位移曲线呈现出一种极为变态的非线性自定义波形，或者用于电子凸轮追剪、飞锯），你可以使用 PT 模式。它允许你直接向板卡发送一连串的 &lt;code&gt;(位置增量, 耗时)&lt;/code&gt; 数据对，板卡会在底层严格按照你的时间表无缝切过这些点。&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;【API 与参数详解】&lt;/strong&gt;&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;code&gt;GT_PrfPt(short axis, short mode)&lt;/code&gt;: 设置为 PT 模式。&lt;/li&gt;
&lt;li&gt;&lt;code&gt;GT_PtSpace(short axis, out short pSpace)&lt;/code&gt;: 查询底层 PT 缓冲区的剩余空间。因为 FIFO 通常很小（只能容纳 32 组点），长路径必须边跑边塞。&lt;/li&gt;
&lt;li&gt;&lt;code&gt;GT_PtData(short axis, double pos, short time, short type)&lt;/code&gt;: 压入数据点。
&lt;ul&gt;
&lt;li&gt;&lt;code&gt;pos&lt;/code&gt; (double): 在这段时间内要求电机走的&lt;strong&gt;位移增量&lt;/strong&gt;（脉冲数）。&lt;/li&gt;
&lt;li&gt;&lt;code&gt;time&lt;/code&gt; (short): 走完这段位移必须花费的&lt;strong&gt;绝对时间&lt;/strong&gt;（毫秒）。&lt;/li&gt;
&lt;li&gt;&lt;code&gt;type&lt;/code&gt; (short): 线型规划枚举。&lt;code&gt;PT_MODE_STATIC&lt;/code&gt; (普通匀速) 或 &lt;code&gt;PT_MODE_DYNAMIC&lt;/code&gt;。&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;&lt;code&gt;GT_PtStart(int mask, int option)&lt;/code&gt;: 启动指定轴的 PT 引擎开始消耗队列。&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;&lt;strong&gt;【PT 模式自定义正弦波震动曲线示例】&lt;/strong&gt;&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;public void StartCustomPtCurve(short axis)
{
    // 1. 清空底层缓冲并设置为 PT 模式 (mode 1 指静态模式)
    mc.GT_PtClear(axis);
    mc.GT_PrfPt(axis, 1);

    // 2. 压入自定义数据
    // 假设我们要模拟一段正弦波振动，自行计算出每次 10ms 需要走的增量 posDelta
    for(int i = 0; i &amp;lt; 50; i++)
    {
        short space;
        mc.GT_PtSpace(axis, out space); // 查询剩余空间
        
        if (space &amp;gt; 0) // 确保板卡 FIFO 还没塞满
        {
            // 通过数学公式计算位移：一个周期波的增量
            double posDelta = Math.Sin(i * 0.1) * 100; 
            
            // 压入指令：强制驱动器花费恰好 10ms 走完 posDelta 的距离
            mc.GT_PtData(axis, posDelta, 10, mc.PT_MODE_STATIC);
        }
    }

    // 3. 开始严格按时间序列执行
    mc.GT_PtStart(1 &amp;lt;&amp;lt; (axis - 1), 0);
}
&lt;/code&gt;&lt;/pre&gt;
&lt;h3&gt;2.5 多轴插补模式 (Interpolation / Crd)&lt;/h3&gt;
&lt;p&gt;&lt;strong&gt;场景描述&lt;/strong&gt;：自动点胶机，需要控制胶枪在 XY 平面上走一个完美的矩形或圆弧路径。此时单独控制 X 和 Y 是没用的（跑出来是锯齿），必须让固高底层 DSP 芯片将两轴联动（插补）。&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;【API 详解】&lt;/strong&gt;&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;code&gt;GT_SetCrdPrm(short crd, ref TCrdPrm prm)&lt;/code&gt;: 初始化坐标系。告诉板卡，几号坐标系由哪几个轴构成。&lt;/li&gt;
&lt;li&gt;&lt;code&gt;GT_CrdClear(short crd, short fifo)&lt;/code&gt;: 清空插补 FIFO 缓冲队列。&lt;/li&gt;
&lt;li&gt;&lt;code&gt;GT_LnXY(short crd, int x, int y, double synVel, double synAcc, double velEnd, short fifo)&lt;/code&gt;: 将一段 XY 直线推入缓冲队列。
&lt;ul&gt;
&lt;li&gt;&lt;code&gt;x, y&lt;/code&gt;: 直线终点的绝对脉冲坐标。&lt;/li&gt;
&lt;li&gt;&lt;code&gt;synVel&lt;/code&gt;: 这一段直线的巡航合成速度。&lt;/li&gt;
&lt;li&gt;&lt;code&gt;velEnd&lt;/code&gt;: 终点速度。如果填 0，代表走到这会停；如果填非 0，代表直接滑过这个点连接下一段线（前瞻）。&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;&lt;code&gt;GT_CrdStart(short mask, short option)&lt;/code&gt;: 一键启动坐标系的插补引擎。&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;&lt;strong&gt;【插补点胶作业场景示例】&lt;/strong&gt;&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;public void StartDispensingSquare()
{
    // 1. 构建一个二维坐标系配置
    mc.TCrdPrm crdPrm = new mc.TCrdPrm();
    crdPrm.dimension = 2;            // 我们需要联动两个轴 (X,Y)
    crdPrm.profile[0] = 1;           // 该坐标系的 X 绑定到物理轴 1
    crdPrm.profile[1] = 2;           // 该坐标系的 Y 绑定到物理轴 2
    
    // 初始化 1 号坐标系
    mc.GT_SetCrdPrm(1, ref crdPrm); 
    
    // 启动前必须清空 1 号坐标系的 0 号 FIFO 数据缓冲区
    mc.GT_CrdClear(1, 0);

    // 2. 依次推入矩形的四条边
    // 参数含义：坐标系1, X点, Y点, 合成速度100, 加速度1, 终点速度(50代表不停车滑过拐角), FIFO区号0
    
    // 走线段1: 到达 (10000, 0)
    mc.GT_LnXY(1, 10000, 0, 100, 1, 50, 0); 
    
    // 走线段2: 到达 (10000, 10000)
    mc.GT_LnXY(1, 10000, 10000, 100, 1, 50, 0); 
    
    // 走线段3: 到达 (0, 10000)
    mc.GT_LnXY(1, 0, 10000, 100, 1, 50, 0); 
    
    // 走线段4: 闭合回到起点 (0, 0)，此时要求停车，故终点速度给 0
    mc.GT_LnXY(1, 0, 0, 100, 1, 0, 0); 

    // 3. 所有路径下发完毕，呼叫底层的 DSP 插补引擎起跑！（掩码位1代表坐标系1）
    mc.GT_CrdStart(1, 0); 
}
&lt;/code&gt;&lt;/pre&gt;
&lt;hr /&gt;
&lt;h2&gt;3. 雷赛智能 (Leadshine) 运动控制卡实战&lt;/h2&gt;
&lt;p&gt;雷赛在 3C 消费电子制造装备（如贴片机、视觉分拣分板机）领域占有率极高，底层通信库通常为 &lt;code&gt;LTDMC.dll&lt;/code&gt;。它的 API 以 &lt;code&gt;dmc_&lt;/code&gt; 开头，比起固高，它的功能分类更加直白。&lt;/p&gt;
&lt;h3&gt;3.1 核心初始化与引脚配置&lt;/h3&gt;
&lt;p&gt;雷赛的架构与固高不同，可以直接通过 API 强行设定很多硬件信号，而不过分依赖于外部配置文件。&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;【API 详解】&lt;/strong&gt;&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;code&gt;dmc_board_init()&lt;/code&gt;: 扫描电脑的插槽并初始化所有雷赛控制卡，返回检测到的可用卡数量。&lt;/li&gt;
&lt;li&gt;&lt;code&gt;dmc_write_sevon_pin(ushort CardNo, ushort NodeId, ushort on_off)&lt;/code&gt;: 强行拉高/拉低 Servo 针脚的电平。1 为使能（电机抱死）。&lt;/li&gt;
&lt;li&gt;&lt;code&gt;dmc_set_pulse_outmode(ushort CardNo, ushort NodeId, ushort outmode)&lt;/code&gt;: 设置输出脉冲的电气模式。大多数市面驱动器采用 &lt;code&gt;1&lt;/code&gt;，即 &lt;code&gt;脉冲(Pulse) + 方向(Dir)&lt;/code&gt; 的单端输出方式。&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;&lt;strong&gt;【WinForm/WPF 极简初始化】&lt;/strong&gt;&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;using csLTDMC; // 引用雷赛官方 C# 封装类

public void InitLeadshineCard()
{
    // 1. 初始化引擎，这步如果没插卡会卡住一小会，所以返回 &amp;lt;=0 代表失败
    short cardCount = LTDMC.dmc_board_init();
    if(cardCount &amp;lt;= 0) 
    {
        MessageBox.Show(&quot;未检测到任何雷赛控制卡在位！&quot;);
        return;
    }

    ushort cardNo = 0; // 板卡标识序号（第一张卡是 0）
    
    // 假设卡上接了 4 个电机轴 (索引 0~3)
    for (ushort axis = 0; axis &amp;lt; 4; axis++)
    {
        // 2. 发送 Servo 使能信号，1 表示强电吸合
        LTDMC.dmc_write_sevon_pin(cardNo, axis, 1); 
        
        // 3. 设置输出模式：1 代表“脉冲+方向”模式
        LTDMC.dmc_set_pulse_outmode(cardNo, axis, 1);
    }
}
&lt;/code&gt;&lt;/pre&gt;
&lt;h3&gt;3.2 P-Move 绝对定位与完美 S 曲线柔化&lt;/h3&gt;
&lt;p&gt;在大多数定长工位间移动时，我们需要机器既跑得快，起步刹车又要稳（防止机器猛然颤抖导致零件掉落）。雷赛通过两组 API 分解了速度曲线。&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;【API 详解】&lt;/strong&gt;&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;code&gt;dmc_set_profile(CardNo, axis, Min_Vel, Max_Vel, Tacc, Tdec)&lt;/code&gt;: 设定梯形主心骨。起步速度、巡航最大速度、加速耗时、减速耗时。&lt;/li&gt;
&lt;li&gt;&lt;code&gt;dmc_set_s_profile(CardNo, axis, s_mode, s_time)&lt;/code&gt;: &lt;strong&gt;灵魂配置！&lt;/strong&gt; 在起步、加速到顶、减速、刹车的四个拐点处，增加 S 形的弧形过渡柔化时间。&lt;/li&gt;
&lt;li&gt;&lt;code&gt;dmc_pmove(CardNo, axis, Dist, posi_mode)&lt;/code&gt;: 触发运动指令。
&lt;ul&gt;
&lt;li&gt;&lt;code&gt;Dist&lt;/code&gt;: 目标脉冲数值。&lt;/li&gt;
&lt;li&gt;&lt;code&gt;posi_mode&lt;/code&gt;: &lt;code&gt;1&lt;/code&gt; 代表绝对坐标（走到刻度10000），&lt;code&gt;0&lt;/code&gt; 代表相对坐标（在当前基础上再往前走10000）。&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;&lt;strong&gt;【移动至组装工位场景示例】&lt;/strong&gt;&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;public void MoveToAssembleStation(ushort axis, int targetPulsePos)
{
    ushort card = 0;

    // 1. 配置梯形速度主骨架
    double startVel = 500;   // 刚起步瞬间的速度 (pulse/s)
    double maxVel = 50000;   // 允许飚到的最高速度 (pulse/s)
    double timeAcc = 0.2;    // 用 0.2 秒从 500 飚到 50000
    double timeDec = 0.2;    // 刹车时间
    LTDMC.dmc_set_profile(card, axis, startVel, maxVel, timeAcc, timeDec);

    // 2. 叠加 S 曲线柔化 (消除机械“点头”抖动)
    // s_mode 填 0 (时间模式)，0.05 代表 S 段柔化耗时占用 0.05 秒
    LTDMC.dmc_set_s_profile(card, axis, 0, 0.05); 

    // 3. 触发指令！参数 1 代表绝对运动，目标绝对位置为 targetPulsePos
    LTDMC.dmc_pmove(card, axis, targetPulsePos, 1); 

    // 4. 等待运动结束 (如果是基于 Task 的后台作业，在此处阻塞等待即可)
    // dmc_check_done 返回 1 代表轴彻底停稳了，返回 0 代表正在跑
    while(LTDMC.dmc_check_done(card, axis) == 0)
    {
        Thread.Sleep(10); // 【铁律】这里必须休眠释放 CPU 时间片，否则程序卡死
    }
    
    // 抵达工位后，触发后续的吹气阀、气缸等动作...
}
&lt;/code&gt;&lt;/pre&gt;
&lt;h3&gt;3.3 高阶应用：Position Compare (硬件飞拍比较输出)&lt;/h3&gt;
&lt;p&gt;&lt;strong&gt;场景描述&lt;/strong&gt;：在极速运行的 SMT 贴片流水线或 CCD 视觉检测站中，如果通过 C# 代码不断询问板卡“你到目标点了吗？”，一旦确认到了再调用相机拍照 API，这中间经过了 USB、Windows 线程调度，至少会产生 10 毫秒的误差。在 2m/s 的运动下，10毫秒意味着相机拍出来的画面完全错位并发虚。&lt;/p&gt;
&lt;p&gt;雷赛的杀手锏是 &lt;strong&gt;Position Compare (飞拍比较)&lt;/strong&gt;：你提前把坐标写入板卡的 DSP 芯片。当高速直线电机掠过该坐标点时，DSP 芯片会在内部产生一个纳秒级的硬中断，瞬间导通板卡外部的特定引脚发出一道电流脉冲给相机。&lt;strong&gt;整个过程零软件延迟，上位机只管事后去取照片&lt;/strong&gt;。&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;【API 详解】&lt;/strong&gt;&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;code&gt;dmc_compare_clear_points(CardNo, cmp)&lt;/code&gt;: 清空某个比较通道内部的点位阵列。&lt;/li&gt;
&lt;li&gt;&lt;code&gt;dmc_compare_add_point(CardNo, cmp, pos, dir, action, actpos)&lt;/code&gt;: 将坐标推入比较器。
&lt;ul&gt;
&lt;li&gt;&lt;code&gt;pos&lt;/code&gt;: 要触发相机的坐标刻度。&lt;/li&gt;
&lt;li&gt;&lt;code&gt;dir&lt;/code&gt;: 运动方向（0为正向碰触触发）。&lt;/li&gt;
&lt;li&gt;&lt;code&gt;action&lt;/code&gt;: 1 为发出有效电平。&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;&lt;code&gt;dmc_compare_start(CardNo, cmp)&lt;/code&gt;: 使能该比较通道，开始监听飞速转动的编码器值。&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;&lt;strong&gt;【飞拍检测线视觉触发示例】&lt;/strong&gt;&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;public void SetupFlyCameraTrigger(ushort axis)
{
    ushort card = 0;
    ushort cmpChannel = 0; // 假定使用 0 号比较通道连接了相机

    // 1. 清空历史触发点
    LTDMC.dmc_compare_clear_points(card, cmpChannel);

    // 2. 设置比较参数配置: 绑定当前 axis，选择使用“规划位置”进行比较
    LTDMC.dmc_compare_set_config(card, cmpChannel, 1, axis, 0, 0);

    // 3. 我们在长条形流水线上，需要在 X 坐标为 10000, 20000, 30000 的地方进行高速无感抓拍
    int[] triggerPositions = new int[] { 10000, 20000, 30000 };
    
    foreach(var pos in triggerPositions)
    {
        // 向 DSP 压入触发点。
        // 参数含义: 卡号, 通道号, 触发位置pos, 方向(0=正向有效), 动作(1=输出有效脉冲), 动作保持时间(无视填0)
        LTDMC.dmc_compare_add_point(card, cmpChannel, pos, 0, 1, 0);
    }

    // 4. 激活监听
    LTDMC.dmc_compare_start(card, cmpChannel);

    // 设置完后，上位机只管让电机狂飙跑过这段路程，硬件板卡在路过时会自动电击相机触发快门
    LTDMC.dmc_pmove(card, axis, 50000, 1); 
}
&lt;/code&gt;&lt;/pre&gt;
&lt;hr /&gt;
&lt;h2&gt;4. 雷赛 vs 固高：插补运动核心差异与选型决策&lt;/h2&gt;
&lt;p&gt;在实际项目选型阶段，&quot;用雷赛还是固高&quot;是工控软件工程师最常面临的决策。两者虽然都能完成运动控制，但在&lt;strong&gt;底层插补架构、API 设计哲学、以及最终适配的应用场景&lt;/strong&gt;上存在本质差异。&lt;/p&gt;
&lt;h3&gt;4.1 架构层面的根本差异&lt;/h3&gt;
&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;对比维度&lt;/th&gt;
&lt;th&gt;固高 (Googol)&lt;/th&gt;
&lt;th&gt;雷赛 (Leadshine)&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;底层芯片&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;自研高性能 DSP (数字信号处理器)，板载独立运算&lt;/td&gt;
&lt;td&gt;ARM + FPGA 混合架构，偏向工业成熟方案&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;插补引擎&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;板载独立坐标系引擎，支持&lt;strong&gt;缓冲区连续插补&lt;/strong&gt; (BufLine/BufArc)，段间速度可连续不停顿&lt;/td&gt;
&lt;td&gt;默认每段插补结束会&lt;strong&gt;减速到零&lt;/strong&gt;，需额外开启&quot;前瞻&quot;或&quot;连续插补&quot;模式才能实现段间不停&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;配置方式&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;强依赖外部 &lt;code&gt;.cfg&lt;/code&gt; 配置文件（由 MCT2008 工具导出），机电参数与代码解耦&lt;/td&gt;
&lt;td&gt;可通过纯 API 在代码中直接配置所有硬件参数，不强制依赖外部文件&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;API 风格&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;偏 C 语言底层风格，返回 &lt;code&gt;short&lt;/code&gt; 错误码，函数名以 &lt;code&gt;GT_&lt;/code&gt; 开头&lt;/td&gt;
&lt;td&gt;更加直白的功能命名，函数名以 &lt;code&gt;dmc_&lt;/code&gt; 开头，参数语义更易读&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;多轴同步触发&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;&lt;code&gt;GT_Update(mask)&lt;/code&gt; 位掩码&lt;strong&gt;一次触发多轴&lt;/strong&gt;，硬件级严格同步，时间差为零&lt;/td&gt;
&lt;td&gt;多轴分别调用 &lt;code&gt;dmc_pmove&lt;/code&gt;，存在&lt;strong&gt;微秒级循环时间差&lt;/strong&gt;，非硬件级同步&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;生态定位&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;偏科研、高精度、复杂轨迹场景（激光切割、精密加工中心）&lt;/td&gt;
&lt;td&gt;偏 3C 电子制造、视觉分拣、贴片机等高通量产线场景&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;
&lt;h3&gt;4.2 插补运动的核心差异详解&lt;/h3&gt;
&lt;h4&gt;固高的插补优势：缓冲区连续路径&lt;/h4&gt;
&lt;p&gt;固高的坐标系引擎允许你将&lt;strong&gt;整段复杂路径一次性压入 FIFO 缓冲区&lt;/strong&gt;，底层 DSP 会自动执行前瞻计算，在直线段与直线段、直线段与圆弧段之间实现&lt;strong&gt;速度连续过渡&lt;/strong&gt;，不需要在每个拐点减速到零再重新加速。&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;// 固高：连续路径插补 —— 段间速度不归零
mc.GT_CrdClear(1, 0);

// velEnd 填非零值 → 告诉 DSP &quot;不要在这个点停车，直接滑过去&quot;
mc.GT_LnXY(1, 10000, 0,     100, 1, 50, 0);  // 第一段终点速度 50，不停
mc.GT_LnXY(1, 10000, 10000, 100, 1, 50, 0);  // 第二段终点速度 50，不停
mc.GT_LnXY(1, 0,     10000, 100, 1, 50, 0);  // 第三段终点速度 50，不停
mc.GT_LnXY(1, 0,     0,     100, 1, 0,  0);  // 最后一段终点速度 0，停车

mc.GT_CrdStart(1, 0); // 一键起跑，四段路径一气呵成
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;&lt;strong&gt;效果&lt;/strong&gt;：胶枪走出的矩形路径拐角处&lt;strong&gt;不会出现断胶或积胶&lt;/strong&gt;，因为速度从未归零。&lt;/p&gt;
&lt;h4&gt;雷赛的插补特点：逐段触发 + 前瞻模式&lt;/h4&gt;
&lt;p&gt;雷赛的默认插补行为是&lt;strong&gt;每段结束后减速到零&lt;/strong&gt;，适合&quot;走一段、停一下、做个动作、再走下一段&quot;的工序。如果需要连续路径，必须显式开启连续插补模式：&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;// 雷赛：需要额外配置才能实现连续插补
ushort card = 0;

// 设置插补坐标系 (将轴 0 和轴 1 绑定为 XY 二维坐标系)
LTDMC.dmc_set_vector_profile_unit(card, 0, 500, 50000, 0.2, 0.2, 0);

// 默认模式：每段结束减速到零
LTDMC.dmc_line_unit(card, 0, new int[]{10000, 0}, 2, 0, 1);    // 走完就停
LTDMC.dmc_line_unit(card, 0, new int[]{10000, 10000}, 2, 0, 1); // 再起步再停

// 如果要实现连续不停，需要开启&quot;连续插补&quot;模式：
// dmc_conti_open_list → 压入路径 → dmc_conti_close_list → dmc_conti_start
LTDMC.dmc_conti_open_list(card, 0, 0);
LTDMC.dmc_conti_line_unit(card, 0, 0, new int[]{10000, 0}, 2, 0, 50000, 0);
LTDMC.dmc_conti_line_unit(card, 0, 0, new int[]{10000, 10000}, 2, 0, 50000, 0);
LTDMC.dmc_conti_close_list(card, 0);
LTDMC.dmc_conti_start(card, 0);
&lt;/code&gt;&lt;/pre&gt;
&lt;h3&gt;4.3 选型决策：什么场景用雷赛，什么场景用固高&lt;/h3&gt;
&lt;h4&gt;选择固高 (Googol) 的典型场景&lt;/h4&gt;
&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;场景&lt;/th&gt;
&lt;th&gt;原因&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;激光切割 / 激光焊接&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;激光束必须沿复杂轮廓连续移动，任何速度归零都会导致过烧或断焊&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;精密数控加工中心 (CNC)&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;需要 3 轴甚至 5 轴联动的复杂空间曲面插补，对轨迹精度有极致要求&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;点胶/涂胶自动化&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;连续胶线不允许断点，固高的缓冲区连续插补天然匹配&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;科研级高精度运动平台&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;板载 DSP 的实时运算能力远优于 ARM，适合需要自定义复杂运动曲线的场景&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;多轴严格同步需求&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;&lt;code&gt;GT_Update(mask)&lt;/code&gt; 实现硬件级零时差多轴同步，适合龙门双驱等对称结构&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;
&lt;h4&gt;选择雷赛 (Leadshine) 的典型场景&lt;/h4&gt;
&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;场景&lt;/th&gt;
&lt;th&gt;原因&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;SMT 贴片机 / 视觉分拣机&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;&quot;走→停→拍照→贴片→走&quot;的离散工序，天然匹配雷赛逐段触发的模式&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;AOI / CCD 视觉检测线&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;雷赛的 Position Compare（飞拍比较）硬件飞拍功能是杀手锏，纳秒级触发精度&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;高通量产线 (3C 电子)&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;雷赛性价比极高，API 学习曲线平缓，适合快速交付的产线项目&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;分板机 / 焊锡机&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;工位间是&quot;点到点&quot;定位运动，不需要复杂连续轨迹，用雷赛的 pmove + S 曲线即可完美胜任&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;快速原型验证 / 小型设备&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;纯 API 配置无需额外工具，代码即配置，适合小团队敏捷开发&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;
&lt;h3&gt;4.4 混合使用的工程实践&lt;/h3&gt;
&lt;p&gt;在大型产线中，&lt;strong&gt;固高和雷赛混合使用&lt;/strong&gt;并不罕见。典型方案是：&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;strong&gt;固高负责核心加工工位&lt;/strong&gt;：如激光头的 XY 联动插补、涂胶臂的三维轨迹&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;雷赛负责物料搬运工位&lt;/strong&gt;：如传送带点位定位、视觉飞拍触发、分拣机械臂的离散搬运&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;两者通过上位机的统一状态机调度，共享同一个 C# 进程的不同线程池。上位机充当&quot;总指挥&quot;，根据当前工序阶段分别下发指令给不同的板卡。&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;+-------------------------------------+
|          C# 上位机 (总控)            |
|    +----------+  +----------+       |
|    | 固高线程  |  | 雷赛线程  |       |
|    | GT_xxx() |  | dmc_xxx()|       |
|    +----+-----+  +----+-----+       |
|         |PCIe         |PCIe         |
+---------+-------------+-------------+
          |             |
   +------+----+  +-----+-----+
   | 固高板卡  |  | 雷赛板卡  |
   | (激光XY) |  | (搬运+飞拍)|
   +----------+  +-----------+
&lt;/code&gt;&lt;/pre&gt;
&lt;hr /&gt;
&lt;h2&gt;5. 总结：写给 C# 上位机开发者的&quot;血泪箴言&quot;与最容易踩的坑&lt;/h2&gt;
&lt;p&gt;无论你是去跟西门子网线打交道，还是在机箱里去插固高和雷赛的 PCIe 板卡，在这条路上踩过的坑总结起来必须全文背诵：&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;
&lt;p&gt;&lt;strong&gt;绝对隔离 UI 线程与硬件总线通讯&lt;/strong&gt;
坚决禁止在 WPF/WinForm 的 &lt;code&gt;Button_Click&lt;/code&gt; 里直接撰写 &lt;code&gt;plc.Write()&lt;/code&gt; 或阻塞等待的 &lt;code&gt;while(dmc_check_done(..) == 0)&lt;/code&gt;。
所有硬件交涉必须通过 &lt;code&gt;Command&lt;/code&gt; 将意图投递到后端的独立 &lt;code&gt;Task&lt;/code&gt; 或状态机线程池中执行。主线程一旦卡死，工控机在车间看起来就跟“死机”了一样，极为凶险。&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;&lt;strong&gt;警惕致命的死循环轮询陷阱&lt;/strong&gt;
在读取 PLC 或者板卡底层状态（如 &lt;code&gt;dmc_check_done&lt;/code&gt;）时，&lt;strong&gt;必须加上 &lt;code&gt;Thread.Sleep(5)&lt;/code&gt; 或 &lt;code&gt;await Task.Delay(5)&lt;/code&gt;&lt;/strong&gt;。
如果你不加休眠进行极速空转，它会以每秒千万次的频率向底层驱动或网卡发起访问，瞬间导致 PCIe 总线阻塞、通讯超时、CPU 单核满载甚至操作系统蓝屏宕机。&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;&lt;strong&gt;多轴同步、插补机制的底层差异与急停逻辑&lt;/strong&gt;&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;strong&gt;多轴同步&lt;/strong&gt;：雷赛多轴分别调 &lt;code&gt;dmc_pmove&lt;/code&gt; 存在循环时间差（几十微秒），如果要严格同步建议用固高的 &lt;code&gt;GT_Update(mask)&lt;/code&gt; 位掩码一次触发多轴。&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;插补运动&lt;/strong&gt;：等待完成必须判断所有参与轴，不能只判断一个轴。固高插补的核心优势是 BufLine/BufArc 缓冲区模式，先把整段路径压入 FIFO 再统一触发，段间速度连续不停顿；雷赛默认每段结束会减速到零，要实现连续需要开启&quot;前瞻&quot;或&quot;连续插补&quot;模式。&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;急停逻辑&lt;/strong&gt;：急停逻辑必须同时取消 &lt;code&gt;CancellationToken&lt;/code&gt; 和调底层停止函数，两者缺一不可。&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;&lt;strong&gt;软件只是辅助，安全硬急停才是最后底线&lt;/strong&gt;
软件写得再精妙，也防不了现场网线被老鼠咬断、车间遭遇瞬间强电磁干扰、或者 Windows 突然后台更新弹窗卡死。
作为上位机开发，你永远只能把电脑当作“大脑指挥中枢”。你必须强烈要求电气硬件工程师在电柜外面串联一个绝对独立的**“大红色蘑菇头物理急停按键”**，用来在发生撞机前，瞬间用物理开关切断所有驱动器的 220V 供电回路。&lt;strong&gt;人身安全，重于一切！&lt;/strong&gt;&lt;/p&gt;
&lt;/li&gt;
&lt;/ol&gt;
</content:encoded></item><item><title>工业级通信与 Modbus 协议全栈指南：从底层串口理论到高并发 Socket 架构实战</title><link>https://meteor-comet.github.io/posts/serialport-communication/</link><guid isPermaLink="true">https://meteor-comet.github.io/posts/serialport-communication/</guid><description>全景解析高低位/CRC校验与 NModbus 集成；探讨 ISO 模型、C# async 异步 Socket 与核心防范通信并发内存问题</description><pubDate>Thu, 05 Sep 2024 00:00:00 GMT</pubDate><content:encoded>&lt;h1&gt;工业级通信全栈指南：从串口硬件报文到 TCP Socket 高并发架构&lt;/h1&gt;
&lt;p&gt;在现代上位机软件（如基于 C#/WPF/Avalonia/WinForm 的设备网关或智控总控终端）开发体系中，工业互联往往是研发实战架构的重中之重。开发者不仅需要与最底层的电气硬件（如单片机、PLC、各类传感器等）通过**本地串口通信（如 RS-232 / RS-485）**进行原始数据交换，更要在系统升维后，构建出能承托成千上万节点交互的 &lt;strong&gt;TCP Socket 广域网传输引擎集群&lt;/strong&gt;。&lt;/p&gt;
&lt;p&gt;许多高级应用架构师虽然能够搭建出完善的前端框架，但在对接底层物理总线和网络基建时，面对“高低位重组、Hex与ASCII解析、CRC校验”乃至高并发环境下的“半开TCP连接、通信粘包、非线程安全集合修改错位”等底层情况时，可能仍会遇到不小的挑战。本文将从串行通信与位运算的基础原点切入，跨越 &lt;code&gt;NModbus4&lt;/code&gt; 通信中间件的业务模型，最终结合 ISO 七层参考模型引入并下探企业级的 C# 异步 TCP/IP Socket 并发编程原理及注意事项。&lt;/p&gt;
&lt;h2&gt;目录&lt;/h2&gt;
&lt;ul&gt;
&lt;li&gt;&lt;a href=&quot;#1-%E4%B8%B2%E5%8F%A3%E9%80%9A%E4%BF%A1%E7%9A%84%E5%9F%BA%E6%9C%AC%E6%A6%82%E5%BF%B5&quot;&gt;1. 串口通信的基本概念&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;#2-%E6%A6%82%E5%BF%B5%E8%A7%A3%E6%9E%90%E9%AB%98%E4%BD%8Dmsb%E4%B8%8E%E4%BD%8E%E4%BD%8Dlsb&quot;&gt;2. 概念解析：高位(MSB)与低位(LSB)&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;#3-%E9%80%9A%E4%BF%A1%E7%9A%84%E8%AF%AD%E8%A8%8016%E8%BF%9B%E5%88%B6hex%E4%B8%8Eascii&quot;&gt;3. 通信的语言：16进制(Hex)与ASCII&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;#4-%E5%AF%B9%E8%AF%9D%E7%9A%84%E8%89%BA%E6%9C%AF%E6%94%B6%E5%8F%91%E9%97%AE%E8%AF%A2%E7%A0%81%E4%B8%8E%E5%B8%A7%E7%BB%93%E6%9E%84&quot;&gt;4. 对话的艺术：收发问询码与帧结构&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;#5-%E5%B8%B8%E8%A7%81%E7%9A%84%E6%95%B0%E6%8D%AE%E6%A0%A1%E9%AA%8C%E7%AE%97%E6%B3%95&quot;&gt;5. 常见的数据校验算法&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;#6-c-winform-%E5%AE%9E%E6%88%98serialport-%E6%A0%B8%E5%BF%83-api-%E4%B8%8E%E5%B1%9E%E6%80%A7&quot;&gt;6. C# WinForm 实战：SerialPort 核心 API 与属性&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;#7-%E5%AE%9E%E6%88%98%E6%94%B6%E5%8F%91hex-%E4%B8%8E-ascii-%E7%9A%84%E8%BD%AC%E6%8D%A2%E6%98%BE%E7%A4%BA%E6%9C%BA%E5%88%B6&quot;&gt;7. 实战收发：Hex 与 ASCII 的转换显示机制&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;#8-%E5%B7%A5%E4%B8%9A%E7%BA%A7%E7%BB%8F%E5%85%B8%E5%8D%8F%E8%AE%AEmodbus-%E6%A0%B8%E5%BF%83%E7%90%86%E8%AE%BA&quot;&gt;8. 工业级经典协议：Modbus 核心理论&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;#9-c-%E7%9A%84-modbus-%E6%A0%B8%E5%BF%83%E6%A1%86%E6%9E%B6nmodbus4-%E5%BC%80%E5%8F%91%E5%AE%9E%E6%88%98&quot;&gt;9. C# 的 Modbus 核心框架：NModbus4 开发实战&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;#10-%E5%AE%8F%E8%A7%82%E9%80%9A%E8%AE%AF%E6%9E%B6%E6%9E%84isoosi-%E4%B8%83%E5%B1%82%E7%BD%91%E7%BB%9C%E6%A8%A1%E5%9E%8B%E8%A7%A3%E6%9E%90&quot;&gt;10. 宏观通讯架构：ISO/OSI 七层网络模型解析&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;#11-%E8%BF%9B%E9%98%B6%E5%B9%BF%E5%9F%9F%E7%BD%91%E5%B1%82tcp-socket-%E5%BC%82%E6%AD%A5%E9%80%9A%E4%BF%A1%E6%A0%B8%E5%BF%83%E9%AA%A8%E6%9E%B6&quot;&gt;11. 进阶广域网层：TCP Socket 异步通信核心骨架&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;#12-%E5%B9%BF%E5%9F%9F%E7%BD%91%E5%8F%A6%E4%B8%80%E6%9E%81udp-%E9%AB%98%E9%80%9F%E6%97%A0%E8%BF%9E%E6%8E%A5%E9%80%9A%E4%BF%A1%E4%B8%8E-tcp-%E5%AF%B9%E6%AF%94&quot;&gt;12. 广域网另一极：UDP 高速无连接通信与 TCP 对比&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;#13-socket-%E9%AB%98%E5%B9%B6%E5%8F%91%E9%80%9A%E4%BF%A1%E5%AE%9E%E6%88%98%E9%98%B2%E8%8C%83%E4%BA%94%E4%B8%AA%E5%85%B8%E5%9E%8B%E9%99%B7%E9%98%B1&quot;&gt;13. Socket 高并发通信实战：防范五个典型陷阱&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;#14-%E7%8E%B0%E4%BB%A3-c-%E9%80%9A%E4%BF%A1%E6%80%A7%E8%83%BD%E6%A0%B8%E5%BF%83%E9%9B%B6%E5%88%86%E9%85%8D-zero-allocation-%E5%86%85%E5%AD%98%E6%BC%94%E8%BF%9B&quot;&gt;14. 现代 C# 通信性能核心：零分配 (Zero-Allocation) 内存演进&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;#15-%E5%B7%A5%E4%B8%9A%E7%BA%A7%E7%BD%91%E7%BB%9C%E4%B8%AD%E9%97%B4%E4%BB%B6%E4%BA%94%E7%BB%B4%E6%9E%B6%E6%9E%84%E4%BD%93%E7%B3%BB%E4%BB%A3%E7%A0%81%E5%AE%9E%E6%88%98&quot;&gt;15. 工业级网络中间件：五维架构体系代码实战&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;#16-c-%E7%BD%91%E7%BB%9C%E9%80%9A%E4%BF%A1%E6%B7%B1%E5%85%A5%E7%9B%B4%E8%BF%9E%E8%AE%BE%E5%A4%87%E4%B8%8E%E7%BD%91%E5%85%B3%E8%BD%AC%E5%8F%91%E5%AE%9E%E6%88%98&quot;&gt;16. C# 网络通信深入：直连设备与网关转发实战&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;#17-%E9%99%84%E5%BD%95%E5%8F%82%E8%80%83-nmodbus4-%E8%B7%A8%E6%A0%88%E9%A9%B1%E5%8A%A8-rtu-tcp-%E4%B8%8E-udp-%E5%8F%82%E6%95%B0%E5%85%A8%E8%A7%A3&quot;&gt;17. 附录参考 NModbus4 跨栈驱动 RTU TCP 与 UDP 参数全解&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;#18-%E6%A0%B8%E5%BF%83%E5%8E%9F%E7%90%86%E6%A2%B3%E7%90%86-%E4%B8%89%E5%A4%A7%E4%BB%8B%E8%B4%A8-api-%E6%B5%81%E7%A8%8B%E4%B8%8E%E9%9D%9E%E6%A0%87%E8%AE%BE%E5%A4%87%E6%8A%A5%E6%96%87%E8%A7%A3%E6%9E%90&quot;&gt;18. 核心原理梳理 三大介质 API 流程与非标设备报文解析&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;#19-%E9%99%84%E5%BD%95-%E5%8E%9F%E7%94%9F-socket-%E4%B8%8E%E9%AB%98%E7%BA%A7%E5%B0%81%E8%A3%85%E7%B1%BB%E6%B7%B1%E5%BA%A6%E5%AF%B9%E6%AF%94%E6%8B%86%E8%A7%A3&quot;&gt;19. 附录 原生 Socket 与高级封装类深度对比拆解&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;#20-%E5%B7%A5%E4%B8%9A%E7%89%A9%E8%81%94%E7%BD%91%E6%B6%88%E6%81%AF%E4%B8%AD%E6%9E%A2mqtt-%E5%8D%8F%E8%AE%AE%E6%A0%B8%E5%BF%83%E5%AE%9E%E6%88%98&quot;&gt;20. 工业物联网消息中枢：MQTT 协议核心实战&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;hr /&gt;
&lt;h2&gt;1. 串口通信的基本概念&lt;/h2&gt;
&lt;p&gt;串口通信是指外设和计算机通过数据信号线、地线等，**按位（bit）**顺序进行数据传输的一种通信方式。&lt;/p&gt;
&lt;p&gt;在建立通信之前，上位机（电脑端软件）和下位机（硬件端）必须要像接头特务一样，&lt;strong&gt;对好暗号（通信参数）&lt;/strong&gt;，否则收到的一定是一堆乱码。这些必备的参数也被称为**“串口四要素”**：&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;strong&gt;波特率 (Baud Rate)&lt;/strong&gt;：表示数据传输的速率，即每秒传输的二进制位数（bps）。常见的有 &lt;code&gt;9600&lt;/code&gt;、&lt;code&gt;19200&lt;/code&gt;、&lt;code&gt;38400&lt;/code&gt;、&lt;code&gt;115200&lt;/code&gt;。两端波特率不匹配，将绝对无法正常解析电平信号。&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;数据位 (Data Bits)&lt;/strong&gt;：衡量通信中实际数据有效位的参数，标准的往往是 &lt;code&gt;8&lt;/code&gt; 位（刚好等于 1 个 Byte 的标准大小）。&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;停止位 (Stop Bits)&lt;/strong&gt;：用于表示单个数据包发送完毕的标志。常用的有 &lt;code&gt;1&lt;/code&gt; 位、&lt;code&gt;1.5&lt;/code&gt; 位、&lt;code&gt;2&lt;/code&gt; 位。一般默认为 &lt;code&gt;1&lt;/code&gt;。&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;校验位 (Parity)&lt;/strong&gt;：用来在由于电磁干扰等原因产生轻微错误时，进行简单的奇或偶错误检测。通常有：无校验(&lt;code&gt;None&lt;/code&gt;)、奇校验(&lt;code&gt;Odd&lt;/code&gt;)、偶校验(&lt;code&gt;Even&lt;/code&gt;)。工程中最常见是不使用硬件校验（&lt;code&gt;None&lt;/code&gt;），而把校验压力放在后续的软件数据帧算法（如 CRC）中。&lt;/li&gt;
&lt;/ul&gt;
&lt;hr /&gt;
&lt;h2&gt;2. 概念解析：高位(MSB)与低位(LSB)&lt;/h2&gt;
&lt;p&gt;在看硬件通信手册时，最能劝退新人的术语就是**“高位优先”、“低位在前”、“大端模式”、“小端模式”**。&lt;/p&gt;
&lt;p&gt;这涉及的是当&lt;strong&gt;一个数据超过了 1 个字节（8 bits）时，它该如何被拆分发送的问题&lt;/strong&gt;。
例如我们平时计算用的整数 &lt;code&gt;short&lt;/code&gt; (16位，占 2 个字节) 或者 &lt;code&gt;int&lt;/code&gt; (32位，占 4 个字节)。&lt;/p&gt;
&lt;p&gt;假设我们需要将一个 16 位的十六进制数值 &lt;code&gt;0x1234&lt;/code&gt; 通过串口发送出去。&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;毫无疑问，由于串口数据位是基于单字节（8 bit / 1 byte）的，这个数值在物理线缆上只能被切成两截逐一发送：分别是 &lt;code&gt;0x12&lt;/code&gt; 和 &lt;code&gt;0x34&lt;/code&gt;。&lt;/li&gt;
&lt;li&gt;其中 &lt;code&gt;0x12&lt;/code&gt; 是数值的&lt;strong&gt;高位字节 (MSB, Most Significant Byte)&lt;/strong&gt;。（它在百位/千位的位置，代表的数量级大，所以叫“最高有效位/高位”）。&lt;/li&gt;
&lt;li&gt;其中 &lt;code&gt;0x34&lt;/code&gt; 是数值的&lt;strong&gt;低位字节 (LSB, Least Significant Byte)&lt;/strong&gt;。（它在个位/十位的位置）。&lt;/li&gt;
&lt;/ul&gt;
&lt;h3&gt;大小端模式 (Endianness) - 决定了物理导线到底先发谁！&lt;/h3&gt;
&lt;p&gt;只要数据超过了 1 个字节（例如 &lt;code&gt;int&lt;/code&gt; 往往是 4 个字节，长达 32 bit），它在物理传输或内存里存储时，就一定会涉及先后顺序的扯皮问题。由于历史上各大芯片巨头架构方向的分裂，世界上诞生了两大主城阵营：&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;
&lt;p&gt;&lt;strong&gt;大端序 (Big-Endian) / 高端字节在前&lt;/strong&gt;：&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;strong&gt;直白解释&lt;/strong&gt;：这也叫“人类直觉默认模式”或是“网络传输经典标准序”。在内存中，把数据的&lt;strong&gt;高位字节&lt;/strong&gt;存储在相对较低的、先被读到的内存地址首位上（大的部位先出头）。&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;发送顺序&lt;/strong&gt;：假设有一串极其庞大的金额数值 &lt;code&gt;0x12345678&lt;/code&gt;。在大端模式下的发送序列一定是 &lt;code&gt;[0x12, 0x34, 0x56, 0x78]&lt;/code&gt;。你看，最左边那个数量级最大的 &lt;code&gt;0x12&lt;/code&gt; 先冲出去了，完全符合我们“读数字从左到右”的习惯。&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;常见运用&lt;/strong&gt;：除了绝大部分协议的 TCP/IP 网络包强制使用这种顺序发送外，很多传统的 PLC 或者老式单片机、工控仪器手册上写的“&lt;strong&gt;高位字节先发 (MSB First)&lt;/strong&gt;”指的就是必须遵从它。&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;&lt;strong&gt;小端序 (Little-Endian) / 低端字节在前&lt;/strong&gt;：&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;strong&gt;直白解释&lt;/strong&gt;：这也叫“计算机底层极度喜爱模式”。在内存中，把数据的&lt;strong&gt;低位字节&lt;/strong&gt;存储在最先遇见的地址零点上（最末尾极其零碎的先存进去头底压下面）。&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;发送顺序&lt;/strong&gt;：假设同样是数值 &lt;code&gt;0x12345678&lt;/code&gt;。在小端模式下，从物理接口吐出的发送序列却是倒装句排列：&lt;code&gt;[0x78, 0x56, 0x34, 0x12]&lt;/code&gt;。&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;常见运用&lt;/strong&gt;：为什么会设计出这么极其反人类习惯的倒装机制？因为你家电脑里的 &lt;strong&gt;x86/x64 架构 CPU（也就是 Windows 系统的老家底座）硬件底层默认全是用小端序进行存取运算的&lt;/strong&gt;！这种排列机制极其精妙：在进行内存强制类型转换操作时（比如把 32位的 &lt;code&gt;int&lt;/code&gt; 硬塞给 16位的 &lt;code&gt;short&lt;/code&gt;），小端序的指针根本不需要重新漂移寻找起点，直接从头切断多余的一半就能光速完成转换。如果硬件手册上写明“&lt;strong&gt;低位字节先发 (LSB First)&lt;/strong&gt;”，说明接驳的这台单片机对方芯片和你的家用台式机电脑“英雄所见略同”。&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;&lt;strong&gt;C# 开发避坑指南&lt;/strong&gt;：在 C# 中，如果我们调用 &lt;code&gt;BitConverter.GetBytes(0x1234)&lt;/code&gt; 试图把这个 &lt;code&gt;short&lt;/code&gt; 转为可发送的字节数组。由于 Windows 默认是“小端模式”，它返回的字节数组元素顺序其实是 &lt;code&gt;[0x34, 0x12]&lt;/code&gt;。如果你对接的外部硬件手册上写着“请使用高位在前模式”，那么你在把数组扔进串口发送区前，&lt;strong&gt;必须要执行一次 &lt;code&gt;Array.Reverse()&lt;/code&gt; 将其翻转&lt;/strong&gt;！&lt;/p&gt;
&lt;h3&gt;2.1 实战：如何用代码优雅地剥取出高低位？&lt;/h3&gt;
&lt;p&gt;在大部分的高性能工控代码中，资深开发者往往&lt;strong&gt;不会使用 &lt;code&gt;BitConverter&lt;/code&gt;&lt;/strong&gt;，因为它会产生额外的数组内存分配（GC 压力），而是直接使用底层的&lt;strong&gt;位运算 (Bitwise Operations)&lt;/strong&gt; 进行拆解，这种方式不仅能达到极限性能，还能自由决定发送顺序！&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;&lt;strong&gt;为什么要用位移 (&lt;code&gt;&amp;gt;&amp;gt;&lt;/code&gt;) 和 按位与 (&lt;code&gt;&amp;amp;&lt;/code&gt;)？&lt;/strong&gt;
对于 &lt;code&gt;0x1234&lt;/code&gt; 这个 16 位整数，它在底层的二进制完整形态是 &lt;code&gt;00010010 00110100&lt;/code&gt;。
我们只需要把它“按位右移”或者跟掩码 &lt;code&gt;0xFF&lt;/code&gt; (全 1 组成的筛子) 进行逻辑比对，就能精准提纯出对应的字节切片。&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;&lt;strong&gt;关于 &lt;code&gt;&amp;amp; 0xFF&lt;/code&gt; 掩码截断的深度解析：&lt;/strong&gt;
为什么在获取低位时不直接强转 &lt;code&gt;(byte)rawData&lt;/code&gt;，偏偏要加上特殊的运算 &lt;code&gt;&amp;amp; 0xFF&lt;/code&gt; 呢？
因为在实际工程中，你面临的数据可能不止 16 位！假设你要从一个 32 位的温度传感器整数 &lt;code&gt;int largeData = 0xAABBCCDD&lt;/code&gt; 中专门捞出中间的 &lt;code&gt;0xCC&lt;/code&gt; 这一段。你把它右移 8 位后，变成了 &lt;code&gt;0x00AABBCC&lt;/code&gt;，其高位仍然残留着不需要的废旧像素 &lt;code&gt;0xAABB&lt;/code&gt;！
如果不进行位图截断，强制转换可能会产生极其危险的数据溢出或符号位污染（尤其是原始数据本身为负数时，底层计算机为了保符号位，向右平移会在最左侧疯狂补 &lt;code&gt;1&lt;/code&gt; 从而产生极大的污染）。
此时 &lt;code&gt;0xFF&lt;/code&gt; (等同于二进制的 &lt;code&gt;00000000 00000000 00000000 11111111&lt;/code&gt;) 就扮演了&lt;strong&gt;绝对隔离防火墙&lt;/strong&gt;的作用：
使用 &lt;code&gt;数据 &amp;amp; 0xFF&lt;/code&gt; 运算后，原来高位的那些所有的杂波乱码在遇到掩码的墙壁 &lt;code&gt;0&lt;/code&gt; 之后全部都会灰飞烟灭变成纯净的 &lt;code&gt;0&lt;/code&gt;，只有处于最低 8 位的数字在遇到 &lt;code&gt;1&lt;/code&gt; 之后被完美复刻原样保留了下来。这就实现了极其绝对的数据提纯操作！&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;代码实战演示：&lt;/strong&gt;&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;// 假设我们的原始业务数据是 32位整型 4660 (十六进制是 0x00001234)
int rawData = 0x1234; 

// 【获取高位 (MSB)】
// 原理：将整体向右平移 8 个坑位，把原本在前面的 0x12 挤到了最低位，同时必须配合 &amp;amp; 0xFF 杀死遗留的其他所有高位干扰
byte msb = (byte)((rawData &amp;gt;&amp;gt; 8) &amp;amp; 0xFF);  // 结果极其安全地变为: 0x12

// 【获取低位 (LSB)】
// 原理：无需平移，直接对原始数据怼上 0xFF的“漏网”，直接一刀切除前面所有的残渣，只留下最底下 8 位。
byte lsb = (byte)(rawData &amp;amp; 0xFF); // 结果被安详地切成: 0x34

// 发送时，如果是手册要求“高位在前”，你的发送缓冲区发车数组就长这样：
byte[] sendBuffer = new byte[] { msb, lsb }; // 发列车：[0x12, 0x34]

// 反过来！如果单片机硬件发给你了高低位被拆散的两个 byte，你该如何组合还原成十进制数字？
// 在工业控制源码中经常能够看到这种经典的位操作连写： short value = (short)((highByte &amp;lt;&amp;lt; 8) | lowByte);
// 这是处理底层协议时 C# 的标准开发手势，其执行效率极高。

// 假设我们截获的物理数据参数如下：
byte highByte = 0x12; // 接收到的高位数据
byte lowByte = 0x34;  // 接收到的低位数据

// 【高低位组合原理深度拆解】
// 核心原理解析 1：利用左移运算符 (&amp;lt;&amp;lt; 8) 腾出低位
// byte 的最大限制为 255。当我们需要将其视作十进制的高位层级时，必须将 `highByte` 向左平移 8 个二进制位，为后续合并低位腾出空间！
// 例如 0x12 (即单纯二进制的 00010010) 左推八位后，变为了 0x1200 (即 00010010 00000000)。
// 请注意此时右方尾部：该数值后方的 8 个房间已经变为纯粹的 0 位了。

// 核心原理解析 2：利用按位或 (|) 执行安全的合并逻辑
// 注意此处建议使用 `|` 而非加法 `+`。将左移后的 0x1200 与 `lowByte` (此时低位自动补齐为 0x0034) 进行对齐按位或计算：
// 位运算物理法则为：“有 1 则为 1，双侧全 0 才是 0”。
// 移位后的高位：0x1200  (00010010 00000000)
//           | （执行按位或合并）
// 原始的低位：0x0034  (00000000 00110100)
// ===========================================
// 合成结果输出：0x1234  (00010010 00110100) -&amp;gt; 这种位运算相对算术加法更加安全，能够有效避免在特定极值或处理负标量时出现进位截断异常的风险。

// 核心原理解析 3：为何最外层需要包含强制类型转换 `(short)` ？
// 在 C# 中存在着隐式类型提升的规则防御机制：对于低于 `int` 的基本整型（如 byte, short）执行位移(&amp;lt;&amp;lt;)或按位运算(|)交互时，编译器为了防止因跨越长度溢出产生的异常丢失，会自动在后台把它们临时提升为 32位的 `int` 进行运算。
// 所以上述括号内运算产出的结果 0x1234，从类型上看其实已经是 32 位 `int` 整型 (存储形态为 0x00001234)。
// 由于目标是要将其正确存放进只有 16位 长度空间的系统类型抽屉里，因此必须在外层显式调用 `(short)`，向编译器宣称截取有意义的最右侧 16 位片段。

short finalValue = (short)((highByte &amp;lt;&amp;lt; 8) | lowByte); // 原始仪器的真实参数彻底还原为 16 位整型：数字 4660。这种写法既安全可靠，又完美避免了利用 `BitConverter` 创建过多中间数组引发的 GC 压力。
&lt;/code&gt;&lt;/pre&gt;
&lt;hr /&gt;
&lt;h2&gt;3. 通信的语言：16进制(Hex)与ASCII&lt;/h2&gt;
&lt;p&gt;在上位机串口收发缓冲池里，流淌的永远只有纯粹的 &lt;code&gt;byte[]&lt;/code&gt; 数组（也就是一个挨着一个的 &lt;code&gt;0-255&lt;/code&gt; 的数字常量）。但在硬件手册中，对于这些数据的“翻译法则”一般分为两派：&lt;/p&gt;
&lt;h3&gt;16进制模式 (Hex / RTU / 二进制直接流)&lt;/h3&gt;
&lt;p&gt;这是工业上&lt;strong&gt;最通用、最节省带宽&lt;/strong&gt;的方式。它不经过任何隐式字符转换，它发出的 &lt;code&gt;0x31&lt;/code&gt; 就是纯粹的数字 &lt;code&gt;49&lt;/code&gt;，代表了一段物理意义（比如状态开关闭合、温度刻度等）。
例如，你想让设备调整运行频率到 255 Hz：
你发送的内容就是极其紧凑的 1 个字节：&lt;code&gt;new byte[] { 0xFF }&lt;/code&gt;（十六进制 0xFF 对应的十进制刚好就是 255）。&lt;/p&gt;
&lt;h3&gt;ASCII 文本模式&lt;/h3&gt;
&lt;p&gt;这是给&lt;strong&gt;人类看&lt;/strong&gt;的文本模式，或者通过极简终端调用的协议（如经典的 AT 指令集，常用于 4G 模组、蓝牙透传模块等）。
例如，你同样想让设备调整频率到 &lt;code&gt;255&lt;/code&gt; Hz：
你发送的不再是干瘪的底层数据，而是一段“能读出来的字”。你必须通过 ASCII 字符表将可见字符 &lt;code&gt;&apos;2&apos;&lt;/code&gt;、&lt;code&gt;&apos;5&apos;&lt;/code&gt;、&lt;code&gt;&apos;5&apos;&lt;/code&gt; 逐个从字母转换为字节！
你实际发出的数组是：&lt;code&gt;new byte[] { 0x32, 0x35, 0x35 }&lt;/code&gt;（分别对应 ASCII 表中这三个数字字符的编码）。
可以看到，原本只需 1 个字节就能表达的参数，现在占据了整整 3 个字节的通讯带宽。但好处是如果你用串口调试助手把接收模式调成“文本展示”，你可以直接在黑框框里看到易读的 &lt;code&gt;&quot;255&quot;&lt;/code&gt;，并且它天然避开了很多特殊控制字符导致的中断。&lt;/p&gt;
&lt;hr /&gt;
&lt;h2&gt;4. 对话的艺术：收发问询码与帧结构&lt;/h2&gt;
&lt;p&gt;串口本质上是一组甚至只有两根线（收RX 和 发TX）的“直连导线”。如果有成百上千个设备挂在同一根总线（如 RS-485）上，大家在这根导线上必须按规矩排队说话，否则电信号会互相撞车变成乱码。&lt;/p&gt;
&lt;p&gt;这也是为什么绝大部分工业串口通信极其依赖 &lt;strong&gt;“主从模式 (Master-Slave)” 的 问询 与 应答 机制&lt;/strong&gt;（经典的如 Modbus RTU 协议）。&lt;/p&gt;
&lt;h3&gt;什么是收发问询码 / 报文 (Message Request/Response)？&lt;/h3&gt;
&lt;p&gt;在这个网络里，你的电脑上位机是主宰（主站），下位机硬件则是苦心干活的仆从（从站）。
&lt;strong&gt;原则：从站永远是个哑巴，绝对不会主动给你发数据！&lt;/strong&gt;
除非主站给它发送了一段针对性的大喊口令——&lt;strong&gt;这段包含特定规则的指令组合就被称为“问询指令码 / 发送报文” (Request Frame)&lt;/strong&gt;。
从站收到这段组合后、进行错位校验、并发现是对自己喊话时，才会乖乖处理逻辑，并在短暂延迟后回复给你一段包含执行结果或感官参数的**“应答报文” (Response Frame)**。如果不符合校验和地址，从机将把它当做垃圾脉冲直接无视剥弃。&lt;/p&gt;
&lt;h3&gt;数据帧的“车厢”结构解析 (Frame Structure)&lt;/h3&gt;
&lt;p&gt;不管是自己造的协议，还是国际标准的行业协议，一次“问询（发）”或“应答（收）”往往被结构化打包成一列极其精密的&lt;strong&gt;火车 (数据帧)&lt;/strong&gt;。一份标准的业务数据帧往往由这几节车厢组合：&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;&lt;strong&gt;帧头 (Header)&lt;/strong&gt;：通信的对暗号。有些自定义协议喜欢用 &lt;code&gt;0xAA 0xBB&lt;/code&gt; 打头，用极其强烈的信号特征告诉机器芯片“注意！新的一句话准备开始了！”&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;地址码 (Slave Address) [常见1字节]&lt;/strong&gt;：例如一根 RS-485 线并联了 10 台电子秤，主站发号施令得带指名道姓：&lt;code&gt;0x01&lt;/code&gt; 号机器出来接旨，其他机器虽然听到了但也立马装死。&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;功能码 / 命令字 (Function Code) [常见1字节]&lt;/strong&gt;：核心动作区。告诉机器你想干嘛。例如行业标准里，经常用 &lt;code&gt;0x03&lt;/code&gt; 代表“我要读取你的寄存档案”，&lt;code&gt;0x06&lt;/code&gt; 代表“我要改变你的控制闸门并写入数据”。&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;数据区 (Data payload)&lt;/strong&gt;：
&lt;ul&gt;
&lt;li&gt;&lt;strong&gt;在主站“发”的问询指令中&lt;/strong&gt;：这节车厢装载的往往是“我要读取从编号多少开始的数据？要连续读多长？”。&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;在从机“收”的应答指令中&lt;/strong&gt;：这往往跟的是塞得满满当当的“从机真实测量到的各项业务数据（如温度/湿度/转速等核心干货数字）”。&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;校验码 (CheckSum / CRC) [常见1-2字节]&lt;/strong&gt;：放在火车尾部，这是一串严密的“封条”。用于主站或者从站反向校验：“刚才这列火车在电线上跑的时候，有没有被隔壁机器的强磁场干扰导致某一节车厢从 0 变成了 1？”。&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;帧尾 (Footer)&lt;/strong&gt;：用来告诉系统“我这句话到了尾音，彻底说完了”，有些协议靠固定的时序中断（停止发送长达 3.5 个字符的时间作为物理终结），也有很多协议强制要求在包裹最后跟上回车换行符 &lt;code&gt;\r\n&lt;/code&gt; (对应 ASCII: &lt;code&gt;0x0D 0x0A&lt;/code&gt;) 作为结束哨声。&lt;/li&gt;
&lt;/ol&gt;
&lt;h4&gt;【收发问询】实战示例浅析&lt;/h4&gt;
&lt;p&gt;假设我们基于标准的 Modbus RTU 协议逻辑，读取大棚里绑定的第 1 号传感器的温湿度值：&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;&lt;strong&gt;主站（上位机开发人员）构建并发送的【问询码】：&lt;/strong&gt;
&lt;code&gt;01 03 00 00 00 01 84 0A&lt;/code&gt; (通过 &lt;code&gt;SerialPort.Write&lt;/code&gt; 发送共 8 个字节)
&lt;em&gt;大白话翻译&lt;/em&gt;：
提取第一节 &lt;strong&gt;01&lt;/strong&gt; (地址码，呼叫一号设备) -&amp;gt; 提取 &lt;strong&gt;03&lt;/strong&gt; (功能码，进行批量读取数据操作) -&amp;gt; 提取 &lt;strong&gt;00 00&lt;/strong&gt; (数据区，代表读取的起始位置是 0 ) -&amp;gt; 提取 &lt;strong&gt;00 01&lt;/strong&gt; (表示要读 1 个地址的连续数据区) -&amp;gt; 提取最后车厢的 &lt;strong&gt;84 0A&lt;/strong&gt; (这是经过CRC16算法，利用高深的数学手段根据前面几个字节生成的低高位校验码封条)。&lt;/p&gt;
&lt;/blockquote&gt;
&lt;blockquote&gt;
&lt;p&gt;&lt;strong&gt;从站（下位机设备）回复到上位机缓冲区的【应答码】：&lt;/strong&gt;
&lt;code&gt;01 03 02 12 34 B5 33&lt;/code&gt; (触发 &lt;code&gt;DataReceived&lt;/code&gt; 收到共 7 个字节的响应)
&lt;em&gt;大白话翻译&lt;/em&gt;：
提取 &lt;strong&gt;01&lt;/strong&gt; (没错，这是对应的一号机器给我的回音) -&amp;gt; 提取 &lt;strong&gt;03&lt;/strong&gt; (回应刚才的读请求) -&amp;gt; 提取 &lt;strong&gt;02&lt;/strong&gt; (它告诉我后面跟着的数据包长度共有整整 2 个字节) -&amp;gt; 提取 &lt;strong&gt;12 34&lt;/strong&gt; (这 2 个字节就是真正测量到的核心数据！这就是你想要的值，你需要把它拼起来算出最终十进制业务表现态！) -&amp;gt; 提取车厢尾部 &lt;strong&gt;B5 33&lt;/strong&gt; (由这台单片机机器发出的二次 CRC16 校验码，你需要把它和你自己算出来的值比对，不相等说明电线上有干扰，这串数据全脏了不能用！)。&lt;/p&gt;
&lt;/blockquote&gt;
&lt;hr /&gt;
&lt;h2&gt;5. 常见的数据校验算法&lt;/h2&gt;
&lt;p&gt;串口及其容易遭受不可抗力杂波干扰导致丢包或数据某些 bit 翻转错乱。如果你截断了车尾的校验码却不去验算，上位机很容易拿到因为电位漂移造成的极其离谱的数字（比如室温突然变成了 8900 度）。&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;&lt;strong&gt;累加和校验 (CheckSum)&lt;/strong&gt;
&lt;ul&gt;
&lt;li&gt;非常直白而原始。直接把前面车厢里所有的字节数值全部进行加法累算，如果产生的总和溢出了一个字节大小（超过 0xFF 即 255），为了塞得下，直接暴力丢弃最高部分只保留低八位（也可以对结果按位取反等简单变形操作）。它的计算开销基本为零。&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;异或校验 (XOR / BCC)&lt;/strong&gt;
&lt;ul&gt;
&lt;li&gt;将整串所有包含内容的字节逐一进行二进制上的“按位异或运算”（C# 中对应 &lt;code&gt;^&lt;/code&gt; 操作符）。多用在低性能的简单电子秤或者极简家用传感器协议中。&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;循环冗余校验 (CRC, Cyclic Redundancy Check) [最重要]&lt;/strong&gt;
&lt;ul&gt;
&lt;li&gt;工业自动化界中最普遍采用且严谨的校验方式。它不是简单相加，而是将所有的字节拼成一个巨大的长多项式组合，随后与一个工业规定的&lt;strong&gt;多项式基准除数&lt;/strong&gt;进行不断地移位与“模二除法”。求出最终那个无法被整除的“余数”，通常占 2 个字节。&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;/ol&gt;
&lt;h3&gt;5.1 实战：CRC16 校验码生成与追加机制&lt;/h3&gt;
&lt;p&gt;在实际开发中，如果使用纯逻辑计算 CRC 会因为每 1 bit 都要进行无尽迭代导致极度耗时，因此 C# 底层开发标准姿势是使用著名的**“查表法 (Lookup Table)”**进行 O(1) 级别的空间换时间秒级运算。&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;&lt;strong&gt;CRC 追加法则注意事项：&lt;/strong&gt;
算出来的 16 位 CRC 码同样存在物理结构上的高低位划分！在最典型的 Modbus-RTU 行业协议中，有一项强硬的发送规定：被追加在数据尾部的 CRC 校验码必须**“低位必须在前，高位必须在后 (LSB First)”**！
例如算出的十六进制 CRC 结果本身是 &lt;code&gt;0x4A6B&lt;/code&gt;。按照常规思路可能会发送 &lt;code&gt;[0x4A, 0x6B]&lt;/code&gt;；但是作为底部的校验码追加时，&lt;strong&gt;必须调换顺序组合成 &lt;code&gt;[0x6B, 0x4A]&lt;/code&gt; 进行发送&lt;/strong&gt;。这是工业通信早期硬件架构遗留的设计规则。许多初级实践者极容易在此处排查错乱，因为算法本身虽计算正确，但未进行端序颠倒导致被下位机通信协议栈否决。&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;&lt;strong&gt;深入原理解析：何为“查表法 (Lookup Table)”？&lt;/strong&gt;
原生的 CRC 计算中，需要手动去模拟数学多项式长除法运算（按位进行异或及平移比对）。如果在微处理器或者要求低时延并发响应的 CPU 中对大量轮询帧逐个硬件运算，将会耗费严重的运算时长。
为了提高运算效率，资深开发群体预先计算出多项式对于 &lt;code&gt;0~255&lt;/code&gt; 所有输入值的异或校验结果大全，并将其固化记录在底层运行时的静态常数数组字典（两张一维静态数组列表 &lt;code&gt;aucCRCHi&lt;/code&gt;和 &lt;code&gt;aucCRCLo&lt;/code&gt;）中。
现在，当执行庞大的串口组装校验时，代码里连一次运算向位移长除都不会触发。我们只需利用当前的数据字节作为 &lt;code&gt;索引 (index)&lt;/code&gt; 去匹配对应静态结果表里查找到事先保存的异或映射常数，就能迅速修正覆盖当前运算缓冲位置。这就是通过查表将计算时间复杂度优化至 &lt;code&gt;O(1)&lt;/code&gt; 的开发手法实例。&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;C# 极速查表法生成 CRC16 与打好封套的反装实战代码：&lt;/strong&gt;&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;public static class CrcTool
{
    // 固化的 Modbus CRC16 标准静态查表映射区域 (节约算法运算耗时开销)：
    private static readonly byte[] aucCRCHi = {
        0x00, 0xC1, 0x81, 0x40, 0x01, 0xC0, 0x80, 0x41, //... (剩余248个字典映射常量，实际项目请直接引入完整的数组表)
    };
    private static readonly byte[] aucCRCLo = {
        0x00, 0xC0, 0xC1, 0x01, 0xC3, 0x03, 0x02, 0xC2, //...
    };

    /// &amp;lt;summary&amp;gt;
    /// 核心方法：为数据帧生成并追加双字节通信校验码
    /// &amp;lt;/summary&amp;gt;
    public static byte[] AppendCrc16(byte[] frameWithoutCrc)
    {
        // 步骤1：初始化寄存器缓冲值（Modbus 约定为 0xFFFF）
        byte crcHi = 0xFF; 
        byte crcLo = 0xFF; 
        
        // 步骤2：执行查表演变！遍历报文中每一位装载了配置的 byte 包裹数据：
        foreach (byte currentByte in frameWithoutCrc)
        {
            // 通过当前字节 与 原已保存的状态低位进行异或排量交叉，推导向下一步要查找的表偏移索引位置
            int index = crcLo ^ currentByte;
            
            // 使用拿到的页面序号翻查提取字典中算好的静态长串位阶影响，瞬间刷新替代现在两部分的高低位数值结果。
            crcLo = (byte)(crcHi ^ aucCRCHi[index]);
            crcHi = aucCRCLo[index];
        }

        // 步骤3：最终查表结算完毕，按需在尾部扩展 2 个字节的空间容量以容纳被追加的校验签名：
        byte[] finalFrame = new byte[frameWithoutCrc.Length + 2];
        Array.Copy(frameWithoutCrc, finalFrame, frameWithoutCrc.Length);

        // 步骤4【务必注意的重点】：追加校验封锁码。此处遵守工业 LSB First 字节序处理。
        // [先发低位，再发高位] 反逻辑组装：
        finalFrame[finalFrame.Length - 2] = crcLo; 
        finalFrame[finalFrame.Length - 1] = crcHi; 

        return finalFrame; 
    }
}
&lt;/code&gt;&lt;/pre&gt;
&lt;hr /&gt;
&lt;h2&gt;6. C# WinForm 实战：SerialPort 核心 API 与属性&lt;/h2&gt;
&lt;p&gt;当你深入掌握了上述通讯世界的硬核理论后，切入 C# 平台便如同降维打击。在 .NET (尤其是传统的 WinForm) 中，我们不再需要调用繁杂的底层 Windows 驱动 API 句柄，系统为我们提供了一个极其成熟的类：&lt;code&gt;System.IO.Ports.SerialPort&lt;/code&gt;。&lt;/p&gt;
&lt;h3&gt;6.1 核心配置属性解析&lt;/h3&gt;
&lt;p&gt;当你从左侧工具栏把 &lt;code&gt;SerialPort&lt;/code&gt; 控件拖入窗体，或者代码里 &lt;code&gt;new SerialPort()&lt;/code&gt; 之后，你需要“对准通讯暗号”：&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;&lt;strong&gt;&lt;code&gt;PortName&lt;/code&gt;&lt;/strong&gt;: 端口号名称。如 &lt;code&gt;&quot;COM1&quot;&lt;/code&gt; 或 &lt;code&gt;&quot;COM3&quot;&lt;/code&gt;。你可以用 &lt;code&gt;SerialPort.GetPortNames()&lt;/code&gt; 这个静态方法动态绑到 ComboBox 里让用户自己选当前插着什么设备。&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;&lt;code&gt;BaudRate&lt;/code&gt;&lt;/strong&gt;: 波特率（常见 9600）。&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;&lt;code&gt;DataBits&lt;/code&gt;&lt;/strong&gt;: 数据位（通常 8 位）。&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;&lt;code&gt;StopBits&lt;/code&gt;&lt;/strong&gt;: 停止位（如 &lt;code&gt;StopBits.One&lt;/code&gt;）。&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;&lt;code&gt;Parity&lt;/code&gt;&lt;/strong&gt;: 校验位（如无奇偶校验 &lt;code&gt;Parity.None&lt;/code&gt;）。&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;&lt;code&gt;ReadTimeout&lt;/code&gt; / &lt;code&gt;WriteTimeout&lt;/code&gt;&lt;/strong&gt;: 读写超时等待时长（毫秒）。当信号掉线导致半包卡死在缓冲区流时，它会通过抛出异常切断僵死任务。&lt;/li&gt;
&lt;/ol&gt;
&lt;pre&gt;&lt;code&gt;using System.IO.Ports;
// ... (在初始化阶段)

SerialPort mySerialPort = new SerialPort();
mySerialPort.PortName = &quot;COM3&quot;;
mySerialPort.BaudRate = 9600;
mySerialPort.DataBits = 8;
mySerialPort.StopBits = StopBits.One;
mySerialPort.Parity = Parity.None;

// 必须执行抛出 Open！这行代码才是真正霸占电脑上的物理 COM 口。如果口被别的软件占了，这里就会抛错！
try
{
    if (!mySerialPort.IsOpen)
    {
        mySerialPort.Open();
    }
}
catch (Exception ex)
{
    MessageBox.Show($&quot;该串口可能被占用或不存在：{ex.Message}&quot;);
}
&lt;/code&gt;&lt;/pre&gt;
&lt;hr /&gt;
&lt;h2&gt;7. 实战收发：Hex 与 ASCII 的转换显示机制&lt;/h2&gt;
&lt;p&gt;在 WinForm 中，我们发送数据主要靠主动 &lt;code&gt;Write&lt;/code&gt;，而接收数据的最佳实践通常是挂载被动的底层事件钩子：&lt;code&gt;DataReceived&lt;/code&gt;。&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;&lt;strong&gt;⚠️ 跨线程 UI 更新的常见问题！&lt;/strong&gt;
&lt;code&gt;DataReceived&lt;/code&gt; 方法事件存在于系统的&lt;strong&gt;后台线程池&lt;/strong&gt;中，而非 WinForm 主 UI 线程。
如果直接在该方法内编写类似 &lt;code&gt;textBox1.Text = xxxx;&lt;/code&gt; 的代码，程序将引发跨线程操作异常（Cross-Thread Exception）。&lt;strong&gt;必须使用 &lt;code&gt;this.Invoke()&lt;/code&gt;&lt;/strong&gt; 委派回主线程进行 UI 更新。&lt;/p&gt;
&lt;/blockquote&gt;
&lt;h3&gt;7.1 完整收发器代码：一键全兼容 Hex 与 文本呈现&lt;/h3&gt;
&lt;p&gt;以下是一段适用于 WinForm 窗体的收发核心结构集，演示了如何通过复选框动态切换以十六进制（Hex，最适合纯下位机二进制打交道）还是 ASCII 字符串格式收发：&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;using System;
using System.Text;
using System.IO.Ports;
using System.Windows.Forms;

public partial class SerialToolForm : Form
{
    private SerialPort _serialPort;

    public SerialToolForm()
    {
        InitializeComponent();
        
        _serialPort = new SerialPort(&quot;COM1&quot;, 9600, Parity.None, 8, StopBits.One);
        
        // 关键挂载！告诉系统一旦导线上有数据涌入，立刻喊醒后面的 SerialPort_DataReceived 方法干活！
        _serialPort.DataReceived += new SerialDataReceivedEventHandler(SerialPort_DataReceived);
        _serialPort.Open();
    }

    // ================== 【主动发送指令 (写串口)】 ==================
    private void btnSend_Click(object sender, EventArgs e)
    {
        if (!_serialPort.IsOpen) return;
        
        string rawText = txtSendInput.Text.Trim(); // 取出用户写入在界面的文字

        // 场景 A: 作为单纯的英文字符串字母发送出去
        if (radioButtonSendASCII.Checked)
        {
            // 将字母按照 ASCII 转为底层的物理数字字节！例如 &quot;A&quot; 会变成 65
            byte[] bytesToSend = Encoding.ASCII.GetBytes(rawText);
            _serialPort.Write(bytesToSend, 0, bytesToSend.Length);
            
            // （注：SerialPort 也有极其简单的封装 _serialPort.Write(rawText)）
        }
        // 场景 B: 作为高阶工程上的 16进制(Hex) 发送出去！
        else if (radioButtonSendHex.Checked)
        {
            // 如果用户在界面输入了 &quot;01 03 00 01&quot;，去掉空格后是 &quot;01030001&quot;
            string hexStr = rawText.Replace(&quot; &quot;, &quot;&quot;); 
            // 调用工具方法，真正地切片并转为四个 byte: [0x01, 0x03, 0x00, 0x01]
            byte[] hexBytes = HexStringToByteArray(hexStr);
            _serialPort.Write(hexBytes, 0, hexBytes.Length);
        }
    }

    // ================== 【被动接收捕获 (读串口)】 ==================
    private void SerialPort_DataReceived(object sender, SerialDataReceivedEventArgs e)
    {
        // 1. 探测现在池子里攒了多少个字节了？（解决拼包的基础）
        int bytesToReadCount = _serialPort.BytesToRead;
        byte[] receiveBuffer = new byte[bytesToReadCount];
        
        // 2. 将它们抽干净装到我们自己申请的水桶 receiveBuffer 里
        _serialPort.Read(receiveBuffer, 0, bytesToReadCount);

        string displayResult = &quot;&quot;;

        // 3. 翻译截获到的底气水桶流（转为人类观看模式）
        if (radioButtonDisplayASCII.Checked)
        {
            // 根据 ASCII 表强行把 65 变回字母 &quot;A&quot;
            displayResult = Encoding.ASCII.GetString(receiveBuffer);
        }
        else if (radioButtonDisplayHex.Checked)
        {
            // 把原生的例如数字 255 展示成文本 &quot;FF&quot;，并且每两个字母空一格
            displayResult = BitConverter.ToString(receiveBuffer).Replace(&quot;-&quot;, &quot; &quot;);
        }

        // 4. 重中之重：返回 UI 主干道，更新界面的大文本框，避免死机！
        this.Invoke(new Action(() =&amp;gt;
        {
            // 拼接打印收到的车厢包裹，并且换行
            txtOutput.AppendText($&quot;[接收到]: {displayResult} \r\n&quot;);
        }));
    }

    // --- 附送必备军火库工具：[十六进制文字文本]转[真实的物理Byte数组] ---
    private byte[] HexStringToByteArray(string s)
    {
        s = s.Replace(&quot; &quot;, &quot;&quot;); 
        byte[] buffer = new byte[s.Length / 2];
        for (int i = 0; i &amp;lt; s.Length; i += 2)
        {
            // 每次挖切两个字符 (例如 &quot;0&quot;, &quot;1&quot;)，作为 16进制 转回对应的数字 1。
            buffer[i / 2] = Convert.ToByte(s.Substring(i, 2), 16);
        }
        return buffer;
    }

    // Form 销毁时，切记将这个霸占系统底层资源的对象杀掉
    protected override void OnFormClosing(FormClosingEventArgs e)
    {
        if (_serialPort != null &amp;amp;&amp;amp; _serialPort.IsOpen)
        {
            _serialPort.Close();
            _serialPort.Dispose();
        }
        base.OnFormClosing(e);
    }
}
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;无论是将收发格式设定为 &lt;code&gt;ASCII&lt;/code&gt; 还是 &lt;code&gt;Hex&lt;/code&gt;，你操作串口的&lt;strong&gt;底层物理载体永远都是 &lt;code&gt;byte[]&lt;/code&gt; 数组。&lt;/strong&gt; 所谓十六进制或者文本，仅仅只是你在 UI 呈现上选择的“翻译眼镜”。当你理清了“底层字节流”与“顶层译本”的关系，再搭配上述解决跨线程的委托注入（&lt;code&gt;Invoke&lt;/code&gt;），你在单独编写原生收发引擎时便扫清最大的障碍了！&lt;/p&gt;
&lt;hr /&gt;
&lt;h2&gt;8. 工业级巅峰：Modbus 协议核心理论&lt;/h2&gt;
&lt;p&gt;如果你在之前徒手用 &lt;code&gt;byte[]&lt;/code&gt; 拼接过报文，你一定会觉得痛苦：不同厂家的数据包格式千奇百怪，我还要自己去写查表法算 CRC 校验？
为了统一天下的规矩，&lt;strong&gt;Modbus 协议&lt;/strong&gt;诞生了，它是这颗星球上工业自动化领域&lt;strong&gt;普及率绝对第一&lt;/strong&gt;的免费公共通信标准。只要学会了它，你可以直接对接全世界 80% 的工业设备。&lt;/p&gt;
&lt;h3&gt;8.1 Modbus 的三大分支&lt;/h3&gt;
&lt;ol&gt;
&lt;li&gt;&lt;strong&gt;Modbus RTU&lt;/strong&gt;：基于串口（RS-232/485）。最硬核、最老牌、&lt;strong&gt;最省波特率带宽&lt;/strong&gt;。它的数据全是纯正不可读的十六进制 &lt;code&gt;0x&lt;/code&gt;，并且强制要求车尾必须挂上 2 个字节的高精度 CRC 校验密码锁。本文重心即是它！&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Modbus ASCII&lt;/strong&gt;：同是基于串口，但是内部字节全转成了像 AT 指令那样人类可读的 ASCII 字符，头部通常带冒号 &lt;code&gt;:&lt;/code&gt;，校验法降级为极其简单的 LRC。它的最大特点是&lt;strong&gt;不用翻译代码，用眼睛看字就能极其容易调试&lt;/strong&gt;，缺点是太浪费通信带宽。&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Modbus TCP&lt;/strong&gt;：直接基于现代网线/以太网（IP地址绑死专属端口 &lt;code&gt;502&lt;/code&gt;）。它直接彻底抛弃了麻烦的尾部 CRC 校验（因为以太网里的 TCP/IP 握手机制本身就自带了绝对安全的底层拆组网络校验），在报文头部换成了专用的 &lt;code&gt;MBAP&lt;/code&gt; 六字节网络识别报头。速度极其恐怖且容纳量巨大。&lt;/li&gt;
&lt;/ol&gt;
&lt;h3&gt;8.2 程序员视角的数据模型：线圈与寄存器&lt;/h3&gt;
&lt;p&gt;千万别被复杂的硬件继电器电路名词唬住，在咱们上位机软件程序员眼里，操控 Modbus 无非是对下位机直接暴露出来的【核心四张数据库表】进行读写：&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;strong&gt;线圈 (Coils) - [读 / 写]&lt;/strong&gt;：可以直接把它当成 C# 里的** &lt;code&gt;bool&lt;/code&gt; 开关变量**。它能切断或者打开某条指令（如机器的开灯/停转），你写入 &lt;code&gt;1&lt;/code&gt; (true) 代表合闸开启，&lt;code&gt;0&lt;/code&gt; (false) 代表断断开。&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;离散输入 (Discrete Inputs) - [只读]&lt;/strong&gt;：也是一种&lt;strong&gt;只读的 &lt;code&gt;bool&lt;/code&gt; 变量&lt;/strong&gt;。它是下位机机器本身对外部物理感知的死规矩状态，比如传感器探头此刻监测到“机器大门现在到底关没关好？”，上位机程序只能读取监测它，绝不能凭空改动物理门的状态。&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;输入寄存器 (Input Registers) - [只读]&lt;/strong&gt;：其实就是&lt;strong&gt;只读的 &lt;code&gt;short&lt;/code&gt; 类型 (16位数)&lt;/strong&gt;。它是设备动态反馈上来的极密物理实时测量波形，例如温度传感器当前的“室温是 28.5度”（发上来是个 285），你只能去刷新它的数值监测，无法人为逆写向它。&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;保持寄存器 (Holding Registers) - [读 / 写] [万物基石]&lt;/strong&gt;：极其重要的&lt;strong&gt;可控 &lt;code&gt;short&lt;/code&gt; 类型参数池&lt;/strong&gt;。这是这台设备暴露给上位机的&lt;strong&gt;绝密运行参数设定控制中心区&lt;/strong&gt;。比如你在上位机通过 UI 面板把烤箱的最高温度报警临界值从 80度 强行修改覆盖为 90度，用的就是写寄存器；你也能随时读取一下查看这台机器当前系统预存的阈值到底是多少配置。&lt;/li&gt;
&lt;/ul&gt;
&lt;h3&gt;8.3 主控端基础指令：功能码 (Function Codes)&lt;/h3&gt;
&lt;p&gt;上位机（主站）主要通过下发特定的【单字节功能码】来指派下位设备执行对应的通信操作，工业现场最常用的核心功能码如下所示：&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;strong&gt;&lt;code&gt;0x01&lt;/code&gt;&lt;/strong&gt;：&lt;strong&gt;读线圈&lt;/strong&gt;（获取连续物理开关的开启或关闭状态）&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;&lt;code&gt;0x02&lt;/code&gt;&lt;/strong&gt;：读离散输入&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;&lt;code&gt;0x03&lt;/code&gt;&lt;/strong&gt;：&lt;strong&gt;批量读取保持寄存器&lt;/strong&gt;（工业开发中最常用的操作，用于连续获取设备的各项配置参数与测量数值）&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;&lt;code&gt;0x04&lt;/code&gt;&lt;/strong&gt;：读输入寄存器&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;&lt;code&gt;0x05&lt;/code&gt;&lt;/strong&gt;：&lt;strong&gt;写单个线圈&lt;/strong&gt;（在上位机下达指令，控制底层继电器执行吸合或断开等物理动作）&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;&lt;code&gt;0x06&lt;/code&gt;&lt;/strong&gt;：写单个保持寄存器（修改设备单项配置参数数值）&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;&lt;code&gt;0x0F&lt;/code&gt;&lt;/strong&gt; (15码)：批量写入多个线圈状态&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;&lt;code&gt;0x10&lt;/code&gt;&lt;/strong&gt; (16码)：批量写入多个保持寄存器参数&lt;/li&gt;
&lt;/ul&gt;
&lt;hr /&gt;
&lt;h2&gt;9. C# 的 Modbus 核心框架：NModbus4 开发实战&lt;/h2&gt;
&lt;p&gt;虽然自己处理底层 &lt;code&gt;byte[]&lt;/code&gt; 和 CRC 位运算有助于理解协议原理，但在高并发的真实业务环境中，推荐使用成熟的基础类库以保证系统的稳定性。在 .NET 生态中，&lt;strong&gt;NModbus4&lt;/strong&gt; 是一款非常稳定、广泛使用的开源基础框架。&lt;/p&gt;
&lt;h3&gt;9.1 项目安装与引用加载集成&lt;/h3&gt;
&lt;p&gt;在你的 IDE（Visual Studio 或 Rider 等）中，打开 NuGet 包管理专属控制台：&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;Install-Package NModbus4
&lt;/code&gt;&lt;/pre&gt;
&lt;h3&gt;9.2 NModbus4 核心参数配置与 API 查阅字典&lt;/h3&gt;
&lt;p&gt;在动手编写具体业务代码之前，我们需要先全局俯瞰 NModbus4 提供给我们的主控制引擎方法及其核心配置体系：&lt;/p&gt;
&lt;h4&gt;【1. 主站引擎创建 API (工厂模式)】&lt;/h4&gt;
&lt;p&gt;NModbus4 通过 &lt;code&gt;ModbusSerialMaster&lt;/code&gt; 和 &lt;code&gt;ModbusIpMaster&lt;/code&gt; 静态工厂类来实现对不同底层通讯管道的包装兼容。&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;strong&gt;&lt;code&gt;ModbusSerialMaster.CreateRtu(IStreamResource streamResource)&lt;/code&gt;&lt;/strong&gt;
&lt;ul&gt;
&lt;li&gt;&lt;strong&gt;作用&lt;/strong&gt;：基于串口（如已实例化的 &lt;code&gt;SerialPort&lt;/code&gt;）创建严格遵循 Modbus-RTU 标准协议的主站端引擎实例。&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;&lt;code&gt;ModbusSerialMaster.CreateAscii(IStreamResource streamResource)&lt;/code&gt;&lt;/strong&gt;
&lt;ul&gt;
&lt;li&gt;&lt;strong&gt;作用&lt;/strong&gt;：基于串口通道资源创建 Modbus-ASCII 文本协议主站引擎。&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;&lt;code&gt;ModbusIpMaster.CreateIp(TcpClient tcpClient)&lt;/code&gt;&lt;/strong&gt;
&lt;ul&gt;
&lt;li&gt;&lt;strong&gt;作用&lt;/strong&gt;：基于 TCP 网络通信通道（已实例化的 &lt;code&gt;TcpClient&lt;/code&gt; 套接层）产生支持工业以太网协议的主站通信引擎。&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;h4&gt;【2. 传输层 (Transport) 核心防护属性】&lt;/h4&gt;
&lt;p&gt;一旦获取到主站代理对象 &lt;code&gt;master&lt;/code&gt;，为了抵御外部硬件或网线被拔的死机等异常情况，我们&lt;strong&gt;必须&lt;/strong&gt;设定其内部 &lt;code&gt;Transport&lt;/code&gt; 相关的传输阈值：&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;strong&gt;&lt;code&gt;master.Transport.ReadTimeout&lt;/code&gt;&lt;/strong&gt;：读指令无响应的超时截断时间（单位：毫秒）。如果给下位机发了功能码却迟迟得不到回应包，超时后触发异常。必须配置（如 &lt;code&gt;1000&lt;/code&gt; 或 &lt;code&gt;2000&lt;/code&gt; ms，视硬件总线负载情况拟定）。&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;&lt;code&gt;master.Transport.WriteTimeout&lt;/code&gt;&lt;/strong&gt;：写指令发出时，底层物理层堵塞无法送达的超时时间（毫秒）。&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;&lt;code&gt;master.Transport.Retries&lt;/code&gt;&lt;/strong&gt;：当出现响应残缺或底盘 CRC 校验失败时的自主重试次数（框架默认为 3 次）。&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;&lt;code&gt;master.Transport.WaitToRetryMilliseconds&lt;/code&gt;&lt;/strong&gt;：发生通信错误并企图内部发起重试指令前，必须强制静默休眠进行时钟缓冲的时间跨度（防止由于总线冲突造成的立即重试导致波形碰撞灾难进一步加剧）。&lt;/li&gt;
&lt;/ul&gt;
&lt;h4&gt;【3. 数据读写操控 API】&lt;/h4&gt;
&lt;p&gt;以下是主站调用下位机存储区的高频方法汇总（所有方法皆支持 &lt;code&gt;Async&lt;/code&gt; 异步无阻塞后缀版本，推荐在带 UI 的独立线程环境中默认使用其异步版本）：&lt;/p&gt;
&lt;h5&gt;读区指令大类：&lt;/h5&gt;
&lt;ul&gt;
&lt;li&gt;&lt;strong&gt;&lt;code&gt;ReadCoils(byte slaveAddress, ushort startAddress, ushort numberOfPoints)&lt;/code&gt;&lt;/strong&gt;
&lt;ul&gt;
&lt;li&gt;&lt;strong&gt;触发功能码&lt;/strong&gt;：&lt;code&gt;0x01&lt;/code&gt;（获取多路离散线圈通断状态）。&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;返回类型&lt;/strong&gt;：&lt;code&gt;bool[]&lt;/code&gt; 数组。&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;&lt;code&gt;ReadInputs(byte slaveAddress, ushort startAddress, ushort numberOfPoints)&lt;/code&gt;&lt;/strong&gt;
&lt;ul&gt;
&lt;li&gt;&lt;strong&gt;触发功能码&lt;/strong&gt;：&lt;code&gt;0x02&lt;/code&gt;（获取设备外部死规矩绑定的离散输入物理针脚信号源状态）。&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;返回类型&lt;/strong&gt;：&lt;code&gt;bool[]&lt;/code&gt; 数组。&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;&lt;code&gt;ReadHoldingRegisters(byte slaveAddress, ushort startAddress, ushort numberOfPoints)&lt;/code&gt;&lt;/strong&gt;
&lt;ul&gt;
&lt;li&gt;&lt;strong&gt;触发功能码&lt;/strong&gt;：&lt;code&gt;0x03&lt;/code&gt;（最核心的高频命令！用于获取存储于内部的可修改工作参数与保持寄存指标）。&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;返回类型&lt;/strong&gt;：&lt;code&gt;ushort[]&lt;/code&gt; 数组（返回 16 位无符号短整型的切片连块结合）。&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;&lt;code&gt;ReadInputRegisters(byte slaveAddress, ushort startAddress, ushort numberOfPoints)&lt;/code&gt;&lt;/strong&gt;
&lt;ul&gt;
&lt;li&gt;&lt;strong&gt;触发功能码&lt;/strong&gt;：&lt;code&gt;0x04&lt;/code&gt;（用于连续获取外部只读测量硬件传感探头的实时模拟量波形值等输入寄存器）。&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;返回类型&lt;/strong&gt;：&lt;code&gt;ushort[]&lt;/code&gt; 数组。&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;h5&gt;写区指令大类：&lt;/h5&gt;
&lt;ul&gt;
&lt;li&gt;&lt;strong&gt;&lt;code&gt;WriteSingleCoil(byte slaveAddress, ushort coilAddress, bool value)&lt;/code&gt;&lt;/strong&gt;
&lt;ul&gt;
&lt;li&gt;&lt;strong&gt;触发功能码&lt;/strong&gt;：&lt;code&gt;0x05&lt;/code&gt;（强控单体物理线圈开闭状态变更，如一键远程启停水泵电机则传入 &lt;code&gt;true&lt;/code&gt;，停止则传入 &lt;code&gt;false&lt;/code&gt;）。&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;&lt;code&gt;WriteSingleRegister(byte slaveAddress, ushort registerAddress, ushort value)&lt;/code&gt;&lt;/strong&gt;
&lt;ul&gt;
&lt;li&gt;&lt;strong&gt;触发功能码&lt;/strong&gt;：&lt;code&gt;0x06&lt;/code&gt;（强行下达数值修改要求并覆订单个寄存器的原有物理参数数据底盘）。&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;&lt;code&gt;WriteMultipleCoils(byte slaveAddress, ushort startAddress, bool[] data)&lt;/code&gt;&lt;/strong&gt;
&lt;ul&gt;
&lt;li&gt;&lt;strong&gt;触发功能码&lt;/strong&gt;：&lt;code&gt;0x0F&lt;/code&gt; (十进制 15)（通过一列车厢一次性灌入并强改沿途多个线圈硬件状态，该重载由于并行发送，极大节约了多次单一命令轮询造成的单线带宽高延迟）。&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;&lt;code&gt;WriteMultipleRegisters(byte slaveAddress, ushort startAddress, ushort[] data)&lt;/code&gt;&lt;/strong&gt;
&lt;ul&gt;
&lt;li&gt;&lt;strong&gt;触发功能码&lt;/strong&gt;：&lt;code&gt;0x10&lt;/code&gt; (十进制 16)（批量灌入数据组并替换连续多项寄存器配置参数）。&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;&lt;code&gt;ReadWriteMultipleRegisters(byte slaveAddress, ushort startReadAddress, ushort numberOfPointsToRead, ushort startWriteAddress, ushort[] writeData)&lt;/code&gt;&lt;/strong&gt;
&lt;ul&gt;
&lt;li&gt;&lt;strong&gt;触发功能码&lt;/strong&gt;：&lt;code&gt;0x17&lt;/code&gt; (十进制 23)（&lt;strong&gt;高阶复合操作&lt;/strong&gt;：在一条总计物理报文中同时实现写特定内存组参数并同步索求读取另一块独立内存组的值效果，专用于要求极限苛刻的光速轮询伺服机控制通信环路）。&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;h3&gt;9.3 最终整合：基于 API 查阅字典打造 WinForm 高可靠通信类&lt;/h3&gt;
&lt;p&gt;有了前面系统的类库与理论配置储备支撑，现在不妨正式基于 WinForm 场景的串口通道，写下一个极其完备、兼具通信抗噪及生命周期管理特性的稳定 &lt;code&gt;Modbus RTU&lt;/code&gt; 连击收发引擎段代码块演示：&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;using Modbus.Device;
using System.IO.Ports;
using System;
using System.Threading.Tasks;

// ... 注意，阻塞型的调用方法必须脱离 UI 主线程（如搭配 Task.Run 裹入任务包装），以防阻塞导致 Windows “由于未响应导致闪退”假象 ...

// 1. 启动并配置宣告底层基础串行通信设施口信息：
SerialPort port = new SerialPort(&quot;COM1&quot;, 9600, Parity.None, 8, StopBits.One);
try
{
    port.Open(); // 强制侵占锁定串行物理硬件资源握手对象
    
    // 2. 利用 NModbus4 工厂模式静态构筑桥接通信引擎：
    IModbusSerialMaster master = ModbusSerialMaster.CreateRtu(port);
    
    // 3. 通信稳定防护界限伞构建：设置物理层通信容忍缓冲限度：
    master.Transport.ReadTimeout = 1000;  // 必须加，防死卡顿！
    master.Transport.WriteTimeout = 1000; // 同上必设。
    master.Transport.Retries = 3;         // 通信容错重压请求上限 

    byte slaveId = 1; // 明确当前总线操作对象索引从地址。

    // === 下达物理功能操作指控信函 ===

    // 【情景 A】监控巡检器：拉取第 0 至 4 号（总共跨度为 5 个阵列长度）的线圈开合当前状况
    bool[] coilsInfo = master.ReadCoils(slaveId, 0, 5); 
    Console.WriteLine($&quot;0号节点控制门的状态截取信号为反馈值即刻为：{coilsInfo[0]}&quot;);

    // 【情景 B】获取测量仪数值：抽取位置索引记录为 100 基础起始基准上的单个保持寄存器的内部承装记录值数据
    ushort[] registers = master.ReadHoldingRegisters(slaveId, 100, 1);
    Console.WriteLine($&quot;截获在序号 100 的索引寄存器内部所含定额数值为：{registers[0]}&quot;);

    // 【情景 C】遥感操控硬件阀门：暴力要求将内部物理结构排行列里的第 2 号物理线圈控制枢纽当即接通（置高动作触发合闸运转）
    master.WriteSingleCoil(slaveId, 2, true); 

    // 【情景 D】运行时状态校准下达：强制把位于参数空间 50 号的配置预置参数直接改写成指令常数 8848 取代原封存在内的固化芯片老旧定额
    master.WriteSingleRegister(slaveId, 50, 8848); 
    
}
catch (TimeoutException tEx)
{
    // 如果 ReadTimeout 生效捕获了未曾响应的断点危机，其往往可以指示对方节点处于拔电，物理线体中断或者是单纯拨错了发送从设备ID导致的下位机协议未理会！
    Console.WriteLine($&quot;传输超时触发安全下挂切断（请优先系统排查排查连接性质量是否过关）：{tEx.Message}&quot;);
}
catch (Exception ex)
{
    // 这个位置往往抛出的是严重的硬件物理访问性灾难事件
    Console.WriteLine($&quot;底层总线条遭遇极其严峻的崩溃错误级：{ex.Message}&quot;);
}
finally
{
    // 收尾的硬法则原则：当一个完整通讯执行交互流程落幕或因为任何突发导致错误结束释放，务必第一时间对导线的排他使用特权进行切断废除。
    if (port.IsOpen) 
    {
        port.Close();
        port.Dispose();
    }
}
&lt;/code&gt;&lt;/pre&gt;
&lt;hr /&gt;
&lt;p&gt;通过前置章节对位移运算、CRC-16 查表验证法则以及数据帧基本拆装结构的剖析，可见底层通信具有相当的复杂性。而 &lt;code&gt;NModbus4&lt;/code&gt; 这种主流工业通信调度包库实际上将这些重担静默进行了封装与抽象：无论是报文的拆解拼装计算、端序对调补充运算、还是底层的防挂死重试机制验证都会在 API 内直接处理收拢。
因此，当你透彻掌握了这些通信底层基本原理后，再进行上层的中间件调用封装，在解决系统互斥通信问题时便能更加游刃有余；并且当未来架构面临升级，需要将传统物理串口通过 TCP/IP 转为高频连接模式的大型物联网平台时，也能轻松适应各种底层模式（如 &lt;code&gt;Modbus Ip Master&lt;/code&gt;）的协议转变实现。&lt;/p&gt;
&lt;hr /&gt;
&lt;h2&gt;10. 宏观通讯架构：ISO/OSI 七层网络模型解析&lt;/h2&gt;
&lt;p&gt;在我们从本地物理串口（RS-232/RS-485）跨越到广域网通信（Modbus TCP / TCP Socket）之前，必须先要在思维层面上建立起现代计算机网络的绝对标准——&lt;strong&gt;ISO/OSI (Open System Interconnection) 七层参考模型&lt;/strong&gt;。&lt;/p&gt;
&lt;p&gt;之所以要理解七层模型，是因为在实际工业与企业开发中，我们在排查“网络不通”时，必须拥有能够分辨“这到底是哪一层报了错”的能力。&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;&lt;strong&gt;物理层 (Physical Layer)&lt;/strong&gt;：规定了网线接口、电平信号。这就是我们前面讲到的 RS-232/485 或者网线光纤的领域。如果这一层断了，系统直接叫“网线没插”。&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;数据链路层 (Data Link Layer)&lt;/strong&gt;：决定了相邻节点之间的数据帧传输和 MAC 地址识别。这就好比我们在工业现场交换机中寻找局域网内机器的底层识别。&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;网络层 (Network Layer)&lt;/strong&gt;：&lt;strong&gt;IP 协议&lt;/strong&gt;的栖身之所！它负责跨越广域网、路由器进行寻址。在这一层，数据被称为“数据包”。你 Ping 不通目标，大概率就是这一层的路由规则或者 IP 分配出了问题。&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;传输层 (Transport Layer)&lt;/strong&gt;：&lt;strong&gt;TCP/UDP 协议&lt;/strong&gt;的核心阵地！它负责端到端建立可靠或不可靠的传输隧道。TCP 提供握手、确认和纠错（像打电话），UDP 则只管发送（像写信）。这也是我们即将展开探讨的&lt;strong&gt;Socket 编程的绝对底层锚点&lt;/strong&gt;。&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;会话层 (Session Layer)&lt;/strong&gt;：负责建立、管理和中段网络节点之间的会话。&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;表示层 (Presentation Layer)&lt;/strong&gt;：负责数据的加密、解密、压缩及格式转换（确保你发送的文本对方能够用正确的编码读懂）。&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;应用层 (Application Layer)&lt;/strong&gt;：直接面向用户的协议层。不论是我们前文讲解的 &lt;strong&gt;Modbus 协议&lt;/strong&gt;，亦或者是网页浏览的 HTTP/HTTPS、控制台的 SSH，全都是基于底层能力搭建出来的应用层协议！&lt;/li&gt;
&lt;/ol&gt;
&lt;p&gt;&lt;strong&gt;结论&lt;/strong&gt;：你在使用 &lt;code&gt;SerialPort&lt;/code&gt; 操作的是物理层；而当你开始编写 &lt;code&gt;Socket&lt;/code&gt; 编程时，你是越过了 1~3 层，直接调用了系统的第 4 层（传输层 TCP）API 能力；而我们在基于 Socket 建立我们自己的组包格式（比如 Modbus）时，实质上就是在创造第 7 层（应用层）的协议规范。&lt;/p&gt;
&lt;hr /&gt;
&lt;h2&gt;11. 进阶广域网层：TCP Socket 异步通信核心骨架&lt;/h2&gt;
&lt;p&gt;在了解了我们是在把控 TCP 层级（第 4 层）后，如果脱离了成熟的 Modbus 包，我们往往需要手写原生的 &lt;code&gt;Socket&lt;/code&gt; 服务通信来对接成千上万的自定义网络硬件（或实现局域网通讯及联机游戏架构）。
在现代企业级 C#（不论是跨平台服务端还是配套 WinForm/WPF/Avalonia 客户端开发）中，&lt;strong&gt;绝对不再推荐使用 &lt;code&gt;Receive&lt;/code&gt; 和 &lt;code&gt;Accept&lt;/code&gt; 之类会直接卡死主线程的同步 API&lt;/strong&gt;。我们将全面拥抱基于 &lt;code&gt;async/await&lt;/code&gt; 的任务驱动模型，构建极限响应能力的 TCP 核心通信体。&lt;/p&gt;
&lt;h3&gt;11.1 服务端层架构：异步侦听与并发派发&lt;/h3&gt;
&lt;p&gt;在服务端架构中，主进程的职责是轻量挂起等待新连接，一旦有客户握手成功，即刻剥离出一条独立的无阻塞后台任务跑去专职服务。&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;using System;
using System.Net;
using System.Net.Sockets;
using System.Text;
using System.Threading.Tasks;

public class PureServer
{
    public async Task StartAsync()
    {
        // 1. 初始化 Socket，绑定通信凭证（网络协议族，字节流模式，TCP协议）
        Socket listener = new Socket(AddressFamily.InterNetwork, SocketType.Stream, ProtocolType.Tcp);
        listener.Bind(new IPEndPoint(IPAddress.Any, 9000));
        listener.Listen(100); // 挂起连接队列（等待握手的最大缓冲量为 100）
        Console.WriteLine(&quot;服务端主干网已启动，持续异步监听在 9000 端口...&quot;);

        while (true)
        {
            // 2. 异步死等接客（使用 await 释放了当前线程资源池，极大地减轻 CPU 轮询负担，不会卡死主线程）
            Socket client = await listener.AcceptAsync();
            Console.WriteLine($&quot;感知到新节点接入，硬件 IP 终点: {client.RemoteEndPoint}&quot;);

            // 3. 客户连入后，立刻派发一个专属后台独立监控任务展开一对一数据交互。
            // 迎宾员主循环瞬间完成交接，重回进入下一个 AcceptAsync 开始等待下一位客户防客。
            _ = Task.Run(() =&amp;gt; HandleClientAsync(client)); 
        }
    }

    private async Task HandleClientAsync(Socket client)
    {
        var buffer = new byte[4096];
        try
        {
            while (true)
            {
                // 4. 异步接水（如果没数据来，这个 Task 会被挂起，把 CPU 让给别人，极度节省性能）
                // 现代 C# 的 Socket 异步推荐使用 ArraySegment 划定内存交互视窗
                int received = await client.ReceiveAsync(new ArraySegment&amp;lt;byte&amp;gt;(buffer), SocketFlags.None);

                // 5. 判断合规断开边界（收到属于 TCP 标准的零字节终止 FIN 塞子）
                if (received == 0)
                {
                    Console.WriteLine($&quot;客户端已请求正常断开下线: {client.RemoteEndPoint}&quot;);
                    break;
                }

                // 6. 成功提取业务数据进行处理
                string msg = Encoding.UTF8.GetString(buffer, 0, received);
                Console.WriteLine($&quot;收到客户端消息: {msg}&quot;);

                // 7. 发起反向数据答复通知
                byte[] sendData = Encoding.UTF8.GetBytes(&quot;服务器确认消息已收悉: &quot; + msg);
                await client.SendAsync(new ArraySegment&amp;lt;byte&amp;gt;(sendData), SocketFlags.None);
            }
        }
        catch (SocketException ex)
        {
            Console.WriteLine($&quot;客户探测产生异常掉线断裂: {ex.Message}&quot;);
        }
        finally
        {
            // 8. 优雅地善后：进行系统的生命周期物理防线释放与强制销毁关闭
            try { client.Shutdown(SocketShutdown.Both); } catch { }
            client.Close();
        }
    }
}
&lt;/code&gt;&lt;/pre&gt;
&lt;h3&gt;11.2 客户端架构：异步拨号与全双工收发轮询&lt;/h3&gt;
&lt;p&gt;客户端在建立连接建立后，需要利用“多路复用”的思想：创建一个辅助任务专门监听并在控制台打出服务端的下发通知，而主干逻辑依然保留给用户做高频即时的输入请求。&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;using System;
using System.Net;
using System.Net.Sockets;
using System.Text;
using System.Threading.Tasks;

public class PureClient
{
    public async Task ConnectAndCommunicateAsync()
    {
        Socket socket = new Socket(AddressFamily.InterNetwork, SocketType.Stream, ProtocolType.Tcp);

        try
        {
            // 1. 发起非阻塞异步拨号，避免引发界面线程雪崩卡死
            await socket.ConnectAsync(IPAddress.Parse(&quot;127.0.0.1&quot;), 9000);
            Console.WriteLine(&quot;TCP 拨号成功连通服务器主干！&quot;);

            // 2. 将来自远端下行数据的捕获抽离，甩入专门的独立异步循环侦听任务池中
            _ = Task.Run(() =&amp;gt; ReceiveFromServerAsync(socket));

            // 3. 在当前节点内依旧保留主动抛送数据的轮询能力
            while (true)
            {
                string input = Console.ReadLine();
                if (input == &quot;exit&quot;) break; // 用户输入 exit 指令申请退出

                byte[] data = Encoding.UTF8.GetBytes(input);
                await socket.SendAsync(new ArraySegment&amp;lt;byte&amp;gt;(data), SocketFlags.None);
            }
        }
        catch (Exception ex)
        {
            Console.WriteLine($&quot;建联连接失败故障: {ex.Message}&quot;);
        }
        finally
        {
            // 4. 用户断开时，彻底清剿本地内存栈与通信端口资源
            try { socket.Shutdown(SocketShutdown.Both); } catch { }
            socket.Close();
            Console.WriteLine(&quot;物理级连接彻底注销。&quot;);
        }
    }

    private async Task ReceiveFromServerAsync(Socket socket)
    {
        var buffer = new byte[4096];
        try
        {
            while (true)
            {
                int received = await socket.ReceiveAsync(new ArraySegment&amp;lt;byte&amp;gt;(buffer), SocketFlags.None);
                if (received == 0) break; // 服务单主动向我方挥手掉线/关服结束

                // 【进阶写法】：使用现代 C# 的 Span 语法，直接切片，性能更高！
                Span&amp;lt;byte&amp;gt; realData = buffer.AsSpan(0, received);
                
                Console.WriteLine(&quot;\n[服务端回话回函] &quot; + Encoding.UTF8.GetString(realData));
            }
        }
        catch { /* 网络级断绝引发的崩溃链通常忽略作吞没处理即可 */ }
    }
}
&lt;/code&gt;&lt;/pre&gt;
&lt;hr /&gt;
&lt;h3&gt;11.3 现代封装体系：TcpListener 与 TcpClient 极简架构&lt;/h3&gt;
&lt;p&gt;就如同前文讨论底层硬件时提及的 &lt;code&gt;SerialPort&lt;/code&gt; 为我们屏蔽了串口驱动层的繁文缛节一样，微软同样在 .NET Framework 时期就为复杂的 &lt;code&gt;Socket&lt;/code&gt; 机制量身打造了一套工业级现代封装：&lt;strong&gt;&lt;code&gt;TcpListener&lt;/code&gt;&lt;/strong&gt;（替代 Server 启动）与 &lt;strong&gt;&lt;code&gt;TcpClient&lt;/code&gt;&lt;/strong&gt;（用于客户端连入连出）。&lt;/p&gt;
&lt;p&gt;它们极大地弱化了底层指针（EndPoint）的概念，并将所有的数据抛接行为整合到了现代流媒体管道（&lt;code&gt;NetworkStream&lt;/code&gt;）之中。&lt;/p&gt;
&lt;h4&gt;1. 现代化服务端：使用 TcpListener&lt;/h4&gt;
&lt;p&gt;无需再手动设定复杂的 &lt;code&gt;AddressFamily&lt;/code&gt; 族规并强行调用底层的 &lt;code&gt;Bind()&lt;/code&gt; 与 &lt;code&gt;Listen()&lt;/code&gt;，只需传入 IP 和接口即可立地成佛：&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;using System;
using System.Net;
using System.Net.Sockets;
using System.Text;
using System.Threading.Tasks;

public class ModernTcpServer
{
    public async Task StartModernServerAsync()
    {
        // 1. 无需关注底层网络协议参数，直白地宣布：在本地 IP (Any) 的 9000 端口接客
        TcpListener listener = new TcpListener(IPAddress.Any, 9000);
        listener.Start(); // 内部自动帮你执行了 Bind() 和 Listen()
        Console.WriteLine(&quot;现代 TCP 枢纽：挂载 9000 端口监听中...&quot;);

        while (true)
        {
            // 2. Accept 接客：但它返回的不再是底层的 Socket，而是被温柔包裹起来的 TcpClient！
            TcpClient connectedClient = await listener.AcceptTcpClientAsync();
            Console.WriteLine($&quot;接获新宾客：{connectedClient.Client.RemoteEndPoint}&quot;);

            // 3. 剥离甩入独立接管车间处理 (防阻塞后续其他大批量的连入)
            _ = Task.Run(() =&amp;gt; HandleModernClientAsync(connectedClient));
        }
    }

    private async Task HandleModernClientAsync(TcpClient client)
    {
        // 核心跃迁：不再直接调用 Socket.Receive()，而是提取一条抽象的流水管道 Stream！
        using NetworkStream stream = client.GetStream();
        byte[] buffer = new byte[1024];

        try
        {
            while (true)
            {
                // 通过标准流进行读取。它的代码感官体验就像在读写本地 txt 文件一样丝滑
                int byteCount = await stream.ReadAsync(buffer, 0, buffer.Length);
                if (byteCount == 0) break; // 对端掉线告辞

                string text = Encoding.UTF8.GetString(buffer, 0, byteCount);
                Console.WriteLine($&quot;收到流文：{text}&quot;);
            }
        }
        catch { /* 被迫断线异常静默掉 */ }
        finally 
        { 
            client.Close(); // 统一安全销毁内部网络管道及附带的 Socket 资源
        }
    }
}
&lt;/code&gt;&lt;/pre&gt;
&lt;h4&gt;2. 现代化客户端：使用 TcpClient&lt;/h4&gt;
&lt;p&gt;作为主动出击拨号的一方，我们不再需要构建 &lt;code&gt;IPEndPoint&lt;/code&gt; 坐标轴并塞入 &lt;code&gt;Connect&lt;/code&gt; 里面核准，一切都变得非常直给：&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;using System;
using System.Net.Sockets;
using System.Text;
using System.Threading.Tasks;

public class ModernTcpApp
{
    public async Task ConnectModernServerAsync()
    {
        using TcpClient client = new TcpClient();
        
        try
        {
            // 连底层的地址解析 IPAddress.Parse 都被框架默认吃掉了，你可以直接写入字符串地址和端口
            await client.ConnectAsync(&quot;127.0.0.1&quot;, 9000);
            
            // 引出属于你的那一段专属双向沟通流水线
            using NetworkStream stream = client.GetStream();
            
            byte[] data = Encoding.UTF8.GetBytes(&quot;Hello, 现代封装管线！&quot;);
            
            // 无需关注 SocketFlags.None，流 (Stream) 的使命就是纯粹且猛烈的读与写
            await stream.WriteAsync(data, 0, data.Length);
            await stream.FlushAsync(); // 确认流管道里的数据全部被推空且发出
            
            Console.WriteLine(&quot;发送结束，切出流隧道。&quot;);
        }
        catch (Exception ex)
        {
            Console.WriteLine($&quot;连接服务器受阻: {ex.Message}&quot;);
        }
    }
}
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;这些类库体系与后续文章将展示的高级防粘包 &lt;code&gt;ArrayPool&lt;/code&gt; 和 &lt;code&gt;Span&lt;/code&gt; 切分策略完全兼容。在目前 95% 以上的大型商业项目里，“网流管线派 (&lt;code&gt;NetworkStream&lt;/code&gt;)”始终占据着统治地位。&lt;/p&gt;
&lt;h3&gt;11.4 界面交互实战：网络服务启停的状态机模式（以 TCP 为例）&lt;/h3&gt;
&lt;p&gt;在 WinForms 或 WPF 界面开发中，通过单个按钮控制“启动/关闭”后台网络服务时，处理不当极易引发“端口被占用（AddressAlreadyInUse）”异常或 UI 线程阻塞假死。
以下是实现异步启停、防止重复启动并安全管理生命周期的标准代码规范：&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;private TcpListener _server;
private CancellationTokenSource _cts;
private bool _isServerRunning = false;

// UI 层按钮点击事件（例如绑定在 btnToggleServer）
private async void btnToggleServer_Click(object sender, EventArgs e)
{
    // [规范 1] 状态干预：禁用按钮，防止用户在执行过程中重复点击
    btnToggleServer.Enabled = false;

    if (!_isServerRunning)
    {
        // ---------------- 【服务器启动流程】 ----------------
        try
        {
            _server = new TcpListener(IPAddress.Any, 502);
            // 启用端口复用：确保在服务程序异常退出后重新启动时，可以直接复用原端口
            _server.Server.SetSocketOption(SocketOptionLevel.Socket, SocketOptionName.ReuseAddress, true);
            
            _server.Start();
            _isServerRunning = true;
            _cts = new CancellationTokenSource(); // 实例化取消令牌对象
            
            btnToggleServer.Text = &quot;关闭服务器&quot;;
            btnToggleServer.BackColor = Color.LightGreen;
            
            // [规范 2] 异步监听：使用 Task.Run 将循环接收连接的任务放置在后台线程，避免阻塞 UI
            _ = Task.Run(() =&amp;gt; AcceptClientsLoopAsync(_cts.Token));
        }
        catch (Exception ex)
        {
            MessageBox.Show($&quot;网络服务启动失败: {ex.Message}&quot;);
            _isServerRunning = false;
        }
    }
    else
    {
        // ---------------- 【服务器停止流程】 ----------------
        try
        {
            // 通过令牌发送取消信号，中止各类依赖 Token 的等待过程
            _cts?.Cancel();
            
            // 关闭底层监听器，触发仍在阻塞中的 Accept 方法引发异常
            _server?.Stop();
        }
        catch { /* 处理停止过程中可能产生的已知通信异常 */ }
        finally
        {
            _isServerRunning = false;
            
            btnToggleServer.Text = &quot;启动服务器&quot;;
            btnToggleServer.BackColor = Color.LightGray;
        }
    }

    // 状态切换完成，恢复界面交互控制权
    btnToggleServer.Enabled = true;
}

private async Task AcceptClientsLoopAsync(CancellationToken token)
{
    // 通过判断令牌是否发出了取消请求来控制主干循环
    while (!token.IsCancellationRequested)
    {
        try
        {
            // 等待远程连接。当调用界面的 _server.Stop() 时，此处会立刻引发底层的异常反馈中断等待
            TcpClient client = await _server.AcceptTcpClientAsync();
            
            // 收到外部设备连入后，分配独立后台任务作数据交互处理
            _ = Task.Run(() =&amp;gt; { /* 执行接收数据流与业务操作... */ });
        }
        catch (ObjectDisposedException) { break; } // _server.Stop() 时触发的正常被动关闭情况
        catch (SocketException) { break; }         // 底层连接释放导致的报错中断
    }
}
&lt;/code&gt;&lt;/pre&gt;
&lt;blockquote&gt;
&lt;p&gt;&lt;strong&gt;💡 通用启停状态机架构：跨越 TCP/UDP 差异的标准范式&lt;/strong&gt;&lt;/p&gt;
&lt;p&gt;上述代码虽然以 &lt;code&gt;TcpListener&lt;/code&gt; 为例，但这种单一按钮控制生命周期的逻辑同样是处理所有&lt;strong&gt;持续性驻留型网络任务的通用设计规范&lt;/strong&gt;。&lt;/p&gt;
&lt;p&gt;无论受控目标是：&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;侦听局域网广播的 &lt;strong&gt;UDP 接收循环&lt;/strong&gt; (&lt;code&gt;UdpClient.ReceiveAsync()&lt;/code&gt;)&lt;/li&gt;
&lt;li&gt;自动探活并尝试恢复连接的 &lt;strong&gt;TCP 客户端重连机制&lt;/strong&gt;&lt;/li&gt;
&lt;li&gt;轮询底层接口的 &lt;strong&gt;SerialPort 数据读取任务&lt;/strong&gt;&lt;/li&gt;
&lt;/ol&gt;
&lt;p&gt;只要涉及使用界面按钮启停持续性的后台大循环，都建议遵循此架构设计中的三个重点思路：&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;strong&gt;状态维护 (State Flag/Lock)&lt;/strong&gt;：通过引入布尔值标志位（如 &lt;code&gt;_isServerRunning&lt;/code&gt;）以及禁用 UI 控件（&lt;code&gt;Enabled = false&lt;/code&gt;），防止并发重复执行以及多次实例化产生的端口争抢。&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;任务取消控制 (Cancellation Token)&lt;/strong&gt;：对于无限后台循环，应当通过 &lt;code&gt;CancellationTokenSource&lt;/code&gt; 传参机制进行安全的任务中断请求。不得使用会引发内存无法预测的异常机制来强杀线程（例如被废弃的 &lt;code&gt;Thread.Abort()&lt;/code&gt; ）。&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;针对预期异常的捕获退出 (Catch Expected Exception)&lt;/strong&gt;：对底层通讯资源的显式释放（类似调用 &lt;code&gt;Stop()&lt;/code&gt; 或 &lt;code&gt;Close()&lt;/code&gt; 等），必然导致仍在阻塞接收的方法抛出如 &lt;code&gt;SocketException&lt;/code&gt;、&lt;code&gt;ObjectDisposedException&lt;/code&gt; 或 &lt;code&gt;OperationCanceledException&lt;/code&gt; 等系统异常。在开发中需主动将其 &lt;code&gt;catch&lt;/code&gt; 隔离并作为 &lt;code&gt;break&lt;/code&gt; 结束任务循环的安全触发阀机制看待。&lt;/li&gt;
&lt;/ul&gt;
&lt;/blockquote&gt;
&lt;hr /&gt;
&lt;h2&gt;12. 广域网另一极：UDP 高速无连接通信与 TCP 对比&lt;/h2&gt;
&lt;p&gt;在详尽探讨了 TCP 之后，我们在广域网通信开发中常常会面临另一个选择分支：&lt;strong&gt;UDP (User Datagram Protocol，用户数据报协议)&lt;/strong&gt;。与 TCP 那种“必须打电话确认有人接听才能说话”的严谨机制不同，UDP 就像是一座“抛石机”，只管把数据包打出去，根本不在乎对方是否成功接收、次序是否颠倒。&lt;/p&gt;
&lt;h3&gt;12.1 UDP 的核心特性与工业适用场景&lt;/h3&gt;
&lt;ul&gt;
&lt;li&gt;&lt;strong&gt;无连接 (Connectionless)&lt;/strong&gt;：完全没有三次握手和挥手过程，这使得它的发包潜伏期（Latency）极低。&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;边界清晰 (Message-oriented)&lt;/strong&gt;：TCP 会由于数据流持续被推入而产生“粘包”现象（详见下文章节段落），但 UDP 的任何一个单独分发的数据报边界都是固定的，一次发送（Send）严格对应一次接收（Receive）。&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;不可靠保障&lt;/strong&gt;：存在丢包、乱序、重复现象，底层不提供超时重传等机制。&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;&lt;strong&gt;【工业/物联网应用场景】：&lt;/strong&gt;&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;&lt;strong&gt;高频极速状态刷新&lt;/strong&gt;：例如一些只需要持续汇报“当前电压、温度报警”的硬件，往往采用 UDP 广播或一对一定向发射。偶尔丢弃一秒内的某个包无所谓，保证最新的一手数据快速覆盖即可。&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;局域网设备自发现 (Discovery)&lt;/strong&gt;：系统启动时向局域网广播 UDP 包寻找厂区内在线控制设备，设备收到后主动返回其真实 IP 以便主流程接管建立稳定连接。&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;看重实时延迟的流播&lt;/strong&gt;：多媒体监控、VoIP语音通讯等领域。&lt;/li&gt;
&lt;/ol&gt;
&lt;h3&gt;12.2 TCP 与 UDP 深度选型对照表&lt;/h3&gt;
&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;对比维度&lt;/th&gt;
&lt;th&gt;TCP (传输控制协议)&lt;/th&gt;
&lt;th&gt;UDP (用户数据报协议)&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;连接机制&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;强制要求连接（三次握手），断开需挥手。&lt;/td&gt;
&lt;td&gt;完全无连接，得知对端终点端点即可抛射。&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;可靠性保证&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;强校验、丢包重传、严格按序送达。网络环境恶劣时容错性高。&lt;/td&gt;
&lt;td&gt;不保证送达，极易随网络波动丢失中间帧，自带无重配。&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;速度与延迟&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;中等。由于须带重传管控确认和流量滑动窗口控制介入。&lt;/td&gt;
&lt;td&gt;极速。全速抛送挥离本机即刻完成。&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;通信拓扑&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;仅限“点到点”一对一的单一隧道双向连通。&lt;/td&gt;
&lt;td&gt;天生支持 单播、多播 (Multicast)、群广播 (Broadcast)。&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;核心适用&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;严谨数据传输、指令必达反馈、Modbus TCP 等。&lt;/td&gt;
&lt;td&gt;视频监控流播、心跳侦测、频发极速状态大盘刷新等。&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;
&lt;h3&gt;12.3 从底层 Socket 到现代封装：代码层面的核心差异&lt;/h3&gt;
&lt;p&gt;为了彻底理解 UDP 的无连接本质，我们必须先抛开高级封装，直接进入最底层的 &lt;code&gt;Socket&lt;/code&gt; 引擎对比 TCP 进行剖析。&lt;/p&gt;
&lt;h4&gt;阶段一：硬核基础版（纯底层 Socket 展现核心差异）&lt;/h4&gt;
&lt;p&gt;回忆前面章节中的 TCP 建立过程：&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;strong&gt;TCP 服务端&lt;/strong&gt;：必须依次执行 &lt;code&gt;Bind()&lt;/code&gt; -&amp;gt; &lt;code&gt;Listen()&lt;/code&gt; 挂起监听队列 -&amp;gt; &lt;code&gt;AcceptAsync()&lt;/code&gt; 阻塞等待并为一个确切的客户生成专属的新 Socket 实例。&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;TCP 客户端&lt;/strong&gt;：必须执行 &lt;code&gt;ConnectAsync(IP)&lt;/code&gt; 完成三次握手建立专用隧道后，才能开始 &lt;code&gt;Send&lt;/code&gt;。&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;而在 UDP 的世界中，&lt;strong&gt;不存在等待握手的 &lt;code&gt;Listen&lt;/code&gt;，不存在专属接客的 &lt;code&gt;Accept&lt;/code&gt;，更不存在发起握手的 &lt;code&gt;Connect&lt;/code&gt;&lt;/strong&gt;。无论收发，大家都只是一条单纯的“路口管道”：&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;using System;
using System.Net;
using System.Net.Sockets;
using System.Text;
using System.Threading.Tasks;

public class RawUdpTransceiver
{
    // === UDP 发送方 (极度纯粹的发射机制) ===
    public async Task SendFastStateWithRawSocketAsync()
    {
        // 1. 指定协议栈：InterNetwork (IPv4), Dgram (数据报模式), Udp 协议
        using Socket sender = new Socket(AddressFamily.InterNetwork, SocketType.Dgram, ProtocolType.Udp);
        byte[] payload = Encoding.UTF8.GetBytes(&quot;设备 01 状态健康_28℃&quot;);
        
        // 2. 【核心差异】：完全不需要 Connect！
        // 因为没有专属隧道，所以每次发送都必须显式贴上“快递单”（明确指定目标 IPEndPoint）
        EndPoint targetAddress = new IPEndPoint(IPAddress.Parse(&quot;192.168.1.100&quot;), 8888);
        
        // 使用 SendTo 抛出报文，抛出即代表任务结束（无论对端是否开机都不保证必达）
        await sender.SendToAsync(new ArraySegment&amp;lt;byte&amp;gt;(payload), SocketFlags.None, targetAddress);
        Console.WriteLine(&quot;底层 Socket：UDP 状态汇报抛射完成。&quot;);
    }

    // === UDP 接收方 (无需派发专属线程接客) ===
    public async Task StartRawUdpListenerAsync()
    {
        using Socket listener = new Socket(AddressFamily.InterNetwork, SocketType.Dgram, ProtocolType.Udp);
        // 1. 绑定在本地路口的 8888 接口上
        listener.Bind(new IPEndPoint(IPAddress.Any, 8888));
        
        // 【核心差异】：没有 listener.Listen(100)！也没有 await listener.AcceptAsync()！
        Console.WriteLine(&quot;底层 Socket：UDP 接应站启动，直接开始接收一切途经 8888 口的数据包...&quot;);

        byte[] buffer = new byte[4096];
        EndPoint remoteSenderAddress = new IPEndPoint(IPAddress.Any, 0); // 准备一个空白信封用来装“发件人真实地址”

        while (true)
        {
            // 2. 因为各路神仙都可以往这个口扔包，所以必须用 ReceiveFrom，它会同时捕获数据和“寄件人具体坐标”
            SocketReceiveFromResult result = await listener.ReceiveFromAsync(
                new ArraySegment&amp;lt;byte&amp;gt;(buffer), SocketFlags.None, remoteSenderAddress);

            string stateMsg = Encoding.UTF8.GetString(buffer, 0, result.ReceivedBytes);
            Console.WriteLine($&quot;收到从 {result.RemoteEndPoint} 发来的飞包: {stateMsg}&quot;);
        }
    }
}
&lt;/code&gt;&lt;/pre&gt;
&lt;h4&gt;阶段二：现代精简版（利用 UdpClient 封装层）&lt;/h4&gt;
&lt;p&gt;在理解了底层“免握手、靠贴地址条发包 (&lt;code&gt;SendTo&lt;/code&gt;) 与收包 (&lt;code&gt;ReceiveFrom&lt;/code&gt;)”的逻辑后，我们再来看日常企业级开发中最常使用的现代化封装类 &lt;code&gt;UdpClient&lt;/code&gt;。它将上述底层的 EndPoint 指针处理等操作抽象到了极简：&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;using System;
using System.Net;
using System.Net.Sockets;
using System.Text;
using System.Threading.Tasks;

public class ModernUdpTransceiver
{
    // === UDP 发送方 ===
    public async Task SendFastStateAsync()
    {
        using UdpClient sender = new UdpClient();
        byte[] dgram = Encoding.UTF8.GetBytes(&quot;设备 01 状态健康_28℃&quot;);
        
        // 进一步精简封装，连 SocketFlags 之类的底层参数都隐藏了，直接填数据和对端地址点射
        await sender.SendAsync(dgram, dgram.Length, new IPEndPoint(IPAddress.Parse(&quot;192.168.1.100&quot;), 8888));
    }

    // === UDP 接收方 ===
    public async Task StartUdpListenerAsync()
    {
        // 实例化时要求传入端口参数，底层会自动帮我们完成上面示例中的 Bind() 动作
        using UdpClient listener = new UdpClient(8888);

        while (true)
        {
            // ReceiveAsync 会返回一个结构体结果对象，既包含了核心载荷，也含有寄件人的 RemoteEndPoint
            UdpReceiveResult result = await listener.ReceiveAsync();
            string stateMsg = Encoding.UTF8.GetString(result.Buffer);
            
            Console.WriteLine($&quot;收到从 {result.RemoteEndPoint} 发送的内容: {stateMsg}&quot;);
        }
    }
}
&lt;/code&gt;&lt;/pre&gt;
&lt;hr /&gt;
&lt;h2&gt;13. Socket 高并发通信实战：防范五个典型陷阱&lt;/h2&gt;
&lt;p&gt;在生产环境的高并发通信场景中（如处理大量物联网设备接入或承担核心网关服务并发访问），入门级的同步收发模型往往难以应对复杂的网络工况，可能会引发系列连接稳定性和内存资源管理方面的问题。以下将逐一梳理实际项目最常见的五大错误陷阱及企业级的 C# 规避机制建议。&lt;/p&gt;
&lt;h3&gt;13.1 陷阱一：半开连接导致的资源耗尽 (Half-Open Connection)&lt;/h3&gt;
&lt;ul&gt;
&lt;li&gt;&lt;strong&gt;【现象】&lt;/strong&gt; 服务端运行数日后，内存和 CPU 占用率异常拉高。内部监测集合发现保留了大量处于“连接已建”的 Socket 占用，但实际上物理信道早已丧失效能，造成内存泄漏与堆积阻塞。&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;【原因】&lt;/strong&gt; 客户端脱离网络环境时（例如无网络信道、硬切断电源），没有规范遵循 TCP 的 &lt;code&gt;FIN&lt;/code&gt; 关闭协议标准发报给对端。服务区端挂起的 &lt;code&gt;ReceiveAsync&lt;/code&gt; 会一直陷入阻塞等待读取，这使得那些僵死连接句柄无法按常规逻辑被终止与清扫释放。&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;【解决方案：应用层心跳探活机制 (Heartbeat)】&lt;/strong&gt;
针对这种未闭合僵尸进程，不应完全倚靠系统默认的 &lt;code&gt;TCP Keep-Alive&lt;/code&gt;，而应补充应用级的主动监控（Heartbeat）。
利用服务器建立定时器 &lt;code&gt;Timer&lt;/code&gt; 循环：定期（如 15 秒间隔）向管线广播轻型空载信号头（如单字节的 &lt;code&gt;0xFF&lt;/code&gt;）。若推送抛出 &lt;code&gt;SocketException&lt;/code&gt; 或由于长时不反馈回应 &lt;code&gt;Pong&lt;/code&gt;，中枢服务器便直接调用 &lt;code&gt;client.Close()&lt;/code&gt;。由于接口受外力干预强行切出，原本那个被锁死的 &lt;code&gt;ReceiveAsync&lt;/code&gt; 当即便抛出崩溃捕捉事件，并由此退回释放流程将系统清空。&lt;/li&gt;
&lt;/ul&gt;
&lt;h3&gt;13.2 陷阱二：非线程安全集合引发的改动错误崩溃 (Collection Modifying)&lt;/h3&gt;
&lt;ul&gt;
&lt;li&gt;&lt;strong&gt;【现象】&lt;/strong&gt; 在高频客户连入与抛脱断线的切换压力中，或是恰逢系统遍历集合对全机群触发全广播事件时，程序遇到异常闪退，错误指示通常为：&lt;code&gt;InvalidOperationException: 集合已修改；可能无法执行枚举操作&lt;/code&gt;。&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;【原因】&lt;/strong&gt; 标准的泛型集合 &lt;code&gt;Dictionary&amp;lt;string, Socket&amp;gt;&lt;/code&gt; 并不具备线程并发安全能力。当一个服务线程正通过 &lt;code&gt;foreach&lt;/code&gt; 迭代广播数据，另一接管线程刚好因外部重接下指令了 &lt;code&gt;Add&lt;/code&gt; 或 &lt;code&gt;Remove&lt;/code&gt; 数据，此越权操作直接打乱并篡改了原先处于挂靠执行链的结构环路引发异常。&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;【解决方案：采用 ConcurrentDictionary 并发读写框架】&lt;/strong&gt;
相比强加常规 &lt;code&gt;lock&lt;/code&gt; 语句带来的阻塞开支，建议选用专门的 &lt;code&gt;System.Collections.Concurrent.ConcurrentDictionary&amp;lt;TKey,TValue&amp;gt;&lt;/code&gt;，它自带了原子特性的锁优化防护能力：&lt;pre&gt;&lt;code&gt;// 线程级并发安全全局字典池
private ConcurrentDictionary&amp;lt;string, Socket&amp;gt; _clients = new ConcurrentDictionary&amp;lt;string, Socket&amp;gt;();

// 取代传统 Add 的并发安全存储
_clients.TryAdd(ip, socket);
// 安全下线注销清理事物操作
_clients.TryRemove(ip, out _);

// 【并发遍历核心】：即使中途面临强拔插连接事件，此遍历依然不会引起数据枚举安全错乱报错
foreach(var client in _clients.Values) { 
    // 持续可靠展开内部下发操作...
} 
&lt;/code&gt;&lt;/pre&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;h3&gt;13.3 陷阱三：粘包处理及碎片分配引起的 GC 波峰波动 (Sticky Packets)&lt;/h3&gt;
&lt;ul&gt;
&lt;li&gt;
&lt;p&gt;&lt;strong&gt;【现象】&lt;/strong&gt; 并发或密集流量投递时出现报文粘断异常：一次下发的两段消息可能被读取合拼成了一串长指令。如果不加以控制直接通过提取特殊标志（如使用 &lt;code&gt;Split(&apos;\n&apos;)&lt;/code&gt;）割裂切割获取，由于切割处理的生成量极大，将会产生海量临时用毕即扔的新字符串缓存 (&lt;code&gt;string&lt;/code&gt; 类型对象) 引发系统产生强制高频 &lt;code&gt;GC&lt;/code&gt; (系统垃圾收集运作) 波峰介入处理，使整个中控终端产生非常可观的处理延迟停顿。&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;&lt;strong&gt;【原因】&lt;/strong&gt; 无论底层的 Socket，或任何纯网络传输均是基准的“流水式传输载体（Stream）”，是不带结构分隔标记的。同时代码如果单纯依靠暴力无止尽 &lt;code&gt;new byte[4096]&lt;/code&gt; 也同样严重损耗主机内务。&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;&lt;strong&gt;【解决方案：TLV 报文设计方案与 ArrayPool 内存池双重组合复用】&lt;/strong&gt;&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;strong&gt;报文设计（TLV：Type-Length-Value）&lt;/strong&gt;：封装通讯帧前先采用头挂置，即通过 &lt;code&gt;BitConverter.GetBytes(结构总长)&lt;/code&gt; 预告下附的数据总额截区长度参数。&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;分配优化（ArrayPool）&lt;/strong&gt;：放弃随接随初始分配模式，从公用缓冲区 &lt;code&gt;System.Buffers.ArrayPool&amp;lt;byte&amp;gt;.Shared.Rent()&lt;/code&gt; 抽取并出借暂存内存资源存放数组承接块。&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;【报头防撞及缓冲重回收参考实现】：&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;// 处理 TLV 格式的粘包防范逻辑

// 拦截定尺获取一个 4 Bytes 的承托板块
byte[] headerBuffer = System.Buffers.ArrayPool&amp;lt;byte&amp;gt;.Shared.Rent(4); 
try
{
    // 挂靠精确过滤：必须精确截停首 4 段头帧标识
    int headerReceived = 0;
    while (headerReceived &amp;lt; 4) 
    {
        int r = await client.ReceiveAsync(
            new ArraySegment&amp;lt;byte&amp;gt;(headerBuffer, headerReceived, 4 - headerReceived), 
            SocketFlags.None);
        if (r == 0) return; 
        headerReceived += r;
    }

    // 测算承接的长度：
    int bodyLength = BitConverter.ToInt32(headerBuffer, 0);

    // 利用提取的数据参数，租借出所需数据量的实体接驳区域
    byte[] bodyBuffer = System.Buffers.ArrayPool&amp;lt;byte&amp;gt;.Shared.Rent(bodyLength);
    try
    {
        int bodyReceived = 0;
        // 控制 TCP 在达满需求参数前不出局
        while (bodyReceived &amp;lt; bodyLength) 
        {
            int r = await client.ReceiveAsync(
                new ArraySegment&amp;lt;byte&amp;gt;(bodyBuffer, bodyReceived, bodyLength - bodyReceived),
                SocketFlags.None);
            if (r == 0) return;
            bodyReceived += r;
        }

        // 获取到无沾染粘包的单次完整业务流
        string actualMessage = Encoding.UTF8.GetString(bodyBuffer, 0, bodyLength);
        // 进入合法通讯执行分片处理 actualMessage...
    }
    finally
    {
        // 交还复用数组以避免触发高频 GC 垃圾回收
        System.Buffers.ArrayPool&amp;lt;byte&amp;gt;.Shared.Return(bodyBuffer);
    }
}
finally
{
    System.Buffers.ArrayPool&amp;lt;byte&amp;gt;.Shared.Return(headerBuffer);
}
&lt;/code&gt;&lt;/pre&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;h3&gt;13.4 陷阱四：极短连接压力造成的 TIME_WAIT 端口枯竭殆尽死机 (TIME_WAIT Exhaustion)&lt;/h3&gt;
&lt;ul&gt;
&lt;li&gt;&lt;strong&gt;【现象】&lt;/strong&gt; 针对终端通讯点进行超密集发包与闭断复用重拨模拟试探测验环境下，会突然弹出：&lt;code&gt;SocketException: 无法连接，因队列过长、无足够的缓冲区或因终端本地接口通道遭耗尽&lt;/code&gt; 问题。&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;【原因】&lt;/strong&gt; 在常规通过 &lt;code&gt;Socket.Close()&lt;/code&gt; 尝试结束接口状态的关闭周期上，计算机网络法例对通信的协议进行了保护补偿防御策略（防止部分幽灵网络滞后包扰乱同口新链接），即端口通道会被操作系统列入并封阻到被为 &lt;code&gt;TIME_WAIT&lt;/code&gt; 的强制延迟待命状态并锁时锁定数分钟以上的时间才释防复用控制权。若是密集调动压测则很轻易导致本地操作系统所有承接可控端口量用罄并报错。&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;【解决方案：重新配置 LingerOption 挂断设定】&lt;/strong&gt;
作为针对部分可容忍略去数据残留容错且以防连接阻塞死机的通讯环境应用项目下：&lt;pre&gt;&lt;code&gt;// 启动零等待超时选项
socket.LingerState = new LingerOption(true, 0); 
// Socket 调用 Close 时，操作系统直接回收资源，规避 TIME_WAIT 长期资源占用
socket.Close(); 
&lt;/code&gt;&lt;/pre&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;h3&gt;13.5 陷阱五：滥用 Available 属性进行完整性轮询与底层脏数据清理&lt;/h3&gt;
&lt;p&gt;在 TCP 网络编程中，&lt;code&gt;TcpClient.Available&lt;/code&gt;（或 &lt;code&gt;NetworkStream.DataAvailable&lt;/code&gt;）表示当前操作系统底层网络缓冲区中，&lt;strong&gt;已经接收到、但还未被你的应用程序读取的字节数&lt;/strong&gt;。
它的核心特性是：&lt;strong&gt;只读、瞬间返回、绝对不阻塞线程&lt;/strong&gt;。&lt;/p&gt;
&lt;p&gt;在现代异步编程（&lt;code&gt;async&lt;/code&gt;/&lt;code&gt;await&lt;/code&gt;）普及后，它的出场率已经大幅降低，但在工业通讯和软件开发中，它主要被用于以下两个特定场景，并且存在一个必须避免的绝对禁忌。&lt;/p&gt;
&lt;h4&gt;核心应用场景 1：发送指令前清空“脏数据” (Flush Buffer)&lt;/h4&gt;
&lt;p&gt;在半双工的问答式通讯（如 Modbus RTU over TCP）中，如果因为网络极度延迟、设备重启或程序异常，导致缓冲区里遗留了上一轮通讯的废弃响应报文，直接发送新请求会导致收发错位，解析崩溃。
此时可以使用 &lt;code&gt;Available&lt;/code&gt; 快速清空历史残留：&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;// 发送新请求前，如果底层缓冲区有遗留脏数据，直接全部读取并丢弃
if (client.Available &amp;gt; 0)
{
    byte[] junk = new byte[client.Available];
    stream.Read(junk, 0, junk.Length);
}

// 此时底层干净了，再安全地发送新报文
stream.Write(newRequestData, 0, newRequestData.Length);
&lt;/code&gt;&lt;/pre&gt;
&lt;h4&gt;核心应用场景 2：单线程环境下的非阻塞探测&lt;/h4&gt;
&lt;p&gt;在某些无法使用多线程或 &lt;code&gt;async/await&lt;/code&gt; 的严格单线程环境中（例如 Unity3D 的 Update 帧循环，或早期的 WinForms 单线程定时器），直接调用同步的 &lt;code&gt;stream.Read()&lt;/code&gt; 若遇上网络无数据，会导致整个 UI 界面永久卡死。
此时 &lt;code&gt;Available&lt;/code&gt; 就充当了“探路者”：&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;// 在定时器或帧循环中高频触发：
if (client.Available &amp;gt; 0) 
{
    // 既然明确知道有数据，此时调用同步 Read 瞬间就能拿回数据，不会卡死界面
    int bytesToRead = client.Available;
    byte[] buffer = new byte[bytesToRead];
    stream.Read(buffer, 0, bytesToRead);
}
&lt;/code&gt;&lt;/pre&gt;
&lt;h4&gt;绝对禁忌：滥用 Available 判断报文完整性或代替异步等待&lt;/h4&gt;
&lt;p&gt;TCP 是一种流式传输协议（面向无边界的字节流）。发送端发出的一个 10 字节报文，在网络层可能会被切碎成 3 字节、2 字节、5 字节分批到达。&lt;/p&gt;
&lt;p&gt;❌ &lt;strong&gt;错误写法（死循环轮询灾难）&lt;/strong&gt;：
试图用 &lt;code&gt;Available&lt;/code&gt; 去等待一个完整的 Modbus 报文。这种写法会极其消耗 CPU，且容易产生死循环。&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;// 试图使用死循环干等 8 个字节的报文到达
while (client.Available &amp;lt; 8) 
{
    Thread.Sleep(10); 
}
stream.Read(buffer, 0, 8);
&lt;/code&gt;&lt;/pre&gt;
&lt;h2&gt;✅ &lt;strong&gt;正确写法（底层事件驱动机制）&lt;/strong&gt;：
忽略 &lt;code&gt;Available&lt;/code&gt;，直接使用 &lt;code&gt;await stream.ReadAsync&lt;/code&gt;。当缓冲区为空时，该方法会自动挂起，将 CPU 资源还给系统；当网卡收到足够数据时，操作系统会从硬件层面唤醒你的线程继续执行。为了更直观地理解，请时刻牢记：&lt;strong&gt;Available 只是操作系统底层缓冲区的“水位计”，而绝非“消息完整度检测仪”&lt;/strong&gt;。&lt;/h2&gt;
&lt;h2&gt;14. 现代 C# 通信性能核心：零分配 (Zero-Allocation) 内存演进&lt;/h2&gt;
&lt;p&gt;在实现高并发的工业网关或物联网通信节点时，每秒往往需承接海量设备的并发请求。如果在这个过程中过度依赖 &lt;code&gt;new byte[count]&lt;/code&gt; 重新分配数组或使用 &lt;code&gt;Array.Copy&lt;/code&gt; 进行报文切片，会导致堆内存 (Heap) 中短生命周期的对象急剧增加，进而迫使垃圾回收器 (GC) 频繁介入并挂起工作线程，造成服务抖动与毫秒级延迟。
为实现&lt;strong&gt;零分配 (Zero-Allocation)&lt;/strong&gt;，微软在 C# 的体系演进中分别给出了池化机制 (&lt;code&gt;ArrayPool&lt;/code&gt;) 以及三个阶段的内存切片结构 (&lt;code&gt;ArraySegment&lt;/code&gt;、&lt;code&gt;Span&lt;/code&gt;、&lt;code&gt;Memory&lt;/code&gt;)。&lt;/p&gt;
&lt;h3&gt;14.1 外围容器池化：ArrayPool&amp;lt;T&amp;gt;&lt;/h3&gt;
&lt;p&gt;使用场景：替代常规的 &lt;code&gt;byte[] temp = new byte[1024]&lt;/code&gt; 数组新建。
机制：&lt;code&gt;System.Buffers.ArrayPool&amp;lt;T&amp;gt;.Shared&lt;/code&gt; 提供了预先初始化的定长数组池。调用 &lt;code&gt;.Rent(size)&lt;/code&gt; 即向系统借出足够容量的数组指针，在业务解析完毕后执行 &lt;code&gt;.Return()&lt;/code&gt; 予以归还挂起。由于全程使用已分配缓冲池区域，该方法清除了高频收发导致 GC 堆栈满溢的诱因。&lt;/p&gt;
&lt;h3&gt;14.2 内存切片技术演进：ArraySegment、Span 与 Memory&lt;/h3&gt;
&lt;p&gt;在利用 &lt;code&gt;ArrayPool&lt;/code&gt; 获取大块重用内存后，处理特定的数据包通常需要进行“报文片段截取”。针对此问题，C# 经历了三代核心切片架构演进：&lt;/p&gt;
&lt;h4&gt;第一代切片支持：ArraySegment&amp;lt;T&amp;gt;&lt;/h4&gt;
&lt;p&gt;&lt;strong&gt;出现背景&lt;/strong&gt;：早期 .NET 框架为了规避 &lt;code&gt;Array.Copy&lt;/code&gt; 带来的内存重复分配开销而诞生。
&lt;strong&gt;结构本质&lt;/strong&gt;：它是一个标准的 &lt;code&gt;struct&lt;/code&gt;。对象本身不存放实际数据，仅封装三个属性：&lt;code&gt;Array&lt;/code&gt; (源数组引用)、&lt;code&gt;Offset&lt;/code&gt; (起始偏移位置)、&lt;code&gt;Count&lt;/code&gt; (切割长度)。&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;byte[] buffer = new byte[4096]; // 假设分配自资源池
// 指定索引区间，创建一个纯逻辑视图窗，无实际数组克隆
ArraySegment&amp;lt;byte&amp;gt; segment = new ArraySegment&amp;lt;byte&amp;gt;(buffer, 0, 8);
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;&lt;strong&gt;工程局限性&lt;/strong&gt;：尽管创建速度快，但其内部被强行绑定在 Managed Array (托管数组) 类型。如果需对接非托管的内存指针、互操作组件，或通过 &lt;code&gt;stackalloc&lt;/code&gt; 开辟的栈内存，它均无法进行兼容或承载。&lt;/p&gt;
&lt;h4&gt;第二代高性能游标：Span&amp;lt;T&amp;gt; / ReadOnlySpan&amp;lt;T&amp;gt;&lt;/h4&gt;
&lt;p&gt;&lt;strong&gt;出现背景&lt;/strong&gt;：从 .NET Core 2.1 开始引入的关键底层结构，是现今 C# 高并发 IO 的性能基础。
&lt;strong&gt;结构本质&lt;/strong&gt;：&lt;code&gt;ref struct&lt;/code&gt; (堆栈限制型结构)。它被设计为完全安全的可控型“内存指针”，剥离了对所属类型的依赖，均等地支持截取普通数组、非托管堆内存甚至本线程栈指针。&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;byte[] buffer = new byte[4096];
// 将数组映射为游标，通过坐标转移实现零损耗切片
Span&amp;lt;byte&amp;gt; span = buffer.AsSpan(0, 8); 

// 二次切割提取单独的功能码字节，全程没有新内存占用
Span&amp;lt;byte&amp;gt; functionCode = span.Slice(1, 1);
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;&lt;strong&gt;安全限制约束（重要工程陷阱）&lt;/strong&gt;：
因为它是 &lt;code&gt;ref struct&lt;/code&gt;，C# 编译器在底层通过安全规范强制此对象永远只能存活在当前线程的 Stack (栈) 深度内，这意味它绝对不能逃逸到 Heap (堆) 上：&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;&lt;strong&gt;禁止装箱与成员化&lt;/strong&gt;：不可作为其他 &lt;code&gt;class&lt;/code&gt; 的全局属性成员。&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;禁止跨边界异步&lt;/strong&gt;：严禁在 &lt;code&gt;async / await&lt;/code&gt; 方法中跨越 &lt;code&gt;await&lt;/code&gt; 关键字的生命周期区域使用。因为 &lt;code&gt;await&lt;/code&gt; 编译层会挂起生成状态机存放于堆区，将在此种语法树下产生逃逸阻断报错。&lt;/li&gt;
&lt;/ol&gt;
&lt;h4&gt;第三代异步桥梁：Memory&amp;lt;T&amp;gt; / ReadOnlyMemory&amp;lt;T&amp;gt;&lt;/h4&gt;
&lt;p&gt;&lt;strong&gt;出现背景&lt;/strong&gt;：用于填补 &lt;code&gt;Span&amp;lt;T&amp;gt;&lt;/code&gt; 无法在现当代 &lt;code&gt;async/await&lt;/code&gt; 异步控制流程中直接跨越挂起点使用的设计空白。
&lt;strong&gt;结构本质&lt;/strong&gt;：一个不受避逸机制影响的标准 &lt;code&gt;struct&lt;/code&gt;，底层同样具有切割标记，它能安全地被存入堆对象的成员内并兼容异步状态机。
&lt;strong&gt;规范协同工作流&lt;/strong&gt;：
在等待 IO 层面上（例如异步 &lt;code&gt;NetworkStream.ReadAsync(Memory&amp;lt;byte&amp;gt;)&lt;/code&gt;），首选传递 &lt;code&gt;Memory&amp;lt;T&amp;gt;&lt;/code&gt; 容忍异步挂起。当获得字节数据，重新跨入解析内部函数的同步步骤域后，调用 &lt;code&gt;.Span&lt;/code&gt; 属性将其瞬间转换回 &lt;code&gt;Span&amp;lt;T&amp;gt;&lt;/code&gt; 展开极致的算法结构切分处理操作。&lt;/p&gt;
&lt;h4&gt;第四代终极形态：ReadOnlySequence&amp;lt;T&amp;gt;（非连续内存的逻辑链条）&lt;/h4&gt;
&lt;p&gt;你问出这个问题，说明你已经触碰到了 C# 网络编程的**&quot;终极形态&quot;**。前三代都有一个极其严苛的物理前提：&lt;strong&gt;连续内存（Contiguous Memory）&lt;/strong&gt;。但是，真实的 TCP 网络环境是残酷的，这就引出了 &lt;code&gt;ReadOnlySequence&amp;lt;T&amp;gt;&lt;/code&gt; 诞生的根本原因——&lt;strong&gt;数据跨越了缓冲区的物理边界（碎片化）&lt;/strong&gt;。&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;痛点：连续内存的崩溃瞬间&lt;/strong&gt;&lt;/p&gt;
&lt;p&gt;假设你的服务器收到了一个 8 字节的 Modbus 报文，但此时你的底层大水缸（buffer[4096]）只剩下最后 3 个字节的空间了。操作系统的网卡驱动会怎么做？它会把报文的前 3 个字节塞进水缸末尾，然后再开辟一个新水缸，把剩下的 5 个字节塞进新水缸开头。&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;内存块 A（旧水缸）：[...其他数据...] [01] [03] [04]
内存块 B（新水缸）：[00] [1E] [00] [28] [8C] [D6] [...空...]
                     ↑__________________↑
                     这8个字节实际是完整的 Modbus 报文
                     但在物理内存上根本不挨着！
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;&lt;strong&gt;前三代武器全部阵亡&lt;/strong&gt;：你无法用一个 &lt;code&gt;Span&amp;lt;byte&amp;gt;&lt;/code&gt; 或 &lt;code&gt;Memory&amp;lt;byte&amp;gt;&lt;/code&gt; 去同时框住这 8 个字节，因为它们在物理内存条上根本不挨着！强行读取需要自己写恶心的拼接代码，产生垃圾（违背零分配追求）。&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;第四代指挥官：ReadOnlySequence&amp;lt;T&amp;gt;&lt;/strong&gt;&lt;/p&gt;
&lt;p&gt;它的本质是一个可以&lt;strong&gt;跨越多个物理内存块的&quot;逻辑视图&quot;&lt;/strong&gt;。你可以把它理解为一条铁链，把一块块断开的 &lt;code&gt;Memory&amp;lt;byte&amp;gt;&lt;/code&gt; 串联起来，让你在代码里**&quot;感觉&quot;**它们是连续的。&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;ReadOnlySequence&amp;lt;T&amp;gt; 内部结构：
┌─────────────────────────────────────────────────────────┐
│  Segment 1 (Memory&amp;lt;byte&amp;gt;) → Segment 2 (Memory&amp;lt;byte&amp;gt;) → ... │
│  链表结构：记录每块的起始/结束位置和下一块指针           │
└─────────────────────────────────────────────────────────┘
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;&lt;strong&gt;为什么它这么重要？&lt;/strong&gt; 微软为配合它，专门在 .NET Core 3.0 引入了 &lt;code&gt;System.IO.Pipelines&lt;/code&gt;（管道模型）。&lt;strong&gt;ASP.NET Core (Kestrel 服务器) 能处理每秒千万级并发的底层秘密，就是全面使用了 Pipelines + ReadOnlySequence！&lt;/strong&gt;&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;四代内存神器终极对比&lt;/strong&gt;：&lt;/p&gt;
&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;特性&lt;/th&gt;
&lt;th&gt;ArraySegment&amp;lt;T&amp;gt;&lt;/th&gt;
&lt;th&gt;Span&amp;lt;T&amp;gt;&lt;/th&gt;
&lt;th&gt;Memory&amp;lt;T&amp;gt;&lt;/th&gt;
&lt;th&gt;ReadOnlySequence&amp;lt;T&amp;gt;&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;内存连续性&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;绝对连续&lt;/td&gt;
&lt;td&gt;绝对连续&lt;/td&gt;
&lt;td&gt;绝对连续&lt;/td&gt;
&lt;td&gt;可以不连续（多片段组成）&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;存储位置&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;堆/栈均可&lt;/td&gt;
&lt;td&gt;只能在栈&lt;/td&gt;
&lt;td&gt;堆/栈均可&lt;/td&gt;
&lt;td&gt;堆/栈均可&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;async/await&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;兼容&lt;/td&gt;
&lt;td&gt;❌ 绝对不兼容&lt;/td&gt;
&lt;td&gt;兼容&lt;/td&gt;
&lt;td&gt;兼容&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;典型场景&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;老代码兼容&lt;/td&gt;
&lt;td&gt;高性能同步解析&lt;/td&gt;
&lt;td&gt;异步等待 IO&lt;/td&gt;
&lt;td&gt;TCP 粘包/拆包/跨边界&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;形象比喻&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;一段胶带&lt;/td&gt;
&lt;td&gt;一把手术刀&lt;/td&gt;
&lt;td&gt;一个密封盒&lt;/td&gt;
&lt;td&gt;一条铁链&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;
&lt;p&gt;&lt;strong&gt;工业级用法：SequenceReader 搭配食用&lt;/strong&gt;&lt;/p&gt;
&lt;p&gt;当你拿到一条 &lt;code&gt;ReadOnlySequence&amp;lt;byte&amp;gt;&lt;/code&gt; 时，不能直接像数组那样 &lt;code&gt;[index]&lt;/code&gt; 取值（因为底层不连续）。微软为它专门配备了搭档：&lt;code&gt;SequenceReader&amp;lt;T&amp;gt;&lt;/code&gt;。&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;// 假设这是从 PipeReader 读出的数据，可能跨越了 3 个内存块
ReadOnlySequence&amp;lt;byte&amp;gt; sequence = buffer; 

// 把它丢给阅读器
SequenceReader&amp;lt;byte&amp;gt; reader = new SequenceReader&amp;lt;byte&amp;gt;(sequence);

// 尝试读取大端的 16 位整数
// 阅读器极其智能：
//   - 如果这 2 个字节刚好在同一个内存块 → 直接用 Span 读取（性能极高）
//   - 如果这 2 个字节刚好跨越了内存块（一块末尾，一块开头）→ 底层自动帮你安全拼接并读取！
if (reader.TryReadBigEndian(out short registerValue))
{
    Console.WriteLine($&quot;读出数值: {registerValue}&quot;);
}

// 读取变长字符串（先读长度，再读内容）
if (reader.TryReadBigEndian(out int stringLength) &amp;amp;&amp;amp; 
    reader.TryReadBytes(stringLength, out var text))
{
    Console.WriteLine($&quot;字符串: {Encoding.UTF8.GetString(text)}&quot;);
}
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;&lt;strong&gt;跨内存边界缓冲区可视化模型&lt;/strong&gt;：&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;真实 TCP 粘包场景：

Buffer A (4096字节)                    Buffer B (4096字节)
┌─────────────────────────┐            ┌─────────────────────────┐
│ [其他数据] [01] [03]    │            │ [04] [00] [1E] [00]... │
│                         │            │                         │
│     ↑                   │            │  ↑                      │
│     └── TCP报文头部 3B ──┘            │  └── TCP报文体 5B ────┘│
│                                     │                         │
└─────────────────────────────────────┴─────────────────────────┘
           ↑___________________________________________↑
                          逻辑上连续的 8 字节报文

用 Span&amp;lt;byte&amp;gt; ❌ 无法同时框住
用 Memory&amp;lt;byte&amp;gt; ❌ 无法同时框住
用 ReadOnlySequence&amp;lt;byte&amp;gt; ✅ 可以用一条铁链串联 A 和 B
用 SequenceReader&amp;lt;byte&amp;gt; ✅ 可以优雅地读取，不产生任何额外分配
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;&lt;strong&gt;Pipeline 完整解析流程&lt;/strong&gt;：&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;async Task ProcessPipelineAsync(PipeReader reader)
{
    while (true)
    {
        // 1. 从管道读取数据（可能跨越多个内存块）
        ReadResult result = await reader.ReadAsync();
        ReadOnlySequence&amp;lt;byte&amp;gt; buffer = result.Buffer;

        // 2. 尝试解析完整报文
        while (TryParseMessage(ref buffer, out var message))
        {
            // 处理业务
            ProcessMessage(message);
        }

        // 3. 标记已消费的位置（管道自动回收）
        reader.AdvanceTo(buffer.Start, buffer.End);

        // 4. 客户端断开
        if (result.IsCompleted) break;
    }
}

bool TryParseMessage(ref ReadOnlySequence&amp;lt;byte&amp;gt; buffer, out var message)
{
    var reader = new SequenceReader&amp;lt;byte&amp;gt;(buffer);

    // 读取固定头：2 字节 Type + 4 字节 Length
    if (!reader.TryReadBigEndian(out short type) || 
        !reader.TryReadBigEndian(out int length))
        return false;  // 数据不完整，等待更多数据

    // 验证数据完整性
    if (length &amp;gt; buffer.Length)
        return false;  // 粘包：还需要更多数据

    // 读取负载
    var payload = reader.Sequence.Slice(reader.Position, length);
    message = new Message { Type = type, Payload = payload };

    // 移动读位置
    reader.Advance(length);
    buffer = reader.Sequence;
    return true;
}
&lt;/code&gt;&lt;/pre&gt;
&lt;hr /&gt;
&lt;h2&gt;15. 工业级网络中间件：五维架构体系代码实战&lt;/h2&gt;
&lt;p&gt;在构建大型网关或设备控制系统时，仅拥有底层的异步机制与内存管理仍略显单薄。为确保系统的可扩展性、稳定性和高吞吐量，建议按照以下“五维架构”标准来组织核心代码：&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;strong&gt;Socket (物理链路)&lt;/strong&gt;：纯粹作为承载字节流的通道，负责维护 TCP 握手、保持心跳（Heartbeat）以及管理物理层断开事件。它应当被隔离在最底层，绝不涉足业务解析。&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;TLV (2+4) (通讯契约)&lt;/strong&gt;：在无边界的字节流传输中，通过定义头协议来进行精确分隔。例如，采用 2 字节（指令类型 Type）加 4 字节（负载长度 Length）的经典 TLV (Type-Length-Value) 结构，形成严格的解析“法律”。&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;ArrayPool + Span (极致后勤)&lt;/strong&gt;：在处理 TLV 切片时，利用 &lt;code&gt;ArrayPool&lt;/code&gt; 作为无需 GC 干预的数据容器，辅以 &lt;code&gt;Span&amp;lt;byte&amp;gt;&lt;/code&gt; 执行零分配的头部与负载解析，保障高并发下的性能支撑。&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Router + Handler (组织架构)&lt;/strong&gt;：面对不同来源不同功能点的大量报文，摒弃臃肿的 &lt;code&gt;switch-case&lt;/code&gt;，转而使用字典映射或依赖注入进行策略分发。针对每一种 TLV 的 Type 提供独立的 Handler 解析。&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;IProgress&amp;lt;T&amp;gt; (沟通桥梁)&lt;/strong&gt;：在后台服务线程向 UI 线程（如 WPF / WinForm）报告状态与解析结果时，严禁直接持有 UI 元素的引用。使用 &lt;code&gt;System.IProgress&amp;lt;T&amp;gt;&lt;/code&gt; 标准化且安全地实现后台数据向前台的稳定投递。&lt;/li&gt;
&lt;/ul&gt;
&lt;h3&gt;15.1 五维架构核心骨架实现&lt;/h3&gt;
&lt;p&gt;以下是一套融合了上述理念的独立组件代码演示。它展示了如何优雅地分离网络接入、协议解析、路由分发与界面通知：&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;using System;
using System.Buffers;
using System.Collections.Generic;
using System.Net.Sockets;
using System.Threading;
using System.Threading.Tasks;

// ---------- [维度 6: 生命周期] CancellationToken 终止令牌 ----------
// 任何合格的异步后端体系都必须具备全链路强制取消能力。
// 结合 CancellationToken，可以在宿主端点停止服务或用户点击“断开”时，立刻截停并释放底层的 ReceiveAsync 原生等待线程。

// ---------- [维度 5: 沟通桥梁] IProgress 契约 ----------
public class NetworkMessage
{
    public string ClientId { get; set; }
    public string Content { get; set; }
}

// ---------- [维度 4: 组织架构] Router 与 Handler ----------
public interface IMessageHandler
{
    void Handle(ReadOnlySpan&amp;lt;byte&amp;gt; payload, IProgress&amp;lt;NetworkMessage&amp;gt; progress);
}

public class TemperatureHandler : IMessageHandler
{
    public void Handle(ReadOnlySpan&amp;lt;byte&amp;gt; payload, IProgress&amp;lt;NetworkMessage&amp;gt; progress)
    {
        // 假设前两个字节代表业务数值
        short temp = BitConverter.ToInt16(payload.ToArray(), 0); // 注意：此处在 .NET Core 可直接传 Span
        progress?.Report(new NetworkMessage { Content = $&quot;温度更新: {temp} ℃&quot; });
    }
}

public class MessageRouter
{
    private readonly Dictionary&amp;lt;short, IMessageHandler&amp;gt; _routes = new();

    public void Register(short messageType, IMessageHandler handler) =&amp;gt; _routes[messageType] = handler;

    public void Dispatch(short messageType, ReadOnlySpan&amp;lt;byte&amp;gt; payload, IProgress&amp;lt;NetworkMessage&amp;gt; progress)
    {
        if (_routes.TryGetValue(messageType, out var handler))
        {
            handler.Handle(payload, progress);
        }
        else
        {
            progress?.Report(new NetworkMessage { Content = $&quot;未知的指令类型: {messageType}&quot; });
        }
    }
}

// ---------- [维度 1, 2, 3: 链路、契约、后勤] 核心接收器 ----------
public class AdvancedSocketServer
{
    private readonly MessageRouter _router;
    private readonly IProgress&amp;lt;NetworkMessage&amp;gt; _progress;

    public AdvancedSocketServer(MessageRouter router, IProgress&amp;lt;NetworkMessage&amp;gt; progress)
    {
        _router = router;
        _progress = progress;
    }

    public async Task ProcessClientAsync(Socket client, CancellationToken cancellationToken)
    {
        // TLV 契约：2 字节 Type + 4 字节 Length
        const int HEADER_SIZE = 6; 
        byte[] headerBuffer = ArrayPool&amp;lt;byte&amp;gt;.Shared.Rent(HEADER_SIZE);

        try
        {
            while (true)
            {
                // 1. 物理链路读取头部，通过传入 CancellationToken，随时响应外部的强制下线中断
                int headerRead = await ReadExactAsync(client, headerBuffer, HEADER_SIZE, cancellationToken);
                if (headerRead == 0) break; // 客户端断开

                Span&amp;lt;byte&amp;gt; headerSpan = headerBuffer.AsSpan(0, HEADER_SIZE);
                short msgType = BitConverter.ToInt16(headerSpan.Slice(0, 2).ToArray(), 0);
                int payloadLength = BitConverter.ToInt32(headerSpan.Slice(2, 4).ToArray(), 0);

                // 2. 极致后勤借用负载载体
                byte[] bodyBuffer = ArrayPool&amp;lt;byte&amp;gt;.Shared.Rent(payloadLength);
                try
                {
                    int bodyRead = await ReadExactAsync(client, bodyBuffer, payloadLength, cancellationToken);
                    if (bodyRead == 0) break; 

                    ReadOnlySpan&amp;lt;byte&amp;gt; bodySpan = bodyBuffer.AsSpan(0, payloadLength);

                    // 3. 经过 Router 分发给对应的 Handler 进行业务隔离处理
                    _router.Dispatch(msgType, bodySpan, _progress);
                }
                finally
                {
                    ArrayPool&amp;lt;byte&amp;gt;.Shared.Return(bodyBuffer);
                }
            }
        }
        catch (OperationCanceledException)
        {
            _progress?.Report(new NetworkMessage { Content = &quot;服务宿主已主动申请中止该通信链路。&quot; });
        }
        catch (Exception ex)
        {
            _progress?.Report(new NetworkMessage { Content = $&quot;连接异常: {ex.Message}&quot; });
        }
        finally
        {
            ArrayPool&amp;lt;byte&amp;gt;.Shared.Return(headerBuffer);
            client.Close();
        }
    }

    // 严谨的长度读取助手，支持 Cancellation 取消令牌防阻塞
    private async Task&amp;lt;int&amp;gt; ReadExactAsync(Socket socket, byte[] buffer, int size, CancellationToken token)
    {
        int totalRead = 0;
        while (totalRead &amp;lt; size)
        {
            // 在现代 .NET 中，利用 Memory&amp;lt;byte&amp;gt; 才能完整激活传入 CancellationToken 的原生支持
            int r = await socket.ReceiveAsync(buffer.AsMemory(totalRead, size - totalRead), SocketFlags.None, token);
            if (r == 0) return 0;
            totalRead += r;
        }
        return totalRead;
    }
}
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;在 UI 层（如 WPF 的 &lt;code&gt;MainWindow.xaml.cs&lt;/code&gt;）组装调用这个结构时，只需声明：&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;// 1. 挂载路由规则与前台跨线程通信更新钩子
var progress = new Progress&amp;lt;NetworkMessage&amp;gt;(msg =&amp;gt; textBlock.Text = msg.Content);
var router = new MessageRouter();
router.Register(0x01, new TemperatureHandler());

var server = new AdvancedSocketServer(router, progress);

// 2. 派发专门的取消掌控源
CancellationTokenSource _cts = new CancellationTokenSource();

// 用户点击连接（或接收到新客户端）时，启动后台挂载并丢入 Cancellation 凭证
_ = Task.Run(() =&amp;gt; server.ProcessClientAsync(clientSocket, _cts.Token));

// 用户点击关闭连接大红按钮时，底层处于挂起等待的 ReceiveAsync 瞬间解锁并抛出中止异常！
_cts.Cancel();
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;通过这种结合 &lt;code&gt;CancellationToken&lt;/code&gt; 取消源的完全解耦设计，底层网络工程师只管收发分包，架构师负责维护路由规则，而前端开发者只通过触发 &lt;code&gt;Cancel()&lt;/code&gt; 控制生杀大权、通过 &lt;code&gt;IProgress&lt;/code&gt; 安全地更新控件，共同达成高效、稳定的企业级协作。&lt;/p&gt;
&lt;hr /&gt;
&lt;h2&gt;16. C# 网络通信深入：直连设备与网关转发实战&lt;/h2&gt;
&lt;p&gt;在掌握了底层的网络框架结构后，对于工业设备的网络通信，通常需要区分设备本身的硬件联网能力。这主要可以分为以下两种核心拓扑场景：&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;strong&gt;场景A：设备支持直连网络（当成服务器）&lt;/strong&gt;。此时设备自身拥有带独立 IP 的网卡，它本身就运行着一个 TCP 服务端。我们的软件不需要中转，直接与其所在 IP 及对应端口产生交互。&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;场景B：设备作为被代理客户端（借由网关转发）&lt;/strong&gt;。设备自身极其简陋，仅具备 RS-485 物理串口，需要将设备的串口引线接驳到具备以太网能力的代理服务器（即&lt;strong&gt;串口转网口器&lt;/strong&gt;，常被称为 DTU 网关）。我们的 C# 客户端只能与该网关的虚拟化 IP 通信，网关随后将电平数据重构转移至目标设备。&lt;/li&gt;
&lt;/ul&gt;
&lt;hr /&gt;
&lt;h3&gt;16.1 场景 A：无中转的 Socket 直连设备交互&lt;/h3&gt;
&lt;p&gt;在这个情境下，我们的 Socket 客户端把请求报文直接发送给指定网络设备的 TCP 被暴露接口。由于整个信道是双工自由的，发送指令完成后，代码必须自行开辟异步轮询任务等待设备的应答报文回传。&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;&lt;strong&gt;⚠️ 核心重点区分：Modbus TCP 报文与 Modbus RTU 报文结构&lt;/strong&gt;&lt;br /&gt;
虽然同样是读写设备的寄存器，一旦走网络流直连协议，就不能随意沿用串口时代的组包规则：&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;原本最长尾部的 &lt;strong&gt;2 字节 CRC16 校验码&lt;/strong&gt;，在 Modbus TCP 中已被完全抛弃剥离。（因为 TCP 底层的链路容错报文重传功能本身能够确保不破损）。&lt;/li&gt;
&lt;li&gt;原本在串口 RTU 只有单字节从站 ID (&lt;code&gt;0x01&lt;/code&gt;) 的报头位置，取而代之的是长达 &lt;strong&gt;6 字节的 MBAP (Modbus Application Protocol) 头部&lt;/strong&gt;。
MBAP 必须精确包含：&lt;code&gt;事务处理标识符(2 Bytes) + 协议标识符(2 Bytes，常规设为 0x0000) + 长度(2 Bytes，指明从单元标识符往后的整体字节数)&lt;/code&gt; 拼接待发数据帧。&lt;/li&gt;
&lt;/ol&gt;
&lt;/blockquote&gt;
&lt;h4&gt;实战演练 1：纯源生 Socket 向直连设备发报&lt;/h4&gt;
&lt;p&gt;当我们尝试自行维护连接与报文解析，并规避第三方封装库时的原生请求流程：&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;using System.Net.Sockets;
using System.Threading.Tasks;

public async Task StraightConnectAndFetchAsync()
{
    using TcpClient client = new TcpClient();
    await client.ConnectAsync(&quot;192.168.1.50&quot;, 502); 
    using NetworkStream stream = client.GetStream();

    // 构建一个完整的 Modbus TCP 读取线圈指令 (请求 0 号设备，读取功能码 0x01 的开关数，读3个引脚) 
    // 组成分析：[ 00 01 (自增事务ID) ] [ 00 00 (强制规定TCP为0) ] [ 00 06 (往后跟6个字节) ] 
    //            + [ 00 (站号) ] [ 01 (功能码) ] [ 00 00 00 03 (起始位+读取数量) ]
    byte[] tcpFrameRequest = { 0x00, 0x01, 0x00, 0x00, 0x00, 0x06, 0x00, 0x01, 0x00, 0x00, 0x00, 0x03 };
    
    // 1. 发射请求给设备
    await stream.WriteAsync(tcpFrameRequest, 0, tcpFrameRequest.Length);
    
    // 2. 独立异步留待接收设备的应答报文 
    // 注意实际由于网络延迟，读到的包可能会被分割，需参照上文五维体系架构进行切分与解析。
    byte[] responseBuffer = new byte[1024];
    int bytesRead = await stream.ReadAsync(responseBuffer, 0, responseBuffer.Length);
}
&lt;/code&gt;&lt;/pre&gt;
&lt;h4&gt;实战演练 2：利用 NModbus4 极简网络连接&lt;/h4&gt;
&lt;p&gt;利用前文介绍的 &lt;code&gt;NModbus4&lt;/code&gt; 时我们讲解了串口（RTU）调用，面对直连网络硬件，你可以直接切用它的 &lt;code&gt;ModbusIpMaster&lt;/code&gt; 工厂对象：&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;using Modbus.Device;
using System.Net.Sockets;
using System.Threading.Tasks;

public async Task RequestDeviceViaModbusIpAsync()
{
    using TcpClient client = new TcpClient(&quot;192.168.1.50&quot;, 502);
    // 引擎更迭：直接产生用于网口传输且拥有自动构建 MBAP 头部序列化特性的网络端
    IModbusMaster ipMaster = ModbusIpMaster.CreateIp(client);
    
    // 底层的重试机制、MBAP组装与TCP包防波浪校验，均已被框架静默抽象。
    ushort[] holdingRegisters = await ipMaster.ReadHoldingRegistersAsync(1, 100, 5); 
}
&lt;/code&gt;&lt;/pre&gt;
&lt;hr /&gt;
&lt;h3&gt;16.2 场景 B：基于服务器转发的中继网关架构&lt;/h3&gt;
&lt;p&gt;大部分传统现场传感器和执行器通常没有独立的网卡配置，我们依靠 485 总线串接它们至工业透传服务器（DTU网络转化）的硬件线路上。&lt;/p&gt;
&lt;p&gt;这种**“串口转网口”服务器**主要承担的职责流程如下：&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;&lt;strong&gt;客户端网口请求汇聚&lt;/strong&gt;：接收从远端 Socket 客户端（或上位机平台网络中心）下发的网络封包含。&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;协议合并提取落盘&lt;/strong&gt;：收到请求包后，执行电控逻辑转换工作，通过底层的 RS-485 串行母排发射端直接将信号向下驱动至连接的硬件串列网络里。&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;设备应答回收（串口上提）&lt;/strong&gt;：传感器设备接收通信特征字后按需返回应答，最底层信号由串口返回到该转换服务器的串行接受母排。&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;服务器解包回传（网络响应）&lt;/strong&gt;：服务器内部重装数据信息组帧为合法的 TCP 帧态，反弹发回给之前请求对应地址来源的 &lt;code&gt;Socket&lt;/code&gt; 网络客户端。&lt;/li&gt;
&lt;/ol&gt;
&lt;p&gt;这种架构下，开发者需要意识到代码中除了计算基本的网络信道延时，还必须叠加串联“转换传输周期与底层设备轮询的时间差”，因此对容错及 &lt;code&gt;ReadTimeout&lt;/code&gt; 的阈值容忍判定需适当设置拉宽跨度容忍间隙防除异常切断。&lt;/p&gt;
&lt;h4&gt;业务流程深度拆解：远端跨地域控制温湿度传感器&lt;/h4&gt;
&lt;p&gt;为了更直观地理解此项“服务器代转发”逻辑（ServerSide+ClientSide 的结合），我们设定一个场景：&lt;strong&gt;“在大连的办公室，通过软件读取位于上海工厂车间里的一台温湿度传感器”&lt;/strong&gt;。&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;1. 准备阶段（角色架构）&lt;/strong&gt;&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;strong&gt;边缘设备（下位机）&lt;/strong&gt;：一台温湿度计，它只有 RS-485 物理串口，完全没有网络接驳口。&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;中继网关（Server端）&lt;/strong&gt;：上海工厂里的一台工控机（或称串口转网口服务器）。它通过串口线直连着传感器，同时该板卡连接了工厂的本地外环以太网。&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;控制终端（Client端）&lt;/strong&gt;：你在大连办公室操作的笔记本电脑。&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;&lt;strong&gt;2. 实际操作流转流程&lt;/strong&gt;&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;
&lt;p&gt;&lt;strong&gt;第一步：构建通信桥梁&lt;/strong&gt;&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;strong&gt;动作描述&lt;/strong&gt;：上海工厂的工控机（网关）启动服务程序。其形同“翻译官”，左手接管对传感器的本地轮询（挂接串口），右手对外开启 TCP 连接通道（如侦听 9999 端口）。&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;代码体现&lt;/strong&gt;：执行 &lt;code&gt;serialPort1.Open()&lt;/code&gt; 使硬件接收中断就绪；并执行如 &lt;code&gt;serverSocket.Bind&lt;/code&gt; 及 &lt;code&gt;Listen&lt;/code&gt; 的指令开启网络等待。&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;&lt;strong&gt;第二步：大连端远程接入&lt;/strong&gt;&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;strong&gt;动作描述&lt;/strong&gt;：你在大连的客户端界面上输入上海工厂对外暴露的网络 IP 和端口，点击“连接”。大连的电脑通过互联网直接锁定了上海的工控机。&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;代码体现&lt;/strong&gt;：运行 &lt;code&gt;client.ConnectAsync&lt;/code&gt;，完成标准 TCP 三次握手。&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;&lt;strong&gt;第三步：发出获取指令（下行请求向串口转发）&lt;/strong&gt;&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;strong&gt;动作描述&lt;/strong&gt;：在大连，你想获取当前温度并点击了“读取”。此刻你的电脑直接发出 8 字节的源生指令规范（如：&lt;code&gt;01 03 00 00 00 04 44 69&lt;/code&gt;，意为查询 1号机的 4段参数）。&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;网关动作&lt;/strong&gt;：上海的网关服务接到这个网络包后不对它进行逻辑处理，直接将这串字节通过底层的发送端口“抛”下给 RS-485 线路上捆绑的传感器。&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;代码体现&lt;/strong&gt;：大连端调用 &lt;code&gt;SendAsync&lt;/code&gt; 或 &lt;code&gt;SocketHelper.SendData&lt;/code&gt; 注入流；上海端 &lt;code&gt;ReceiveData&lt;/code&gt; 抽取出 byte 数组后调用 &lt;code&gt;SerialPort.Write&lt;/code&gt;。&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;&lt;strong&gt;第四步：传感器数据回传（上行动力响应转网络广播）&lt;/strong&gt;&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;strong&gt;动作描述&lt;/strong&gt;：温湿度传感器识别到自己被指令唤醒，沿线反馈了一组流（如吐出：&lt;code&gt;01 03 08 00 19 ...&lt;/code&gt; 其中关键的 &lt;code&gt;00 19&lt;/code&gt; 意为温度25度）。&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;网关动作&lt;/strong&gt;：上海网关的串口检测器识别电平并抓取到这串原始字节流，此时服务器把它们封装回 TCP 包中，顺着通讯队列向大连终端回传发回。&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;代码体现&lt;/strong&gt;：上海工控机触发 &lt;code&gt;serialPort1_DataReceived&lt;/code&gt; 数据被动接收事件，并在事件中提取全节点会话合集，通过 &lt;code&gt;foreach&lt;/code&gt; 循环下放执行发送并原封不动沿网络投递。&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;&lt;strong&gt;第五步：终端应用提取结果&lt;/strong&gt;&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;strong&gt;动作描述&lt;/strong&gt;：远在大连的笔记本网络端收到了以太包裹，从中提取有效片段计算值（例如 &lt;code&gt;buffer[3] * 256 + buffer[4]&lt;/code&gt; 算出 25 ），最终在 UI 文本框刷新出 “当前室温：25℃”。&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;代码体现&lt;/strong&gt;：如本文 16.3 节利用 &lt;code&gt;BitConverter&lt;/code&gt; 组装校验换算，最终调用 &lt;code&gt;Invoke&lt;/code&gt; 回主线程进行画面安全刷新展示。&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;&lt;strong&gt;3. 核心机制价值总结&lt;/strong&gt;
这一实例揭示了工业基建的一个核心底层优势：通过代转机制，本不具备上云素质的盲设备完成了&lt;strong&gt;远程远端接入&lt;/strong&gt;。此外，它的拓展价值在于“多点分发（广播）”—— 如果此时北京、大连、上海的三位操作员同时连入了这台服务器工控机，只要任何一人发起了上述第三步获取命令请求，由于服务器在底层采用多向广播（&lt;code&gt;foreach&lt;/code&gt; 下发给所有建立的 &lt;code&gt;Socket&lt;/code&gt;），这三个地方便能同频率刷新展示这一变动，彻底衔接了 TCP 网口和串口层级差异。&lt;/p&gt;
&lt;hr /&gt;
&lt;h3&gt;16.3 实战演练：空气质量变送器数据采集机制与大小端处理&lt;/h3&gt;
&lt;p&gt;根据底层物理类型转换体系，我们需要关注网络环境中结合实际环境监控如空气质量及温湿度采集的重构工作。&lt;/p&gt;
&lt;p&gt;部分 Modbus 反馈模型中的一个独立状态参数往往使用占用两字节跨度容量的 &lt;code&gt;short&lt;/code&gt; / &lt;code&gt;ushort&lt;/code&gt; 返回组建。同时结合&lt;a href=&quot;https://blog.csdn.net/chongjian1990/article/details/149027534&quot;&gt;计算机大端和小端 (Endianness) 结构特征&lt;/a&gt;，针对由 &lt;code&gt;byte[]&lt;/code&gt; 体系转化而回的值则需要代码执行更为紧凑的策略应用：&lt;/p&gt;
&lt;h4&gt;1. 底层存储类型简要分析&lt;/h4&gt;
&lt;p&gt;可参阅 &lt;a href=&quot;https://blog.csdn.net/qq_59062726/article/details/136805567&quot;&gt;C# 中整型的互换原语机制特性&lt;/a&gt; 了解详细的隐性装箱细节。&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;strong&gt;&lt;code&gt;byte&lt;/code&gt;&lt;/strong&gt;：占据基础的 8 个二进制位长度。&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;&lt;code&gt;short&lt;/code&gt; (带符号位的16位数)&lt;/strong&gt; 与 &lt;strong&gt;&lt;code&gt;ushort&lt;/code&gt; (全正数值的无符号16位数)&lt;/strong&gt;：占用对应 2 个基本字节位，此类跨度正好是仪器存储基础传感输出最常见配置标准位。&lt;/li&gt;
&lt;/ul&gt;
&lt;h4&gt;2. C#获取流并转换解析机制展示：&lt;/h4&gt;
&lt;p&gt;假设我们已经通过 Socket 环境读取取得了传感设备响应流装入包含真实寄存器参数的两段 &lt;code&gt;byte&lt;/code&gt;。由于大部分仪器报文传输遵守 &lt;strong&gt;网络通讯大端模式 (Big-Endian)&lt;/strong&gt; 优先出栈，而我们上位机常用的 x86 或 x64 CPU 的默认装载是计算用的低优先级位先处理的小端流架构 (Little-Endian)。因此我们不可直接执行直接的数据转类型：&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;using System;

public class EnvironmentSensorParser
{
    // 获取从机 TCP/串口 转换穿透回传的物理原始温度流，比如由两个字节构成的连续位偏移位置信息
    public float ParseTemperaturePayload(byte[] rawPayload, int startOffset)
    {
        // 定制切片数组阶段：从给定切面提取装载信息的物理参数承载（占用2个字节位）
        byte[] tempSlice = { rawPayload[startOffset], rawPayload[startOffset + 1] };
        
        // 【防御卡点】：检验操作环境架构是否需要换向执行，防错乱读取！
        // 若当前宿主系统位权序列符合计算低阶端为头流结构的小端模式情况：
        if (BitConverter.IsLittleEndian)
        {
            // 通过环境判定后调用基础底层函数将 硬件设备网络流通发来的 高位先行倒排序转换为正确计算架构
            Array.Reverse(tempSlice); 
        }

        // 纠正执行整合还原：利用 BitConverter 将安全的端序队列还原构建回原生 16 位整型的内存格式数据
        ushort numericValue = BitConverter.ToUInt16(tempSlice, 0);

        // 工业传感器通常带有数值转换刻度补偿值，这往往是传递缩小系数放大值反馈（例如真实状况 28.5摄氏度，而上报流发出的则是 285）。
        return numericValue / 10f;
    }
}
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;针对以上严谨组合步骤处理，便可规避常见的网络报文错乱现象与硬件传输偏移计算故障，并最终稳固打造包含无论是系统直签直连协议还是透过物理透转中介站模式的稳固工业端业务层架构设计。&lt;/p&gt;
&lt;h3&gt;16.4 工业级网关核心防重载架构：数据映射缓存式 (Polling DataStore) 中转机制&lt;/h3&gt;
&lt;p&gt;在 &lt;strong&gt;16.2 节&lt;/strong&gt;中提及的这种“网关代发透传”结构，通常只适合请求量较小的简单场景。但一旦遇到&lt;strong&gt;高并发、高频极速调度的严苛网络挑战&lt;/strong&gt;，传统透传中转就会面临崩溃：&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;致命瓶颈：为什么“单纯透明透传”会走向死胡同？&lt;/strong&gt;&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;strong&gt;串口物理单行道&lt;/strong&gt;：底层 RS-485 乃至 232 本质是半双工信道，必须排队“一发一收”，绝不容许并行干扰。&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;高并发网络请求轰炸&lt;/strong&gt;：假设局域网内有 10 个以上客户端正以极高频（例如 100ms 一次）向“通讯机/网关”索取现场数据，如果网关“接到请求马上透传给串口”，瞬间如洪水般的指令会被挤占向速率极慢（如 9600 bps）的底层导线中。一方面会导致队列深卡甚至超时闪退，另一方面极高频的回响探测很可能直接把脆弱的下位机单片机打到死机瘫痪！&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;&lt;strong&gt;完美救赎：建立“中转站主动轮询”与“内存池缓存 (DataStore) 隔离”设计&lt;/strong&gt;
这正是成熟工业网关广泛采用的核心设计哲学：让网关中转服务成为拥有强大护城河的&lt;strong&gt;内存集散列阵&lt;/strong&gt;！&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;&lt;strong&gt;下端平稳轮询 (Polling)&lt;/strong&gt;：
网关启动一条专属的后台循环调度线，以&lt;strong&gt;极其稳健、有序的长频节奏&lt;/strong&gt;（譬如安全地每 1000 毫秒才去底层串口巡查拉取一次各项核心机器参数），并在成功返回后，将被更新拉回来的最纯净数据存装到网关自身系统所驻留的大内存 &lt;code&gt;DataStore&lt;/code&gt; 缓存池阵列里不断去覆盖陈旧的数值。&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;上端即读即回 (极速并发)&lt;/strong&gt;：
此时无论是处于网段外头的 1 个节点网络查询器，还是成千上百个正在同时通过 TCP(或UDP) 向网关砸来探读指令池的客户端群，网关接到这些网络封包时，&lt;strong&gt;根本无需再次下发触发底层那慢如牛车的串口查询指令流&lt;/strong&gt;。它们需要的只是参数而已，而这些参数已经摆在极速运作的电子内存上。网关能在短短十来微秒的时间刻度内瞬间命中结果立刻反向群发馈送回应给索取端。高频网络压力在这招之下瞬间被消弭化解于无形。&lt;/li&gt;
&lt;/ol&gt;
&lt;blockquote&gt;
&lt;p&gt;&lt;strong&gt;🌟 在 NModbus4 架构体系内的极速应用实现&lt;/strong&gt;：
要借助您自建的机制实现此效果，你可以完美利用库中赐予的绝佳原生基础件 &lt;code&gt;DataStore&lt;/code&gt;。
我们只需要启调一条 &lt;code&gt;Task.Run()&lt;/code&gt; 使用 &lt;code&gt;ModbusSerialMaster&lt;/code&gt; 做死循环每间隔 1 秒拉取赋值于它：&lt;code&gt;_dataStore.HoldingRegisters[x] = 底层新值&lt;/code&gt;。
再在外侧面对大量索求网络的开放端暴露出 &lt;code&gt;ModbusTcpSlave&lt;/code&gt; / &lt;code&gt;ModbusUdpSlave&lt;/code&gt; 服务，只需将我们那个刚刚被不断刷洗保活值的 &lt;code&gt;_dataStore&lt;/code&gt; 对象属性直接对接依附在两者身上作为共享数据驱动池。此等操作，高压的并发重流危机也便自此不复存在！&lt;/p&gt;
&lt;/blockquote&gt;
&lt;h4&gt;核心疑问剖析：既然中转站已经在实时轮询了，为什么客户端还要开个定时器去“自动读取”？&lt;/h4&gt;
&lt;p&gt;很多初学者在这里会产生疑惑：“既然网关已经在后台玩命一样拼命刷新数据了，客户端难道不能躺平直接等数据推过来吗？”
答案是：&lt;strong&gt;不能。因为 Modbus 协议天生不支持“主动推送”！&lt;/strong&gt;&lt;/p&gt;
&lt;p&gt;这其实是由两种截然不同的架构决定的：&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;&lt;strong&gt;中转站的实时读取（为了保证数据的“保鲜”）&lt;/strong&gt;：中转站的轮询是为了将底层串口数据抓上来，并替换掉自己内存 (&lt;code&gt;DataStore&lt;/code&gt;) 里的旧值。如果不做实时抓取，客户端任何时候来打听，网关给的永远是一小时前的发霉旧参数。&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;客户端的实时读取（为了刷新前端 UI 画面）&lt;/strong&gt;：Modbus TCP 骨子里依旧是个**“问答机制 (Request-Response)”**协议。它不是 WebSocket，也不是 MQTT订阅发布机制，工业设备永远高傲且沉默。中转站（Slave服务端）绝对不会主动向客户端（Master端）发击网络包。客户端要想让自己的 UI 界面心跳动起来，必须自己开启一个定时器，不断向中转站发出探求包。
&lt;em&gt;不同之处在于，此时客户端查询的是极为极速的网关内存，而不是慢吞吞的硬件串口，因此哪怕每一毫秒查询一次整个流程都是零卡顿！&lt;/em&gt;&lt;/li&gt;
&lt;/ol&gt;
&lt;h4&gt;网关缓存式中转极简代码原型 (C#)&lt;/h4&gt;
&lt;p&gt;利用 &lt;code&gt;NModbus4&lt;/code&gt; 实装以上神级架构的核心剥离代码如下，可以直接用于各位工程师搭建属于自己的 DTU 中控平台架构：&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;using Modbus.Data;
using Modbus.Device;
using System.IO.Ports;
using System.Net;
using System.Net.Sockets;
using System.Threading.Tasks;

public class GatewayManager
{
    private DataStore _dataStore;
    private ModbusSlave _tcpSlave;

    public void StartGateway()
    {
        // 1. 创建全军最高级别的数据中转枢纽 (内存数据库)
        _dataStore = DataStoreFactory.CreateDefaultDataStore();

        // 2. 开启网络大门 (TCP 被动对外接客)
        TcpListener tcpListener = new TcpListener(IPAddress.Any, 502);
        
        // 重要防坑：防止死点残留导致强制重启报错 &quot;一个端口只能用一次 (AddressAlreadyInUse)&quot;
        tcpListener.Server.SetSocketOption(SocketOptionLevel.Socket, SocketOptionName.ReuseAddress, true);
        
        _tcpSlave = ModbusTcpSlave.CreateTcp(slaveId: 1, tcpListener);
        _tcpSlave.DataStore = _dataStore; // 【核心】：将内存池绑给接客网络服务！
        
        // 开始异步运行网络监听，脱离主线程
        Task.Run(() =&amp;gt; _tcpSlave.Listen());

        // 3. 启动绝密刺客任务去底层抓取数据（这里以单发轮询示范）
        StartHardwarePollingTask();
    }

    private void StartHardwarePollingTask()
    {
        Task.Run(async () =&amp;gt;
        {
            using SerialPort port = new SerialPort(&quot;COM1&quot;, 9600, Parity.None, 8, StopBits.One);
            port.Open();
            
            IModbusSerialMaster hardwareMaster = ModbusSerialMaster.CreateRtu(port);
            hardwareMaster.Transport.ReadTimeout = 1000;

            while (true) // 永动机
            {
                try
                {
                    // [主站身份]：在底层的土路上向真实的下级机器要起步从 0 跨度长度为 10 的寄存器参数值
                    ushort[] rawData = hardwareMaster.ReadHoldingRegisters(1, 0, 10);
                    
                    // 将热乎的新鲜数据强行覆盖抹入系统的网关大内存池内
                    for (int i = 0; i &amp;lt; rawData.Length; i++)
                    {
                        // NModbus4 底层设计约定 DataStore 内部位图下标默认以 1 开始算 (因此+1)
                        _dataStore.HoldingRegisters[0 + i + 1] = rawData[i];
                    }
                }
                catch { /* 屏蔽因为串口偶发掉线等硬物理干扰导致的系统假死闪退故障 */ }

                // 强制要求喘息，以防把慢速物理导线给打满打卡死！
                await Task.Delay(1000); 
            }
        });
    }
}
&lt;/code&gt;&lt;/pre&gt;
&lt;h3&gt;16.5 工业物联网网关转发深化：全透传、协议透传与 DataStore 架构的对比&lt;/h3&gt;
&lt;p&gt;在 DTU（串行转网络）网关程序的演进路径中，针对如何处理“外部网络请求”与“内部底层硬件交互”的矛盾，开发中通常存在三种层层递进的架构模型：全透传、协议透传以及基于 DataStore 的缓存镜像池架构。&lt;/p&gt;
&lt;h4&gt;1. 全透传 (Full Transparent Passthrough)&lt;/h4&gt;
&lt;p&gt;&lt;strong&gt;定义与机制&lt;/strong&gt;：
网关完全不干涉双向通信的数据内容、协议规范以及寻址逻辑。它纯粹担任一条“物理网络延长线”。网络接口收到什么字节流，就原封不动地通过串口发送；串口读到什么报文，也丝毫不变地直接通过网络流推给客户端。
&lt;strong&gt;实现方式&lt;/strong&gt;：
在 &lt;code&gt;Socket.Receive()&lt;/code&gt; 触发获取到 Byte 数组时，同步调用底层 &lt;code&gt;SerialPort.Write()&lt;/code&gt;。反之同理。
&lt;strong&gt;适用场景&lt;/strong&gt;：
多用于私有定制协议、极简指令集、或测试阶段。
&lt;strong&gt;核心缺陷&lt;/strong&gt;：
如果局域网内存在多个客户端同时向该网关发起访问，相互交错的数据包会在底层串行线路上瞬间引发“粘包”或“错序”物理冲突，完全无法应对高并发需求。&lt;/p&gt;
&lt;h4&gt;2. 协议透传 (Protocol-Aware Passthrough)&lt;/h4&gt;
&lt;p&gt;&lt;strong&gt;定义与机制&lt;/strong&gt;：
网关充当了协议翻译路由器系统，它深度参与报文协议层面的解析重组工作以弥合媒介特征差异。业界最典型的应用为 &lt;strong&gt;Modbus TCP 到 Modbus RTU 的实时互译&lt;/strong&gt;：&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;&lt;strong&gt;数据剥离&lt;/strong&gt;：网关截获客户端发来的 TCP 请求报文，去除 TCP 框架独有、在串行总线中无意义的 MBAP 首部报头（6 字节）。&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;重组下送&lt;/strong&gt;：提取出最核心的功能代码段，并在其尾端依据算法重新计算拼接出 Modbus RTU 强制依赖的 2 字节 CRC16 硬件差错检验码，随后发往底层的 RS-485 线缆。&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;逆向复原&lt;/strong&gt;：串口在捕获到下位机的正确回复后，二次去除设备返回包携带的 CRC16 标识并重组回 MBAP 网络报头，转换为合法的 TCP 回执对象发往客户端。
&lt;strong&gt;核心缺陷&lt;/strong&gt;：
此模式虽然解决了跨类型协议兼容问题，但极度依赖底层线路速率。它本质上仍将外部高频并发流量原样穿透到了硬件总线上。当面对外部大规模拉取阵列时，低速末端设备极易因处理超时而引发数据链假死。&lt;/li&gt;
&lt;/ol&gt;
&lt;h4&gt;3. 缓存映射透传：DataStore 隔离模式下的网关防阻断架构&lt;/h4&gt;
&lt;p&gt;&lt;strong&gt;定义与核心机制&lt;/strong&gt;：
这正是 &lt;strong&gt;16.4 节代码展示的核心设计真谛&lt;/strong&gt;。它彻底切断外网流量和内网慢速硬件的强同步耦合关系，基于本地高速内存的数据调度隔离池——&lt;code&gt;DataStore&lt;/code&gt; 实现了多向削峰：&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;strong&gt;向内轮询（主站身份主动集采）&lt;/strong&gt;：网关摆脱由于外部请求带来的被动触发窘境，在自身系统内设立独立的后台任务（例如设定恒定位 1000ms 周期执行），凭借 &lt;code&gt;ModbusSerialMaster&lt;/code&gt; 向底层硬件发出参数查询请求。在获得正确参数后，并不回馈给任何外部对象，而是直接将最新数值覆盖写入网关系统自身维护的实例变量 &lt;code&gt;DataStore&lt;/code&gt;。&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;对外分发（从站响应缓冲池）&lt;/strong&gt;：面对一切外部 TCP/UDP 网络访问指令，程序暴露出 &lt;code&gt;ModbusTcpSlave&lt;/code&gt; 服务端对象，并进行关键业务赋予：&lt;code&gt;_slave.DataStore = _dataStore&lt;/code&gt;（将主站不断洗刷的实时数据库赋值关联给系统网络服务器节点）。
&lt;strong&gt;核心工程价值&lt;/strong&gt;：
依托这一整合重排策略，庞大的外网 TCP 频繁调度压力仅会触达处于内存层面的网关 &lt;code&gt;DataStore&lt;/code&gt; 并被瞬间化解返回。最底端的串行硬件通讯总线则得到了完美的硬件屏障保护，处于绝对的安全稳固轮询节奏之中。这也即是实现百万级可靠企业数据中台网关运转的基石设计模型。&lt;/li&gt;
&lt;/ul&gt;
&lt;hr /&gt;
&lt;h2&gt;17. 附录参考 NModbus4 跨栈驱动 RTU TCP 与 UDP 参数全解&lt;/h2&gt;
&lt;p&gt;在前面对 Modbus 的理论基础与原生机制讲解之后，在实际企业级开发中，通常会使用开源库 &lt;code&gt;NModbus4&lt;/code&gt; 来处理标准协议通信。本章节将系统梳理 &lt;code&gt;NModbus4&lt;/code&gt; 的核心读写参数，并演示如何在同一套代码体系下适配 &lt;strong&gt;串口(RTU)&lt;/strong&gt;、&lt;strong&gt;网口长连(TCP)&lt;/strong&gt;、&lt;strong&gt;网口无连接(UDP)&lt;/strong&gt; 三大物理介质。&lt;/p&gt;
&lt;h3&gt;17.1 四大核心读取与写入 API 参数释义&lt;/h3&gt;
&lt;p&gt;无论是基于串口还是网口，当 &lt;code&gt;IModbusMaster&lt;/code&gt; 接口实例化后，对下位机的读写操作主要依赖几个核心方法。以下是对关键参数的解析：&lt;/p&gt;
&lt;h4&gt;1. 读操作核心函数参数释义&lt;/h4&gt;
&lt;p&gt;以常用的 &lt;code&gt;ReadHoldingRegistersAsync(byte slaveAddress, ushort startAddress, ushort numberOfPoints)&lt;/code&gt; 为例：&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;strong&gt;&lt;code&gt;slaveAddress&lt;/code&gt; (从站设备地址 ID)&lt;/strong&gt;：
&lt;ul&gt;
&lt;li&gt;&lt;strong&gt;取值范围&lt;/strong&gt;：&lt;code&gt;1-247&lt;/code&gt;。&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;作用域&lt;/strong&gt;：在 485 总线轮询或使用“串口转网口(DTU)”多点挂载的场景下，必须精确对应目标硬件节点的从站 ID。&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;特殊情况&lt;/strong&gt;：在纯 TCP/IP 直连软路由环境下（独立网线直连单一设备），设备通过 IP 地址唯一鉴权，其底层的 &lt;code&gt;slaveAddress&lt;/code&gt; 参数通常会被忽略（通常填 &lt;code&gt;1&lt;/code&gt; 即可）。&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;&lt;code&gt;startAddress&lt;/code&gt; (读取起始地址)&lt;/strong&gt;：
&lt;ul&gt;
&lt;li&gt;&lt;strong&gt;取值范围&lt;/strong&gt;：&lt;code&gt;0-65535&lt;/code&gt;。&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;地址偏移注意事项&lt;/strong&gt;：工业设备的说明书中通常采用 PLC 习惯寻址（例如保持寄存器 &lt;code&gt;40001&lt;/code&gt; 代表第一个寄存器）。但是在底层 Modbus 协议通信中，地址索引是从 &lt;code&gt;0&lt;/code&gt; 开始计算的。因此，当文档指示读取 &lt;code&gt;40002&lt;/code&gt; 时，代码中的 &lt;code&gt;startAddress&lt;/code&gt; 参数应填入 &lt;code&gt;1&lt;/code&gt;，不可直接填写 40002，否则会导致非法数据地址异常。&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;&lt;code&gt;numberOfPoints&lt;/code&gt; (预期读取数量/跨度)&lt;/strong&gt;：
&lt;ul&gt;
&lt;li&gt;&lt;strong&gt;协议限制最大值&lt;/strong&gt;：受 Modbus 报文长度限制，单次读取保持寄存器的数量上限通常为 &lt;code&gt;125&lt;/code&gt; 个（250 字节有效载荷），单次读取线圈的状态数量上限通常为 &lt;code&gt;2000&lt;/code&gt; 个。&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;数据拼接需求&lt;/strong&gt;：当处理占据双寄存器的高精度浮点数 (Float) 或长字符串时，需连续读取多个寄存器（跨度填 &lt;code&gt;2&lt;/code&gt; 或 &lt;code&gt;4&lt;/code&gt; 等），随后结合 &lt;code&gt;Span&lt;/code&gt; 和 &lt;code&gt;BitConverter&lt;/code&gt; 进行字节重组还原。&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;h4&gt;2. 写操作核心函数注意项&lt;/h4&gt;
&lt;p&gt;以 &lt;code&gt;WriteSingleRegisterAsync(byte slaveAddress, ushort registerAddress, ushort value)&lt;/code&gt; 为例：&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;strong&gt;&lt;code&gt;value&lt;/code&gt; (下发值的数据类型)&lt;/strong&gt;：该参数接收 &lt;code&gt;ushort&lt;/code&gt; (无符号 16 位整数) 类型。若需要下发带有负数概念的数据（例如设定目标温度为 &lt;code&gt;-5&lt;/code&gt;），不可直接强转。正确的处理方式是利用位运算进行补码转换，例如：&lt;code&gt;unchecked((ushort)-5)&lt;/code&gt;，以确保传递给下位机的底层位值准确无误。&lt;/li&gt;
&lt;/ul&gt;
&lt;hr /&gt;
&lt;h3&gt;17.2 串列总线物理层：Modbus RTU 的构建部署&lt;/h3&gt;
&lt;p&gt;&lt;strong&gt;应用场景&lt;/strong&gt;：直接通过物理串口线缆（如 RS-485 / RS-232）连接工控机与下位机设备。
&lt;strong&gt;核心依赖库&lt;/strong&gt;：依赖 .NET 基础类库 &lt;code&gt;System.IO.Ports.SerialPort&lt;/code&gt;。&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;using Modbus.Device;
using System.IO.Ports;
using System.Threading.Tasks;

public async Task BuildAndInitRtuAsync()
{
    // [步骤一] 配置串口物理通讯参数
    // 参数含义依次为：端口号, 波特率, 奇偶校验, 数据位, 停止位
    using SerialPort port = new SerialPort(&quot;COM3&quot;, 9600, Parity.None, 8, StopBits.One);
    
    // [步骤二] 开启串口
    // 注意：串口为独占资源，如果被其他程序占用会抛出访问被拒异常。
    port.Open();

    // [步骤三] 初始化 NModbus4 主站实例
    // 该库将自动处理目标 SlaveID 包装，并在报文末尾附加 CRC16 校验码。
    IModbusMaster master = ModbusSerialMaster.CreateRtu(port);

    // [参数配置] 设定超时与重试时间
    master.Transport.ReadTimeout = 500;  // 设定读取超时时间为 500ms
    master.Transport.Retries = 2;        // 设定失败重试次数为 2 次以应对偶发信噪干扰
    
    // 发送写入指令：向 1 号从站的 0 地址位写入线圈闭合状态
    await master.WriteSingleCoilAsync(1, 0, true);
}
&lt;/code&gt;&lt;/pre&gt;
&lt;hr /&gt;
&lt;h3&gt;17.3 稳定长连接通道：Modbus TCP 的构建部署&lt;/h3&gt;
&lt;p&gt;&lt;strong&gt;应用场景&lt;/strong&gt;：设备连接在以太网交换机网络中，或通过 DTU 模块映射网域，需维持常态化 TCP 数据流。
&lt;strong&gt;核心依赖库&lt;/strong&gt;：系统自带网络库 &lt;code&gt;System.Net.Sockets.TcpClient&lt;/code&gt;。&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;using Modbus.Device;
using System.Net.Sockets;
using System.Threading.Tasks;

public async Task BuildAndInitTcpAsync()
{
    using TcpClient client = new TcpClient();

    // [步骤一] TCP 握手与连接建立
    // 直接连接离线 IP 会导致默认的较长超时阻塞，建议使用 Task.WhenAny 包装建立明确的超时熔断机制。
    var connectTask = client.ConnectAsync(&quot;192.168.0.100&quot;, 502);
    if (await Task.WhenAny(connectTask, Task.Delay(2000)) != connectTask)
         throw new TimeoutException(&quot;尝试连接目标设备超时。&quot;);

    // [步骤二] 抽象底层连接实例
    // TCP 协议在报文前部添加 6 字节 MBAP 头部，并移除了串口的尾部 CRC 序列。
    IModbusMaster master = ModbusIpMaster.CreateIp(client);
    
    // 配置长连接读写超时预警（避免线程死锁挂起）
    master.Transport.ReadTimeout = 1000;

    // 建议：对于工业监控体系常态化服务，不要在单词操作后立即调用 client.Close()，应由主程长效保有该 client 单例，进行定时任务轮询读取即可防止频繁耗费连接重建资源。
    
    // 对指定地址读请求
    ushort[] val = await master.ReadHoldingRegistersAsync(1, 100, 2);
}
&lt;/code&gt;&lt;/pre&gt;
&lt;hr /&gt;
&lt;h3&gt;17.4 无连接通信架构：Modbus UDP 的构建部署&lt;/h3&gt;
&lt;p&gt;&lt;strong&gt;应用场景&lt;/strong&gt;：适用于局域网内大量节点、容许低概率丢包且对实时性并发吞吐要求极致高的监控情况，避免 TCP 连接确认带来额外负担。
&lt;strong&gt;核心依赖库&lt;/strong&gt;：数据报发送机制 &lt;code&gt;System.Net.Sockets.UdpClient&lt;/code&gt;。&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;using Modbus.Device;
using System.Net.Sockets;
using System.Threading.Tasks;

public async Task BuildAndInitUdpAsync()
{
    // [步骤一] UDP 客户端初始化
    // 实例化 UDP 容器过程本身不产生网络数据交互。
    using UdpClient client = new UdpClient();
    
    // 此处的 Connect 并不执行真实的网络三次握手建立连接，而是锁定投递的默认目标终点站。
    client.Connect(&quot;192.168.0.222&quot;, 502); 

    // [步骤二] 生成 Modbus IP 驱动包装 
    // 发送的数据与 TCP 保持一致结构（含 MBAP 头，无 CRC），仅传递层的传输协议由流变更为数据报模式。
    IModbusMaster master = ModbusIpMaster.CreateIp(client);
    
    // [重要参数配置] 必须设定严格明确的读取超时控制
    // 由于 UDP 本身无法感知连接断开事件，必须通过设置 ReadTimeout 保障服务能在远端设备下线不回包时主动熔断异常。
    master.Transport.ReadTimeout = 300; 

    // 执行寄存器拉取动作
    ushort[] val = await master.ReadHoldingRegistersAsync(1, 200, 10);
}
&lt;/code&gt;&lt;/pre&gt;
&lt;hr /&gt;
&lt;hr /&gt;
&lt;h2&gt;18. 核心原理梳理 三大介质 API 流程与非标设备报文解析&lt;/h2&gt;
&lt;p&gt;在实际工业现场中，部分非标协议设备虽然基于 Modbus 进行通信，但因其内部实现不标准，导致无法直接使用 &lt;code&gt;NModbus4&lt;/code&gt; 等第三方封装库进行自动化解析。此时，需要剥离高级封装，直接操作底层通道 API 并手动构建报文。以下将分别对【串口】、【TCP】、【UDP】三者的数据流转与解析机制进行拆解：&lt;/p&gt;
&lt;h3&gt;18.1 【纯原生串口】API 梳理与 CRC 校验处理&lt;/h3&gt;
&lt;p&gt;在串口通信中，硬件设备直接通过高低电平传输字节流。为保证数据完整性，SerialPort (串口) 发送的数据帧由于缺少底层协议层的校验机制，必须在报文尾部附加两字节的 &lt;strong&gt;CRC16 (循环冗余校验码)&lt;/strong&gt;。&lt;/p&gt;
&lt;h4&gt;1. 底层 API 流转周期&lt;/h4&gt;
&lt;pre&gt;&lt;code&gt;using System.IO.Ports;

// 1. 初始化并打开串口
using SerialPort port = new SerialPort(&quot;COM1&quot;, 9600, Parity.None, 8, StopBits.One);
port.Open();

// 2. 发送请求帧（需包含完整的数据主体与 CRC 校验码）
byte[] rawBytes = { ... }; 
port.Write(rawBytes, 0, rawBytes.Length);

// 3. 通过事件监听接收数据
port.DataReceived += (s, e) =&amp;gt; 
{
    // 获取当前缓冲区内的可用字节数
    int bytesToRead = port.BytesToRead;
    byte[] buffer = new byte[bytesToRead];
    port.Read(buffer, 0, bytesToRead);
    // 此处接收到的 buffer 即为包含两字节 CRC 尾部的完整响应帧
};
&lt;/code&gt;&lt;/pre&gt;
&lt;h4&gt;2. 手动构建报文：带有 CRC 的 RTU 帧格式&lt;/h4&gt;
&lt;p&gt;RTU 帧的基础结构为：&lt;code&gt;[1字节从机地址] + [1字节功能码] + [2字节起始地址] + [2字节读取数量]&lt;/code&gt;，共计 6 字节。随后利用 CRC16 算法计算出两字节校验码并附加至末尾，构成 8 字节的完整数据帧。&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;byte[] MakeRtuPayload()
{
    // 核心载荷：[从机号01] [读寄存器03] [起始地址 00 00] [读取数量 00 02]
    byte[] core = new byte[] { 0x01, 0x03, 0x00, 0x00, 0x00, 0x02 };
    
    // 计算 CRC16 校验码，注意结果通常使用小端序（低位在前，高位在后）
    byte[] crcBytes = CalculateCrc16(core); // 假设计算结果为 0xC4, 0x0B
    
    // 拼接成完整的 8 字节数据帧
    byte[] finalFrame = new byte[8];
    Array.Copy(core, 0, finalFrame, 0, 6);
    finalFrame[6] = crcBytes[0]; 
    finalFrame[7] = crcBytes[1]; 
    
    return finalFrame; // 随后可通过 port.Write 发送至串口
}
&lt;/code&gt;&lt;/pre&gt;
&lt;h4&gt;3. RTU 报文响应的解析逻辑&lt;/h4&gt;
&lt;p&gt;标准的设备响应帧格式为：&lt;code&gt;[1字节从机地址][1字节功能码][1字节有效数据长度(字节数)][有效数据...][2字节 CRC]&lt;/code&gt;。&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;void ParseRtuResponse(byte[] buffer)
{
    // 第一步：提取前 N-2 个字节计算 CRC，并与尾部两字节进行比对，验证数据完整性。
    
    // 第二步：读取第 3 个字节（下标为 2），获取后续有效数据的准确长度。
    int dataLength = buffer[2];
    
    // 第三步：使用 Span&amp;lt;byte&amp;gt; 跳过前三字节的协议头信息，切片获取核心数据区。
    Span&amp;lt;byte&amp;gt; realData = buffer.AsSpan(3, dataLength);
    
    // 第四步：若网络设备采用大端网络字节序，而在小端系统下解析，需使用 Array.Reverse 转换字节序后再转为整型或浮点型数据。
}
&lt;/code&gt;&lt;/pre&gt;
&lt;h3&gt;18.2 【原生 TCP】API 梳理与 MBAP 头部封装&lt;/h3&gt;
&lt;p&gt;TCP 通道由于工作在传输层，本身具备基于序号与确认的流控制校验机制，数据丢失与损坏概率极低。因此，Modbus TCP 去除了末尾的 CRC 校验码，并在报文首部引入了 6 字节的 &lt;strong&gt;MBAP (Modbus Application Protocol) 报文头&lt;/strong&gt;，以应对多客户端并发场景下的事务匹配和分发。&lt;/p&gt;
&lt;h4&gt;1. 底层 API 流转周期&lt;/h4&gt;
&lt;pre&gt;&lt;code&gt;using System.Net.Sockets;

// 1. 建立 TCP 连接
using TcpClient client = new TcpClient();
await client.ConnectAsync(&quot;192.168.1.100&quot;, 502);
using NetworkStream stream = client.GetStream(); 

// 2. 发送带有 MBAP 头的写入流
byte[] rawBytes = { ... }; 
await stream.WriteAsync(rawBytes, 0, rawBytes.Length);

// 3. 异步读取 TCP 响应数据
byte[] buffer = new byte[1024];
int readCount = await stream.ReadAsync(buffer, 0, buffer.Length);
// 此处的 tcpBuffer 包含了完整的 MBAP 头部信息
&lt;/code&gt;&lt;/pre&gt;
&lt;h4&gt;2. 手动构建报文：TCP 序列封装（添加 MBAP 并移除 CRC）&lt;/h4&gt;
&lt;p&gt;发送请求时，帧结构变更为：&lt;code&gt;[MBAP 6字节] + [1字节从机地址] + [1字节功能码] + [2字节起始地址] + [2字节读取数量]&lt;/code&gt;，总计 12 字节的数据包，不再计算并附加 CRC。&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;byte[] MakeTcpPayload()
{
    // MBAP 前缀（固定占用 6 字节）：
    // [2字节事务标识符 00 01] + [2字节协议标识符 00 00] + [2字节后续报文总长度 00 06]
    byte[] mbap = new byte[] { 0x00, 0x01, 0x00, 0x00, 0x00, 0x06 };
    
    // 基础操作载荷（无需附加 CRC，共计 6 字节）
    byte[] core = new byte[] { 0x01, 0x03, 0x00, 0x00, 0x00, 0x02 };
    
    byte[] finalFrame = new byte[12];
    Array.Copy(mbap, 0, finalFrame, 0, 6);
    Array.Copy(core, 0, finalFrame, 6, 6);
    
    return finalFrame; // 随后交由 stream.WriteAsync 执行网络发送
}
&lt;/code&gt;&lt;/pre&gt;
&lt;h4&gt;3. TCP 接收数据的解析步骤&lt;/h4&gt;
&lt;p&gt;在接收到响应时，报文头部同样会包含对方原样返回的 6 字节 MBAP 头。&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;void ParseTcpResponse(byte[] buffer)
{
    // 服务器响应的报文结构前置部分必然为：[MBAP 6字节] + [1字节从机地址] + [1字节功能码]
    // 因此，第 9 个字节（下标索引 8）即指示“有效数据长度信息”
    int dataLength = buffer[8]; 
    
    // 配合 Span 切片特性，跳过前 9 个字节的协议头部区域，直接提取实质数据段
    Span&amp;lt;byte&amp;gt; realData = buffer.AsSpan(9, dataLength);
}
&lt;/code&gt;&lt;/pre&gt;
&lt;h3&gt;18.3 【原生 UDP】结构梳理与无连接发送机制&lt;/h3&gt;
&lt;p&gt;网络开发中需要注意：&lt;strong&gt;&lt;code&gt;Modbus UDP&lt;/code&gt; 的报文结构设计与 &lt;code&gt;Modbus TCP&lt;/code&gt; 是完全一致的。&lt;/strong&gt;
当协议规定为 &lt;code&gt;Modbus UDP&lt;/code&gt; 时，其&lt;strong&gt;构造出的数据帧在应用层载荷应与上述 TCP 代码逻辑保持对应&lt;/strong&gt;（包括含有 6 字节 MBAP 且不含 CRC ），两者的核心区别仅仅在于建立机制不同，UDP 无需事先建立会话连接。&lt;/p&gt;
&lt;h4&gt;1. 介质源生 API 流转大周期&lt;/h4&gt;
&lt;pre&gt;&lt;code&gt;using System.Net.Sockets;
using System.Net;

// 1. 初始化 UDP 客户端组件（无 Connect 握手过程）
using UdpClient client = new UdpClient();

// 2. 定向发送：复用基于 MBAP 结构化打包生成的 12 字节报文进行 UDP 投递
byte[] mpabWrappedBytes = MakeTcpPayload(); // 报文内容机制同 TCP
await client.SendAsync(mpabWrappedBytes, mpabWrappedBytes.Length, new IPEndPoint(IPAddress.Parse(&quot;192.168.1.100&quot;), 502));

// 3. 异步监听接收网络 UDP 数据包
UdpReceiveResult result = await client.ReceiveAsync();

// 后续针对 result.Buffer 的解析动作，可以直接调用与 ParseTcpResponse 相同的位运算或切片提取逻辑处理。
&lt;/code&gt;&lt;/pre&gt;
&lt;hr /&gt;
&lt;h2&gt;19. 附录 原生 Socket 与高级封装类深度对比拆解&lt;/h2&gt;
&lt;p&gt;在 C# 网络编程中，开发者经常会在 &lt;code&gt;Socket&lt;/code&gt; 与它的高级封装类（&lt;code&gt;TcpListener&lt;/code&gt;、&lt;code&gt;TcpClient&lt;/code&gt;、&lt;code&gt;UdpClient&lt;/code&gt;）之间产生选择困惑。本节将严格按照“流程、容器、参数”，对这两种写法的每一行进行深度拆解对比。&lt;/p&gt;
&lt;h3&gt;19.1 一、 TCP 服务端对比：监听与接收&lt;/h3&gt;
&lt;p&gt;&lt;strong&gt;目标流程&lt;/strong&gt;：在本地 &lt;code&gt;502&lt;/code&gt; 端口开启服务 -&amp;gt; 等待客户端连入 -&amp;gt; 接收数据到内存。&lt;/p&gt;
&lt;h4&gt;1. 使用原生 Socket 实现&lt;/h4&gt;
&lt;pre&gt;&lt;code&gt;Socket server = new Socket(AddressFamily.InterNetwork, SocketType.Stream, ProtocolType.Tcp);
IPEndPoint ep = new IPEndPoint(IPAddress.Any, 502);
server.Bind(ep);
server.Listen(10); 
Socket clientSocket = server.Accept();
byte[] buffer = new byte[1024];
int count = clientSocket.Receive(buffer);
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;&lt;strong&gt;逐行深度解析&lt;/strong&gt;：&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;code&gt;Socket server = new ...&lt;/code&gt;: 实例化底层通讯引擎。
&lt;ul&gt;
&lt;li&gt;&lt;strong&gt;容器/对象&lt;/strong&gt;：创建了一个 Socket 实例。&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;参数&lt;/strong&gt;：&lt;code&gt;AddressFamily.InterNetwork&lt;/code&gt; 明确使用 IPv4 寻址；&lt;code&gt;SocketType.Stream&lt;/code&gt; 明确使用流式传输；&lt;code&gt;ProtocolType.Tcp&lt;/code&gt; 明确应用 TCP 协议。三者缺一不可。&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;&lt;code&gt;IPEndPoint ep = new ...&lt;/code&gt;: 定义网络终点。
&lt;ul&gt;
&lt;li&gt;&lt;strong&gt;容器/对象&lt;/strong&gt;：&lt;code&gt;IPEndPoint&lt;/code&gt; 是包装 IP 和端口号的数据结构。&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;参数&lt;/strong&gt;：&lt;code&gt;IPAddress.Any&lt;/code&gt; 表示监听本机所有网卡（WiFi、有线等）；&lt;code&gt;502&lt;/code&gt; 是 Modbus 默认端口。&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;&lt;code&gt;server.Bind(ep);&lt;/code&gt;: 绑定流程。将底层 Socket 与操作系统的 502 端口强绑定。如果端口被占用，此行抛出异常。&lt;/li&gt;
&lt;li&gt;&lt;code&gt;server.Listen(10);&lt;/code&gt;: 开启监听流程。
&lt;ul&gt;
&lt;li&gt;&lt;strong&gt;参数&lt;/strong&gt;：&lt;code&gt;10&lt;/code&gt; 是 backlog（挂起连接队列的最大长度）。如果同时有 11 个设备并发连入，第 11 个会被系统拒绝。&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;&lt;code&gt;Socket clientSocket = server.Accept();&lt;/code&gt;: 接客流程（阻塞）。
&lt;ul&gt;
&lt;li&gt;&lt;strong&gt;关联&lt;/strong&gt;：代码运行到这里会彻底卡住（死等），直到有真实客户端连入。&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;返回值&lt;/strong&gt;：一旦有人连入，操作系统会创建一个全新的 Socket（&lt;code&gt;clientSocket&lt;/code&gt;）专门用于和该客户一对一通讯，原有的 server 继续回去站岗监听。&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;&lt;code&gt;byte[] buffer = new byte[1024];&lt;/code&gt;: 创建字节数组容器，作为接收数据的“水桶”。&lt;/li&gt;
&lt;li&gt;&lt;code&gt;int count = clientSocket.Receive(buffer);&lt;/code&gt;: 接收流程（阻塞）。
&lt;ul&gt;
&lt;li&gt;&lt;strong&gt;参数与动作&lt;/strong&gt;：将网卡缓冲区的数据舀进 buffer 水桶中。&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;返回值&lt;/strong&gt;：&lt;code&gt;count&lt;/code&gt; 记录了真实接到的有效字节数。&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;h4&gt;2. 使用 TcpListener 实现&lt;/h4&gt;
&lt;pre&gt;&lt;code&gt;TcpListener listener = new TcpListener(IPAddress.Any, 502);
listener.Start();
TcpClient client = listener.AcceptTcpClient();
NetworkStream stream = client.GetStream();
byte[] buffer = new byte[1024];
int count = stream.Read(buffer, 0, buffer.Length); 
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;&lt;strong&gt;逐行深度解析&lt;/strong&gt;：&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;code&gt;TcpListener listener = new ...&lt;/code&gt;: 实例化监听器。
&lt;ul&gt;
&lt;li&gt;&lt;strong&gt;封装对比&lt;/strong&gt;：直接传入 IP 和端口，内部自动完成了 &lt;code&gt;AddressFamily&lt;/code&gt; 等复杂的 Socket 配置。&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;&lt;code&gt;listener.Start();&lt;/code&gt;: 启动流程。
&lt;ul&gt;
&lt;li&gt;&lt;strong&gt;封装对比&lt;/strong&gt;：这一行代码内部自动执行了原生的 &lt;code&gt;Bind()&lt;/code&gt; 和 &lt;code&gt;Listen()&lt;/code&gt;。&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;&lt;code&gt;TcpClient client = listener.AcceptTcpClient();&lt;/code&gt;: 接客流程（阻塞）。
&lt;ul&gt;
&lt;li&gt;&lt;strong&gt;封装对比&lt;/strong&gt;：底层调用的仍是 &lt;code&gt;Accept()&lt;/code&gt;，但返回的不再是原生的 &lt;code&gt;Socket&lt;/code&gt;，而是经过二次封装的 &lt;code&gt;TcpClient&lt;/code&gt; 对象。&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;&lt;code&gt;NetworkStream stream = client.GetStream();&lt;/code&gt;: 核心流转换。
&lt;ul&gt;
&lt;li&gt;&lt;strong&gt;容器&lt;/strong&gt;：&lt;code&gt;NetworkStream&lt;/code&gt; 是 C# 提供的一种专门用于网络的流容器。&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;为什么这样用&lt;/strong&gt;：微软希望你把网络通讯当成“读写本地文件”一样简单，流机制自动处理了底层的指针和内存分配。&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;&lt;code&gt;byte[] buffer = new byte[1024];&lt;/code&gt;: 同样创建字节数组容器。&lt;/li&gt;
&lt;li&gt;&lt;code&gt;int count = stream.Read(buffer, 0, buffer.Length);&lt;/code&gt;: 流式读取。
&lt;ul&gt;
&lt;li&gt;&lt;strong&gt;参数&lt;/strong&gt;：水桶 &lt;code&gt;buffer&lt;/code&gt;；&lt;code&gt;0&lt;/code&gt; 是存放起始位；&lt;code&gt;buffer.Length&lt;/code&gt; 是最大读取量。从流中提取数据。&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;hr /&gt;
&lt;h3&gt;19.2 二、 TCP 客户端对比：连接与发送&lt;/h3&gt;
&lt;p&gt;&lt;strong&gt;目标流程&lt;/strong&gt;：连接到远端 &lt;code&gt;192.168.1.100:502&lt;/code&gt; -&amp;gt; 发送一段 RTU 报文。&lt;/p&gt;
&lt;h4&gt;1. 使用原生 Socket 实现&lt;/h4&gt;
&lt;pre&gt;&lt;code&gt;Socket client = new Socket(AddressFamily.InterNetwork, SocketType.Stream, ProtocolType.Tcp);
IPEndPoint ep = new IPEndPoint(IPAddress.Parse(&quot;192.168.1.100&quot;), 502);
client.Connect(ep);
byte[] data = { 0x01, 0x03, 0x00, 0x00, 0x00, 0x04, 0x44, 0x09 };
client.Send(data);
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;&lt;strong&gt;逐行深度解析&lt;/strong&gt;：&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;code&gt;Socket client = new ...&lt;/code&gt;: 繁琐的基础初始化，同服务端。&lt;/li&gt;
&lt;li&gt;&lt;code&gt;IPEndPoint ep = new ...&lt;/code&gt;: 构建目标服务器的终点容器。
&lt;ul&gt;
&lt;li&gt;&lt;strong&gt;参数&lt;/strong&gt;：&lt;code&gt;IPAddress.Parse&lt;/code&gt; 将字符串 IP 转换为系统底层的二进制 IP 格式。&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;&lt;code&gt;client.Connect(ep);&lt;/code&gt;: 握手流程（阻塞）。触发 TCP 底层的“三次握手”，如果网络不通会卡顿并抛出超时异常。&lt;/li&gt;
&lt;li&gt;&lt;code&gt;byte[] data = { ... };&lt;/code&gt;: 将 Modbus RTU 报文固化到字节数组容器中。&lt;/li&gt;
&lt;li&gt;&lt;code&gt;client.Send(data);&lt;/code&gt;: 发送流程。直接将字节数组交给网卡驱动发送。&lt;/li&gt;
&lt;/ul&gt;
&lt;h4&gt;2. 使用 TcpClient 实现&lt;/h4&gt;
&lt;pre&gt;&lt;code&gt;TcpClient client = new TcpClient(&quot;192.168.1.100&quot;, 502);
byte[] data = { 0x01, 0x03, 0x00, 0x00, 0x00, 0x04, 0x44, 0x09 };
NetworkStream stream = client.GetStream();
stream.Write(data, 0, data.Length);
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;&lt;strong&gt;逐行深度解析&lt;/strong&gt;：&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;code&gt;TcpClient client = new ...&lt;/code&gt;: 初始化并握手。
&lt;ul&gt;
&lt;li&gt;&lt;strong&gt;封装对比&lt;/strong&gt;：构造函数直接接收 string 类型的 IP 和 int 类型的端口。这一行代码内部自动完成了 Socket 创建、&lt;code&gt;IPEndPoint&lt;/code&gt; 解析以及 &lt;code&gt;Connect()&lt;/code&gt; 三次握手动作。如果远端不通，这行直接报错。&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;&lt;code&gt;byte[] data = { ... };&lt;/code&gt;: 报文准备，同上。&lt;/li&gt;
&lt;li&gt;&lt;code&gt;NetworkStream stream = client.GetStream();&lt;/code&gt;: 获取管道。获取与远端连接的流容器。&lt;/li&gt;
&lt;li&gt;&lt;code&gt;stream.Write(data, 0, data.Length);&lt;/code&gt;: 写入流。
&lt;ul&gt;
&lt;li&gt;&lt;strong&gt;动作&lt;/strong&gt;：按照文件流的操作习惯，将数据推入网络流中。&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;hr /&gt;
&lt;h3&gt;19.3 三、 UDP 广播对比：无连接发送&lt;/h3&gt;
&lt;p&gt;&lt;strong&gt;目标流程&lt;/strong&gt;：将一段文本向局域网广播（端口 &lt;code&gt;8888&lt;/code&gt;）。&lt;/p&gt;
&lt;h4&gt;1. 使用原生 Socket 实现&lt;/h4&gt;
&lt;pre&gt;&lt;code&gt;Socket udpServer = new Socket(AddressFamily.InterNetwork, SocketType.Dgram, ProtocolType.Udp);
udpServer.EnableBroadcast = true;
byte[] data = Encoding.UTF8.GetBytes(&quot;Hello&quot;);
IPEndPoint broadcastEP = new IPEndPoint(IPAddress.Broadcast, 8888);
udpServer.SendTo(data, broadcastEP);
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;&lt;strong&gt;逐行深度解析&lt;/strong&gt;：&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;code&gt;Socket udpServer = new ...&lt;/code&gt;: 实例化。
&lt;ul&gt;
&lt;li&gt;&lt;strong&gt;参数变化&lt;/strong&gt;：注意此时变成了 &lt;code&gt;SocketType.Dgram&lt;/code&gt;（数据报）和 &lt;code&gt;ProtocolType.Udp&lt;/code&gt;。&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;&lt;code&gt;udpServer.EnableBroadcast = true;&lt;/code&gt;: 权限开关。
&lt;ul&gt;
&lt;li&gt;&lt;strong&gt;为什么这样用&lt;/strong&gt;：操作系统默认禁止程序发送广播包以防网络风暴，必须显式开启此权限。&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;&lt;code&gt;byte[] data = ...&lt;/code&gt;: 将文本转换为 UTF8 字节序列存入容器。&lt;/li&gt;
&lt;li&gt;&lt;code&gt;IPEndPoint broadcastEP = new ...&lt;/code&gt;: 定义广播终点。
&lt;ul&gt;
&lt;li&gt;&lt;strong&gt;参数&lt;/strong&gt;：&lt;code&gt;IPAddress.Broadcast&lt;/code&gt; 相当于 &lt;code&gt;255.255.255.255&lt;/code&gt;（全局广播地址）。&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;&lt;code&gt;udpServer.SendTo(data, broadcastEP);&lt;/code&gt;: 无连接发送流程。
&lt;ul&gt;
&lt;li&gt;&lt;strong&gt;关联&lt;/strong&gt;：因为 UDP 没有 &lt;code&gt;Connect()&lt;/code&gt; 环节，所以必须调用 &lt;code&gt;SendTo&lt;/code&gt;，每次发送都要临时告诉网卡包裹要寄给谁（&lt;code&gt;broadcastEP&lt;/code&gt;）。&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;h4&gt;2. 使用 UdpClient 实现&lt;/h4&gt;
&lt;pre&gt;&lt;code&gt;UdpClient udp = new UdpClient();
udp.EnableBroadcast = true;
byte[] data = Encoding.UTF8.GetBytes(&quot;Hello&quot;);
IPEndPoint broadcastEP = new IPEndPoint(IPAddress.Broadcast, 8888);
udp.Send(data, data.Length, broadcastEP);
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;&lt;strong&gt;逐行深度解析&lt;/strong&gt;：&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;code&gt;UdpClient udp = new UdpClient();&lt;/code&gt;: 极简实例化。内部自动创建并配置了正确的 UDP Socket 结构。&lt;/li&gt;
&lt;li&gt;&lt;code&gt;udp.EnableBroadcast = true;&lt;/code&gt;: 同上，打开系统广播权限。&lt;/li&gt;
&lt;li&gt;&lt;code&gt;byte[] data = ...&lt;/code&gt;: 准备数据容器。&lt;/li&gt;
&lt;li&gt;&lt;code&gt;IPEndPoint broadcastEP = ...&lt;/code&gt;: 构建广播终点。&lt;/li&gt;
&lt;li&gt;&lt;code&gt;udp.Send(data, data.Length, broadcastEP);&lt;/code&gt;: 发送动作。
&lt;ul&gt;
&lt;li&gt;&lt;strong&gt;封装对比&lt;/strong&gt;：调用 &lt;code&gt;Send&lt;/code&gt; 方法，内部自动包装并执行了原生 Socket 的 &lt;code&gt;SendTo&lt;/code&gt; 操作。&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;h3&gt;19.4 四、 UDP 接收与响应对比：无连接服务端侦听&lt;/h3&gt;
&lt;p&gt;&lt;strong&gt;目标流程&lt;/strong&gt;：在本地 &lt;code&gt;8888&lt;/code&gt; 端口开启监听 -&amp;gt; 接收任意来源的 UDP 报文 -&amp;gt; 获取发送方的 IP 与端口信息。&lt;/p&gt;
&lt;h4&gt;1. 使用原生 Socket 实现&lt;/h4&gt;
&lt;pre&gt;&lt;code&gt;Socket udpServer = new Socket(AddressFamily.InterNetwork, SocketType.Dgram, ProtocolType.Udp);
IPEndPoint localEP = new IPEndPoint(IPAddress.Any, 8888);
udpServer.Bind(localEP);

byte[] buffer = new byte[1024];
EndPoint remoteEP = new IPEndPoint(IPAddress.Any, 0); // 用于存放发送方的终点容器
int count = udpServer.ReceiveFrom(buffer, ref remoteEP);
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;&lt;strong&gt;逐行深度解析&lt;/strong&gt;：&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;code&gt;Socket udpServer = new ...&lt;/code&gt;: 实例化 UDP 引擎并配置 &lt;code&gt;Dgram&lt;/code&gt; (数据报)。&lt;/li&gt;
&lt;li&gt;&lt;code&gt;udpServer.Bind(localEP);&lt;/code&gt;: 绑定本地端口 &lt;code&gt;8888&lt;/code&gt;，声明程序监听进入该端口的网络数据。&lt;/li&gt;
&lt;li&gt;&lt;code&gt;EndPoint remoteEP = ...&lt;/code&gt;: 构建一个空的端点对象，功能类似于来访登记表。&lt;/li&gt;
&lt;li&gt;&lt;code&gt;udpServer.ReceiveFrom(buffer, ref remoteEP);&lt;/code&gt;: 接收动作（阻塞等待）。
&lt;ul&gt;
&lt;li&gt;&lt;strong&gt;机制核心&lt;/strong&gt;：通过 &lt;code&gt;ref&lt;/code&gt; 传入空端点。一旦收到网络数据包，操作系统不但会将数据载荷存入 &lt;code&gt;buffer&lt;/code&gt;，还会把来源地址赋予 &lt;code&gt;remoteEP&lt;/code&gt;，从而实现数据与来源的精确追踪对应。&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;h4&gt;2. 使用 UdpClient 实现&lt;/h4&gt;
&lt;pre&gt;&lt;code&gt;UdpClient udp = new UdpClient(8888);
IPEndPoint remoteEP = new IPEndPoint(IPAddress.Any, 0);
byte[] data = udp.Receive(ref remoteEP);
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;&lt;strong&gt;逐行深度解析&lt;/strong&gt;：&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;code&gt;UdpClient udp = new UdpClient(8888);&lt;/code&gt;: 初始化时直接传入数字端口，底层自动完成了 &lt;code&gt;Socket&lt;/code&gt; 的建立和 &lt;code&gt;Bind()&lt;/code&gt; 绑定状态。&lt;/li&gt;
&lt;li&gt;&lt;code&gt;IPEndPoint remoteEP = ...&lt;/code&gt;: 定义空的来源地址追踪容器。&lt;/li&gt;
&lt;li&gt;&lt;code&gt;byte[] data = udp.Receive(ref remoteEP);&lt;/code&gt;: 接收动作（阻塞等待）。
&lt;ul&gt;
&lt;li&gt;&lt;strong&gt;封装差异&lt;/strong&gt;：返回值直接是一块刚好等于数据有效载荷大小的新 &lt;code&gt;byte[]&lt;/code&gt; 数组。底层的 &lt;code&gt;1024&lt;/code&gt; 缓冲区分配等底层细节被自动屏蔽（由于省去了每次判断 count 的手动提纯，开发便利性极高，但也意味着高频接收会产生较高的临时 GC 内存分配）。&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;hr /&gt;
&lt;h3&gt;19.5 五、 UDP 单播对比：定向点对点发送&lt;/h3&gt;
&lt;p&gt;&lt;strong&gt;目标流程&lt;/strong&gt;：不维持握手连接状态，精准将数据单次发往远端的 &lt;code&gt;192.168.1.100:502&lt;/code&gt; 目标机器。&lt;/p&gt;
&lt;h4&gt;1. 使用原生 Socket 实现&lt;/h4&gt;
&lt;pre&gt;&lt;code&gt;Socket udpClient = new Socket(AddressFamily.InterNetwork, SocketType.Dgram, ProtocolType.Udp);
byte[] data = Encoding.UTF8.GetBytes(&quot;Command&quot;);
IPEndPoint targetEP = new IPEndPoint(IPAddress.Parse(&quot;192.168.1.100&quot;), 502);
udpClient.SendTo(data, targetEP);
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;&lt;strong&gt;对比说明&lt;/strong&gt;：和前一节的广播使用毫无代码结构上的差异，唯独 &lt;code&gt;SendTo&lt;/code&gt; 中指定的终点从全局属性变为了一个含有具体 IP 数字的单点目标。&lt;/p&gt;
&lt;h4&gt;2. 使用 UdpClient 实现&lt;/h4&gt;
&lt;pre&gt;&lt;code&gt;UdpClient udp = new UdpClient();
// 开启面向底层的“伪连接”，告知底层接下来的默认通讯抛射点
udp.Connect(&quot;192.168.1.100&quot;, 502); 
byte[] data = Encoding.UTF8.GetBytes(&quot;Command&quot;);
udp.Send(data, data.Length);
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;&lt;strong&gt;逐行深度解析&lt;/strong&gt;：&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;code&gt;udp.Connect(&quot;192.168.1.100&quot;, 502);&lt;/code&gt;: 这是一个极具迷惑性的 API 命名设计。由于 UDP 本质上没有物理的三次握手环节，调用 &lt;code&gt;Connect&lt;/code&gt; 方法实际上不会在物理网线产生任何比特流交流！它只是&lt;strong&gt;在客户端程序内存中将目标 IP 设为常驻的发送默认值&lt;/strong&gt;。&lt;/li&gt;
&lt;li&gt;&lt;code&gt;udp.Send(data, data.Length);&lt;/code&gt;: 由于前面已经调用了伪 &lt;code&gt;Connect&lt;/code&gt;，所以此处短化版本的 &lt;code&gt;Send&lt;/code&gt; 方法省略了目标 &lt;code&gt;IPEndPoint&lt;/code&gt; 参数，表面上达到了类似 &lt;code&gt;TcpClient.GetStream().Write&lt;/code&gt; 的业务手感。这是框架封装旨在统一全栈网络流调用体验的巧妙设计。&lt;/li&gt;
&lt;/ul&gt;
&lt;hr /&gt;
&lt;h3&gt;19.6 总结关联&lt;/h3&gt;
&lt;ul&gt;
&lt;li&gt;&lt;strong&gt;容器维度&lt;/strong&gt;：原生 &lt;code&gt;Socket&lt;/code&gt; 始终在直接操作 &lt;code&gt;byte[]&lt;/code&gt; 缓冲区；而高级封装类将其升级为了 &lt;code&gt;NetworkStream&lt;/code&gt; 流容器，方便与其他 C# 流架构（如文件流、内存流等）进行极度顺滑的生态组合。&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;流程维度&lt;/strong&gt;：封装类将繁杂的“参数组装、AddressFamily、强类型转换、连接”完全压缩进了构造函数或 &lt;code&gt;Start()&lt;/code&gt; 初始阶段中，极大减少了业务代码量，使得工程师的开发心智能全部收束在“网络数据解析本身”。&lt;/li&gt;
&lt;/ul&gt;
&lt;hr /&gt;
&lt;h3&gt;总结&lt;/h3&gt;
&lt;ul&gt;
&lt;li&gt;&lt;strong&gt;入门级&lt;/strong&gt;：会写 &lt;code&gt;Connect&lt;/code&gt;、&lt;code&gt;Send&lt;/code&gt;、&lt;code&gt;Receive&lt;/code&gt;，知道用 &lt;code&gt;byte[]&lt;/code&gt; 互传数据通信。&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;进阶级（以往的水平）&lt;/strong&gt;：知道判断 &lt;code&gt;Receive&lt;/code&gt; 为 0 时为正常断开情况，知道用 &lt;code&gt;try-catch&lt;/code&gt; 捕获异常反馈信息，知道利用分割符（如 &lt;code&gt;\n&lt;/code&gt;）简单处理粘包情境。&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;企业级（未来的方向）&lt;/strong&gt;：使用 &lt;code&gt;*Async&lt;/code&gt; 等模型压榨 CPU 并发性能效能调配；使用无锁 &lt;code&gt;ConcurrentDictionary&lt;/code&gt; 防踩踏并发处理阻塞；通过复用预分配 &lt;code&gt;ArrayPool&lt;/code&gt; 降低高频 GC 波峰开销；设定严密头设计的 &lt;code&gt;TLV&lt;/code&gt; 处理粘半包二进制协议闭环约束；增加超时及探活 &lt;code&gt;Heartbeat&lt;/code&gt; 功能机制，主动阻断并清理连接悬置遗留的非正常退出的端口资源。&lt;/li&gt;
&lt;/ul&gt;
&lt;hr /&gt;
&lt;h2&gt;20. 工业物联网消息中枢：MQTT 协议核心实战&lt;/h2&gt;
&lt;p&gt;在工业物联网（IIoT）场景中，设备数量庞大、网络环境复杂（可能通过不稳定的企业防火墙或 NAT 网络），传统 TCP Socket 的点对点直连模式往往难以满足需求。&lt;strong&gt;MQTT（Message Queuing Telemetry Transport）&lt;/strong&gt; 作为专为受限设备和低带宽、高延迟网络设计的轻量级发布/订阅消息传输协议，已成为 IoT 通信的事实标准。相比直接操作 Socket，MQTT 提供了：自动重连机制、QoS 质量等级保障、遗嘱消息（Last Will）等开箱即用的工业级特性。&lt;/p&gt;
&lt;p&gt;本文以 &lt;strong&gt;MQTTnet&lt;/strong&gt;（目前 .NET 生态中最成熟、功能最完整的 MQTT Broker/Client 实现库）为技术栈，深入解析：服务端启动与事件体系、客户端连接订阅最佳实践、以及完整消息收发时序与关键开发陷阱。&lt;/p&gt;
&lt;h3&gt;20.1 快速入门：核心概念一览&lt;/h3&gt;
&lt;p&gt;在动手之前，先理解 MQTT 的四大核心概念：&lt;/p&gt;
&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;概念&lt;/th&gt;
&lt;th&gt;类比理解&lt;/th&gt;
&lt;th&gt;关键点&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;Broker&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;消息邮局&lt;/td&gt;
&lt;td&gt;接收消息、按 Topic 路由转发给订阅者&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;Publisher&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;寄信人&lt;/td&gt;
&lt;td&gt;产生数据，发到 Broker 的某个 Topic&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;Subscriber&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;收信人&lt;/td&gt;
&lt;td&gt;订阅感兴趣的 Topic，自动收到推送&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;Topic&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;邮政编码&lt;/td&gt;
&lt;td&gt;层级字符串，如 &lt;code&gt;factory/line1/sensor/temp&lt;/code&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;
&lt;p&gt;&lt;strong&gt;MQTT 的三大特色机制&lt;/strong&gt;：&lt;/p&gt;
&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;机制&lt;/th&gt;
&lt;th&gt;作用&lt;/th&gt;
&lt;th&gt;工业场景应用&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;QoS 质量等级&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;0=可能丢、1=至少一次、2=恰好一次&lt;/td&gt;
&lt;td&gt;告警用 QoS 2，传感器数据用 QoS 0&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;遗嘱消息 (Last Will)&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;异常断线时 Broker 自动代发&lt;/td&gt;
&lt;td&gt;设备掉电告警、状态监控&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;Retained 消息&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;Broker 缓存最新一条，新订阅者立即收到&lt;/td&gt;
&lt;td&gt;设备在线状态广播&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;
&lt;hr /&gt;
&lt;h3&gt;20.2 服务端（Broker）开发：从启动到发消息&lt;/h3&gt;
&lt;h4&gt;20.2.1 服务端启动三步曲&lt;/h4&gt;
&lt;p&gt;Broker 的生命周期非常清晰：&lt;strong&gt;创建 → 注册事件 → 启动监听&lt;/strong&gt;。&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;// ① 创建工厂（MQTTnet 统一入口）
var factory = new MqttFactory();

// ② 构建配置（端口、连接数等）
var serverOptions = new MqttServerOptionsBuilder()
    .WithDefaultEndpoint()            // 启用 TCP，默认 1883
    .WithDefaultEndpointPort(1883)
    .Build();

// ③ 创建并启动
var server = factory.CreateMqttServer(serverOptions);
await server.StartAsync();
Console.WriteLine(&quot;Broker 已就绪，监听 :1883&quot;);
&lt;/code&gt;&lt;/pre&gt;
&lt;blockquote&gt;
&lt;p&gt;&lt;strong&gt;💡 为什么用工厂模式？&lt;/strong&gt; &lt;code&gt;MqttFactory&lt;/code&gt; 可以创建 &lt;code&gt;IMqttServer&lt;/code&gt; 和 &lt;code&gt;IMqttClient&lt;/code&gt;，统一入口便于切换实现。&lt;/p&gt;
&lt;/blockquote&gt;
&lt;h4&gt;20.2.2 注册四大事件钩子&lt;/h4&gt;
&lt;pre&gt;&lt;code&gt;客户端连接流程中的四个关键钩子：
                                   
  Client ──→ CONNECT ──→ [1.ValidatingConnection] ──→ [4.ClientConnected]
                   │         ↓                              ↓
                   │      允许/拒绝                    连接成功
                   │         ↓
                   │      [2.InterceptingPublish] ←─── 消息流转
                   │         ↓
                   │      放行/丢弃/修改
                   │         ↓
  Client ──← DISCONNECT ←── [3.ClientDisconnected]
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;&lt;strong&gt;代码实现&lt;/strong&gt;：&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;// 【1】连接鉴权 - 第一道安全门
server.ValidatingConnectionAsync += e =&amp;gt;
{
    Console.WriteLine($&quot;[{e.ClientId}] 尝试连接，用户={e.UserName}&quot;);
    // 密码错误则拒绝
    if (e.Password != &quot;secret123&quot;)
        e.ReasonCode = MQTTnet.Protocol.MqttConnectReasonCode.BadUserNameOrPassword;
    return Task.CompletedTask;
};

// 【2】消息拦截 - 可修改/丢弃/记录
server.InterceptingPublishAsync += e =&amp;gt;
{
    Console.WriteLine($&quot;[拦截] {e.ClientId} → {e.ApplicationMessage.Topic}&quot;);
    // e.ProcessPublish = false;  // 阻止转发
    return Task.CompletedTask;
};

// 【3-4】连接/断开通知
server.ClientConnectedAsync    += e =&amp;gt; Console.WriteLine($&quot;+ {e.ClientId}&quot;);
server.ClientDisconnectedAsync += e =&amp;gt; Console.WriteLine($&quot;- {e.ClientId}&quot;);
&lt;/code&gt;&lt;/pre&gt;
&lt;h4&gt;20.2.3 服务端主动发消息：InjectApplicationMessage&lt;/h4&gt;
&lt;p&gt;Broker 发消息和客户端发消息是&lt;strong&gt;两个完全不同的 API&lt;/strong&gt;：&lt;/p&gt;
&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;API&lt;/th&gt;
&lt;th&gt;调用者&lt;/th&gt;
&lt;th&gt;传输方式&lt;/th&gt;
&lt;th&gt;典型场景&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;PublishAsync&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;客户端&lt;/td&gt;
&lt;td&gt;经过 TCP 转发&lt;/td&gt;
&lt;td&gt;设备上报数据&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;InjectApplicationMessage&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Broker&lt;/td&gt;
&lt;td&gt;内存直接投递&lt;/td&gt;
&lt;td&gt;服务端主动推送&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;
&lt;p&gt;&lt;strong&gt;广播给所有订阅者&lt;/strong&gt;：&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;await server.InjectApplicationMessage(
    new InjectedMqttApplicationMessage(
        new MqttApplicationMessageBuilder()
            .WithTopic(&quot;notice/all&quot;)
            .WithPayload(&quot;系统将于10分钟后重启&quot;)
            .WithQualityOfServiceLevel(MQTTnet.Protocol.MqttQualityOfServiceLevel.AtLeastOnce)
            .Build()
    ) { SenderClientId = &quot;server&quot; }  // 标记来源，拦截器可识别
);
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;&lt;strong&gt;定向单播给某个客户端&lt;/strong&gt;：MQTT 没有&quot;发给指定 ClientId&quot;的 API，正确的做法是&lt;strong&gt;用专属 Topic&lt;/strong&gt;：&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;// 客户端订阅自己的专属 Topic
await client.SubscribeAsync($&quot;device/{clientId}/cmd&quot;);

// 服务端发到该 Topic
await server.InjectApplicationMessage(
    new InjectedMqttApplicationMessage(
        new MqttApplicationMessageBuilder()
            .WithTopic($&quot;device/{targetClientId}/cmd&quot;)  // 精准投递
            .WithPayload(&quot;{\&quot;action\&quot;: \&quot;reboot\&quot;}&quot;)
            .Build()
    ) { SenderClientId = &quot;server&quot; }
);
&lt;/code&gt;&lt;/pre&gt;
&lt;hr /&gt;
&lt;h3&gt;20.3 客户端开发：连接 → 订阅 → 收发&lt;/h3&gt;
&lt;h4&gt;20.3.1 连接选项配置&lt;/h4&gt;
&lt;pre&gt;&lt;code&gt;var options = new MqttClientOptionsBuilder()
    .WithTcpServer(&quot;127.0.0.1&quot;, 1883)     // Broker 地址
    .WithClientId(&quot;Device_A&quot;)              // 唯一标识
    .WithCredentials(&quot;user&quot;, &quot;secret123&quot;) // 用户名密码
    .WithKeepAlivePeriod(TimeSpan.FromSeconds(30))  // 心跳间隔
    .WithCleanSession(true)              // true=断线清会话，false=保留
    .Build();
&lt;/code&gt;&lt;/pre&gt;
&lt;h4&gt;20.3.2 ⚠️ 事件注册顺序：先订阅，后连接！&lt;/h4&gt;
&lt;p&gt;&lt;strong&gt;这是最容易踩的坑！&lt;/strong&gt; &lt;code&gt;ConnectedAsync&lt;/code&gt; 可能在 &lt;code&gt;ConnectAsync&lt;/code&gt; 返回之前就触发，如果事件还没注册，订阅就会丢失。&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;var factory = new MqttFactory();
var client  = factory.CreateMqttClient();

// ✅ 正确顺序：
// 第1步：注册 ConnectedAsync（里面包含订阅）
client.ConnectedAsync += async e =&amp;gt;
{
    Console.WriteLine(&quot;已连接&quot;);
    // 所有订阅都放在这里，重连时也会自动触发
    await client.SubscribeAsync(new MqttTopicFilterBuilder()
        .WithTopic(&quot;home/+/temperature&quot;)
        .WithQualityOfServiceLevel(MQTTnet.Protocol.MqttQualityOfServiceLevel.AtLeastOnce)
        .Build());
};

// 第2步：注册 DisconnectedAsync（自动重连）
client.DisconnectedAsync += async e =&amp;gt;
{
    if (e.ClientWasConnected)
    {
        await Task.Delay(TimeSpan.FromSeconds(5));  // 防暴力重试
        await client.ConnectAsync(options);
    }
};

// 第3步：注册消息回调（收到数据）
client.ApplicationMessageReceivedAsync += e =&amp;gt;
{
    var topic = e.ApplicationMessage.Topic;
    var payload = e.ApplicationMessage.ConvertPayloadToString();
    Console.WriteLine($&quot;[收到] {topic} → {payload}&quot;);
    return Task.CompletedTask;
};

// 第4步：最后才调用 ConnectAsync
await client.ConnectAsync(options);  // 此时所有事件已就绪
&lt;/code&gt;&lt;/pre&gt;
&lt;h4&gt;20.3.3 发布消息&lt;/h4&gt;
&lt;pre&gt;&lt;code&gt;var message = new MqttApplicationMessageBuilder()
    .WithTopic(&quot;home/living_room/temperature&quot;)  // Topic 路径
    .WithPayload(&quot;23.5&quot;)                         // 数据载荷
    .WithQualityOfServiceLevel(
        MQTTnet.Protocol.MqttQualityOfServiceLevel.AtLeastOnce)
    .WithRetainFlag(false)   // true=保留消息，新订阅者立即收到
    .Build();

var result = await client.PublishAsync(message);

// 检查结果（QoS 1/2 必须检查）
if (result.ReasonCode != MqttClientPublishReasonCode.Success)
    Console.WriteLine($&quot;发布失败: {result.ReasonCode}&quot;);
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;&lt;strong&gt;Payload 的三种写法&lt;/strong&gt;：&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;// A. 字符串（最常用，内部 UTF-8 编码）
.WithPayload(&quot;hello world&quot;)

// B. byte[]（二进制协议，如 Modbus RTU）
byte[] raw = { 0x01, 0xFF, 0x3A };
.WithPayload(raw)

// C. JSON（IoT 标准数据格式）
var data = new { DeviceId = &quot;D01&quot;, Temp = 23.5 };
.WithPayload(System.Text.Json.JsonSerializer.Serialize(data))
&lt;/code&gt;&lt;/pre&gt;
&lt;h4&gt;20.3.4 Retain 消息：设备状态广播&lt;/h4&gt;
&lt;p&gt;&lt;code&gt;Retain = true&lt;/code&gt; 让 Broker 记住这条消息，新订阅者&lt;strong&gt;立即收到&lt;/strong&gt;，不用等下次发布：&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;// 设备上线时广播在线状态
var msg = new MqttApplicationMessageBuilder()
    .WithTopic(&quot;devices/D01/status&quot;)
    .WithPayload(&quot;online&quot;)
    .WithRetainFlag(true)   // ⬅️ 关键：保留消息
    .Build();
await client.PublishAsync(msg);

// 设备离线时，清除 Retain（发空消息）
var clearMsg = new MqttApplicationMessageBuilder()
    .WithTopic(&quot;devices/D01/status&quot;)
    .WithPayload(Array.Empty&amp;lt;byte&amp;gt;())  // 空 = 清除
    .WithRetainFlag(true)
    .Build();
await client.PublishAsync(clearMsg);
&lt;/code&gt;&lt;/pre&gt;
&lt;hr /&gt;
&lt;h3&gt;20.4 QoS 质量等级详解&lt;/h3&gt;
&lt;p&gt;MQTT 的 QoS 决定消息能否到达、是否重复，是选型时最重要的决策点。&lt;/p&gt;
&lt;h4&gt;20.4.1 三种等级对比&lt;/h4&gt;
&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;等级&lt;/th&gt;
&lt;th&gt;名称&lt;/th&gt;
&lt;th&gt;包交换次数&lt;/th&gt;
&lt;th&gt;可靠性&lt;/th&gt;
&lt;th&gt;延迟&lt;/th&gt;
&lt;th&gt;适用场景&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;QoS 0&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;AtMostOnce（至多一次）&lt;/td&gt;
&lt;td&gt;1 次&lt;/td&gt;
&lt;td&gt;❌ 可能丢消息&lt;/td&gt;
&lt;td&gt;⭐ 最低&lt;/td&gt;
&lt;td&gt;高频传感器、温湿度（偶尔丢可接受）&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;QoS 1&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;AtLeastOnce（至少一次）&lt;/td&gt;
&lt;td&gt;2 次&lt;/td&gt;
&lt;td&gt;✅ 必达，⚠️ 可能重复&lt;/td&gt;
&lt;td&gt;⭐⭐ 中等&lt;/td&gt;
&lt;td&gt;命令下发、告警通知&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;QoS 2&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;ExactlyOnce（恰好一次）&lt;/td&gt;
&lt;td&gt;4 次&lt;/td&gt;
&lt;td&gt;✅ 必达，✅ 不重复&lt;/td&gt;
&lt;td&gt;⭐⭐⭐ 最高&lt;/td&gt;
&lt;td&gt;计费交易、支付指令&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;
&lt;h4&gt;20.4.2 消息时序图&lt;/h4&gt;
&lt;p&gt;&lt;strong&gt;QoS 1 完整流程&lt;/strong&gt;：&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;Publisher                  Broker                   Subscriber
    |                        |                           |
    |─── PUBLISH (QoS 1) ───&amp;gt;│                           |
    |                        │─── PUBLISH (QoS 1) ──────&amp;gt;│
    |                        │                           |
    |                        │&amp;lt;─── PUBACK ───────────────│
    |&amp;lt;───────────────────────│                           |
    |                        │                           |
    ✅ Publisher 确认消息已转发
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;&lt;strong&gt;QoS 2 四次握手&lt;/strong&gt;：&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;Publisher                  Broker                   Subscriber
    |                        |                           |
    |─── PUBLISH ───────────&amp;gt;│                           |
    |                        │─── PUBLISH ──────────────&amp;gt;│
    |                        │                           |
    |&amp;lt;─── PUBREC ────────────│                           |
    |─── PUBREL ────────────&amp;gt;│                           |
    |                        │&amp;lt;─── PUBCOMP ─────────────│
    |&amp;lt;───────────────────────│                           |
    |                        │─── PUBCOMP ─────────────&amp;gt;│
    |                        |                           |
&lt;/code&gt;&lt;/pre&gt;
&lt;h4&gt;20.4.3 QoS 实战选择&lt;/h4&gt;
&lt;pre&gt;&lt;code&gt;// QoS 0：高频传感器，每秒上报多次，丢一条无所谓
await client.PublishAsync(new MqttApplicationMessageBuilder()
    .WithTopic(&quot;sensor/temperature&quot;)
    .WithPayload(&quot;22.1&quot;)
    .WithQualityOfServiceLevel(MQTTnet.Protocol.MqttQualityOfServiceLevel.AtMostOnce)
    .Build());  // 立即返回，不等任何确认

// QoS 1：设备控制命令，必须到达，允许重复
await client.PublishAsync(new MqttApplicationMessageBuilder()
    .WithTopic(&quot;device/cmd&quot;)
    .WithPayload(&quot;reboot&quot;)
    .WithQualityOfServiceLevel(MQTTnet.Protocol.MqttQualityOfServiceLevel.AtLeastOnce)
    .Build());  // 等待 PUBACK 才返回

// QoS 2：计费交易，不允许重复（业务层也需要幂等设计）
await client.PublishAsync(new MqttApplicationMessageBuilder()
    .WithTopic(&quot;billing/event&quot;)
    .WithPayload(&quot;{\&quot;amount\&quot;: 9.9}&quot;)
    .WithQualityOfServiceLevel(MQTTnet.Protocol.MqttQualityOfServiceLevel.ExactlyOnce)
    .Build());
&lt;/code&gt;&lt;/pre&gt;
&lt;hr /&gt;
&lt;h3&gt;20.5 高级应用模式&lt;/h3&gt;
&lt;h4&gt;20.5.1 定时心跳广播&lt;/h4&gt;
&lt;p&gt;服务端定期向所有客户端推送心跳，包含服务器时间和在线设备数：&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;_ = Task.Run(async () =&amp;gt;
{
    while (!cts.Token.IsCancellationRequested)
    {
        await Task.Delay(TimeSpan.FromSeconds(30), cts.Token);

        var payload = System.Text.Json.JsonSerializer.Serialize(new
        {
            ServerTime = DateTime.UtcNow,
            Clients = (await server.GetClientsAsync()).Count()
        });

        await server.InjectApplicationMessage(
            new InjectedMqttApplicationMessage(
                new MqttApplicationMessageBuilder()
                    .WithTopic(&quot;server/heartbeat&quot;)
                    .WithPayload(payload)
                    .Build()
            ) { SenderClientId = &quot;server&quot; }
        );
    }
});
&lt;/code&gt;&lt;/pre&gt;
&lt;h4&gt;20.5.2 请求-回复模式（模拟 RPC）&lt;/h4&gt;
&lt;p&gt;MQTT 是单向发布/订阅，通过两个 Topic 模拟双向通信：&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;服务端处理并回复&lt;/strong&gt;：&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;server.InterceptingPublishAsync += async e =&amp;gt;
{
    if (!e.ApplicationMessage.Topic.StartsWith(&quot;request/&quot;))
        return;

    var clientId = e.ClientId;
    var request  = e.ApplicationMessage.ConvertPayloadToString();

    // 业务处理（查询数据库、调用服务等）
    var response = ProcessRequest(request);

    // 回复到发送者的专属 Topic
    await server.InjectApplicationMessage(
        new InjectedMqttApplicationMessage(
            new MqttApplicationMessageBuilder()
                .WithTopic($&quot;reply/{clientId}&quot;)
                .WithPayload(response)
                .Build()
        ) { SenderClientId = &quot;server&quot; }
    );
};
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;&lt;strong&gt;客户端订阅回复并发起请求&lt;/strong&gt;：&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;// 订阅自己的回复 Topic
client.ConnectedAsync += async e =&amp;gt;
{
    await client.SubscribeAsync($&quot;reply/{myClientId}&quot;);
};

// 收到回复时处理
client.ApplicationMessageReceivedAsync += e =&amp;gt;
{
    if (e.ApplicationMessage.Topic.StartsWith(&quot;reply/&quot;))
    {
        var reply = e.ApplicationMessage.ConvertPayloadToString();
        Console.WriteLine($&quot;收到回复: {reply}&quot;);
    }
    return Task.CompletedTask;
};
&lt;/code&gt;&lt;/pre&gt;
&lt;h4&gt;20.5.3 遗嘱消息：设备掉线告警&lt;/h4&gt;
&lt;p&gt;设备异常断电时，来不及发送 &lt;code&gt;DISCONNECT&lt;/code&gt; 包。通过遗嘱消息，Broker 自动代发离线通知：&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;var options = new MqttClientOptionsBuilder()
    .WithTcpServer(&quot;127.0.0.1&quot;, 1883)
    .WithClientId(&quot;PLC_Gateway_01&quot;)
    // 遗嘱消息配置
    .WithWillTopic(&quot;devices/PLC_Gateway_01/status&quot;)
    .WithWillPayload(&quot;offline&quot;)
    .WithWillRetainFlag(true)
    .WithWillQualityOfServiceLevel(
        MQTTnet.Protocol.MqttQualityOfServiceLevel.AtLeastOnce)
    .Build();
&lt;/code&gt;&lt;/pre&gt;
&lt;hr /&gt;
&lt;h3&gt;20.6 开发避坑指南&lt;/h3&gt;
&lt;h4&gt;坑1：拦截器递归触发&lt;/h4&gt;
&lt;p&gt;在 &lt;code&gt;InterceptingPublishAsync&lt;/code&gt; 里调用 &lt;code&gt;InjectApplicationMessage&lt;/code&gt; 时，如果注入的 Topic 也匹配拦截条件，会无限循环：&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;// ❌ 错误：可能死循环
server.InterceptingPublishAsync += async e =&amp;gt;
{
    if (e.ApplicationMessage.Topic == &quot;request/query&quot;)
    {
        await server.InjectApplicationMessage(
            new InjectedMqttApplicationMessage(
                new MqttApplicationMessageBuilder()
                    .WithTopic(&quot;reply/...&quot;)  // 这条也会进入拦截器！
                    .Build()
            )
        );
    }
};

// ✅ 正确：用 SenderClientId 标记来源，跳过自己的消息
server.InterceptingPublishAsync += async e =&amp;gt;
{
    if (e.ClientId == &quot;server&quot;) return;  // 跳过服务端注入的

    if (e.ApplicationMessage.Topic == &quot;request/query&quot;)
    {
        await server.InjectApplicationMessage(
            new InjectedMqttApplicationMessage(...)
            ) { SenderClientId = &quot;server&quot; }  // ⬅️ 防止递归
        );
    }
};
&lt;/code&gt;&lt;/pre&gt;
&lt;h4&gt;坑2：消息回调阻塞&lt;/h4&gt;
&lt;p&gt;&lt;code&gt;ApplicationMessageReceivedAsync&lt;/code&gt; 是&lt;strong&gt;串行执行&lt;/strong&gt;的，耗时操作会堵塞整个接收队列：&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;// ❌ 错误：数据库操作阻塞后续消息
client.ApplicationMessageReceivedAsync += e =&amp;gt;
{
    SaveToDatabase(e.ApplicationMessage.Topic, payload);  // 阻塞！
    return Task.CompletedTask;
};

// ✅ 正确：丢到线程池，不阻塞
client.ApplicationMessageReceivedAsync += e =&amp;gt;
{
    _ = Task.Run(() =&amp;gt; SaveToDatabase(
        e.ApplicationMessage.Topic,
        e.ApplicationMessage.ConvertPayloadToString()
    ));
    return Task.CompletedTask;
};
&lt;/code&gt;&lt;/pre&gt;
&lt;h4&gt;坑3：批量发布飞行窗口&lt;/h4&gt;
&lt;p&gt;每个 QoS 1/2 消息都占用一个 packetId，无限制并发会撑爆 Broker：&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;// 推荐：SemaphoreSlim 控制并发数
var sem = new SemaphoreSlim(10);  // 最多 10 条同时在途

var tasks = sensorReadings.Select(async reading =&amp;gt;
{
    await sem.WaitAsync();
    try
    {
        await client.PublishAsync(BuildMessage(reading));
    }
    finally { sem.Release(); }
});

await Task.WhenAll(tasks);
&lt;/code&gt;&lt;/pre&gt;
&lt;h4&gt;坑4：CleanSession 选择&lt;/h4&gt;
&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;场景&lt;/th&gt;
&lt;th&gt;推荐值&lt;/th&gt;
&lt;th&gt;原因&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;临时设备、移动端&lt;/td&gt;
&lt;td&gt;&lt;code&gt;true&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;每次重新连接，不保留会话&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;关键告警设备&lt;/td&gt;
&lt;td&gt;&lt;code&gt;false&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;离线消息会补发，断线重连订阅自动恢复&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;
&lt;hr /&gt;
&lt;h3&gt;20.7 Topic 命名规范&lt;/h3&gt;
&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;规范&lt;/th&gt;
&lt;th&gt;✅ 正确&lt;/th&gt;
&lt;th&gt;❌ 错误&lt;/th&gt;
&lt;th&gt;说明&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;层级分隔&lt;/td&gt;
&lt;td&gt;&lt;code&gt;factory/line1/sensor&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;&lt;code&gt;factory.line1.sensor&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;MQTT 标准用 &lt;code&gt;/&lt;/code&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;避免前导 &lt;code&gt;/&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;&lt;code&gt;home/kitchen/light&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;&lt;code&gt;/home/kitchen/light&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;避免空层级&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;多租户隔离&lt;/td&gt;
&lt;td&gt;&lt;code&gt;tenant-a/device/001/temp&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;&lt;code&gt;device/001/temp&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;生产环境必备&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;状态/命令分离&lt;/td&gt;
&lt;td&gt;&lt;code&gt;device/001/status&lt;/code&gt;、&lt;code&gt;device/001/cmd&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;&lt;code&gt;device/001&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;便于权限控制&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;通配符订阅&lt;/td&gt;
&lt;td&gt;&lt;code&gt;home/+/temperature&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;-&lt;/td&gt;
&lt;td&gt;&lt;code&gt;+&lt;/code&gt; 单层，&lt;code&gt;#&lt;/code&gt; 多层&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;
&lt;hr /&gt;
&lt;h3&gt;20.8 MQTT vs TCP Socket&lt;/h3&gt;
&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;维度&lt;/th&gt;
&lt;th&gt;TCP Socket 直连&lt;/th&gt;
&lt;th&gt;MQTT 发布/订阅&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;连接模型&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;点对点直连 N×N&lt;/td&gt;
&lt;td&gt;星型拓扑 1→N&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;耦合度&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;高（发布者需知订阅者）&lt;/td&gt;
&lt;td&gt;低（通过 Topic 解耦）&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;断线重连&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;需手动实现&lt;/td&gt;
&lt;td&gt;Broker 自动处理&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;QoS 保证&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;无内置&lt;/td&gt;
&lt;td&gt;原生 QoS 0/1/2&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;协议开销&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;极低（仅数据）&lt;/td&gt;
&lt;td&gt;固定头 2 字节&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;适用规模&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;数十~数百节点&lt;/td&gt;
&lt;td&gt;可达百万级设备&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;典型场景&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;高速工控、PLC 直连&lt;/td&gt;
&lt;td&gt;IoT 云平台、跨防火墙&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;
&lt;hr /&gt;
&lt;h3&gt;20.9 总结：三级能力图谱&lt;/h3&gt;
&lt;pre&gt;&lt;code&gt;┌─────────────────────────────────────────────────────────────┐
│  入门级：会用基础 API                                         │
│  ├── MqttFactory 创建 Server/Client                         │
│  ├── ConnectAsync / PublishAsync / SubscribeAsync           │
│  └── 理解 Topic 基本结构                                     │
├─────────────────────────────────────────────────────────────┤
│  进阶级：掌握可靠性机制                                       │
│  ├── ConnectedAsync 里订阅（重连自动恢复）                    │
│  ├── 自动重连 + DisconnectedAsync                           │
│  ├── QoS 选型（传感器 QoS0，命令 QoS1）                     │
│  └── CleanSession / Retain / 遗嘱消息                        │
├─────────────────────────────────────────────────────────────┤
│  企业级：构建高可靠生产系统                                    │
│  ├── Task.Run 防消息回调阻塞                                 │
│  ├── SemaphoreSlim 批量发布限流                              │
│  ├── 拦截器递归防护 + 安全审计                                │
│  ├── InjectApplicationMessage 主动推送                       │
│  └── TLS 加密 + PKI 设备认证                                 │
└─────────────────────────────────────────────────────────────┘
&lt;/code&gt;&lt;/pre&gt;
&lt;hr /&gt;
&lt;h3&gt;20.10 终极速查字典：MQTT 核心 API 大全与各代版本演进对比&lt;/h3&gt;
&lt;p&gt;在 C# 工业通信开发中，MQTTnet 无疑是目前最具代表性的开发库。随着业务的发展，无论是在 &lt;strong&gt;MQTTnet 官方 SDK 的代码结构上&lt;/strong&gt;，还是在 &lt;strong&gt;MQTT 通信协议本身&lt;/strong&gt; 上，都经历了巨大的迭代。为了在后续开发中不陷入陈旧的坑底，以下汇总列出了新旧系统交替中最核心的 API 演进字典。&lt;/p&gt;
&lt;h4&gt;1. SDK 架构 API 演进：旧版 (v3.x 体系) VS 现代版本 (v4.x/v5.x 体系)&lt;/h4&gt;
&lt;p&gt;在较新的版本之后，所有依赖于 &lt;code&gt;UseXxxHandler&lt;/code&gt; 的老旧事件委托写法被官方全面清除，改为通过 &lt;code&gt;Task&lt;/code&gt;-based 的异步事件流机制（&lt;code&gt;XxxAsync += ...&lt;/code&gt;）。&lt;/p&gt;
&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;核心调度行为&lt;/th&gt;
&lt;th&gt;上古写法 (维护老旧系统时最常见)&lt;/th&gt;
&lt;th&gt;现代版本 API (当前推荐标准)&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;客户端接收报文&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;&lt;code&gt;client.UseApplicationMessageReceivedHandler(e =&amp;gt; { ... });&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;&lt;code&gt;client.ApplicationMessageReceivedAsync += e =&amp;gt; { ...; return Task.CompletedTask; };&lt;/code&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;客户端掉线重连配置&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;&lt;code&gt;client.UseDisconnectedHandler(async e =&amp;gt; { await client.ConnectAsync(...); });&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;&lt;code&gt;client.DisconnectedAsync += async e =&amp;gt; { await client.ConnectAsync(...); };&lt;/code&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;客户端连接成功触发&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;&lt;code&gt;client.UseConnectedHandler(e =&amp;gt; { ... });&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;&lt;code&gt;client.ConnectedAsync += e =&amp;gt; { ...; return Task.CompletedTask; };&lt;/code&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;服务端启动伺服&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;&lt;code&gt;await server.StartAsync(new MqttServerOptionsBuilder().Build());&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;&lt;code&gt;await server.StartAsync(serverOptions);&lt;/code&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;服务端拦截通讯&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;&lt;code&gt;server.UseApplicationMessageReceivedHandler(...)&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;&lt;code&gt;server.InterceptingPublishAsync += e =&amp;gt; { ...; return Task.CompletedTask; };&lt;/code&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;服务端客机验证&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;&lt;code&gt;server.UseClientConnectedHandler(...)&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;&lt;code&gt;server.ValidatingConnectionAsync += e =&amp;gt; { e.ReasonCode = ...; return Task.CompletedTask; };&lt;/code&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;
&lt;h4&gt;2. 通信协议演进：经典早期 API (MQTT 3.1.1) VS 新代专属前沿特性 (MQTT 5.0)&lt;/h4&gt;
&lt;p&gt;若是平台配置强制指定使用更高段位的协定 (&lt;code&gt;WithProtocolVersion(MqttProtocolVersion.V500)&lt;/code&gt;)，您将瞬间解锁旧版物理隔离层所不具备的四大核心 API 参数武器：&lt;/p&gt;
&lt;h5&gt;【连接篇：参数分离的高级生命周期重连】&lt;/h5&gt;
&lt;ul&gt;
&lt;li&gt;&lt;strong&gt;旧版本 API (MQTT 3.1.1)&lt;/strong&gt;：唯一的清理入口。&lt;pre&gt;&lt;code&gt;var ops = new MqttClientOptionsBuilder()
    .WithCleanSession(true) // 每次断开直接被动丢弃服务端残余连接内所有的离线订阅缓存
    .Build();
&lt;/code&gt;&lt;/pre&gt;
&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;5.x 版本 API (MQTT 5.0 专享)&lt;/strong&gt;：完美分离了“初始启动”与“中途意外重连”。&lt;pre&gt;&lt;code&gt;var ops = new MqttClientOptionsBuilder()
    .WithCleanStart(true) // 针对第一次启动的完全清洁
    .WithSessionExpiryInterval(3600) // 哪怕短线，Broker 也会将当前会话和重要订阅缓存挂起维持 3600 秒不动
    .Build();
&lt;/code&gt;&lt;/pre&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;h5&gt;【属性绑定篇：多维标识用户附属属性 API】&lt;/h5&gt;
&lt;ul&gt;
&lt;li&gt;&lt;strong&gt;背景短板&lt;/strong&gt;：旧版 API 若要在报文外额外传输“控制权限字段”、“Token 身份设备号”，开发者被迫必须去改写和污染核心负荷区 &lt;code&gt;Payload&lt;/code&gt; 的 JSON。&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;5.x 版本 API (MQTT 5.0 专享)&lt;/strong&gt;：新增原生的无限拓展 &lt;code&gt;UserProperty&lt;/code&gt; 投递入口：&lt;pre&gt;&lt;code&gt;var message = new MqttApplicationMessageBuilder()
    .WithTopic(&quot;smart/factory/room1&quot;)
    .WithPayload(&quot;25&quot;) // 极简纯粹的数据载荷
    .WithUserProperty(&quot;Encryption&quot;, &quot;AES256&quot;) // 5.x 独立字段：就像 HTTP Header 一般任意无限附加拓展头！
    .WithUserProperty(&quot;Operator&quot;, &quot;Admin-01&quot;) 
    .Build();
&lt;/code&gt;&lt;/pre&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;h5&gt;【吞吐优化极致篇：带内别名绑定 API】&lt;/h5&gt;
&lt;ul&gt;
&lt;li&gt;&lt;strong&gt;背景短板&lt;/strong&gt;：工业传感中往往会带着非常漫长复杂的路径 Topic (例如 &lt;code&gt;cn/sh/factoryA/line4/robotic/temp&lt;/code&gt;)。每一帧的头部中如果带着这段近 40 字节的信息进行毫秒射击，将吞噬巨大的通讯额度与处理损耗。&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;5.x 版本 API (MQTT 5.0 专享)&lt;/strong&gt;：支持动态寻址替换：&lt;pre&gt;&lt;code&gt;// 初始化上行：下发全称并为其约定配置标记为 1 号
var message = new MqttApplicationMessageBuilder()
    .WithTopic(&quot;cn/sh/factoryA/line4/robotic/temp&quot;) 
    .WithTopicAlias(1) 
    .Build();

// 之后疯狂的高频数据涌入时：Topic 地址参数传入空壳代码！网络驱动底层仅传一个短小精干的数字 1 来实施映射！省流爆炸！
var fastMessage = new MqttApplicationMessageBuilder()
    .WithTopic(&quot;&quot;) 
    .WithTopicAlias(1) 
    .WithPayload(&quot;25&quot;)
    .Build();
&lt;/code&gt;&lt;/pre&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;h5&gt;【容错监控篇：指令响应回执状态枚举 API】&lt;/h5&gt;
&lt;ul&gt;
&lt;li&gt;&lt;strong&gt;旧版本 API (MQTT 3.1.1)&lt;/strong&gt;：发布者发出的请求哪怕未被分发（或因为服务端身份鉴定失被定丢弃），客机端的发布 &lt;code&gt;PublishAsync&lt;/code&gt; 仍然视为结束，全无底层消息反馈。&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;5.x 版本 API (MQTT 5.0 专享)&lt;/strong&gt;：通过全面内置反馈机制，每一次行为都附带完整的结果枚举码：&lt;pre&gt;&lt;code&gt;var result = await client.PublishAsync(message);
if (result.ReasonCode != MqttClientPublishReasonCode.Success)
{
    // 5.x 的直接反馈：提取出业务失败溯源，例如 NotAuthorized(无权限)、TopicFilterInvalid 等
    Console.WriteLine($&quot;数据派发中途遇阻结束，服务端真实回复状况为：{result.ReasonCode}&quot;);
}
&lt;/code&gt;&lt;/pre&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;h2&gt;21. 工业数据上报：HttpClient 与 Web API 集成&lt;/h2&gt;
&lt;p&gt;在工业物联网场景中，上位机常通过 SerialPort 或 Modbus 采集本地硬件数据，随后通过 HTTP 协议将数据汇总上报至云端或企业内部的 Web API 接口。&lt;/p&gt;
&lt;h3&gt;21.1 客户端生命周期管理&lt;/h3&gt;
&lt;p&gt;对于基于 WinForms / WPF 的桌面端应用，控制 &lt;code&gt;HttpClient&lt;/code&gt; 的实例化方式是维持网络稳定性的前提：&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;strong&gt;长连接复用（推荐）&lt;/strong&gt;：使用 &lt;code&gt;private static readonly HttpClient _client = new HttpClient();&lt;/code&gt;。&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;设计逻辑&lt;/strong&gt;：&lt;code&gt;HttpClient&lt;/code&gt; 实例被设计为复用对象。在循环数据采集或高频上报任务中，反复由于 &lt;code&gt;using&lt;/code&gt; 语句创建并销毁该实例会导致底层的 TCP 套接字未能立刻释放，长时间处于 &lt;code&gt;TIME_WAIT&lt;/code&gt; 状态，极易耗尽可能用的通信端口。&lt;/li&gt;
&lt;/ul&gt;
&lt;h3&gt;21.2 WinForms 下的异步请求模式&lt;/h3&gt;
&lt;p&gt;工业通信应用通常具有长期的主干线程维持通信，因此网络上报不能阻塞系统的 UI 线程。&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;strong&gt;异步操作&lt;/strong&gt;：需要在事件交互函数中使用 &lt;code&gt;async/await&lt;/code&gt;。&lt;/li&gt;
&lt;/ul&gt;
&lt;pre&gt;&lt;code&gt;private async void OnDataArrived(object sender, SerialDataReceivedEventArgs e) 
{
    // 1. 同步读取本地硬件数据
    string rawData = serialPort1.ReadExisting();
    
    // 2. 将数据执行异步网络传输 (此步骤不阻塞界面重绘)
    await PostDataToCloudAsync(rawData); 
    
    // 3. UI 状态指示 (任务完成后自动恢复回 UI 主线程环境)
    txtLog.Text = &quot;数据上传成功&quot;;
}
&lt;/code&gt;&lt;/pre&gt;
&lt;h3&gt;21.3 标准的请求构建与结果处理&lt;/h3&gt;
&lt;p&gt;编写稳定的 HTTP 客户端上传逻辑，通常包含以下步骤：&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;&lt;strong&gt;URL 与查询参数构建&lt;/strong&gt;：使用拼接字符串如 &lt;code&gt;$&quot;api/upload?sensorId={id}&amp;amp;page={page}&quot;&lt;/code&gt; 发送特定的附属标识。&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;响应状态验证&lt;/strong&gt;：优先调用 &lt;code&gt;response.EnsureSuccessStatusCode()&lt;/code&gt;。当 Web 服务器返回非正常代码（如 500、404）时，方法将立即抛出异常，防止解析出错的报文体。&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;JSON 序列化操作&lt;/strong&gt;：
&lt;ul&gt;
&lt;li&gt;读取数据：&lt;code&gt;await response.Content.ReadAsStringAsync()&lt;/code&gt;。&lt;/li&gt;
&lt;li&gt;实体转换：使用 &lt;code&gt;JsonConvert.DeserializeObject&amp;lt;T&amp;gt;&lt;/code&gt; 将接收的 JSON 数据结构转化为 C# 对象以便后续处理（例如 &lt;code&gt;Newtonsoft.Json&lt;/code&gt; 类库）。&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;/ol&gt;
&lt;h3&gt;21.4 HttpClient 通信错误处理&lt;/h3&gt;
&lt;p&gt;现场网络环境并不绝对稳定，程序需要设定合理的应对机制：&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;strong&gt;硬超时控制&lt;/strong&gt;：必须为 &lt;code&gt;HttpClient&lt;/code&gt; 设置明确的 &lt;code&gt;Timeout&lt;/code&gt; 属性（如设置为 5 到 10 秒）。&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;异常捕获&lt;/strong&gt;：使用 &lt;code&gt;try-catch&lt;/code&gt; 包裹请求范围，并至少拦截 &lt;code&gt;TaskCanceledException&lt;/code&gt; (超时错误) 以及 &lt;code&gt;HttpRequestException&lt;/code&gt; (网络中断或服务器无响应)。&lt;/li&gt;
&lt;/ul&gt;
&lt;pre&gt;&lt;code&gt;private static readonly HttpClient _httpClient = new HttpClient() 
{ 
    Timeout = TimeSpan.FromSeconds(5) 
};

public async Task UploadSensorDataAsync(string data)
{
    try 
    {
        var payload = new StringContent(data, Encoding.UTF8, &quot;application/json&quot;);
        var response = await _httpClient.PostAsync(&quot;http://server-ip/api/data&quot;, payload);
        response.EnsureSuccessStatusCode();
    }
    catch (TaskCanceledException)
    {
        Console.WriteLine(&quot;请求在限时内未获回应 (Timeout)&quot;);
    }
    catch (HttpRequestException ex)
    {
        Console.WriteLine($&quot;无法连接目标接口: {ex.Message}&quot;);
    }
}
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;$content&lt;/p&gt;
</content:encoded></item><item><title>WPF Prism框架学习笔记</title><link>https://meteor-comet.github.io/posts/wpf-prism-framework/</link><guid isPermaLink="true">https://meteor-comet.github.io/posts/wpf-prism-framework/</guid><description>深入理解与实战Prism这一强大的WPF MVVM框架</description><pubDate>Thu, 25 Jul 2024 00:00:00 GMT</pubDate><content:encoded>&lt;h1&gt;主要内容：WPF Prism框架&lt;/h1&gt;
&lt;h2&gt;目录&lt;/h2&gt;
&lt;ul&gt;
&lt;li&gt;&lt;a href=&quot;#1-prism%E6%A1%86%E6%9E%B6%E6%A6%82%E5%BF%B5%E4%B8%8E%E6%A0%B8%E5%BF%83%E5%86%85%E5%AE%B9&quot;&gt;1. Prism框架概念与核心内容&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;#2-prism%E6%A1%86%E6%9E%B6%E5%90%AF%E5%8A%A8%E4%B8%8E-appxamlcs-%E6%A0%B8%E5%BF%83%E6%B3%A8%E5%86%8C%E8%AF%A6%E8%A7%A3&quot;&gt;2. Prism框架启动与 App.xaml.cs 核心注册详解&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;#3-prism%E7%9A%84%E5%91%BD%E5%90%8D%E7%BA%A6%E5%AE%9A%E5%AE%9A%E4%BD%8D%E5%99%A8%E4%B8%8E%E5%9F%BA%E7%A1%80-mvvm&quot;&gt;3. Prism的命名约定、定位器与基础 MVVM&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;#4-%E5%8C%BA%E5%9F%9F%E7%AE%A1%E7%90%86%E5%99%A8-regionmanager-%E4%B8%8E%E5%8C%BA%E5%9F%9F%E9%80%82%E9%85%8D%E5%99%A8&quot;&gt;4. 区域管理器 (RegionManager) 与区域适配器&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;#5-%E5%AF%BC%E8%88%AA%E5%8A%9F%E8%83%BD%E8%B7%B3%E8%BD%AC%E4%BC%A0%E5%8F%82%E6%97%A5%E5%BF%97%E5%AE%88%E5%8D%AB&quot;&gt;5. 导航功能（跳转、传参、日志、守卫）&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;#6-%E4%BA%8B%E4%BB%B6%E8%81%9A%E5%90%88%E5%99%A8-eventaggregator&quot;&gt;6. 事件聚合器 (EventAggregator)&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;#7-%E5%AF%B9%E8%AF%9D%E6%A1%86%E6%9C%8D%E5%8A%A1-dialogservice&quot;&gt;7. 对话框服务 (DialogService)&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;#8-%E6%A8%A1%E5%9D%97%E5%8C%96%E5%BC%80%E5%8F%91-modularity&quot;&gt;8. 模块化开发 (Modularity)&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;hr /&gt;
&lt;h2&gt;1. Prism框架概念与核心内容&lt;/h2&gt;
&lt;h3&gt;1.1 Prism框架概念&lt;/h3&gt;
&lt;p&gt;Prism 是一个用于在 WPF (Windows Presentation Foundation)、UWP 和 Xamarin.Forms 中构建松散耦合、可维护和可测试的 XAML 应用程序的框架。在WPF开发中，Prism 是最成熟、功能最强大的 MVVM 框架之一，它不仅提供了 MVVM 的基础功能（如数据绑定、命令），还提供了模块化开发、区域（Region）UI合成、动态导航等高级特性，非常适合开发大型的企业级桌面应用。&lt;/p&gt;
&lt;h3&gt;1.2 Prism核心内容&lt;/h3&gt;
&lt;p&gt;Prism 的核心内容包括以下几个方面：&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;strong&gt;MVVM 支持&lt;/strong&gt;：实现了 &lt;code&gt;BindableBase&lt;/code&gt;、&lt;code&gt;DelegateCommand&lt;/code&gt; 以及属性注入等常规 MVVM 所需的基础类库。&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;ViewModelLocator&lt;/strong&gt;：视图模型定位器，能够根据约定自动将 View 绑定到对应的 ViewModel 上，极大地简化了代码隐藏（Code-Behind）的处理。&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;模块化 (Modularity)&lt;/strong&gt;：Prism 支持将复杂的应用程序划分为多个独立的模块（Modules），各个模块有自己的 View、ViewModel、Services 等，能够独立开发、测试，在运行时动态加载。&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;UI 合成与区域 (Region)&lt;/strong&gt;：使用 &lt;code&gt;RegionManager&lt;/code&gt; 将主界面划分为多个独立的区域，实现了 UI 元素的低耦合，通过视图注入（View Injection）功能可以动态装载不同的页面或者控件。&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;依赖注入与控制反转 (IOC)&lt;/strong&gt;：Prism 高度依赖依赖注入容器（如 DryIoc、Unity 等）。容器负责所有的对象的生命周期管理以及依赖关系的自动解析。&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;导航 (Navigation)&lt;/strong&gt;：Prism 提供了强大的基于 Region 的导航机制，支持导航传参、导航日志记录以及导航确认（守卫拦截）。&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;事件聚合器 (EventAggregator)&lt;/strong&gt;：用于在松散耦合的组件之间进行通信。&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;对话框服务 (DialogService)&lt;/strong&gt;：提供了一种在 ViewModel 中以解耦方式显示对话框的机制。&lt;/li&gt;
&lt;/ul&gt;
&lt;h3&gt;1.3 Prism和其它MVVM框架对比&lt;/h3&gt;
&lt;ul&gt;
&lt;li&gt;&lt;strong&gt;Prism vs CommunityToolkit.Mvvm (原 MVVM Light)&lt;/strong&gt;：
&lt;ul&gt;
&lt;li&gt;&lt;strong&gt;CommunityToolkit.Mvvm&lt;/strong&gt;：轻量级，性能极高（使用了源生成器等新技术），侧重于基础的 MVVM 基础设施（ViewModel基类、Messenger、Command）。如果不涉及复杂的UI动态组装和模块加载，CommunityToolkit.Mvvm 是首选。&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Prism&lt;/strong&gt;：重型框架，功能大而全。它不仅提供 MVVM 基础，更核心的是其 UI 合成、基于区域的导航、模块化架构。适合极其复杂的企业级大型系统（例如：ERP界面、拥有诸多独立插件的系统）。&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Prism vs Caliburn.Micro&lt;/strong&gt;：两者都支持高级视图模型绑定和窗口管理，但 Prism 在模块加载和 Region 管理上更加规范、严谨和成熟。&lt;/li&gt;
&lt;/ul&gt;
&lt;hr /&gt;
&lt;h2&gt;2. Prism框架启动与 App.xaml.cs 核心注册详解&lt;/h2&gt;
&lt;p&gt;在 Prism 框架体系中，&lt;code&gt;App.xaml&lt;/code&gt; 和 &lt;code&gt;App.xaml.cs&lt;/code&gt; 是整个应用程序的核心入口，负责接管原生 WPF 的启动流程，并初始化依赖注入容器（IOC）。掌握这一部分是 Prism 进阶的关键一步。&lt;/p&gt;
&lt;h3&gt;2.1 改造原生的 App.xaml&lt;/h3&gt;
&lt;p&gt;要使用 Prism，必须剥夺 WPF 原生的启动权。我们需要将根节点 &lt;code&gt;Application&lt;/code&gt; 替换为 &lt;code&gt;prism:PrismApplication&lt;/code&gt;，并&lt;strong&gt;严格删除 &lt;code&gt;StartupUri=&quot;MainWindow.xaml&quot;&lt;/code&gt; 属性&lt;/strong&gt;，因为主窗体的创建将由 Prism 的依赖注入容器接管。&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;&amp;lt;prism:PrismApplication x:Class=&quot;MyPrismApp.App&quot;
             xmlns=&quot;http://schemas.microsoft.com/winfx/2006/xaml/presentation&quot;
             xmlns:x=&quot;http://schemas.microsoft.com/winfx/2006/xaml&quot;
             xmlns:prism=&quot;http://prismlibrary.com/&quot;&amp;gt;
    &amp;lt;Application.Resources&amp;gt;
        &amp;lt;!-- 全局样式和资源字典在此处引入 --&amp;gt;
    &amp;lt;/Application.Resources&amp;gt;
&amp;lt;/prism:PrismApplication&amp;gt;
&lt;/code&gt;&lt;/pre&gt;
&lt;h3&gt;2.2 App.xaml.cs 核心引导程序：CreateShell 与 RegisterTypes&lt;/h3&gt;
&lt;p&gt;修改 &lt;code&gt;App.xaml.cs&lt;/code&gt; 使其继承自 &lt;code&gt;PrismApplication&lt;/code&gt;。此时你必须实现两个抽象方法：&lt;code&gt;CreateShell&lt;/code&gt;（创建主窗体）和 &lt;code&gt;RegisterTypes&lt;/code&gt;（依赖注入与服务大管家）。&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;using Prism.Ioc;
using Prism.Unity; // 核心：使用的是基于 Unity 或 DryIoc 的 Prism 容器引擎版本
using System.Windows;
using MyPrismApp.Views;
using MyPrismApp.Services;

namespace MyPrismApp
{
    public partial class App : PrismApplication
    {
        /// &amp;lt;summary&amp;gt;
        /// 1. 指定应用程序的启动主窗体 (Shell)
        /// &amp;lt;/summary&amp;gt;
        protected override Window CreateShell()
        {
            // 绝对不能用 new MainWindow()！
            // 必须通过 IOC 容器解析，这样 MainWindow 及其 ViewModel 构造函数里的依赖才会被自动注入。
            return Container.Resolve&amp;lt;MainWindow&amp;gt;();
        }

        /// &amp;lt;summary&amp;gt;
        /// 2. 核心注册方法：在此处统一注册所有业务服务、导航页面和弹窗。
        /// 这是 Prism 实现强解耦的核心，也是各模块互通的基础。
        /// &amp;lt;/summary&amp;gt;
        protected override void RegisterTypes(IContainerRegistry containerRegistry)
        {
            // ==============================================
            // A. 注册基础业务服务 (依赖注入 Service)
            // ==============================================
            
            // 瞬态 (Transient)：每次被请求时，都会创建一个全新的实例
            containerRegistry.Register&amp;lt;ILogService, LogService&amp;gt;();
            
            // 单例 (Singleton)：全局唯一的实例，整个程序生命周期共享一个对象
            containerRegistry.RegisterSingleton&amp;lt;IUserService, UserService&amp;gt;();
            
            // 实例注册：将程序启动时已经实例化的一个现成对象，注册为单例直接托管给容器
            // var localConfig = new ConfigLoader().Load();
            // containerRegistry.RegisterInstance&amp;lt;IConfig&amp;gt;(localConfig);

            // ==============================================
            // B. 注册基于 Region 路由的导航页面 (Navigation)
            // ==============================================
            
            // 注意：只有在这里注册过的 View，才能在 RequestNavigate 导航时被识别。
            // 默认路由别名为视图类名，例如下面注册后，路由地址就是 &quot;ViewA&quot;
            containerRegistry.RegisterForNavigation&amp;lt;ViewA&amp;gt;();
            
            // 进阶：显式指定 ViewModel，并起一个方便好记的路由别名 &quot;StudentPage&quot;
            containerRegistry.RegisterForNavigation&amp;lt;StudentView, StudentViewModel&amp;gt;(&quot;StudentPage&quot;);

            // ==============================================
            // C. 注册对话框弹窗视图 (Dialog)
            // ==============================================
            
            // 专供 IDialogService 调用的独立弹窗页面，也必须提前注册
            containerRegistry.RegisterDialog&amp;lt;AlertDialog, AlertDialogViewModel&amp;gt;();
            containerRegistry.RegisterDialog&amp;lt;ConfirmDialog&amp;gt;(&quot;ConfirmWindow&quot;);
        }
        
        /// &amp;lt;summary&amp;gt;
        /// 3. (可选) 注册/覆盖区域适配器
        /// &amp;lt;/summary&amp;gt;
        protected override void ConfigureRegionAdapterMappings(RegionAdapterMappings regionAdapterMappings)
        {
            base.ConfigureRegionAdapterMappings(regionAdapterMappings);
            // 注册针对特定控件 (如 StackPanel) 的自定义区域适配器
            // regionAdapterMappings.RegisterMapping(typeof(StackPanel), Container.Resolve&amp;lt;StackPanelRegionAdapter&amp;gt;());
        }

        /// &amp;lt;summary&amp;gt;
        /// 4. (可选) 配置模块化目录 (详情见第8章)
        /// &amp;lt;/summary&amp;gt;
        protected override void ConfigureModuleCatalog(IModuleCatalog moduleCatalog)
        {
            base.ConfigureModuleCatalog(moduleCatalog);
            // moduleCatalog.AddModule&amp;lt;SomeModule&amp;gt;();
        }
    }
}
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;通过这一套流程，Prism 彻底接管了应用程序的生命周期、窗口创建机制以及内部服务的装配。&lt;/p&gt;
&lt;h3&gt;2.3 进阶实战：在主窗体弹出前阻塞显示登录窗体&lt;/h3&gt;
&lt;p&gt;在企业级应用开发中，最常见的需求是：&lt;strong&gt;程序启动时先弹出登录框，如果登录成功才显示主界面，失败则直接退出程序。&lt;/strong&gt;
理解 &lt;code&gt;PrismApplication&lt;/code&gt; 内部的启动顺序非常关键，它的调用链路为：&lt;code&gt;Initialize()&lt;/code&gt; -&amp;gt; &lt;code&gt;CreateShell()&lt;/code&gt; -&amp;gt; &lt;code&gt;InitializeShell()&lt;/code&gt; -&amp;gt; &lt;code&gt;OnInitialized()&lt;/code&gt;。
既然在 &lt;code&gt;CreateShell&lt;/code&gt; 阶段主窗体已经被实例化，如果在主窗体正式呈现给用户之前进行拦截，最佳的重写位置是 &lt;strong&gt;&lt;code&gt;InitializeShell()&lt;/code&gt;&lt;/strong&gt; 方法。&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;完整实现示例：&lt;/strong&gt;&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;public partial class App : PrismApplication
{
    // ... 前文的 CreateShell 和 RegisterTypes 代码保持不变 ...

    /// &amp;lt;summary&amp;gt;
    /// 拦截壳窗体的初始化过程，执行前置的条件弹窗逻辑（如：登录验证）
    /// &amp;lt;/summary&amp;gt;
    protected override void InitializeShell(Window shell)
    {
        // 此时 CreateShell() 已经执行，但主窗体 shell 还没有被真正 Show() 出来。
        // 我们利用 IOC 容器解析出一个纯净的登录窗体（前提是 LoginWindow 必须已被注册或能够被容器解析）
        var loginWindow = Container.Resolve&amp;lt;LoginWindow&amp;gt;();
        
        // 使用 ShowDialog() 阻塞当前启动线程，等待用户的登录操作
        bool? loginResult = loginWindow.ShowDialog();

        if (loginResult == true)
        {
            // 登录成功！调用基类方法。
            // 基类底层的默认实现为：Application.Current.MainWindow = shell; shell.Show();
            base.InitializeShell(shell); 
        }
        else
        {
            // 登录失败或用户主动点击了关闭，直接终结整个应用程序
            Application.Current.Shutdown();
        }
    }
    
    /// &amp;lt;summary&amp;gt;
    /// 在容器初始化完毕、且主窗体 Shell 显示之后触发。
    /// 常用来做初始化完成后的首次默认路由跳转。
    /// &amp;lt;/summary&amp;gt;
    protected override void OnInitialized()
    {
        base.OnInitialized();
        
        // 例如：登录成功显示主界面后，默认往主窗体的 ContentRegion 推入后台首页仪表盘
        // Container.Resolve&amp;lt;IRegionManager&amp;gt;().RequestNavigate(&quot;ContentRegion&quot;, &quot;DashboardView&quot;);
    }
}
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;这是一种在 Prism 中的标准拦截方案。它既保证了登录窗体（&lt;code&gt;LoginWindow&lt;/code&gt;）能够充分享受 Prism 的 IOC 依赖注入功能，又在架构上保证了未认证用户无法接触到主窗体及任何核心服务资源。&lt;/p&gt;
&lt;hr /&gt;
&lt;h2&gt;3. Prism的命名约定、定位器与基础 MVVM&lt;/h2&gt;
&lt;h3&gt;3.1 约定优于配置 (Convention over Configuration)&lt;/h3&gt;
&lt;p&gt;Prism 通过一套强有力的命名约定，自动消灭了传统 WPF 中需要手动绑定 &lt;code&gt;DataContext&lt;/code&gt; 的繁琐代码：&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;&lt;strong&gt;文件夹约定&lt;/strong&gt;：视图文件必须存放在 &lt;code&gt;Views&lt;/code&gt; 文件夹中，视图模型必须存放在 &lt;code&gt;ViewModels&lt;/code&gt; 文件夹中。&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;命名映射约定&lt;/strong&gt;：后缀映射。如果 View 命名为 &lt;code&gt;MainWindow&lt;/code&gt;，其 ViewModel 必须命名为 &lt;code&gt;MainWindowViewModel&lt;/code&gt;。如果 View 叫 &lt;code&gt;StudentView&lt;/code&gt;，则对应 &lt;code&gt;StudentViewModel&lt;/code&gt;。&lt;/li&gt;
&lt;/ol&gt;
&lt;h3&gt;3.2 视图模型定位器 (ViewModelLocator)&lt;/h3&gt;
&lt;p&gt;当遵循了上述约定后，只需要在 XAML 根节点中挂载一行附加属性，Prism 便会利用反射机制，自动在 IOC 容器中实例化 ViewModel 并挂载到视图的 &lt;code&gt;DataContext&lt;/code&gt; 上：&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;&amp;lt;Window x:Class=&quot;MyPrismApp.Views.MainWindow&quot;
        xmlns=&quot;http://schemas.microsoft.com/winfx/2006/xaml/presentation&quot;
        xmlns:x=&quot;http://schemas.microsoft.com/winfx/2006/xaml&quot;
        xmlns:prism=&quot;http://prismlibrary.com/&quot;
        prism:ViewModelLocator.AutoWireViewModel=&quot;True&quot;&amp;gt; 
    &amp;lt;!-- 开启自动装配：AutoWireViewModel=&quot;True&quot; --&amp;gt;
&amp;lt;/Window&amp;gt;
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;&lt;strong&gt;如何覆盖默认约定？&lt;/strong&gt;
如果在老旧项目中无法遵循文件夹和命名规范，可以在 &lt;code&gt;App.xaml.cs&lt;/code&gt; 中重写 &lt;code&gt;ConfigureViewModelLocator&lt;/code&gt; 进行手动配置：&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;protected override void ConfigureViewModelLocator()
{
    base.ConfigureViewModelLocator();
    // 强制声明：遇到 MainWindow 视图时，无论它在哪，都给它强制挂载 CustomMainWindowViewModel
    ViewModelLocationProvider.Register&amp;lt;MainWindow, CustomMainWindowViewModel&amp;gt;();
}
&lt;/code&gt;&lt;/pre&gt;
&lt;h3&gt;3.3 属性绑定 (&lt;code&gt;BindableBase&lt;/code&gt;)&lt;/h3&gt;
&lt;p&gt;要让属性支持双向 UI 刷新通知，ViewModel 必须继承 &lt;code&gt;BindableBase&lt;/code&gt; 基类，并使用 &lt;code&gt;SetProperty&lt;/code&gt; 方法以触发底层的 &lt;code&gt;INotifyPropertyChanged&lt;/code&gt;。&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;public class MainWindowViewModel : BindableBase
{
    private string _title = &quot;Prism Application&quot;;
    public string Title
    {
        get =&amp;gt; _title;
        set =&amp;gt; SetProperty(ref _title, value); 
    }
}
&lt;/code&gt;&lt;/pre&gt;
&lt;h3&gt;3.4 命令注入 (&lt;code&gt;DelegateCommand&lt;/code&gt;)&lt;/h3&gt;
&lt;p&gt;Prism 的 &lt;code&gt;DelegateCommand&lt;/code&gt; 替代了原生臃肿的 &lt;code&gt;ICommand&lt;/code&gt;，大大简化了命令交互的编写，并支持响应属性变化。&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;public class MainWindowViewModel : BindableBase
{
    public DelegateCommand ClickCommand { get; private set; }
    public DelegateCommand&amp;lt;string&amp;gt; PassParamCommand { get; private set; }

    public MainWindowViewModel()
    {
        ClickCommand = new DelegateCommand(ExecuteClick, CanExecuteClick);
        // ObservesProperty 的高级应用：当 Title 属性变化时，自动重新评估该按钮是否允许被点击！
        ClickCommand.ObservesProperty(() =&amp;gt; Title); 
        
        PassParamCommand = new DelegateCommand&amp;lt;string&amp;gt;(ExecutePassParam);
    }

    private void ExecuteClick() { /* 按钮点击执行的方法 */ }
    
    private bool CanExecuteClick() =&amp;gt; !string.IsNullOrEmpty(Title);
    
    private void ExecutePassParam(string param) { /* 带参数的命令执行 */ }
}
&lt;/code&gt;&lt;/pre&gt;
&lt;hr /&gt;
&lt;h2&gt;4. 区域管理器 (RegionManager) 与区域适配器&lt;/h2&gt;
&lt;h3&gt;4.1 区域 (Region) 概念&lt;/h3&gt;
&lt;p&gt;&lt;code&gt;RegionManager&lt;/code&gt; 是 Prism 处理复杂界面组装的核心。它允许我们将主窗体挖出若干个“占位坑位”（即区域 Region），然后在运行时将具体的 UI 用户控件（Views）动态注入到这些坑位中。这种“搭积木”的设计实现了界面极度低耦合。&lt;/p&gt;
&lt;h3&gt;4.2 如何定义区域？&lt;/h3&gt;
&lt;p&gt;在 XAML 文件的容器控件上设置附加属性 &lt;code&gt;prism:RegionManager.RegionName&lt;/code&gt; 即可开辟坑位：&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;&amp;lt;Window x:Class=&quot;MyPrismApp.Views.MainWindow&quot; ... &amp;gt;
    &amp;lt;Grid&amp;gt;
        &amp;lt;!-- 定义了顶部导航区 --&amp;gt;
        &amp;lt;ContentControl prism:RegionManager.RegionName=&quot;HeaderRegion&quot; Height=&quot;60&quot;/&amp;gt;
        
        &amp;lt;!-- 定义了主体内容展示区 --&amp;gt;
        &amp;lt;ContentControl prism:RegionManager.RegionName=&quot;MainContentRegion&quot; /&amp;gt;
    &amp;lt;/Grid&amp;gt;
&amp;lt;/Window&amp;gt;
&lt;/code&gt;&lt;/pre&gt;
&lt;h3&gt;4.3 区域适配器 (RegionAdapter)&lt;/h3&gt;
&lt;p&gt;&lt;strong&gt;注意&lt;/strong&gt;：并不是所有的 UI 控件都支持通过 &lt;code&gt;RegionName&lt;/code&gt; 标签作为区域使用。
默认情况下，Prism 引擎只认识三种支持做区域的控件：&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;&lt;code&gt;ContentControl&lt;/code&gt; (用来装载单页)&lt;/li&gt;
&lt;li&gt;&lt;code&gt;ItemsControl&lt;/code&gt; (用来装载多页，如 ListBox)&lt;/li&gt;
&lt;li&gt;&lt;code&gt;Selector&lt;/code&gt; (如 TabControl)&lt;/li&gt;
&lt;/ol&gt;
&lt;p&gt;如果你的美工设计了一个流式布局 &lt;code&gt;WrapPanel&lt;/code&gt;，你想把四个独立的看板部件注入进去，若直接写 &lt;code&gt;prism:RegionManager.RegionName=&quot;DashRegion&quot;&lt;/code&gt; &lt;strong&gt;程序会直接抛出异常崩溃&lt;/strong&gt;。
此时必须手写区域适配器来教会 Prism 如何往里面塞内容：&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;// 1. 继承 RegionAdapterBase 并指定目标控件类型为 StackPanel 或 WrapPanel
public class StackPanelRegionAdapter : RegionAdapterBase&amp;lt;StackPanel&amp;gt;
{
    public StackPanelRegionAdapter(IRegionBehaviorFactory regionBehaviorFactory) 
        : base(regionBehaviorFactory) { }

    // 核心逻辑：定义当有新视图注入该区域时，目标控件应该如何把视图接纳进来
    protected override void Adapt(IRegion region, StackPanel regionTarget)
    {
        // 监控区域内部视图集合的变化
        region.Views.CollectionChanged += (s, e) =&amp;gt;
        {
            if (e.Action == System.Collections.Specialized.NotifyCollectionChangedAction.Add)
            {
                // 当有新视图通过导航或 RegisterViewWithRegion 注入时，将其添加为子元素
                foreach (FrameworkElement element in e.NewItems)
                    regionTarget.Children.Add(element);
            }
        };
    }

    protected override IRegion CreateRegion() =&amp;gt; new AllActiveRegion();
}
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;写完后，&lt;strong&gt;必须回到 &lt;code&gt;App.xaml.cs&lt;/code&gt; 中的 &lt;code&gt;ConfigureRegionAdapterMappings&lt;/code&gt; 方法里将其注册&lt;/strong&gt;（见本文 2.2 节代码），至此，你才能安全地在 &lt;code&gt;StackPanel&lt;/code&gt; 上使用 &lt;code&gt;RegionName&lt;/code&gt;。&lt;/p&gt;
&lt;h3&gt;4.4 视图注入 (View Injection)&lt;/h3&gt;
&lt;p&gt;在 ViewModel 中，能够通过 IOC 容器拿到 &lt;code&gt;IRegionManager&lt;/code&gt;，它管理着所有的区域坑位。&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;如何向区域中静态注册页面？&lt;/strong&gt;&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;public MainWindowViewModel(IRegionManager regionManager)
{
    _regionManager = regionManager;
    // 一启动，就向名单为 &apos;HeaderRegion&apos; 的区域中注入 HeaderView 实例
    // 注意：RegisterViewWithRegion 主要用于启动时的固定框架静态加载。对于功能切换，使用后续的【导航】功能。
    _regionManager.RegisterViewWithRegion(&quot;HeaderRegion&quot;, typeof(HeaderView));
}
&lt;/code&gt;&lt;/pre&gt;
&lt;hr /&gt;
&lt;h2&gt;5. 导航功能（跳转、传参、日志、守卫）&lt;/h2&gt;
&lt;p&gt;除了基础的视图装载，Prism 在区域内引入了一套极其完整的导航机制，让你像开发 Web/APP 路由一样开发 WPF。&lt;/p&gt;
&lt;h3&gt;5.1 如何执行导航？&lt;/h3&gt;
&lt;blockquote&gt;
&lt;p&gt;&lt;strong&gt;核心 API：&lt;/strong&gt; &lt;code&gt;regionManager.Regions[&quot;RegionName&quot;].RequestNavigate(&quot;View名&quot;)&lt;/code&gt;
或快捷方式：&lt;code&gt;regionManager.RequestNavigate(&quot;RegionName&quot;, &quot;View名&quot;)&lt;/code&gt;&lt;/p&gt;
&lt;/blockquote&gt;
&lt;pre&gt;&lt;code&gt;private void NavigateToViewA()
{
    // 将名为 &quot;ViewA&quot; 的页面导航推入 &quot;ContentRegion&quot; 区域。
    // ViewA 是一个用户控件，并且必须在 IOC 容器里提前注册为支持导航的对象！
    _regionManager.RequestNavigate(&quot;ContentRegion&quot;, &quot;ViewA&quot;);
}
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;&lt;strong&gt;必须把页面添加到 IOC 中（容器注册）！&lt;/strong&gt;
在 &lt;code&gt;App.xaml.cs&lt;/code&gt; 中重写的 &lt;code&gt;RegisterTypes&lt;/code&gt; 里，通知容器该页面可用于导航：&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;protected override void RegisterTypes(IContainerRegistry containerRegistry)
{
    // 将 ViewA 和 ViewB 注册为导航使用的页面。如果不给别名，默认名字就是 ViewA
    containerRegistry.RegisterForNavigation&amp;lt;ViewA&amp;gt;();
    // 可以指定别名导航
    containerRegistry.RegisterForNavigation&amp;lt;ViewB&amp;gt;(&quot;MyCustomViewB&quot;); 
}
&lt;/code&gt;&lt;/pre&gt;
&lt;h3&gt;5.2 导航传参 &lt;code&gt;NavigationParameters&lt;/code&gt; &amp;amp; &lt;code&gt;INavigationAware&lt;/code&gt; 接口&lt;/h3&gt;
&lt;p&gt;&lt;code&gt;INavigationAware&lt;/code&gt; 是导航感知接口，它包含三个方法：&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;code&gt;OnNavigatedTo&lt;/code&gt;：&lt;strong&gt;导航目标已达到（进入页面时）触发&lt;/strong&gt;。接收参数和页面初始化。&lt;/li&gt;
&lt;li&gt;&lt;code&gt;IsNavigationTarget&lt;/code&gt;：&lt;strong&gt;是否重用当前实例&lt;/strong&gt;。返回 True 则复用当前缓存实例； False 则每次跳转重新创建。&lt;/li&gt;
&lt;li&gt;&lt;code&gt;OnNavigatedFrom&lt;/code&gt;：&lt;strong&gt;离开当前页面时触发&lt;/strong&gt;。&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;&lt;strong&gt;发起导航与传参：&lt;/strong&gt;&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;var parameters = new NavigationParameters();
parameters.Add(&quot;id&quot;, 1001);
parameters.Add(&quot;name&quot;, &quot;张三&quot;);

// 进行带参数的跳转！
_regionManager.RequestNavigate(&quot;ContentRegion&quot;, &quot;ViewB&quot;, parameters);
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;&lt;strong&gt;接收方 ViewModel 实现接口：&lt;/strong&gt;&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;public class ViewBViewModel : BindableBase, INavigationAware
{
    public void OnNavigatedTo(NavigationContext navigationContext)
    {
        // 接收强类型参数
        var id = navigationContext.Parameters.GetValue&amp;lt;int&amp;gt;(&quot;id&quot;);
        var name = navigationContext.Parameters.GetValue&amp;lt;string&amp;gt;(&quot;name&quot;);
    }

    public bool IsNavigationTarget(NavigationContext navigationContext)
    {
        return true; // 表示保持该页面缓存实例，不重复创建
    }

    public void OnNavigatedFrom(NavigationContext navigationContext)
    {
        // 离开页面时可以保存暂存数据
    }
}
&lt;/code&gt;&lt;/pre&gt;
&lt;h3&gt;5.3 导航守卫（确认机制） &lt;code&gt;IConfirmNavigationRequest&lt;/code&gt;&lt;/h3&gt;
&lt;p&gt;如果用户在这个页面填了一半的表单忘了保存，不小心点了跳走怎么办？你需要拦截离开请求。
让 ViewModel 继承自 &lt;code&gt;IConfirmNavigationRequest&lt;/code&gt;（它自带了 &lt;code&gt;INavigationAware&lt;/code&gt; 的三个方法）：&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;public class FormViewModel : BindableBase, IConfirmNavigationRequest
{
    // ... INavigationAware的三个方法省略 ...

    public void ConfirmNavigationRequest(NavigationContext navigationContext, Action&amp;lt;bool&amp;gt; continuationCallback)
    {
        bool result = MessageBox.Show(&quot;您的内容还未保存，确定要离开吗？&quot;, &quot;提示&quot;, MessageBoxButton.YesNo) == MessageBoxResult.Yes;
        // 回调告诉 Prism 框架，是继续导航 (true)，还是立即终止并留在当前页 (false)
        continuationCallback(result); 
    }
}
&lt;/code&gt;&lt;/pre&gt;
&lt;h3&gt;5.4 导航日志/历史记录 &lt;code&gt;IRegionNavigationJournal&lt;/code&gt;&lt;/h3&gt;
&lt;p&gt;Prism 会在这个区域内自动记录你去过的页面，类似浏览器的历史记录（能前进、后退）。我们可以在上下文 &lt;code&gt;NavigationContext&lt;/code&gt; 中拿到它。&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;public class ViewBViewModel : BindableBase, INavigationAware
{
    private IRegionNavigationJournal _journal;

    // 前进和后退按钮命令
    public DelegateCommand GoBackCommand { get; set; }
    public DelegateCommand GoForwardCommand { get; set; }

    public ViewBViewModel()
    {
        GoBackCommand = new DelegateCommand(() =&amp;gt; _journal.GoBack(), () =&amp;gt; _journal.CanGoBack);
        GoForwardCommand = new DelegateCommand(() =&amp;gt; _journal.GoForward(), () =&amp;gt; _journal.CanGoForward);
    }

    public void OnNavigatedTo(NavigationContext navigationContext)
    {
        // 任何被推入的页面的上下文中，都顺带装着导航日志服务对象！
        _journal = navigationContext.NavigationService.Journal;
        
        GoBackCommand.RaiseCanExecuteChanged();
        GoForwardCommand.RaiseCanExecuteChanged();
    }
    // ...
}
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;&lt;em&gt;日常日志操作主要 API&lt;/em&gt;：&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;code&gt;CanGoBack&lt;/code&gt; / &lt;code&gt;CanGoForward&lt;/code&gt;：判断是否有前一页/后一页历史&lt;/li&gt;
&lt;li&gt;&lt;code&gt;GoBack()&lt;/code&gt; / &lt;code&gt;GoForward()&lt;/code&gt;：执行回到上页或前往下页的功能&lt;/li&gt;
&lt;li&gt;&lt;code&gt;Clear()&lt;/code&gt;：清除该区域内的所有跳转历史记录（通常在登录成功进入主页时使用！）。&lt;/li&gt;
&lt;/ul&gt;
&lt;hr /&gt;
&lt;h2&gt;6. 事件聚合器 (EventAggregator)&lt;/h2&gt;
&lt;p&gt;事件聚合器是&lt;strong&gt;多模块、强解耦场景下的消息总线机制&lt;/strong&gt;。
有些视图完全不相互认识，甚至跨越了不能互相引用的不同模块类库。想要相互发消息（例如在“导航栏模块”发一个用户注销消息，在“主工作区模块”要清空数据），只能通过 &lt;code&gt;IEventAggregator&lt;/code&gt; 进行全局广播。&lt;/p&gt;
&lt;h3&gt;6.1 定义事件载体&lt;/h3&gt;
&lt;p&gt;我们需要声明一个继承自 &lt;code&gt;PubSubEvent&amp;lt;参数类型&amp;gt;&lt;/code&gt; 的事件类：&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;// 这是一个传递字符串的消息，你也可以传递自定义实体类
public class MessageSentEvent : PubSubEvent&amp;lt;string&amp;gt; { }
&lt;/code&gt;&lt;/pre&gt;
&lt;h3&gt;6.2 订阅事件（接收方）&lt;/h3&gt;
&lt;p&gt;在接收方的 ViewModel 中直接注入 &lt;code&gt;IEventAggregator&lt;/code&gt;：&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;public class TargetViewModel
{
    public TargetViewModel(IEventAggregator eventAggregator)
    {
        // 订阅该事件。只要有人发 MessageSentEvent 消息，就执行 OnMessageReceived
        eventAggregator.GetEvent&amp;lt;MessageSentEvent&amp;gt;().Subscribe(OnMessageReceived);
        
        // 更高级：允许你设置运行在 UI 线程，以及通过 filter 过滤特定条件的消息
        // eventAggregator.GetEvent&amp;lt;MessageSentEvent&amp;gt;().Subscribe(OnMessageReceived, ThreadOption.UIThread, false, msg =&amp;gt; msg.Contains(&quot;Error&quot;));
    }

    private void OnMessageReceived(string message)
    {
        // 处理收到的消息
    }
}
&lt;/code&gt;&lt;/pre&gt;
&lt;h3&gt;6.3 发布事件（发送方）&lt;/h3&gt;
&lt;p&gt;发送方的 ViewModel 同样通过依赖注入拿到 &lt;code&gt;IEventAggregator&lt;/code&gt;：&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;public class SourceViewModel
{
    private readonly IEventAggregator _eventAggregator;

    public SourceViewModel(IEventAggregator eventAggregator)
    {
        _eventAggregator = eventAggregator;
    }

    private void SendMessage()
    {
        // 向全局广播这条消息！全世界订阅这个事件的人都能收到
        _eventAggregator.GetEvent&amp;lt;MessageSentEvent&amp;gt;().Publish(&quot;Hello from Source!&quot;);
    }
}
&lt;/code&gt;&lt;/pre&gt;
&lt;hr /&gt;
&lt;h2&gt;7. 对话框服务 (DialogService)&lt;/h2&gt;
&lt;p&gt;在标准的 MVVM 模式中，如果在 ViewModel 中直接调用 &lt;code&gt;MessageBox.Show()&lt;/code&gt; 会破坏解耦（因为引入了 UI 相关的命名空间），且无法进行单元测试。
Prism 提供了 &lt;code&gt;IDialogService&lt;/code&gt; 以解决这个问题。通过它可以按显示标准弹窗或者自定义的浮层（比如专门做的一个确认框 View）。&lt;/p&gt;
&lt;h3&gt;7.1 创建和注册对话框 View 和 ViewModel&lt;/h3&gt;
&lt;p&gt;&lt;em&gt;像注册普通页面一样，只不过专门有个 RegisterDialog API。&lt;/em&gt;&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;新建 &lt;code&gt;AlertDialog&lt;/code&gt;(View) 和 &lt;code&gt;AlertDialogViewModel&lt;/code&gt; (必须实现 &lt;code&gt;IDialogAware&lt;/code&gt; 接口)。&lt;/li&gt;
&lt;li&gt;在 &lt;code&gt;App.xaml.cs&lt;/code&gt; 中注册它：&lt;/li&gt;
&lt;/ol&gt;
&lt;pre&gt;&lt;code&gt;protected override void RegisterTypes(IContainerRegistry containerRegistry)
{
    containerRegistry.RegisterDialog&amp;lt;AlertDialog, AlertDialogViewModel&amp;gt;();
}
&lt;/code&gt;&lt;/pre&gt;
&lt;h3&gt;7.2 在 ViewModel 中实现 &lt;code&gt;IDialogAware&lt;/code&gt;&lt;/h3&gt;
&lt;p&gt;这是提供给弹层对话框专用的接口。&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;public class AlertDialogViewModel : BindableBase, IDialogAware
{
    // 实现 IDialogAware 接口属性
    public string Title =&amp;gt; &quot;系统提示&quot;;
    public event Action&amp;lt;IDialogResult&amp;gt; RequestClose; // 用它主动关闭弹窗

    public void OnDialogOpened(IDialogParameters parameters)
    {
        // 弹层开启时执行，并能强类型接参数
        var message = parameters.GetValue&amp;lt;string&amp;gt;(&quot;message&quot;);
    }

    public bool CanCloseDialog() =&amp;gt; true;

    public void OnDialogClosed() { }

    private void CloseDialogFunc()
    {
        // 通过传递返回结果来结束自己！可以带上 OK 或 Cancel 以及返回值参数
        var result = new DialogResult(ButtonResult.OK);
        RequestClose?.Invoke(result);
    }
}
&lt;/code&gt;&lt;/pre&gt;
&lt;h3&gt;7.3 调用/弹出对话框&lt;/h3&gt;
&lt;p&gt;在需要弹窗的普通 ViewModel 中注入 &lt;code&gt;IDialogService&lt;/code&gt; 即可。&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;public class MainViewModel
{
    private readonly IDialogService _dialogService;
    
    public MainViewModel(IDialogService dialogService)
    {
        _dialogService = dialogService;
    }

    private void ShowAlert()
    {
        var parameters = new DialogParameters();
        parameters.Add(&quot;message&quot;, &quot;这是一条警告弹窗！&quot;);

        // 调用刚才注册的 AlertDialog。并等待它的回调结果
        _dialogService.ShowDialog(&quot;AlertDialog&quot;, parameters, result =&amp;gt;
        {
            if (result.Result == ButtonResult.OK)
            {
                // 用户在弹窗里点击了确认！
            }
        });
    }
}
&lt;/code&gt;&lt;/pre&gt;
&lt;hr /&gt;
&lt;h2&gt;8. 模块化开发 (Modularity)&lt;/h2&gt;
&lt;p&gt;模块化是大型 WPF 应用使用 Prism 最为核心的原因之一。Prism 允许我们将独立开发的业务放到不同的类库（类项目）中（例如：仓储模块类库、人员模块类库），并在主程序启动时去加载扫描它们。&lt;/p&gt;
&lt;h3&gt;8.1 定义一个模块 (Module)&lt;/h3&gt;
&lt;p&gt;新建一个 WPF 类库，引用 Prism，并编写一个实现了 &lt;code&gt;IModule&lt;/code&gt; 的入口类：&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;public class InventoryModule : IModule
{
    // 步骤1：向容器注册该模块专有的服务、页面路由
    public void RegisterTypes(IContainerRegistry containerRegistry)
    {
        containerRegistry.RegisterForNavigation&amp;lt;InventoryView&amp;gt;();
        containerRegistry.RegisterSingleton&amp;lt;IInventoryService, InventoryService&amp;gt;();
    }

    // 步骤2：执行模块初始化相关逻辑（比如去给主页面的导航菜单注册一个侧边栏按钮）
    public void OnInitialized(IContainerProvider containerProvider)
    {
        var regionManager = containerProvider.Resolve&amp;lt;IRegionManager&amp;gt;();
        regionManager.RegisterViewWithRegion(&quot;MenuRegion&quot;, typeof(InventoryMenuBtnView));
    }
}
&lt;/code&gt;&lt;/pre&gt;
&lt;h3&gt;8.2 在宿主程序配置加载哪些模块 (ModuleCatalog)&lt;/h3&gt;
&lt;p&gt;在 &lt;code&gt;App.xaml.cs&lt;/code&gt; 中：&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;protected override void ConfigureModuleCatalog(IModuleCatalog moduleCatalog)
{
    // 方法一：代码通过引用直接注册
    moduleCatalog.AddModule&amp;lt;InventoryModule&amp;gt;();
    
    // 方法二：目录扫描注册。主程序甚至不需要引用这个雷库，直接把编译出来的dll丢进 Modules 文件夹即可！(高度解耦、支持热插拔插件)
    // base.ConfigureModuleCatalog(moduleCatalog);
}

// 针对目录扫描（DirectoryModuleCatalog）：
protected override IModuleCatalog CreateModuleCatalog()
{
    return new DirectoryModuleCatalog() { ModulePath = @&quot;.\Modules&quot; };
}
&lt;/code&gt;&lt;/pre&gt;
&lt;h3&gt;最终总结&lt;/h3&gt;
&lt;p&gt;Prism 的这套大闭环架构：利用 &lt;code&gt;RegionManager&lt;/code&gt; 将整个应用进行解耦拆块区域化，借助 &lt;code&gt;Modularity&lt;/code&gt; 将业务逻辑物理分拆成不同的 dll，再通过 &lt;code&gt;View Injection&lt;/code&gt; 、支持带参数和拦截器的 &lt;strong&gt;Navigation机制&lt;/strong&gt;、实现跨模块通信的 &lt;strong&gt;EventAggregator 消息系统&lt;/strong&gt; 以及解耦友善的 &lt;strong&gt;DialogService&lt;/strong&gt; 在各个模块之间互通有无。掌握这些，即可架构出现代完善的大型桌面客户端。&lt;/p&gt;
</content:encoded></item><item><title>WPF学习指南</title><link>https://meteor-comet.github.io/posts/wpf-learning/</link><guid isPermaLink="true">https://meteor-comet.github.io/posts/wpf-learning/</guid><description>Windows Presentation Foundation完整开发教程</description><pubDate>Mon, 20 May 2024 00:00:00 GMT</pubDate><content:encoded>&lt;h1&gt;WPF 全面指南&lt;/h1&gt;
&lt;h2&gt;目录&lt;/h2&gt;
&lt;ul&gt;
&lt;li&gt;&lt;a href=&quot;#1-wpf%E6%A6%82%E8%BF%B0&quot;&gt;1. WPF概述&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;#2-xaml%E5%9F%BA%E7%A1%80&quot;&gt;2. XAML基础&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;#3-wpf%E5%B8%83%E5%B1%80%E7%B3%BB%E7%BB%9F&quot;&gt;3. WPF布局系统&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;#4-wpf%E6%8E%A7%E4%BB%B6%E8%AF%A6%E8%A7%A3&quot;&gt;4. WPF控件详解&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;#5-wpf-%E6%A0%B8%E5%BF%83%E9%AA%A8%E6%9E%B6%E4%BE%9D%E8%B5%96%E5%B1%9E%E6%80%A7%E4%B8%8E%E9%99%84%E5%8A%A0%E5%B1%9E%E6%80%A7%E8%AF%A6%E8%A7%A3&quot;&gt;5. WPF 核心骨架：依赖属性与附加属性详解&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;#6-wpf-%E7%9A%84%E8%84%89%E7%BB%9C%E8%B7%AF%E7%94%B1%E4%BA%8B%E4%BB%B6%E7%B3%BB%E7%BB%9F-routed-events&quot;&gt;6. WPF 的脉络：路由事件系统 (Routed Events)&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;#7-%E6%95%B0%E6%8D%AE%E7%BB%91%E5%AE%9Adata-binding&quot;&gt;7. 数据绑定（Data Binding）&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;#8-mvvm%E8%AE%BE%E8%AE%A1%E6%A8%A1%E5%BC%8F&quot;&gt;8. MVVM设计模式&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;#9-%E6%A0%B7%E5%BC%8F%E4%B8%8E%E6%A8%A1%E6%9D%BF&quot;&gt;9. 样式与模板&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;#10-%E5%8A%A8%E7%94%BB%E4%B8%8E%E5%9B%BE%E5%BD%A2&quot;&gt;10. 动画与图形&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;#11-%E8%B7%AF%E7%94%B1%E4%BA%8B%E4%BB%B6%E4%B8%8E%E5%91%BD%E4%BB%A4%E7%B3%BB%E7%BB%9F&quot;&gt;11. 路由事件与命令系统&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;#12-wpf%E6%80%A7%E8%83%BD%E4%BC%98%E5%8C%96%E4%B8%8E%E6%9C%80%E4%BD%B3%E5%AE%9E%E8%B7%B5&quot;&gt;12. WPF性能优化与最佳实践&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;#13-mvvm%E6%A8%A1%E5%BC%8F%E8%BF%9B%E9%98%B6%E4%B8%8E%E5%B8%B8%E7%94%A8%E6%A1%86%E6%9E%B6&quot;&gt;13. MVVM模式进阶与常用框架&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;#14-communitytoolkitmvvm-%E6%A1%86%E6%9E%B6%E8%AF%A6%E8%A7%A3&quot;&gt;14. CommunityToolkit.Mvvm 框架详解&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;#15-%E8%BD%BB%E9%87%8F%E7%BA%A7%E5%9B%BD%E4%BA%A7-orm-%E7%A5%9E%E5%99%A8sqlsugar-%E8%AF%A6%E7%BB%86%E4%BD%BF%E7%94%A8%E6%8C%87%E5%8D%97&quot;&gt;15. 轻量级国产 ORM 神器：SqlSugar 详细使用指南&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;#16-%E5%85%A8%E7%90%83%E6%80%A7%E8%83%BD%E7%AC%AC%E4%B8%80%E7%9A%84%E5%BE%AE%E5%9E%8B-ormdapper-%E5%85%A8%E5%9C%BA%E6%99%AF-api-%E4%BD%BF%E7%94%A8%E5%A4%A7%E5%85%A8&quot;&gt;16. 全球性能第一的微型 ORM：Dapper 全场景 API 使用大全&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;#17-wpf-%E7%8E%B0%E4%BB%A3-ui-%E6%8E%A7%E4%BB%B6%E5%BA%93%E8%AF%A6%E8%A7%A3handycontrol-%E5%85%A8%E5%9C%BA%E6%99%AF%E5%AE%9E%E6%88%98&quot;&gt;17. WPF 现代 UI 控件库详解：HandyControl 全场景实战&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;h2&gt;1. WPF概述&lt;/h2&gt;
&lt;h3&gt;1.1 什么是WPF&lt;/h3&gt;
&lt;p&gt;Windows Presentation Foundation (WPF) 是微软推出的基于 .NET Framework 的用户界面框架，用于创建 Windows 桌面应用程序。WPF 提供了丰富的图形、动画和交互功能，支持数据绑定、样式、模板等现代 UI 开发特性，是构建现代化 Windows 应用程序的理想选择。&lt;/p&gt;
&lt;p&gt;WPF 于 2006 年随 .NET Framework 3.0 一起发布，旨在替代传统的 Windows Forms 技术，提供更现代、更灵活的 UI 开发体验。&lt;/p&gt;
&lt;h3&gt;1.2 WPF的体系结构&lt;/h3&gt;
&lt;p&gt;WPF 的体系结构由以下几个主要部分组成：&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;
&lt;p&gt;&lt;strong&gt;表示子系统&lt;/strong&gt;：负责渲染和显示 UI 元素，包括：&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;DirectX 渲染引擎：使用 DirectX 进行硬件加速渲染&lt;/li&gt;
&lt;li&gt;媒体集成：支持音频、视频和 3D 内容&lt;/li&gt;
&lt;li&gt;文本渲染：基于 ClearType 技术的高质量文本渲染&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;&lt;strong&gt;内容呈现系统&lt;/strong&gt;：定义 UI 元素的结构和行为，包括：&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;视觉树（Visual Tree）：表示 UI 的实际渲染结构&lt;/li&gt;
&lt;li&gt;逻辑树（Logical Tree）：表示应用程序定义的 UI 结构&lt;/li&gt;
&lt;li&gt;元素树（Element Tree）：逻辑树的扩展，包含更多的布局信息&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;&lt;strong&gt;应用程序模型&lt;/strong&gt;：提供应用程序的生命周期管理，包括：&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;应用程序启动和关闭&lt;/li&gt;
&lt;li&gt;窗口管理&lt;/li&gt;
&lt;li&gt;导航系统&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;&lt;strong&gt;核心服务&lt;/strong&gt;：提供各种支持功能，包括：&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;数据绑定&lt;/li&gt;
&lt;li&gt;样式和模板&lt;/li&gt;
&lt;li&gt;资源系统&lt;/li&gt;
&lt;li&gt;命令系统&lt;/li&gt;
&lt;li&gt;事件系统&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;/ol&gt;
&lt;h3&gt;1.3 WPF与WinForms的区别&lt;/h3&gt;
&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;特性&lt;/th&gt;
&lt;th&gt;WPF&lt;/th&gt;
&lt;th&gt;WinForms&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;渲染引擎&lt;/td&gt;
&lt;td&gt;DirectX 硬件加速&lt;/td&gt;
&lt;td&gt;GDI+ 软件渲染&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;UI 定义&lt;/td&gt;
&lt;td&gt;XAML（声明式）&lt;/td&gt;
&lt;td&gt;代码（命令式）&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;布局系统&lt;/td&gt;
&lt;td&gt;灵活的布局面板（Grid、StackPanel等）&lt;/td&gt;
&lt;td&gt;固定位置和大小&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;数据绑定&lt;/td&gt;
&lt;td&gt;强大的双向数据绑定&lt;/td&gt;
&lt;td&gt;有限的数据绑定能力&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;样式和模板&lt;/td&gt;
&lt;td&gt;丰富的样式和模板系统&lt;/td&gt;
&lt;td&gt;有限的样式支持&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;动画支持&lt;/td&gt;
&lt;td&gt;内置动画系统&lt;/td&gt;
&lt;td&gt;需要第三方库或自定义实现&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;3D 支持&lt;/td&gt;
&lt;td&gt;内置 3D 渲染能力&lt;/td&gt;
&lt;td&gt;不支持&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;分辨率独立性&lt;/td&gt;
&lt;td&gt;基于设备无关单位&lt;/td&gt;
&lt;td&gt;基于像素&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;学习曲线&lt;/td&gt;
&lt;td&gt;较陡峭&lt;/td&gt;
&lt;td&gt;较平缓&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;
&lt;h2&gt;2. XAML基础&lt;/h2&gt;
&lt;h3&gt;2.1 XAML简介&lt;/h3&gt;
&lt;p&gt;XAML（Extensible Application Markup Language）是一种声明式标记语言，用于定义 WPF 应用程序的用户界面。XAML 基于 XML，提供了一种简洁、直观的方式来创建和布局 UI 元素。&lt;/p&gt;
&lt;p&gt;XAML 的主要优点：&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;声明式语法，使 UI 定义更加清晰&lt;/li&gt;
&lt;li&gt;与代码分离，提高代码的可维护性&lt;/li&gt;
&lt;li&gt;工具支持良好，Visual Studio 和 Blend 都提供了可视化编辑功能&lt;/li&gt;
&lt;li&gt;支持复杂的对象初始化和属性设置&lt;/li&gt;
&lt;/ul&gt;
&lt;h3&gt;2.2 XAML元素与属性&lt;/h3&gt;
&lt;p&gt;XAML 中的每个元素对应一个 .NET 类，属性对应类的属性。例如：&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;&amp;lt;!-- 元素 = 类，属性 = 类的属性 --&amp;gt;
&amp;lt;Button Content=&quot;Click Me&quot; Width=&quot;100&quot; Height=&quot;30&quot; /&amp;gt;

&amp;lt;!-- 等同于 C# 代码 --&amp;gt;
// Button button = new Button();
// button.Content = &quot;Click Me&quot;;
// button.Width = 100;
// button.Height = 30;
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;XAML 元素可以包含子元素，这对应于对象的组合关系：&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;&amp;lt;Grid&amp;gt;
    &amp;lt;Button Content=&quot;Click Me&quot; /&amp;gt;
&amp;lt;/Grid&amp;gt;

&amp;lt;!-- 等同于 C# 代码 --&amp;gt;
// Grid grid = new Grid();
// Button button = new Button();
// button.Content = &quot;Click Me&quot;;
// grid.Children.Add(button);
&lt;/code&gt;&lt;/pre&gt;
&lt;h3&gt;2.3 对象与属性初始化&lt;/h3&gt;
&lt;p&gt;XAML 提供了多种方式来初始化对象和设置属性：&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;
&lt;p&gt;&lt;strong&gt;属性语法&lt;/strong&gt;：使用属性名和值的方式设置属性&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;&amp;lt;Button Width=&quot;100&quot; Height=&quot;30&quot; /&amp;gt;
&lt;/code&gt;&lt;/pre&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;&lt;strong&gt;元素语法&lt;/strong&gt;：使用子元素的方式设置属性&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;&amp;lt;Button&amp;gt;
    &amp;lt;Button.Width&amp;gt;100&amp;lt;/Button.Width&amp;gt;
    &amp;lt;Button.Height&amp;gt;30&amp;lt;/Button.Height&amp;gt;
&amp;lt;/Button&amp;gt;
&lt;/code&gt;&lt;/pre&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;&lt;strong&gt;内容语法&lt;/strong&gt;：对于只有一个内容属性的元素，可以直接设置内容&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;&amp;lt;Button&amp;gt;Click Me&amp;lt;/Button&amp;gt;
&lt;/code&gt;&lt;/pre&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;&lt;strong&gt;集合语法&lt;/strong&gt;：对于集合类型的属性，可以使用子元素添加集合项&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;&amp;lt;Grid&amp;gt;
    &amp;lt;Grid.ColumnDefinitions&amp;gt;
        &amp;lt;ColumnDefinition Width=&quot;*&quot; /&amp;gt;
        &amp;lt;ColumnDefinition Width=&quot;*&quot; /&amp;gt;
    &amp;lt;/Grid.ColumnDefinitions&amp;gt;
&amp;lt;/Grid&amp;gt;
&lt;/code&gt;&lt;/pre&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;&lt;strong&gt;标记扩展&lt;/strong&gt;：使用特殊的语法来引用其他对象或执行特殊操作&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;&amp;lt;Button Content=&quot;{Binding ButtonText}&quot; /&amp;gt;
&lt;/code&gt;&lt;/pre&gt;
&lt;/li&gt;
&lt;/ol&gt;
&lt;h3&gt;2.4 XAML命名空间&lt;/h3&gt;
&lt;p&gt;XAML 文件中的命名空间定义了可用的元素和类型：&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;&amp;lt;Window x:Class=&quot;MyWpfApp.MainWindow&quot;
        xmlns=&quot;http://schemas.microsoft.com/winfx/2006/xaml/presentation&quot;
        xmlns:x=&quot;http://schemas.microsoft.com/winfx/2006/xaml&quot;
        xmlns:local=&quot;clr-namespace:MyWpfApp&quot;
        Title=&quot;MainWindow&quot; Height=&quot;450&quot; Width=&quot;800&quot;&amp;gt;
    &amp;lt;!-- 内容 --&amp;gt;
&amp;lt;/Window&amp;gt;
&lt;/code&gt;&lt;/pre&gt;
&lt;ul&gt;
&lt;li&gt;&lt;code&gt;xmlns=&quot;http://schemas.microsoft.com/winfx/2006/xaml/presentation&quot;&lt;/code&gt;：WPF 核心命名空间，包含所有 UI 元素&lt;/li&gt;
&lt;li&gt;&lt;code&gt;xmlns:x=&quot;http://schemas.microsoft.com/winfx/2006/xaml&quot;&lt;/code&gt;：XAML 语言命名空间，包含 XAML 特定的功能&lt;/li&gt;
&lt;li&gt;&lt;code&gt;xmlns:local=&quot;clr-namespace:MyWpfApp&quot;&lt;/code&gt;：本地命名空间，引用当前项目中的类型&lt;/li&gt;
&lt;/ul&gt;
&lt;h2&gt;3. WPF布局系统&lt;/h2&gt;
&lt;h3&gt;3.1 布局面板概览&lt;/h3&gt;
&lt;p&gt;WPF 提供了多种布局面板，用于组织和排列 UI 元素：&lt;/p&gt;
&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;布局面板&lt;/th&gt;
&lt;th&gt;描述&lt;/th&gt;
&lt;th&gt;适用场景&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;Grid&lt;/td&gt;
&lt;td&gt;网格布局，支持行列划分&lt;/td&gt;
&lt;td&gt;复杂布局，需要精确定位&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;StackPanel&lt;/td&gt;
&lt;td&gt;堆叠布局，水平或垂直排列&lt;/td&gt;
&lt;td&gt;简单的线性排列&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;WrapPanel&lt;/td&gt;
&lt;td&gt;自动换行的堆叠布局&lt;/td&gt;
&lt;td&gt;动态数量的元素排列&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;DockPanel&lt;/td&gt;
&lt;td&gt;停靠布局，元素可以停靠在边缘&lt;/td&gt;
&lt;td&gt;工具栏、状态栏等&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Canvas&lt;/td&gt;
&lt;td&gt;画布布局，使用绝对坐标定位&lt;/td&gt;
&lt;td&gt;自定义绘图、游戏界面&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;UniformGrid&lt;/td&gt;
&lt;td&gt;均匀网格布局，所有单元格大小相同&lt;/td&gt;
&lt;td&gt;图标网格、棋盘等&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Viewbox&lt;/td&gt;
&lt;td&gt;自动缩放内容以适应容器&lt;/td&gt;
&lt;td&gt;需要自适应大小的内容&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;
&lt;h3&gt;3.2 Grid布局&lt;/h3&gt;
&lt;p&gt;Grid 是最常用的布局面板，它允许通过定义行和列来创建复杂的布局：&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;&amp;lt;Grid&amp;gt;
    &amp;lt;!-- 定义列 --&amp;gt;
    &amp;lt;Grid.ColumnDefinitions&amp;gt;
        &amp;lt;ColumnDefinition Width=&quot;100&quot; /&amp;gt; &amp;lt;!-- 固定宽度 --&amp;gt;
        &amp;lt;ColumnDefinition Width=&quot;*&quot; /&amp;gt; &amp;lt;!-- 自适应宽度 --&amp;gt;
        &amp;lt;ColumnDefinition Width=&quot;2*&quot; /&amp;gt; &amp;lt;!-- 自适应宽度，是前一列的两倍 --&amp;gt;
    &amp;lt;/Grid.ColumnDefinitions&amp;gt;
    
    &amp;lt;!-- 定义行 --&amp;gt;
    &amp;lt;Grid.RowDefinitions&amp;gt;
        &amp;lt;RowDefinition Height=&quot;50&quot; /&amp;gt; &amp;lt;!-- 固定高度 --&amp;gt;
        &amp;lt;RowDefinition Height=&quot;*&quot; /&amp;gt; &amp;lt;!-- 自适应高度 --&amp;gt;
        &amp;lt;RowDefinition Height=&quot;40&quot; /&amp;gt; &amp;lt;!-- 固定高度 --&amp;gt;
    &amp;lt;/Grid.RowDefinitions&amp;gt;
    
    &amp;lt;!-- 放置控件 --&amp;gt;
    &amp;lt;Label Content=&quot;用户名:&quot; Grid.Column=&quot;0&quot; Grid.Row=&quot;0&quot; VerticalAlignment=&quot;Center&quot; /&amp;gt;
    &amp;lt;TextBox Grid.Column=&quot;1&quot; Grid.Row=&quot;0&quot; Grid.ColumnSpan=&quot;2&quot; Margin=&quot;5&quot; /&amp;gt;
    
    &amp;lt;Label Content=&quot;密码:&quot; Grid.Column=&quot;0&quot; Grid.Row=&quot;1&quot; VerticalAlignment=&quot;Top&quot; Margin=&quot;0,5,0,0&quot; /&amp;gt;
    &amp;lt;PasswordBox Grid.Column=&quot;1&quot; Grid.Row=&quot;1&quot; Grid.ColumnSpan=&quot;2&quot; Margin=&quot;5&quot; /&amp;gt;
    
    &amp;lt;Button Content=&quot;登录&quot; Grid.Column=&quot;1&quot; Grid.Row=&quot;2&quot; HorizontalAlignment=&quot;Right&quot; Margin=&quot;5&quot; /&amp;gt;
    &amp;lt;Button Content=&quot;取消&quot; Grid.Column=&quot;2&quot; Grid.Row=&quot;2&quot; HorizontalAlignment=&quot;Left&quot; Margin=&quot;5&quot; /&amp;gt;
&amp;lt;/Grid&amp;gt;
&lt;/code&gt;&lt;/pre&gt;
&lt;h3&gt;3.3 StackPanel与WrapPanel&lt;/h3&gt;
&lt;p&gt;StackPanel 用于创建简单的线性布局，元素可以水平或垂直排列：&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;&amp;lt;!-- 垂直排列 --&amp;gt;
&amp;lt;StackPanel Orientation=&quot;Vertical&quot; Margin=&quot;10&quot; Padding=&quot;10&quot; Background=&quot;LightGray&quot;&amp;gt;
    &amp;lt;TextBlock Text=&quot;个人信息&quot; FontSize=&quot;16&quot; FontWeight=&quot;Bold&quot; Margin=&quot;0,0,0,10&quot; /&amp;gt;
    &amp;lt;StackPanel Orientation=&quot;Horizontal&quot; Margin=&quot;0,5,0,5&quot;&amp;gt;
        &amp;lt;TextBlock Text=&quot;姓名:&quot; Width=&quot;80&quot; VerticalAlignment=&quot;Center&quot; /&amp;gt;
        &amp;lt;TextBox Width=&quot;200&quot; /&amp;gt;
    &amp;lt;/StackPanel&amp;gt;
    &amp;lt;StackPanel Orientation=&quot;Horizontal&quot; Margin=&quot;0,5,0,5&quot;&amp;gt;
        &amp;lt;TextBlock Text=&quot;年龄:&quot; Width=&quot;80&quot; VerticalAlignment=&quot;Center&quot; /&amp;gt;
        &amp;lt;TextBox Width=&quot;200&quot; /&amp;gt;
    &amp;lt;/StackPanel&amp;gt;
    &amp;lt;StackPanel Orientation=&quot;Horizontal&quot; Margin=&quot;0,10,0,0&quot; HorizontalAlignment=&quot;Right&quot;&amp;gt;
        &amp;lt;Button Content=&quot;保存&quot; Margin=&quot;0,0,10,0&quot; /&amp;gt;
        &amp;lt;Button Content=&quot;取消&quot; /&amp;gt;
    &amp;lt;/StackPanel&amp;gt;
&amp;lt;/StackPanel&amp;gt;
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;WrapPanel 类似于 StackPanel，但当元素超出容器边界时会自动换行：&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;&amp;lt;WrapPanel Orientation=&quot;Horizontal&quot; Margin=&quot;10&quot; Padding=&quot;10&quot; Background=&quot;LightGray&quot;&amp;gt;
    &amp;lt;Button Content=&quot;按钮 1&quot; Margin=&quot;5&quot; Width=&quot;100&quot; /&amp;gt;
    &amp;lt;Button Content=&quot;按钮 2&quot; Margin=&quot;5&quot; Width=&quot;100&quot; /&amp;gt;
    &amp;lt;Button Content=&quot;按钮 3&quot; Margin=&quot;5&quot; Width=&quot;100&quot; /&amp;gt;
    &amp;lt;Button Content=&quot;按钮 4&quot; Margin=&quot;5&quot; Width=&quot;100&quot; /&amp;gt;
    &amp;lt;Button Content=&quot;按钮 5&quot; Margin=&quot;5&quot; Width=&quot;100&quot; /&amp;gt;
    &amp;lt;Button Content=&quot;按钮 6&quot; Margin=&quot;5&quot; Width=&quot;100&quot; /&amp;gt;
&amp;lt;/WrapPanel&amp;gt;
&lt;/code&gt;&lt;/pre&gt;
&lt;h3&gt;3.4 DockPanel与Canvas&lt;/h3&gt;
&lt;p&gt;DockPanel 允许元素停靠在容器的边缘：&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;&amp;lt;DockPanel LastChildFill=&quot;True&quot;&amp;gt;
    &amp;lt;Menu DockPanel.Dock=&quot;Top&quot;&amp;gt;
        &amp;lt;MenuItem Header=&quot;文件&quot;&amp;gt;
            &amp;lt;MenuItem Header=&quot;新建&quot; /&amp;gt;
            &amp;lt;MenuItem Header=&quot;打开&quot; /&amp;gt;
            &amp;lt;MenuItem Header=&quot;保存&quot; /&amp;gt;
        &amp;lt;/MenuItem&amp;gt;
        &amp;lt;MenuItem Header=&quot;编辑&quot;&amp;gt;
            &amp;lt;MenuItem Header=&quot;复制&quot; /&amp;gt;
            &amp;lt;MenuItem Header=&quot;粘贴&quot; /&amp;gt;
        &amp;lt;/MenuItem&amp;gt;
    &amp;lt;/Menu&amp;gt;
    
    &amp;lt;ToolBar DockPanel.Dock=&quot;Top&quot;&amp;gt;
        &amp;lt;Button Content=&quot;新建&quot; /&amp;gt;
        &amp;lt;Button Content=&quot;打开&quot; /&amp;gt;
        &amp;lt;Button Content=&quot;保存&quot; /&amp;gt;
    &amp;lt;/ToolBar&amp;gt;
    
    &amp;lt;StatusBar DockPanel.Dock=&quot;Bottom&quot;&amp;gt;
        &amp;lt;TextBlock&amp;gt;状态栏信息&amp;lt;/TextBlock&amp;gt;
    &amp;lt;/StatusBar&amp;gt;
    
    &amp;lt;StackPanel DockPanel.Dock=&quot;Left&quot; Width=&quot;200&quot; Background=&quot;LightGray&quot;&amp;gt;
        &amp;lt;TextBlock Text=&quot;导航菜单&quot; Margin=&quot;10&quot; FontWeight=&quot;Bold&quot; /&amp;gt;
        &amp;lt;Button Content=&quot;首页&quot; Margin=&quot;10&quot; /&amp;gt;
        &amp;lt;Button Content=&quot;设置&quot; Margin=&quot;10&quot; /&amp;gt;
        &amp;lt;Button Content=&quot;帮助&quot; Margin=&quot;10&quot; /&amp;gt;
    &amp;lt;/StackPanel&amp;gt;
    
    &amp;lt;Grid Background=&quot;White&quot;&amp;gt;
        &amp;lt;TextBlock Text=&quot;主内容区域&quot; HorizontalAlignment=&quot;Center&quot; VerticalAlignment=&quot;Center&quot; /&amp;gt;
    &amp;lt;/Grid&amp;gt;
&amp;lt;/DockPanel&amp;gt;
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Canvas 使用绝对坐标定位元素，适用于需要精确控制元素位置的场景：&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;&amp;lt;Canvas Width=&quot;400&quot; Height=&quot;300&quot; Background=&quot;LightGray&quot;&amp;gt;
    &amp;lt;Ellipse Canvas.Left=&quot;50&quot; Canvas.Top=&quot;50&quot; Width=&quot;100&quot; Height=&quot;100&quot; Fill=&quot;Red&quot; /&amp;gt;
    &amp;lt;Rectangle Canvas.Left=&quot;200&quot; Canvas.Top=&quot;100&quot; Width=&quot;120&quot; Height=&quot;80&quot; Fill=&quot;Blue&quot; /&amp;gt;
    &amp;lt;TextBlock Canvas.Left=&quot;70&quot; Canvas.Top=&quot;90&quot; FontSize=&quot;16&quot; Foreground=&quot;White&quot;&amp;gt;红色圆形&amp;lt;/TextBlock&amp;gt;
    &amp;lt;TextBlock Canvas.Left=&quot;220&quot; Canvas.Top=&quot;130&quot; FontSize=&quot;16&quot; Foreground=&quot;White&quot;&amp;gt;蓝色矩形&amp;lt;/TextBlock&amp;gt;
&amp;lt;/Canvas&amp;gt;
&lt;/code&gt;&lt;/pre&gt;
&lt;h3&gt;3.5 布局最佳实践&lt;/h3&gt;
&lt;ol&gt;
&lt;li&gt;&lt;strong&gt;使用合适的布局面板&lt;/strong&gt;：根据具体需求选择合适的布局面板&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;优先使用 Grid&lt;/strong&gt;：对于复杂布局，Grid 提供了最灵活的控制&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;避免过度嵌套&lt;/strong&gt;：过多的布局嵌套会影响性能&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;使用 Margin 和 Padding&lt;/strong&gt;：合理使用边距和内边距来创建空间&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;使用 Alignment 属性&lt;/strong&gt;：利用 HorizontalAlignment 和 VerticalAlignment 来控制元素的对齐方式&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;使用 Star 尺寸&lt;/strong&gt;：对于需要自适应的行和列，使用 Star 尺寸&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;考虑响应式设计&lt;/strong&gt;：设计能够适应不同窗口大小的布局&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;使用 Viewbox&lt;/strong&gt;：对于需要缩放的内容，使用 Viewbox&lt;/li&gt;
&lt;/ol&gt;
&lt;h3&gt;3.6 布局相关附加属性总览（Attached Properties）&lt;/h3&gt;
&lt;p&gt;布局面板大量依赖“附加属性（Attached Property）”来描述子元素在布局中的行为。这些属性看似是子控件的属性，实际上由父布局面板定义。&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;
&lt;p&gt;&lt;strong&gt;Grid 相关附加属性&lt;/strong&gt;&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;code&gt;Grid.Row&lt;/code&gt;：子元素所在的行索引（从 0 开始）&lt;/li&gt;
&lt;li&gt;&lt;code&gt;Grid.Column&lt;/code&gt;：子元素所在的列索引（从 0 开始）&lt;/li&gt;
&lt;li&gt;&lt;code&gt;Grid.RowSpan&lt;/code&gt;：子元素跨越的行数&lt;/li&gt;
&lt;li&gt;&lt;code&gt;Grid.ColumnSpan&lt;/code&gt;：子元素跨越的列数&lt;/li&gt;
&lt;li&gt;&lt;code&gt;Grid.IsSharedSizeScope&lt;/code&gt;：用在父级 Grid 上，使多个 Grid 之间共享列宽&lt;/li&gt;
&lt;li&gt;&lt;code&gt;Grid.ShowGridLines&lt;/code&gt;：调试时常用，显示网格线（一般仅在开发阶段打开）&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;&lt;strong&gt;DockPanel 相关附加属性&lt;/strong&gt;&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;code&gt;DockPanel.Dock&lt;/code&gt;：取值 &lt;code&gt;Left&lt;/code&gt;、&lt;code&gt;Top&lt;/code&gt;、&lt;code&gt;Right&lt;/code&gt;、&lt;code&gt;Bottom&lt;/code&gt;&lt;/li&gt;
&lt;li&gt;&lt;code&gt;LastChildFill&lt;/code&gt;：DockPanel 自身的属性，最后一个子元素是否填充剩余空间&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;&lt;strong&gt;Canvas 相关附加属性&lt;/strong&gt;&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;code&gt;Canvas.Left&lt;/code&gt;：相对 Canvas 左边的偏移&lt;/li&gt;
&lt;li&gt;&lt;code&gt;Canvas.Top&lt;/code&gt;：相对 Canvas 顶部的偏移&lt;/li&gt;
&lt;li&gt;&lt;code&gt;Canvas.Right&lt;/code&gt;：相对 Canvas 右边的偏移（较少使用）&lt;/li&gt;
&lt;li&gt;&lt;code&gt;Canvas.Bottom&lt;/code&gt;：相对 Canvas 底部的偏移（较少使用）&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;&lt;strong&gt;其他常见布局附加属性&lt;/strong&gt;&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;code&gt;ScrollViewer.HorizontalScrollBarVisibility&lt;/code&gt;：指定水平滚动条显示策略（Disabled、Auto、Hidden、Visible）&lt;/li&gt;
&lt;li&gt;&lt;code&gt;ScrollViewer.VerticalScrollBarVisibility&lt;/code&gt;：指定垂直滚动条显示策略&lt;/li&gt;
&lt;li&gt;&lt;code&gt;DockPanel.Dock&lt;/code&gt;：控制元素在 DockPanel 中停靠的位置&lt;/li&gt;
&lt;li&gt;&lt;code&gt;Viewbox.Stretch&lt;/code&gt;：拉伸方式（None、Fill、Uniform、UniformToFill）&lt;/li&gt;
&lt;li&gt;&lt;code&gt;Viewbox.StretchDirection&lt;/code&gt;：拉伸方向（UpOnly、DownOnly、Both）&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;/ol&gt;
&lt;p&gt;综合示例：&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;&amp;lt;Grid ShowGridLines=&quot;True&quot; Grid.IsSharedSizeScope=&quot;True&quot;&amp;gt;
    &amp;lt;Grid.ColumnDefinitions&amp;gt;
        &amp;lt;ColumnDefinition Width=&quot;Auto&quot; SharedSizeGroup=&quot;LabelColumn&quot; /&amp;gt;
        &amp;lt;ColumnDefinition Width=&quot;*&quot; /&amp;gt;
    &amp;lt;/Grid.ColumnDefinitions&amp;gt;

    &amp;lt;TextBlock Text=&quot;姓名:&quot; Grid.Row=&quot;0&quot; Grid.Column=&quot;0&quot; Margin=&quot;5&quot; /&amp;gt;
    &amp;lt;TextBox Grid.Row=&quot;0&quot; Grid.Column=&quot;1&quot; Margin=&quot;5&quot; /&amp;gt;

    &amp;lt;TextBlock Text=&quot;年龄:&quot; Grid.Row=&quot;1&quot; Grid.Column=&quot;0&quot; Margin=&quot;5&quot; /&amp;gt;
    &amp;lt;TextBox Grid.Row=&quot;1&quot; Grid.Column=&quot;1&quot; Margin=&quot;5&quot; /&amp;gt;
&amp;lt;/Grid&amp;gt;
&lt;/code&gt;&lt;/pre&gt;
&lt;h3&gt;3.7 常用布局对比清单&lt;/h3&gt;
&lt;p&gt;下表从“尺寸计算”“子元素对齐”“典型属性”等角度对常用布局进行总结，便于快速选择：&lt;/p&gt;
&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;面板&lt;/th&gt;
&lt;th&gt;子元素定位方式&lt;/th&gt;
&lt;th&gt;尺寸计算特点&lt;/th&gt;
&lt;th&gt;典型属性&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;Grid&lt;/td&gt;
&lt;td&gt;行列+附加属性&lt;/td&gt;
&lt;td&gt;先计算行列尺寸，再放置子元素&lt;/td&gt;
&lt;td&gt;&lt;code&gt;RowDefinitions&lt;/code&gt;、&lt;code&gt;ColumnDefinitions&lt;/code&gt;、&lt;code&gt;Grid.Row&lt;/code&gt;、&lt;code&gt;Grid.Column&lt;/code&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;StackPanel&lt;/td&gt;
&lt;td&gt;顺序堆叠&lt;/td&gt;
&lt;td&gt;先计算布局方向上的总尺寸，再测量另一方向最大值&lt;/td&gt;
&lt;td&gt;&lt;code&gt;Orientation&lt;/code&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;WrapPanel&lt;/td&gt;
&lt;td&gt;堆叠且自动换行&lt;/td&gt;
&lt;td&gt;行满后换行，每行高度取该行最大元素高度&lt;/td&gt;
&lt;td&gt;&lt;code&gt;ItemWidth&lt;/code&gt;、&lt;code&gt;ItemHeight&lt;/code&gt;、&lt;code&gt;Orientation&lt;/code&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;DockPanel&lt;/td&gt;
&lt;td&gt;边缘停靠&lt;/td&gt;
&lt;td&gt;按顺序将子元素停靠到四边，最后子元素可填充剩余空间&lt;/td&gt;
&lt;td&gt;&lt;code&gt;DockPanel.Dock&lt;/code&gt;、&lt;code&gt;LastChildFill&lt;/code&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Canvas&lt;/td&gt;
&lt;td&gt;绝对坐标&lt;/td&gt;
&lt;td&gt;不参与复杂布局，仅根据坐标放置&lt;/td&gt;
&lt;td&gt;&lt;code&gt;Canvas.Left&lt;/code&gt;、&lt;code&gt;Canvas.Top&lt;/code&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;UniformGrid&lt;/td&gt;
&lt;td&gt;均匀网格&lt;/td&gt;
&lt;td&gt;所有单元格大小一致&lt;/td&gt;
&lt;td&gt;&lt;code&gt;Rows&lt;/code&gt;、&lt;code&gt;Columns&lt;/code&gt;、&lt;code&gt;FirstColumn&lt;/code&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Viewbox&lt;/td&gt;
&lt;td&gt;缩放内容&lt;/td&gt;
&lt;td&gt;按比例缩放内部内容以适配自身大小&lt;/td&gt;
&lt;td&gt;&lt;code&gt;Stretch&lt;/code&gt;、&lt;code&gt;StretchDirection&lt;/code&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;
&lt;p&gt;实际项目中一般遵循：&lt;strong&gt;整体用 Grid 构架、局部用 StackPanel/WrapPanel 组织、特定区域用 DockPanel 或 Canvas 实现特殊效果&lt;/strong&gt;。&lt;/p&gt;
&lt;h2&gt;4. WPF控件详解&lt;/h2&gt;
&lt;h3&gt;4.1 常用控件概览&lt;/h3&gt;
&lt;p&gt;WPF 提供了丰富的内置控件，以下是一些常用的控件：&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;
&lt;p&gt;&lt;strong&gt;基础输入控件&lt;/strong&gt;：&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;Button：按钮&lt;/li&gt;
&lt;li&gt;TextBox：文本输入框&lt;/li&gt;
&lt;li&gt;PasswordBox：密码输入框&lt;/li&gt;
&lt;li&gt;CheckBox：复选框&lt;/li&gt;
&lt;li&gt;RadioButton：单选按钮&lt;/li&gt;
&lt;li&gt;Slider：滑块&lt;/li&gt;
&lt;li&gt;ComboBox：下拉选择框&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;&lt;strong&gt;显示控件&lt;/strong&gt;：&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;TextBlock：文本块&lt;/li&gt;
&lt;li&gt;Label：标签&lt;/li&gt;
&lt;li&gt;Image：图片&lt;/li&gt;
&lt;li&gt;ProgressBar：进度条&lt;/li&gt;
&lt;li&gt;Calendar：日历&lt;/li&gt;
&lt;li&gt;DatePicker：日期选择器&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;&lt;strong&gt;列表和表格控件&lt;/strong&gt;：&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;ListBox：列表框&lt;/li&gt;
&lt;li&gt;ComboBox：下拉列表&lt;/li&gt;
&lt;li&gt;ListView：列表视图&lt;/li&gt;
&lt;li&gt;DataGrid：数据表格&lt;/li&gt;
&lt;li&gt;TreeView：树状视图&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;&lt;strong&gt;容器控件&lt;/strong&gt;：&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;Grid：网格布局&lt;/li&gt;
&lt;li&gt;StackPanel：堆叠布局&lt;/li&gt;
&lt;li&gt;WrapPanel：自动换行布局&lt;/li&gt;
&lt;li&gt;DockPanel：停靠布局&lt;/li&gt;
&lt;li&gt;Canvas：画布布局&lt;/li&gt;
&lt;li&gt;GroupBox：分组框&lt;/li&gt;
&lt;li&gt;TabControl：选项卡控件&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;/ol&gt;
&lt;h3&gt;4.2 内容控件（ContentControl）&lt;/h3&gt;
&lt;p&gt;ContentControl 是 WPF 中一个重要的控件基类，它可以包含单个子元素（Content）。常见的 ContentControl 派生类包括：&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;Button&lt;/li&gt;
&lt;li&gt;Label&lt;/li&gt;
&lt;li&gt;TextBlock&lt;/li&gt;
&lt;li&gt;CheckBox&lt;/li&gt;
&lt;li&gt;RadioButton&lt;/li&gt;
&lt;li&gt;ScrollViewer&lt;/li&gt;
&lt;li&gt;Window&lt;/li&gt;
&lt;li&gt;UserControl&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;ContentControl 的使用示例：&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;&amp;lt;Button&amp;gt;
    &amp;lt;StackPanel Orientation=&quot;Horizontal&quot;&amp;gt;
        &amp;lt;Image Source=&quot;icon.png&quot; Width=&quot;20&quot; Height=&quot;20&quot; /&amp;gt;
        &amp;lt;TextBlock Text=&quot;带有图标的按钮&quot; Margin=&quot;5,0,0,0&quot; /&amp;gt;
    &amp;lt;/StackPanel&amp;gt;
&amp;lt;/Button&amp;gt;

&amp;lt;Label&amp;gt;
    &amp;lt;StackPanel Orientation=&quot;Vertical&quot;&amp;gt;
        &amp;lt;TextBlock Text=&quot;标题&quot; FontSize=&quot;16&quot; FontWeight=&quot;Bold&quot; /&amp;gt;
        &amp;lt;TextBlock Text=&quot;副标题&quot; FontSize=&quot;12&quot; Foreground=&quot;Gray&quot; /&amp;gt;
    &amp;lt;/StackPanel&amp;gt;
&amp;lt;/Label&amp;gt;
&lt;/code&gt;&lt;/pre&gt;
&lt;h3&gt;4.3 ItemsControl及其派生类&lt;/h3&gt;
&lt;p&gt;ItemsControl 是用于显示集合数据的控件基类，它可以包含多个子元素（Items）。常见的 ItemsControl 派生类包括：&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;ListBox&lt;/li&gt;
&lt;li&gt;ComboBox&lt;/li&gt;
&lt;li&gt;ListView&lt;/li&gt;
&lt;li&gt;DataGrid&lt;/li&gt;
&lt;li&gt;TreeView&lt;/li&gt;
&lt;li&gt;Menu&lt;/li&gt;
&lt;li&gt;ToolBar&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;ItemsControl 的使用示例：&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;&amp;lt;!-- 直接添加项目 --&amp;gt;
&amp;lt;ListBox&amp;gt;
    &amp;lt;ListBoxItem&amp;gt;项目 1&amp;lt;/ListBoxItem&amp;gt;
    &amp;lt;ListBoxItem&amp;gt;项目 2&amp;lt;/ListBoxItem&amp;gt;
    &amp;lt;ListBoxItem&amp;gt;项目 3&amp;lt;/ListBoxItem&amp;gt;
&amp;lt;/ListBox&amp;gt;

&amp;lt;!-- 绑定集合数据 --&amp;gt;
&amp;lt;ListBox ItemsSource=&quot;{Binding Items}&quot; DisplayMemberPath=&quot;Name&quot; /&amp;gt;

&amp;lt;!-- 使用数据模板 --&amp;gt;
&amp;lt;ListBox ItemsSource=&quot;{Binding Items}&quot;&amp;gt;
    &amp;lt;ListBox.ItemTemplate&amp;gt;
        &amp;lt;DataTemplate&amp;gt;
            &amp;lt;StackPanel Orientation=&quot;Horizontal&quot;&amp;gt;
                &amp;lt;Image Source=&quot;{Binding Icon}&quot; Width=&quot;20&quot; Height=&quot;20&quot; /&amp;gt;
                &amp;lt;TextBlock Text=&quot;{Binding Name}&quot; Margin=&quot;5,0,0,0&quot; /&amp;gt;
                &amp;lt;TextBlock Text=&quot;{Binding Description}&quot; Margin=&quot;10,0,0,0&quot; Foreground=&quot;Gray&quot; /&amp;gt;
            &amp;lt;/StackPanel&amp;gt;
        &amp;lt;/DataTemplate&amp;gt;
    &amp;lt;/ListBox.ItemTemplate&amp;gt;
&amp;lt;/ListBox&amp;gt;
&lt;/code&gt;&lt;/pre&gt;
&lt;h3&gt;4.4 输入控件与验证&lt;/h3&gt;
&lt;p&gt;WPF 提供了多种输入控件，同时支持数据验证：&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;&amp;lt;StackPanel Margin=&quot;10&quot;&amp;gt;
    &amp;lt;TextBox Text=&quot;{Binding Name, Mode=TwoWay, UpdateSourceTrigger=PropertyChanged}&quot;&amp;gt;
        &amp;lt;TextBox.Style&amp;gt;
            &amp;lt;Style TargetType=&quot;TextBox&quot;&amp;gt;
                &amp;lt;Setter Property=&quot;BorderBrush&quot; Value=&quot;Gray&quot; /&amp;gt;
                &amp;lt;Style.Triggers&amp;gt;
                    &amp;lt;Trigger Property=&quot;Validation.HasError&quot; Value=&quot;True&quot;&amp;gt;
                        &amp;lt;Setter Property=&quot;BorderBrush&quot; Value=&quot;Red&quot; /&amp;gt;
                        &amp;lt;Setter Property=&quot;ToolTip&quot; Value=&quot;{Binding RelativeSource={RelativeSource Self}, Path=(Validation.Errors)[0].ErrorContent}&quot; /&amp;gt;
                    &amp;lt;/Trigger&amp;gt;
                &amp;lt;/Style.Triggers&amp;gt;
            &amp;lt;/Style&amp;gt;
        &amp;lt;/TextBox.Style&amp;gt;
    &amp;lt;/TextBox&amp;gt;
    
    &amp;lt;PasswordBox Password=&quot;{Binding Password, Mode=TwoWay}&quot; Margin=&quot;0,10,0,0&quot; /&amp;gt;
    
    &amp;lt;CheckBox IsChecked=&quot;{Binding RememberMe, Mode=TwoWay}&quot; Content=&quot;记住我&quot; Margin=&quot;0,10,0,0&quot; /&amp;gt;
    
    &amp;lt;Button Content=&quot;登录&quot; Command=&quot;{Binding LoginCommand}&quot; Margin=&quot;0,10,0,0&quot; /&amp;gt;
&amp;lt;/StackPanel&amp;gt;
&lt;/code&gt;&lt;/pre&gt;
&lt;h3&gt;4.5 命令绑定&lt;/h3&gt;
&lt;p&gt;WPF 的命令系统允许将用户操作与业务逻辑分离：&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;&amp;lt;Button Content=&quot;保存&quot; Command=&quot;{Binding SaveCommand}&quot; CommandParameter=&quot;{Binding SelectedItem}&quot; /&amp;gt;

&amp;lt;MenuItem Header=&quot;复制&quot; Command=&quot;ApplicationCommands.Copy&quot; /&amp;gt;
&amp;lt;MenuItem Header=&quot;粘贴&quot; Command=&quot;ApplicationCommands.Paste&quot; /&amp;gt;

&amp;lt;ListBox ItemsSource=&quot;{Binding Items}&quot; SelectedItem=&quot;{Binding SelectedItem}&quot;&amp;gt;
    &amp;lt;ListBox.ItemContainerStyle&amp;gt;
        &amp;lt;Style TargetType=&quot;ListBoxItem&quot;&amp;gt;
            &amp;lt;Setter Property=&quot;Command&quot; Value=&quot;{Binding DataContext.ItemCommand, RelativeSource={RelativeSource AncestorType=ListBox}}&quot; /&amp;gt;
            &amp;lt;Setter Property=&quot;CommandParameter&quot; Value=&quot;{Binding}&quot; /&amp;gt;
        &amp;lt;/Style&amp;gt;
    &amp;lt;/ListBox.ItemContainerStyle&amp;gt;
&amp;lt;/ListBox&amp;gt;
&lt;/code&gt;&lt;/pre&gt;
&lt;h3&gt;4.6 控件继承层次与通用属性总表&lt;/h3&gt;
&lt;p&gt;大多数控件都继承自几个核心基类，这些基类上的属性和事件是“所有控件通用的基础知识”。理解这些基类，比死记每个控件所有属性更高效。&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;
&lt;p&gt;&lt;strong&gt;DependencyObject / UIElement / FrameworkElement&lt;/strong&gt;&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;常用依赖属性（大多在 &lt;code&gt;FrameworkElement&lt;/code&gt;）：
&lt;ul&gt;
&lt;li&gt;&lt;code&gt;Width&lt;/code&gt; / &lt;code&gt;Height&lt;/code&gt; / &lt;code&gt;MinWidth&lt;/code&gt; / &lt;code&gt;MinHeight&lt;/code&gt; / &lt;code&gt;MaxWidth&lt;/code&gt; / &lt;code&gt;MaxHeight&lt;/code&gt;&lt;/li&gt;
&lt;li&gt;&lt;code&gt;Margin&lt;/code&gt; / &lt;code&gt;Padding&lt;/code&gt;（注意：Padding 从 &lt;code&gt;Control&lt;/code&gt; 开始才有）&lt;/li&gt;
&lt;li&gt;&lt;code&gt;HorizontalAlignment&lt;/code&gt; / &lt;code&gt;VerticalAlignment&lt;/code&gt;&lt;/li&gt;
&lt;li&gt;&lt;code&gt;Visibility&lt;/code&gt;：Visible / Hidden / Collapsed&lt;/li&gt;
&lt;li&gt;&lt;code&gt;DataContext&lt;/code&gt;：数据绑定的入口&lt;/li&gt;
&lt;li&gt;&lt;code&gt;Style&lt;/code&gt; / &lt;code&gt;Resources&lt;/code&gt;：样式与资源&lt;/li&gt;
&lt;li&gt;&lt;code&gt;ToolTip&lt;/code&gt;：提示信息（可绑定任意对象）&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;常用事件：
&lt;ul&gt;
&lt;li&gt;鼠标：&lt;code&gt;MouseDown&lt;/code&gt; / &lt;code&gt;MouseUp&lt;/code&gt; / &lt;code&gt;MouseMove&lt;/code&gt; / &lt;code&gt;MouseEnter&lt;/code&gt; / &lt;code&gt;MouseLeave&lt;/code&gt; 等&lt;/li&gt;
&lt;li&gt;键盘：&lt;code&gt;KeyDown&lt;/code&gt; / &lt;code&gt;KeyUp&lt;/code&gt;&lt;/li&gt;
&lt;li&gt;布局：&lt;code&gt;Loaded&lt;/code&gt; / &lt;code&gt;SizeChanged&lt;/code&gt; / &lt;code&gt;LayoutUpdated&lt;/code&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;&lt;strong&gt;Control（大多数交互控件的基类）&lt;/strong&gt;&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;视觉相关属性：
&lt;ul&gt;
&lt;li&gt;&lt;code&gt;Foreground&lt;/code&gt; / &lt;code&gt;Background&lt;/code&gt;&lt;/li&gt;
&lt;li&gt;&lt;code&gt;FontFamily&lt;/code&gt; / &lt;code&gt;FontSize&lt;/code&gt; / &lt;code&gt;FontWeight&lt;/code&gt; / &lt;code&gt;FontStyle&lt;/code&gt; / &lt;code&gt;TextDecorations&lt;/code&gt;&lt;/li&gt;
&lt;li&gt;&lt;code&gt;BorderBrush&lt;/code&gt; / &lt;code&gt;BorderThickness&lt;/code&gt;&lt;/li&gt;
&lt;li&gt;&lt;code&gt;Padding&lt;/code&gt;&lt;/li&gt;
&lt;li&gt;&lt;code&gt;Cursor&lt;/code&gt;：鼠标指针样式&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;交互状态：
&lt;ul&gt;
&lt;li&gt;&lt;code&gt;IsEnabled&lt;/code&gt;：是否可交互&lt;/li&gt;
&lt;li&gt;&lt;code&gt;IsHitTestVisible&lt;/code&gt;：是否参与命中测试&lt;/li&gt;
&lt;li&gt;&lt;code&gt;TabIndex&lt;/code&gt; / &lt;code&gt;IsTabStop&lt;/code&gt;：键盘导航顺序&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;模板相关：
&lt;ul&gt;
&lt;li&gt;&lt;code&gt;Template&lt;/code&gt;：&lt;code&gt;ControlTemplate&lt;/code&gt;&lt;/li&gt;
&lt;li&gt;&lt;code&gt;OverridesDefaultStyle&lt;/code&gt;：是否忽略默认样式&lt;/li&gt;
&lt;li&gt;&lt;code&gt;FocusVisualStyle&lt;/code&gt;：键盘焦点时的虚线框样式&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;&lt;strong&gt;ContentControl（Button、Label、GroupBox、UserControl、Window 等）&lt;/strong&gt;&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;关键属性：
&lt;ul&gt;
&lt;li&gt;&lt;code&gt;Content&lt;/code&gt;：单个内容对象（可以是任意 XAML 元素或字符串）&lt;/li&gt;
&lt;li&gt;&lt;code&gt;ContentTemplate&lt;/code&gt;：&lt;code&gt;DataTemplate&lt;/code&gt;，定义 Content 的展示方式&lt;/li&gt;
&lt;li&gt;&lt;code&gt;ContentTemplateSelector&lt;/code&gt;：用于根据数据动态选择模板&lt;/li&gt;
&lt;li&gt;&lt;code&gt;ContentStringFormat&lt;/code&gt;：内容转为字符串的格式&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;&lt;strong&gt;ItemsControl（ListBox、ListView、ComboBox、TreeView、Menu 等）&lt;/strong&gt;&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;关键属性：
&lt;ul&gt;
&lt;li&gt;&lt;code&gt;ItemsSource&lt;/code&gt;：集合数据源&lt;/li&gt;
&lt;li&gt;&lt;code&gt;Items&lt;/code&gt;：手动添加的项集合&lt;/li&gt;
&lt;li&gt;&lt;code&gt;ItemTemplate&lt;/code&gt;：每一项的 &lt;code&gt;DataTemplate&lt;/code&gt;&lt;/li&gt;
&lt;li&gt;&lt;code&gt;ItemTemplateSelector&lt;/code&gt;：动态选择模板&lt;/li&gt;
&lt;li&gt;&lt;code&gt;ItemContainerStyle&lt;/code&gt;：Item 容器（如 &lt;code&gt;ListBoxItem&lt;/code&gt;）的样式&lt;/li&gt;
&lt;li&gt;&lt;code&gt;ItemsPanel&lt;/code&gt;：&lt;code&gt;ItemsPanelTemplate&lt;/code&gt;，内部使用的布局面板&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;与选择相关（来自 &lt;code&gt;Selector&lt;/code&gt; 基类，如 ListBox、ComboBox 等）：
&lt;ul&gt;
&lt;li&gt;&lt;code&gt;SelectedItem&lt;/code&gt; / &lt;code&gt;SelectedIndex&lt;/code&gt; / &lt;code&gt;SelectedValue&lt;/code&gt;&lt;/li&gt;
&lt;li&gt;&lt;code&gt;SelectedValuePath&lt;/code&gt;&lt;/li&gt;
&lt;li&gt;&lt;code&gt;IsSynchronizedWithCurrentItem&lt;/code&gt;：是否与集合的 CurrentItem 同步&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;&lt;strong&gt;文本输入控件（TextBoxBase / TextBox / RichTextBox）&lt;/strong&gt;&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;TextBox 常用属性：
&lt;ul&gt;
&lt;li&gt;&lt;code&gt;Text&lt;/code&gt;：文本内容&lt;/li&gt;
&lt;li&gt;&lt;code&gt;AcceptsReturn&lt;/code&gt;：是否接受回车（多行输入）&lt;/li&gt;
&lt;li&gt;&lt;code&gt;AcceptsTab&lt;/code&gt;：是否接受 Tab&lt;/li&gt;
&lt;li&gt;&lt;code&gt;MaxLength&lt;/code&gt;：最大长度&lt;/li&gt;
&lt;li&gt;&lt;code&gt;IsReadOnly&lt;/code&gt;：只读&lt;/li&gt;
&lt;li&gt;&lt;code&gt;CharacterCasing&lt;/code&gt;：大小写（Normal、Upper、Lower）&lt;/li&gt;
&lt;li&gt;&lt;code&gt;TextWrapping&lt;/code&gt;：自动换行（NoWrap、Wrap、WrapWithOverflow）&lt;/li&gt;
&lt;li&gt;&lt;code&gt;SelectionStart&lt;/code&gt; / &lt;code&gt;SelectionLength&lt;/code&gt; / &lt;code&gt;SelectedText&lt;/code&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;&lt;strong&gt;范围类控件（Slider、ProgressBar、ScrollBar 等，基类 RangeBase）&lt;/strong&gt;&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;code&gt;Minimum&lt;/code&gt; / &lt;code&gt;Maximum&lt;/code&gt;：范围边界&lt;/li&gt;
&lt;li&gt;&lt;code&gt;Value&lt;/code&gt;：当前值&lt;/li&gt;
&lt;li&gt;&lt;code&gt;SmallChange&lt;/code&gt; / &lt;code&gt;LargeChange&lt;/code&gt;：微调和大幅度变化值&lt;/li&gt;
&lt;li&gt;典型用法：通过绑定将 &lt;code&gt;Value&lt;/code&gt; 与 ViewModel 数值属性联动&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;/ol&gt;
&lt;h3&gt;4.7 常用控件速查表（用途 + 关键属性）&lt;/h3&gt;
&lt;ol&gt;
&lt;li&gt;
&lt;p&gt;&lt;strong&gt;Button / ToggleButton / CheckBox / RadioButton&lt;/strong&gt;&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;用途：触发命令、状态切换&lt;/li&gt;
&lt;li&gt;关键属性：
&lt;ul&gt;
&lt;li&gt;Button：&lt;code&gt;Content&lt;/code&gt;、&lt;code&gt;Command&lt;/code&gt;、&lt;code&gt;CommandParameter&lt;/code&gt;&lt;/li&gt;
&lt;li&gt;ToggleButton：&lt;code&gt;IsChecked&lt;/code&gt;、&lt;code&gt;IsThreeState&lt;/code&gt;&lt;/li&gt;
&lt;li&gt;CheckBox：继承自 ToggleButton，多用于布尔标记&lt;/li&gt;
&lt;li&gt;RadioButton：&lt;code&gt;GroupName&lt;/code&gt; 控制互斥分组&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;&lt;strong&gt;TextBlock / Label&lt;/strong&gt;&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;用途：文本展示&lt;/li&gt;
&lt;li&gt;TextBlock：轻量、布局灵活，支持内联格式化（&lt;code&gt;Run&lt;/code&gt;、&lt;code&gt;Span&lt;/code&gt;）&lt;/li&gt;
&lt;li&gt;Label：带有 &lt;code&gt;Target&lt;/code&gt; 属性，可与输入控件联动（辅助无障碍）&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;&lt;strong&gt;ComboBox / ListBox / ListView / DataGrid&lt;/strong&gt;&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;共性：都基于 &lt;code&gt;ItemsControl&lt;/code&gt; + &lt;code&gt;Selector&lt;/code&gt;&lt;/li&gt;
&lt;li&gt;ComboBox：下拉选择（单选），关键属性 &lt;code&gt;IsEditable&lt;/code&gt;、&lt;code&gt;SelectedItem&lt;/code&gt;、&lt;code&gt;SelectedValuePath&lt;/code&gt;&lt;/li&gt;
&lt;li&gt;ListBox：列表选择（可多选），&lt;code&gt;SelectionMode&lt;/code&gt;（Single/Multiple/Extended）&lt;/li&gt;
&lt;li&gt;ListView：基于 ListBox，结合 &lt;code&gt;View=GridView&lt;/code&gt; 实现列表列头展示&lt;/li&gt;
&lt;li&gt;DataGrid：表格控件，关键属性：
&lt;ul&gt;
&lt;li&gt;&lt;code&gt;AutoGenerateColumns&lt;/code&gt;、&lt;code&gt;Columns&lt;/code&gt;&lt;/li&gt;
&lt;li&gt;&lt;code&gt;CanUserAddRows&lt;/code&gt;、&lt;code&gt;CanUserDeleteRows&lt;/code&gt;、&lt;code&gt;CanUserSortColumns&lt;/code&gt;&lt;/li&gt;
&lt;li&gt;&lt;code&gt;IsReadOnly&lt;/code&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;&lt;strong&gt;TreeView / Menu / ToolBar&lt;/strong&gt;&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;TreeView：树状结构，常与 &lt;code&gt;HierarchicalDataTemplate&lt;/code&gt; 搭配&lt;/li&gt;
&lt;li&gt;Menu：菜单栏，配合命令使用&lt;/li&gt;
&lt;li&gt;ToolBar：工具栏，适合放置图标按钮和常用操作&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;&lt;strong&gt;TabControl / GroupBox / Expander&lt;/strong&gt;&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;TabControl：选项卡界面，关键属性 &lt;code&gt;ItemsSource&lt;/code&gt;、&lt;code&gt;SelectedItem&lt;/code&gt;、&lt;code&gt;ItemTemplate&lt;/code&gt;、&lt;code&gt;ContentTemplate&lt;/code&gt;&lt;/li&gt;
&lt;li&gt;GroupBox：带标题的内容分组容器&lt;/li&gt;
&lt;li&gt;Expander：可折叠/展开的容器，关键属性 &lt;code&gt;IsExpanded&lt;/code&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;/ol&gt;
&lt;p&gt;通过以上“基类 + 常用控件速查”，在查官方文档时可以快速定位到更完整的属性列表。&lt;/p&gt;
&lt;h2&gt;5. WPF 核心骨架：依赖属性与附加属性详解&lt;/h2&gt;
&lt;p&gt;在传统的 .NET 开发中（如 WinForms），控件的外观和状态保存在各自私有的 CLR 变量字段中（比如 &lt;code&gt;int width&lt;/code&gt;）。这带来了极大的内存开销——哪怕一个按钮完全采用系统默认样式，它身上几百个属性也切切实实地在 Heap 堆里分配了内存。
WPF 为了实现&lt;strong&gt;数据绑定、动画、样式继承和极限的内存压缩&lt;/strong&gt;，彻底舍弃了传统的 CLR 属性后备字段机制，发明了&lt;strong&gt;依赖属性 (Dependency Property)&lt;/strong&gt; 构架。&lt;/p&gt;
&lt;h3&gt;5.1 依赖属性 (Dependency Property) 的运行机制&lt;/h3&gt;
&lt;p&gt;&lt;strong&gt;核心思想&lt;/strong&gt;：属性的值并不直接存在对象自己肚子里，而是&lt;strong&gt;依赖于&lt;/strong&gt;一个全局的巨型静态字典库 &lt;code&gt;DependencyProperty.Register&lt;/code&gt;，用到谁才存谁。&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;strong&gt;内存极度节省&lt;/strong&gt;：WPF 的控件树可能有上万个节点。如果大家都没改背景色（依然是默认的白色），传统模式下这上万个控件都要自己存一次白色的内存对象。而在依赖属性下，控件自身什么都不存！当系统问它“你的背景色是什么”时，它发现自己没设置过，系统会自动顺着视觉树往上辈找，最终取到顶层定义的一份“静态的白色”发配下来。&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;自带变化通知&lt;/strong&gt;：传统 C# 变量改变神不知鬼不觉，WPF 依赖属性一旦被改，它底层的内部字典引擎会自动向四周疯狂发射“我变了！”的广播，从而驱动 UI 自动渲染（这就是数据绑定得以工作的根基核心）。&lt;/li&gt;
&lt;/ul&gt;
&lt;h4&gt;5.1.1 如何手写注册一个自定义的依赖属性？&lt;/h4&gt;
&lt;p&gt;假设我们写了一个自定义的圆角卡片控件 &lt;code&gt;MyCardControl&lt;/code&gt;，我们要给它开辟一个具有绑定功能的 &lt;code&gt;CornerRadius&lt;/code&gt; 属性。&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;public class MyCardControl : Control
{
    // 1. 注册核心：这是一个 全局静态只读 字段！它才是正主。
    // Register 参数解释：(&quot;对外名称&quot;, 属性类型, 拥有这个属性的主人类型, 默认值及回调配置)
    public static readonly DependencyProperty CardCornerRadiusProperty =
        DependencyProperty.Register(
            &quot;CardCornerRadius&quot;, 
            typeof(double), 
            typeof(MyCardControl), 
            new PropertyMetadata(5.0, OnCornerRadiusChanged) // 默认值为 5.0，而且改变时会调用钩子函数
        );

    // 2. CLR 属性包装器 (Wrapper)：只是为了让我们在 C# 代码里能像调普通属性一样打点调用而已！
    // 警告：这里面绝对不要写别的逻辑（比如弹 MessageBox 等），因为 WPF 引擎在解析 XAML 时，是直接调用底层 SetValue 的，根本不会走这层 Wrapper 包装壳。
    public double CardCornerRadius
    {
        get { return (double)GetValue(CardCornerRadiusProperty); } // GetValue 来自基类 DependencyObject
        set { SetValue(CardCornerRadiusProperty, value); }
    }

    // 3. 值变化时的静态回调钩子
    private static void OnCornerRadiusChanged(DependencyObject d, DependencyPropertyChangedEventArgs e)
    {
        var myCard = d as MyCardControl;
        double oldVal = (double)e.OldValue;
        double newVal = (double)e.NewValue;
        // 在这里执行当别人用 Binding 修改了这个卡片的圆角尺寸时，内部要触发的动画或绘图代码！
    }
}
&lt;/code&gt;&lt;/pre&gt;
&lt;h3&gt;5.2 附加属性 (Attached Property)&lt;/h3&gt;
&lt;p&gt;&lt;strong&gt;附加属性&lt;/strong&gt;是依赖属性的特殊变异品种。核心思想是：&lt;strong&gt;“把别人家的属性，强行挂在自己的脖子上”&lt;/strong&gt;。&lt;/p&gt;
&lt;p&gt;你在布局经常写的：&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;&amp;lt;Grid&amp;gt;
    &amp;lt;Button Grid.Row=&quot;1&quot; Grid.Column=&quot;2&quot; Content=&quot;我是按钮&quot; /&amp;gt;
&amp;lt;/Grid&amp;gt;
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;&lt;strong&gt;仔细想想：&lt;/strong&gt; &lt;code&gt;Button&lt;/code&gt; 的底层源码里有 &lt;code&gt;Row&lt;/code&gt; 或者 &lt;code&gt;Column&lt;/code&gt; 这个属性吗？绝对没有。因为 Button 除了被塞在 &lt;code&gt;Grid&lt;/code&gt; 里，还有可能在 &lt;code&gt;Canvas&lt;/code&gt;、在 &lt;code&gt;StackPanel&lt;/code&gt; 里。
所以 &lt;code&gt;Grid.Row&lt;/code&gt; 是一种由 &lt;code&gt;Grid&lt;/code&gt; 提供的&lt;strong&gt;附加属性&lt;/strong&gt;！当 Button 放在了 Grid 里，XAML 解析器就会把这个 &lt;code&gt;1&lt;/code&gt; 和 &lt;code&gt;2&lt;/code&gt; 强行塞进 Button 的一个隐藏字典兜衣里。当 Grid 开始计算排版时，Grid 跑到每个孩子那去搜身，从 Button 的兜里搜出它想要的 Row 信息。&lt;/p&gt;
&lt;h4&gt;5.2.1 极其强大的手写附加属性（MVVM 核心挂载物魔法）&lt;/h4&gt;
&lt;p&gt;如果我们想要让一个普通的密码框 &lt;code&gt;PasswordBox&lt;/code&gt; 支持双向绑定（原生是不支持的），我们就经常用到附加属性，把“绑定源”附加到别人身上：&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;public static class PasswordBoxHelper
{
    // 1. 注册附加属性使用 RegisterAttached 
    public static readonly DependencyProperty AttachPasswordProperty =
        DependencyProperty.RegisterAttached(
            &quot;AttachPassword&quot;,
            typeof(string),
            typeof(PasswordBoxHelper),
            new PropertyMetadata(string.Empty, OnPasswordAttachedChanged));

    // 2. 必须提供静态的 Get 和 Set 方法（取代了上文的 CLR Wrapper 包装器）
    public static string GetAttachPassword(DependencyObject d) 
        =&amp;gt; (string)d.GetValue(AttachPasswordProperty);

    public static void SetAttachPassword(DependencyObject d, string value) 
        =&amp;gt; d.SetValue(AttachPasswordProperty, value);

    private static void OnPasswordAttachedChanged(DependencyObject d, DependencyPropertyChangedEventArgs e)
    {
        // 我们利用这个钩子，一旦 XAML 把这个幽灵属性挂载到了 PasswordBox 身上，
        // 我们就在背后偷偷劫持 PasswordBox 原生的 PasswordChanged 事件，强行打通 MVVM 任督二脉！
        if (d is PasswordBox pbox)
        {
            pbox.PasswordChanged -= Pbox_PasswordChanged; // 先卸载防重复
            pbox.PasswordChanged += Pbox_PasswordChanged;
        }
    }
    
    // ... 中略劫持同步逻辑 ...
}
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;&lt;strong&gt;XAML 魔法使用它&lt;/strong&gt;（强行塞给可怜普通的密码框）：
&lt;code&gt;&amp;lt;PasswordBox local:PasswordBoxHelper.AttachPassword=&quot;{Binding MyViewModelPwdStr}&quot; /&amp;gt;&lt;/code&gt;&lt;/p&gt;
&lt;hr /&gt;
&lt;h2&gt;6. WPF 的脉络：路由事件系统 (Routed Events)&lt;/h2&gt;
&lt;p&gt;传统的 C# &lt;code&gt;.NET&lt;/code&gt; 事件模型（CLR Event）是&lt;strong&gt;点对点&lt;/strong&gt;的点穴攻击。按钮 &lt;code&gt;Click += 方法&lt;/code&gt;。这在复杂的 UI 树嵌套里是致命的灾难：
如果你在一张包含了（边界+圆角容器+文字区块+图标）的复杂超级大按钮控件上点击，究竟算谁触发了点击事件？要是刚好点在图片的 1 像素内，只有图片响应该怎么办？你难道要给内部的五个元素排着队全部写一遍 &lt;code&gt;+=&lt;/code&gt; 吗？&lt;/p&gt;
&lt;p&gt;WPF 为了解决这个问题，仿照&lt;strong&gt;蜘蛛网的震动传导机制&lt;/strong&gt;，创造了&lt;strong&gt;路由事件（Routed Events）&lt;/strong&gt;。&lt;/p&gt;
&lt;h3&gt;6.1 路由事件三大走向阵营&lt;/h3&gt;
&lt;p&gt;&lt;strong&gt;1. 冒泡事件 (Bubbling)&lt;/strong&gt;
最常见的方向，像水底的气泡一样&lt;strong&gt;从底朝顶浮&lt;/strong&gt;上天。&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;比如 &lt;code&gt;MouseDown&lt;/code&gt;。如果用户点中了深层的&lt;strong&gt;文字图标&lt;/strong&gt; -&amp;gt; 文字图标触发 &lt;code&gt;MouseDown&lt;/code&gt; -&amp;gt; 紧接着震波传递给包裹它的 &lt;strong&gt;StackPanel&lt;/strong&gt; 触发 &lt;code&gt;MouseDown&lt;/code&gt; -&amp;gt; 再传递给大 &lt;strong&gt;Button&lt;/strong&gt; 触发 &lt;code&gt;MouseDown&lt;/code&gt; -&amp;gt; 最后传递到顶层的 &lt;strong&gt;Window 主窗体&lt;/strong&gt;。&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;用途&lt;/strong&gt;：你只需要在最上层的巨大统率容器里挂一个监听器，就能捕获包裹在里面所有几千个极小部件的集体点击冒泡信号！&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;截断器&lt;/strong&gt;：任何途经路线抓到这件事的元素，只要它觉得“这事我处理完了上级长官不用管了”，只要将代码里写上 &lt;code&gt;e.Handled = true;&lt;/code&gt;，这颗冒泡水泡在它这层就会被彻底捏爆消灭，上空不再会收到地震波。&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;&lt;strong&gt;2. 隧道事件 (Tunneling) [所有以 Preview 开头的事件都是这货]&lt;/strong&gt;
方向刚好相反。像光线从天花板打下深井底。&lt;strong&gt;从顶朝底扎&lt;/strong&gt;。&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;比如 &lt;code&gt;PreviewKeyDown&lt;/code&gt;（预览键盘按压）。&lt;/li&gt;
&lt;li&gt;当焦点在一个文本框上你敲了一个按键，&lt;strong&gt;顶层 Window 先第一个得知&lt;/strong&gt;！Window 发出 &lt;code&gt;Preview&lt;/code&gt; -&amp;gt; 把信号发送给次级大容器 Grid -&amp;gt; ... 最后到底层真正被聚焦的 &lt;code&gt;TextBox&lt;/code&gt;。&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;用途：帝王防线（劫持器）&lt;/strong&gt;。如果在输入时我不允许输入数字。我在顶流 Window 层直接把光卡死设置 &lt;code&gt;e.Handled = true;&lt;/code&gt;。底层连光都见不到，别说是响成了。&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;&lt;strong&gt;3. 直达事件 (Direct)&lt;/strong&gt;
就是传统的死对点不传递事件。比如 &lt;code&gt;MouseEnter&lt;/code&gt;。因为如果鼠标进入小按钮的同时也宣布它进入了最外层大窗体的话，这逻辑判断太狂野容易死循环。&lt;/p&gt;
&lt;h3&gt;6.2 在复杂场景处理路由事件的完美范例&lt;/h3&gt;
&lt;p&gt;&lt;strong&gt;场景&lt;/strong&gt;：大列表控件中装了几十个小按钮。你想全局统一监控“删除操作”，而不是给这 50 个按钮在 &lt;code&gt;.cs&lt;/code&gt; 里写 50 个 &lt;code&gt;+=&lt;/code&gt; 死绑。&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;&amp;lt;!-- 在外层的骨架容器里，我们通过路由机制，挂载一个全局大网雷达 Button.Click --&amp;gt;
&amp;lt;StackPanel Button.Click=&quot;OnAnyChildButtonClicked_Handler&quot;&amp;gt;

    &amp;lt;!-- 这里面的任何一个可怜的小按钮被普通点击，震波都会像水底丢核弹一样浮上并炸毁监控总网 --&amp;gt;
    &amp;lt;Button Content=&quot;删除文件 A&quot; Tag=&quot;A&quot; /&amp;gt;
    &amp;lt;Button Content=&quot;删除文件 B&quot; Tag=&quot;B&quot; /&amp;gt;
    
    &amp;lt;Grid&amp;gt;
       &amp;lt;Button Content=&quot;埋得很深的文件 C&quot; Tag=&quot;C&quot; /&amp;gt;
    &amp;lt;/Grid&amp;gt;

&amp;lt;/StackPanel&amp;gt;
&lt;/code&gt;&lt;/pre&gt;
&lt;pre&gt;&lt;code&gt;// 在后台代码里：
private void OnAnyChildButtonClicked_Handler(object sender, RoutedEventArgs e)
{
    // sender：是谁挂载了这个监听总网？是那个外层至高无上的大 StackPanel 皇帝
    // e.OriginalSource：是谁真正发射了这颗起爆核弹指令？是我们想要的深处的小按钮！
    
    if (e.OriginalSource is Button clickedBtn)
    {
        string fileToDel = clickedBtn.Tag.ToString();
        MessageBox.Show($&quot;长官，我拦截到了总网下的深层动作，它要删文件：{fileToDel}&quot;);
        
        // 这一句捏爆水泡。如果外头还有更高层的 Window.Click 雷达，别想再收到了！
        e.Handled = true; 
    }
}
&lt;/code&gt;&lt;/pre&gt;
&lt;hr /&gt;
&lt;h2&gt;7. 数据绑定（Data Binding）&lt;/h2&gt;
&lt;h3&gt;7.1 绑定基础&lt;/h3&gt;
&lt;p&gt;数据绑定是 WPF 的核心特性之一，它允许 UI 元素与数据源自动同步：&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;&amp;lt;!-- 基本数据绑定 --&amp;gt;
&amp;lt;TextBox Text=&quot;{Binding Name}&quot; /&amp;gt;
&amp;lt;TextBlock Text=&quot;{Binding Age}&quot; /&amp;gt;
&amp;lt;Button Content=&quot;{Binding ButtonText}&quot; Command=&quot;{Binding ClickCommand}&quot; /&amp;gt;
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;数据绑定的基本概念：&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;strong&gt;源（Source）&lt;/strong&gt;：提供数据的对象&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;目标（Target）&lt;/strong&gt;：接收数据的 UI 元素&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;路径（Path）&lt;/strong&gt;：源对象中数据的路径&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;模式（Mode）&lt;/strong&gt;：绑定的方向&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;更新触发器（UpdateSourceTrigger）&lt;/strong&gt;：源更新的触发时机&lt;/li&gt;
&lt;/ul&gt;
&lt;h3&gt;7.2 Binding的关键属性&lt;/h3&gt;
&lt;p&gt;Binding 对象有以下关键属性：&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;&lt;strong&gt;Source&lt;/strong&gt;：绑定的数据源&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Path&lt;/strong&gt;：数据源中属性的路径&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Mode&lt;/strong&gt;：绑定模式（OneWay、TwoWay、OneWayToSource、OneTime、Default）&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;UpdateSourceTrigger&lt;/strong&gt;：源更新的触发时机（PropertyChanged、LostFocus、Explicit、Default）&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Converter&lt;/strong&gt;：值转换器&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;ConverterParameter&lt;/strong&gt;：传递给转换器的参数&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;ConverterCulture&lt;/strong&gt;：转换器使用的文化信息&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;StringFormat&lt;/strong&gt;：字符串格式化&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;TargetNullValue&lt;/strong&gt;：目标为空时的值&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;FallbackValue&lt;/strong&gt;：绑定失败时的值&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;ValidatesOnDataErrors&lt;/strong&gt;：是否启用数据错误验证&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;ValidatesOnExceptions&lt;/strong&gt;：是否启用异常验证&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;NotifyOnValidationError&lt;/strong&gt;：是否在验证错误时触发事件&lt;/li&gt;
&lt;/ol&gt;
&lt;h3&gt;7.3 绑定模式（Mode）&lt;/h3&gt;
&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;绑定模式&lt;/th&gt;
&lt;th&gt;描述&lt;/th&gt;
&lt;th&gt;适用场景&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;OneWay&lt;/td&gt;
&lt;td&gt;从源到目标的单向绑定&lt;/td&gt;
&lt;td&gt;显示数据，不需要用户修改&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;TwoWay&lt;/td&gt;
&lt;td&gt;双向绑定，源和目标相互同步&lt;/td&gt;
&lt;td&gt;编辑表单，需要双向同步&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;OneWayToSource&lt;/td&gt;
&lt;td&gt;从目标到源的单向绑定&lt;/td&gt;
&lt;td&gt;仅需要将 UI 值传递给数据源&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;OneTime&lt;/td&gt;
&lt;td&gt;只绑定一次，后续变化不影响&lt;/td&gt;
&lt;td&gt;静态数据，不需要更新&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Default&lt;/td&gt;
&lt;td&gt;根据目标属性的默认行为&lt;/td&gt;
&lt;td&gt;大多数情况下的默认选择&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;
&lt;h3&gt;7.4 值转换器（ValueConverter）&lt;/h3&gt;
&lt;p&gt;值转换器用于在绑定过程中转换数据类型或格式化数据：&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;// 日期转换器示例
public class DateConverter : IValueConverter
{
    public object Convert(object value, Type targetType, object parameter, CultureInfo culture)
    {
        if (value is DateTime date)
        {
            return date.ToString(&quot;yyyy-MM-dd&quot;);
        }
        return string.Empty;
    }

    public object ConvertBack(object value, Type targetType, object parameter, CultureInfo culture)
    {
        if (value is string str &amp;amp;&amp;amp; DateTime.TryParse(str, out DateTime date))
        {
            return date;
        }
        return DateTime.Now;
    }
}
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;使用值转换器：&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;&amp;lt;Window.Resources&amp;gt;
    &amp;lt;local:DateConverter x:Key=&quot;DateConverter&quot; /&amp;gt;
&amp;lt;/Window.Resources&amp;gt;

&amp;lt;TextBlock Text=&quot;{Binding BirthDate, Converter={StaticResource DateConverter}}&quot; /&amp;gt;
&amp;lt;TextBox Text=&quot;{Binding BirthDate, Converter={StaticResource DateConverter}, Mode=TwoWay}&quot; /&amp;gt;
&lt;/code&gt;&lt;/pre&gt;
&lt;h3&gt;7.5 集合绑定与ObservableCollection&lt;/h3&gt;
&lt;p&gt;对于集合数据，WPF 提供了特殊的绑定支持：&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;// 使用 ObservableCollection 实现自动更新
public class ViewModel
{
    public ObservableCollection&amp;lt;Person&amp;gt; People { get; set; }
    
    public ViewModel()
    {
        People = new ObservableCollection&amp;lt;Person&amp;gt;
        {
            new Person { Name = &quot;张三&quot;, Age = 25 },
            new Person { Name = &quot;李四&quot;, Age = 30 },
            new Person { Name = &quot;王五&quot;, Age = 35 }
        };
        
        // 添加项
        People.Add(new Person { Name = &quot;赵六&quot;, Age = 40 });
        
        // 删除项
        People.RemoveAt(0);
    }
}
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;绑定到集合：&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;&amp;lt;ListBox ItemsSource=&quot;{Binding People}&quot;&amp;gt;
    &amp;lt;ListBox.ItemTemplate&amp;gt;
        &amp;lt;DataTemplate&amp;gt;
            &amp;lt;StackPanel Orientation=&quot;Horizontal&quot;&amp;gt;
                &amp;lt;TextBlock Text=&quot;{Binding Name}&quot; Width=&quot;100&quot; /&amp;gt;
                &amp;lt;TextBlock Text=&quot;{Binding Age}&quot; /&amp;gt;
            &amp;lt;/StackPanel&amp;gt;
        &amp;lt;/DataTemplate&amp;gt;
    &amp;lt;/ListBox.ItemTemplate&amp;gt;
&amp;lt;/ListBox&amp;gt;
&lt;/code&gt;&lt;/pre&gt;
&lt;h3&gt;7.6 绑定源的查找顺序与 DataContext 传递&lt;/h3&gt;
&lt;p&gt;理解 &lt;code&gt;DataContext&lt;/code&gt; 及其传递规则，是掌握绑定行为的基础：&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;
&lt;p&gt;&lt;strong&gt;DataContext 的继承规则&lt;/strong&gt;&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;子元素默认继承父元素的 &lt;code&gt;DataContext&lt;/code&gt;&lt;/li&gt;
&lt;li&gt;一旦在某个元素上显式设置了 &lt;code&gt;DataContext&lt;/code&gt;，该元素及其子元素都以新的 &lt;code&gt;DataContext&lt;/code&gt; 为准&lt;/li&gt;
&lt;li&gt;以下元素&lt;strong&gt;不会&lt;/strong&gt;继承 DataContext，需要单独设置：
&lt;ul&gt;
&lt;li&gt;&lt;code&gt;Binding&lt;/code&gt; 在 &lt;code&gt;Style&lt;/code&gt;、&lt;code&gt;ControlTemplate&lt;/code&gt; 中时，默认的源是 &lt;code&gt;TemplatedParent&lt;/code&gt; 而不是视觉树上的 DataContext&lt;/li&gt;
&lt;li&gt;独立弹出的 &lt;code&gt;Window&lt;/code&gt;、对话框，需手动设置 &lt;code&gt;DataContext&lt;/code&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;&lt;strong&gt;Binding.Source / ElementName / RelativeSource 的优先级&lt;/strong&gt;&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;当同时指定多个来源时，优先级为：
&lt;ol&gt;
&lt;li&gt;&lt;code&gt;Source&lt;/code&gt;&lt;/li&gt;
&lt;li&gt;&lt;code&gt;ElementName&lt;/code&gt;&lt;/li&gt;
&lt;li&gt;&lt;code&gt;RelativeSource&lt;/code&gt;&lt;/li&gt;
&lt;li&gt;继承的 &lt;code&gt;DataContext&lt;/code&gt;&lt;/li&gt;
&lt;/ol&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;&lt;strong&gt;典型错误场景&lt;/strong&gt;&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;在 &lt;code&gt;ItemContainerStyle&lt;/code&gt; 中绑定 &lt;code&gt;DataContext&lt;/code&gt; 的属性，需要使用 &lt;code&gt;RelativeSource&lt;/code&gt; 或 &lt;code&gt;TemplatedParent&lt;/code&gt;&lt;/li&gt;
&lt;li&gt;在 &lt;code&gt;ControlTemplate&lt;/code&gt; 内部想绑定到 ViewModel，需要使用 &lt;code&gt;RelativeSource TemplatedParent&lt;/code&gt; 然后从控件的 &lt;code&gt;DataContext&lt;/code&gt; 再取属性&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;/ol&gt;
&lt;h3&gt;7.7 ElementName 与 RelativeSource&lt;/h3&gt;
&lt;ol&gt;
&lt;li&gt;
&lt;p&gt;&lt;strong&gt;ElementName 绑定&lt;/strong&gt;&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;用于绑定到 XAML 中同级或祖先元素的属性：&lt;/li&gt;
&lt;/ul&gt;
&lt;pre&gt;&lt;code&gt;&amp;lt;Slider x:Name=&quot;sld&quot; Minimum=&quot;0&quot; Maximum=&quot;100&quot; Value=&quot;20&quot; /&amp;gt;
&amp;lt;TextBlock Text=&quot;{Binding ElementName=sld, Path=Value}&quot; Margin=&quot;10,0,0,0&quot; /&amp;gt;
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;典型用途：多个控件之间联动，如滑块与文本、CheckBox 控制 Panel 的 &lt;code&gt;IsEnabled&lt;/code&gt; 等。&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;&lt;strong&gt;RelativeSource 绑定&lt;/strong&gt;&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;常见模式：
&lt;ul&gt;
&lt;li&gt;&lt;code&gt;RelativeSource Self&lt;/code&gt;：绑定到自身属性&lt;/li&gt;
&lt;li&gt;&lt;code&gt;RelativeSource FindAncestor&lt;/code&gt;：向上查找指定类型的祖先元素&lt;/li&gt;
&lt;li&gt;&lt;code&gt;RelativeSource TemplatedParent&lt;/code&gt;：在模板内部绑定到使用模板的控件&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;示例：绑定到最近的 &lt;code&gt;ListBoxItem&lt;/code&gt;：&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;&amp;lt;TextBlock Text=&quot;{Binding RelativeSource={RelativeSource AncestorType=ListBoxItem}, Path=IsSelected}&quot; /&amp;gt;
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;在 &lt;code&gt;ControlTemplate&lt;/code&gt; 中绑定到控件自身的 &lt;code&gt;IsMouseOver&lt;/code&gt;：&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;&amp;lt;Border Background=&quot;{Binding RelativeSource={RelativeSource TemplatedParent}, Path=Background}&quot;&amp;gt;
    &amp;lt;ContentPresenter /&amp;gt;
&amp;lt;/Border&amp;gt;
&lt;/code&gt;&lt;/pre&gt;
&lt;/li&gt;
&lt;/ol&gt;
&lt;h3&gt;7.8 MultiBinding 与 PriorityBinding&lt;/h3&gt;
&lt;p&gt;当单一 Binding 无法满足需求时，可以使用 &lt;code&gt;MultiBinding&lt;/code&gt; 与 &lt;code&gt;PriorityBinding&lt;/code&gt;：&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;
&lt;p&gt;&lt;strong&gt;MultiBinding&lt;/strong&gt;&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;将多个源值组合成一个目标值，需要实现 &lt;code&gt;IMultiValueConverter&lt;/code&gt;：&lt;/li&gt;
&lt;/ul&gt;
&lt;pre&gt;&lt;code&gt;public class FullNameConverter : IMultiValueConverter
{
    public object Convert(object[] values, Type targetType, object parameter, CultureInfo culture)
    {
        var firstName = values[0] as string;
        var lastName = values[1] as string;
        return $&quot;{lastName}{firstName}&quot;;
    }

    public object[] ConvertBack(object value, Type[] targetTypes, object parameter, CultureInfo culture)
    {
        var full = value as string ?? string.Empty;
        if (full.Length &amp;gt;= 2)
            return new object[] { full.Substring(1), full.Substring(0, 1) };
        return new object[] { string.Empty, string.Empty };
    }
}
&lt;/code&gt;&lt;/pre&gt;
&lt;pre&gt;&lt;code&gt;&amp;lt;Window.Resources&amp;gt;
    &amp;lt;local:FullNameConverter x:Key=&quot;FullNameConverter&quot; /&amp;gt;
&amp;lt;/Window.Resources&amp;gt;

&amp;lt;TextBlock&amp;gt;
    &amp;lt;TextBlock.Text&amp;gt;
        &amp;lt;MultiBinding Converter=&quot;{StaticResource FullNameConverter}&quot;&amp;gt;
            &amp;lt;Binding Path=&quot;FirstName&quot; /&amp;gt;
            &amp;lt;Binding Path=&quot;LastName&quot; /&amp;gt;
        &amp;lt;/MultiBinding&amp;gt;
    &amp;lt;/TextBlock.Text&amp;gt;
&amp;lt;/TextBlock&amp;gt;
&lt;/code&gt;&lt;/pre&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;&lt;strong&gt;PriorityBinding&lt;/strong&gt;&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;为目标属性提供一组候选绑定，按顺序使用第一个成功的绑定：&lt;/li&gt;
&lt;/ul&gt;
&lt;pre&gt;&lt;code&gt;&amp;lt;TextBlock&amp;gt;
    &amp;lt;TextBlock.Text&amp;gt;
        &amp;lt;PriorityBinding&amp;gt;
            &amp;lt;!-- 优先使用服务器数据 --&amp;gt;
            &amp;lt;Binding Path=&quot;ServerTitle&quot; FallbackValue=&quot;{x:Null}&quot; /&amp;gt;
            &amp;lt;!-- 其次使用本地缓存 --&amp;gt;
            &amp;lt;Binding Path=&quot;LocalTitle&quot; FallbackValue=&quot;{x:Null}&quot; /&amp;gt;
            &amp;lt;!-- 最后使用硬编码默认值 --&amp;gt;
            &amp;lt;Binding Source=&quot;默认标题&quot; /&amp;gt;
        &amp;lt;/PriorityBinding&amp;gt;
    &amp;lt;/TextBlock.Text&amp;gt;
&amp;lt;/TextBlock&amp;gt;
&lt;/code&gt;&lt;/pre&gt;
&lt;/li&gt;
&lt;/ol&gt;
&lt;h3&gt;7.9 Binding 常用参数组合清单&lt;/h3&gt;
&lt;ol&gt;
&lt;li&gt;&lt;strong&gt;文本输入场景&lt;/strong&gt;&lt;/li&gt;
&lt;/ol&gt;
&lt;pre&gt;&lt;code&gt;&amp;lt;TextBox Text=&quot;{Binding UserName,
                        Mode=TwoWay,
                        UpdateSourceTrigger=PropertyChanged,
                        ValidatesOnDataErrors=True,
                        NotifyOnValidationError=True}&quot; /&amp;gt;
&lt;/code&gt;&lt;/pre&gt;
&lt;ol&gt;
&lt;li&gt;&lt;strong&gt;只读展示场景&lt;/strong&gt;&lt;/li&gt;
&lt;/ol&gt;
&lt;pre&gt;&lt;code&gt;&amp;lt;TextBlock Text=&quot;{Binding OrderTotal,
                          Mode=OneWay,
                          StringFormat=&apos;总金额：{0:C2}&apos;}&quot; /&amp;gt;
&lt;/code&gt;&lt;/pre&gt;
&lt;ol&gt;
&lt;li&gt;&lt;strong&gt;模板内部绑定到 ViewModel 属性&lt;/strong&gt;&lt;/li&gt;
&lt;/ol&gt;
&lt;pre&gt;&lt;code&gt;&amp;lt;ControlTemplate TargetType=&quot;Button&quot;&amp;gt;
    &amp;lt;Border Background=&quot;{TemplateBinding Background}&quot;&amp;gt;
        &amp;lt;TextBlock Text=&quot;{Binding DataContext.ButtonCaption,
                                  RelativeSource={RelativeSource TemplatedParent}}&quot; /&amp;gt;
    &amp;lt;/Border&amp;gt;
&amp;lt;/ControlTemplate&amp;gt;
&lt;/code&gt;&lt;/pre&gt;
&lt;ol&gt;
&lt;li&gt;&lt;strong&gt;带转换器与参数的绑定&lt;/strong&gt;&lt;/li&gt;
&lt;/ol&gt;
&lt;pre&gt;&lt;code&gt;&amp;lt;TextBlock Text=&quot;{Binding Progress,
                          Converter={StaticResource ProgressToPercentConverter},
                          ConverterParameter=&apos;0&apos;}&quot; /&amp;gt;
&lt;/code&gt;&lt;/pre&gt;
&lt;h2&gt;8. MVVM设计模式&lt;/h2&gt;
&lt;h3&gt;8.1 MVVM的基本概念&lt;/h3&gt;
&lt;p&gt;MVVM（Model-View-ViewModel）是一种专门为 WPF 设计的设计模式，它将应用程序分为三个主要部分：&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;&lt;strong&gt;Model&lt;/strong&gt;：数据模型，包含业务逻辑和数据访问&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;View&lt;/strong&gt;：视图，用户界面，由 XAML 定义&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;ViewModel&lt;/strong&gt;：视图模型，连接 Model 和 View，包含 UI 逻辑&lt;/li&gt;
&lt;/ol&gt;
&lt;p&gt;MVVM 的主要优点：&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;关注点分离：UI 和业务逻辑分离&lt;/li&gt;
&lt;li&gt;可测试性：ViewModel 可以独立于 View 进行测试&lt;/li&gt;
&lt;li&gt;可维护性：代码结构清晰，易于维护&lt;/li&gt;
&lt;li&gt;可重用性：ViewModel 可以在不同的 View 中重用&lt;/li&gt;
&lt;/ul&gt;
&lt;h3&gt;8.2 ViewModel的职责&lt;/h3&gt;
&lt;p&gt;ViewModel 的主要职责包括：&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;&lt;strong&gt;数据转换&lt;/strong&gt;：将 Model 中的数据转换为 View 可以显示的形式&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;状态管理&lt;/strong&gt;：管理 View 的状态&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;命令处理&lt;/strong&gt;：处理用户的交互命令&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;数据验证&lt;/strong&gt;：验证用户输入的数据&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;导航逻辑&lt;/strong&gt;：处理页面或视图之间的导航&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;服务协调&lt;/strong&gt;：协调各种服务的调用&lt;/li&gt;
&lt;/ol&gt;
&lt;h3&gt;8.3 INotifyPropertyChanged接口&lt;/h3&gt;
&lt;p&gt;INotifyPropertyChanged 接口用于通知 View 数据源的属性值发生了变化：&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;public class Person : INotifyPropertyChanged
{
    private string _name;
    private int _age;
    
    public string Name
    {
        get { return _name; }
        set
        {
            if (_name != value)
            {
                _name = value;
                OnPropertyChanged(nameof(Name));
            }
        }
    }
    
    public int Age
    {
        get { return _age; }
        set
        {
            if (_age != value)
            {
                _age = value;
                OnPropertyChanged(nameof(Age));
            }
        }
    }
    
    public event PropertyChangedEventHandler PropertyChanged;
    
    protected virtual void OnPropertyChanged(string propertyName)
    {
        PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(propertyName));
    }
}
&lt;/code&gt;&lt;/pre&gt;
&lt;h3&gt;8.4 命令（ICommand）实现&lt;/h3&gt;
&lt;p&gt;ICommand 接口用于实现命令模式，将用户操作与业务逻辑分离：&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;public class RelayCommand : ICommand
{
    private readonly Action&amp;lt;object&amp;gt; _execute;
    private readonly Func&amp;lt;object, bool&amp;gt; _canExecute;
    
    public event EventHandler CanExecuteChanged;
    
    public RelayCommand(Action&amp;lt;object&amp;gt; execute, Func&amp;lt;object, bool&amp;gt; canExecute = null)
    {
        _execute = execute;
        _canExecute = canExecute;
    }
    
    public bool CanExecute(object parameter)
    {
        return _canExecute == null || _canExecute(parameter);
    }
    
    public void Execute(object parameter)
    {
        _execute(parameter);
    }
    
    public void RaiseCanExecuteChanged()
    {
        CanExecuteChanged?.Invoke(this, EventArgs.Empty);
    }
}
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;使用 RelayCommand：&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;public class ViewModel
{
    public ICommand SaveCommand { get; set; }
    public ICommand DeleteCommand { get; set; }
    
    public ViewModel()
    {
        SaveCommand = new RelayCommand(Save, CanSave);
        DeleteCommand = new RelayCommand(Delete, CanDelete);
    }
    
    private void Save(object parameter)
    {
        // 保存逻辑
    }
    
    private bool CanSave(object parameter)
    {
        // 检查是否可以保存
        return true;
    }
    
    private void Delete(object parameter)
    {
        // 删除逻辑
    }
    
    private bool CanDelete(object parameter)
    {
        // 检查是否可以删除
        return true;
    }
}
&lt;/code&gt;&lt;/pre&gt;
&lt;h3&gt;8.5 MVVM示例&lt;/h3&gt;
&lt;pre&gt;&lt;code&gt;&amp;lt;!-- View --&amp;gt;
&amp;lt;Window x:Class=&quot;MyWpfApp.MainWindow&quot;
        xmlns=&quot;http://schemas.microsoft.com/winfx/2006/xaml/presentation&quot;
        xmlns:x=&quot;http://schemas.microsoft.com/winfx/2006/xaml&quot;
        xmlns:local=&quot;clr-namespace:MyWpfApp&quot;
        Title=&quot;MVVM示例&quot; Height=&quot;450&quot; Width=&quot;800&quot;&amp;gt;
    &amp;lt;Window.DataContext&amp;gt;
        &amp;lt;local:MainViewModel /&amp;gt;
    &amp;lt;/Window.DataContext&amp;gt;
    
    &amp;lt;Grid&amp;gt;
        &amp;lt;StackPanel Margin=&quot;20&quot;&amp;gt;
            &amp;lt;TextBlock Text=&quot;用户管理&quot; FontSize=&quot;20&quot; FontWeight=&quot;Bold&quot; Margin=&quot;0,0,0,20&quot; /&amp;gt;
            
            &amp;lt;ListBox ItemsSource=&quot;{Binding Users}&quot; SelectedItem=&quot;{Binding SelectedUser}&quot; Margin=&quot;0,0,0,20&quot;&amp;gt;
                &amp;lt;ListBox.ItemTemplate&amp;gt;
                    &amp;lt;DataTemplate&amp;gt;
                        &amp;lt;StackPanel Orientation=&quot;Horizontal&quot;&amp;gt;
                            &amp;lt;TextBlock Text=&quot;{Binding Name}&quot; Width=&quot;100&quot; /&amp;gt;
                            &amp;lt;TextBlock Text=&quot;{Binding Age}&quot; Width=&quot;50&quot; /&amp;gt;
                            &amp;lt;TextBlock Text=&quot;{Binding Email}&quot; /&amp;gt;
                        &amp;lt;/StackPanel&amp;gt;
                    &amp;lt;/DataTemplate&amp;gt;
                &amp;lt;/ListBox.ItemTemplate&amp;gt;
            &amp;lt;/ListBox&amp;gt;
            
            &amp;lt;Grid&amp;gt;
                &amp;lt;Grid.ColumnDefinitions&amp;gt;
                    &amp;lt;ColumnDefinition Width=&quot;100&quot; /&amp;gt;
                    &amp;lt;ColumnDefinition Width=&quot;*&quot; /&amp;gt;
                &amp;lt;/Grid.ColumnDefinitions&amp;gt;
                &amp;lt;Grid.RowDefinitions&amp;gt;
                    &amp;lt;RowDefinition Height=&quot;30&quot; /&amp;gt;
                    &amp;lt;RowDefinition Height=&quot;30&quot; /&amp;gt;
                    &amp;lt;RowDefinition Height=&quot;30&quot; /&amp;gt;
                    &amp;lt;RowDefinition Height=&quot;40&quot; /&amp;gt;
                &amp;lt;/Grid.RowDefinitions&amp;gt;
                
                &amp;lt;TextBlock Text=&quot;姓名:&quot; Grid.Column=&quot;0&quot; Grid.Row=&quot;0&quot; VerticalAlignment=&quot;Center&quot; /&amp;gt;
                &amp;lt;TextBox Text=&quot;{Binding SelectedUser.Name, Mode=TwoWay}&quot; Grid.Column=&quot;1&quot; Grid.Row=&quot;0&quot; /&amp;gt;
                
                &amp;lt;TextBlock Text=&quot;年龄:&quot; Grid.Column=&quot;0&quot; Grid.Row=&quot;1&quot; VerticalAlignment=&quot;Center&quot; /&amp;gt;
                &amp;lt;TextBox Text=&quot;{Binding SelectedUser.Age, Mode=TwoWay}&quot; Grid.Column=&quot;1&quot; Grid.Row=&quot;1&quot; /&amp;gt;
                
                &amp;lt;TextBlock Text=&quot;邮箱:&quot; Grid.Column=&quot;0&quot; Grid.Row=&quot;2&quot; VerticalAlignment=&quot;Center&quot; /&amp;gt;
                &amp;lt;TextBox Text=&quot;{Binding SelectedUser.Email, Mode=TwoWay}&quot; Grid.Column=&quot;1&quot; Grid.Row=&quot;2&quot; /&amp;gt;
                
                &amp;lt;StackPanel Orientation=&quot;Horizontal&quot; Grid.Column=&quot;0&quot; Grid.Row=&quot;3&quot; Grid.ColumnSpan=&quot;2&quot; HorizontalAlignment=&quot;Right&quot; Margin=&quot;0,10,0,0&quot;&amp;gt;
                    &amp;lt;Button Content=&quot;添加&quot; Command=&quot;{Binding AddCommand}&quot; Margin=&quot;0,0,10,0&quot; /&amp;gt;
                    &amp;lt;Button Content=&quot;保存&quot; Command=&quot;{Binding SaveCommand}&quot; Margin=&quot;0,0,10,0&quot; /&amp;gt;
                    &amp;lt;Button Content=&quot;删除&quot; Command=&quot;{Binding DeleteCommand}&quot; /&amp;gt;
                &amp;lt;/StackPanel&amp;gt;
            &amp;lt;/Grid&amp;gt;
        &amp;lt;/StackPanel&amp;gt;
    &amp;lt;/Grid&amp;gt;
&amp;lt;/Window&amp;gt;
&lt;/code&gt;&lt;/pre&gt;
&lt;pre&gt;&lt;code&gt;// Model
public class User : INotifyPropertyChanged
{
    private string _name;
    private int _age;
    private string _email;
    
    public string Name
    {
        get { return _name; }
        set { SetProperty(ref _name, value); }
    }
    
    public int Age
    {
        get { return _age; }
        set { SetProperty(ref _age, value); }
    }
    
    public string Email
    {
        get { return _email; }
        set { SetProperty(ref _email, value); }
    }
    
    public event PropertyChangedEventHandler PropertyChanged;
    
    protected void SetProperty&amp;lt;T&amp;gt;(ref T field, T value, [CallerMemberName] string propertyName = null)
    {
        if (!EqualityComparer&amp;lt;T&amp;gt;.Default.Equals(field, value))
        {
            field = value;
            PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(propertyName));
        }
    }
}

// ViewModel
public class MainViewModel
{
    public ObservableCollection&amp;lt;User&amp;gt; Users { get; set; }
    public User SelectedUser { get; set; }
    
    public ICommand AddCommand { get; set; }
    public ICommand SaveCommand { get; set; }
    public ICommand DeleteCommand { get; set; }
    
    public MainViewModel()
    {
        Users = new ObservableCollection&amp;lt;User&amp;gt;
        {
            new User { Name = &quot;张三&quot;, Age = 25, Email = &quot;zhangsan@example.com&quot; },
            new User { Name = &quot;李四&quot;, Age = 30, Email = &quot;lisi@example.com&quot; },
            new User { Name = &quot;王五&quot;, Age = 35, Email = &quot;wangwu@example.com&quot; }
        };
        
        SelectedUser = Users[0];
        
        AddCommand = new RelayCommand(AddUser);
        SaveCommand = new RelayCommand(SaveUser);
        DeleteCommand = new RelayCommand(DeleteUser);
    }
    
    private void AddUser(object parameter)
    {
        var newUser = new User { Name = &quot;新用户&quot;, Age = 0, Email = &quot;&quot; };
        Users.Add(newUser);
        SelectedUser = newUser;
    }
    
    private void SaveUser(object parameter)
    {
        // 保存逻辑
    }
    
    private void DeleteUser(object parameter)
    {
        if (SelectedUser != null)
        {
            Users.Remove(SelectedUser);
            if (Users.Count &amp;gt; 0)
            {
                SelectedUser = Users[0];
            }
            else
            {
                SelectedUser = new User();
            }
        }
    }
}
&lt;/code&gt;&lt;/pre&gt;
&lt;h3&gt;8.6 MVVM 工程结构推荐&lt;/h3&gt;
&lt;p&gt;常见的 MVVM 工程结构示例：&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;MyWpfApp
├─ Models
│  ├─ User.cs
│  └─ Order.cs
├─ ViewModels
│  ├─ MainViewModel.cs
│  └─ OrderDetailViewModel.cs
├─ Views
│  ├─ MainWindow.xaml
│  └─ OrderDetailView.xaml
├─ Services
│  ├─ INavigationService.cs
│  ├─ IDataService.cs
│  └─ MessageService.cs
├─ Commands
│  └─ RelayCommand.cs
└─ App.xaml / App.xaml.cs
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;关键思想：&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;strong&gt;View 只关心界面和交互细节&lt;/strong&gt;（XAML + 少量 Code-behind 事件转发到命令）&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;ViewModel 不引用具体的 View 类型&lt;/strong&gt;，通过接口（服务）做导航、弹窗等操作&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Model 只关注业务与数据，不依赖 UI 框架&lt;/strong&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;h3&gt;8.7 MVVM 中的异步命令与长耗时操作&lt;/h3&gt;
&lt;p&gt;在 MVVM 中处理异步操作时，通常会引入“异步命令”：&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;public class AsyncCommand : ICommand
{
    private readonly Func&amp;lt;Task&amp;gt; _execute;
    private readonly Func&amp;lt;bool&amp;gt; _canExecute;
    private bool _isExecuting;

    public event EventHandler CanExecuteChanged;

    public AsyncCommand(Func&amp;lt;Task&amp;gt; execute, Func&amp;lt;bool&amp;gt; canExecute = null)
    {
        _execute = execute;
        _canExecute = canExecute;
    }

    public bool CanExecute(object parameter)
    {
        return !_isExecuting &amp;amp;&amp;amp; (_canExecute?.Invoke() ?? true);
    }

    public async void Execute(object parameter)
    {
        _isExecuting = true;
        RaiseCanExecuteChanged();
        try
        {
            await _execute();
        }
        finally
        {
            _isExecuting = false;
            RaiseCanExecuteChanged();
        }
    }

    private void RaiseCanExecuteChanged()
    {
        CanExecuteChanged?.Invoke(this, EventArgs.Empty);
    }
}
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;在 ViewModel 中使用：&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;public class DashboardViewModel
{
    public ICommand LoadDataCommand { get; }

    private bool _isBusy;
    public bool IsBusy
    {
        get =&amp;gt; _isBusy;
        set { _isBusy = value; /* OnPropertyChanged 省略 */ }
    }

    public DashboardViewModel()
    {
        LoadDataCommand = new AsyncCommand(LoadDataAsync, () =&amp;gt; !IsBusy);
    }

    private async Task LoadDataAsync()
    {
        IsBusy = true;
        try
        {
            await Task.Delay(1000);
            // 加载数据逻辑
        }
        finally
        {
            IsBusy = false;
        }
    }
}
&lt;/code&gt;&lt;/pre&gt;
&lt;h3&gt;8.8 MVVM 中的验证与错误提示&lt;/h3&gt;
&lt;p&gt;MVVM 中常用两种验证接口：&lt;code&gt;IDataErrorInfo&lt;/code&gt; 与 &lt;code&gt;INotifyDataErrorInfo&lt;/code&gt;。&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;&lt;strong&gt;IDataErrorInfo（简单同步验证）&lt;/strong&gt;&lt;/li&gt;
&lt;/ol&gt;
&lt;pre&gt;&lt;code&gt;public class UserEditModel : INotifyPropertyChanged, IDataErrorInfo
{
    public string Name { get; set; }
    public int Age { get; set; }

    public string Error =&amp;gt; null;

    public string this[string columnName]
    {
        get
        {
            return columnName switch
            {
                nameof(Name) when string.IsNullOrWhiteSpace(Name) =&amp;gt; &quot;姓名不能为空&quot;,
                nameof(Age) when Age &amp;lt;= 0 || Age &amp;gt; 120 =&amp;gt; &quot;年龄范围不合法&quot;,
                _ =&amp;gt; null
            };
        }
    }
}
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;配合绑定使用：&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;&amp;lt;TextBox Text=&quot;{Binding Name, UpdateSourceTrigger=PropertyChanged, ValidatesOnDataErrors=True}&quot; /&amp;gt;
&amp;lt;TextBox Text=&quot;{Binding Age, UpdateSourceTrigger=PropertyChanged, ValidatesOnDataErrors=True}&quot; /&amp;gt;
&lt;/code&gt;&lt;/pre&gt;
&lt;ol&gt;
&lt;li&gt;&lt;strong&gt;INotifyDataErrorInfo（适合异步/复杂验证）&lt;/strong&gt;&lt;/li&gt;
&lt;/ol&gt;
&lt;p&gt;用于需要异步验证（例如服务端校验）时，错误集合可按属性名维护。&lt;/p&gt;
&lt;h3&gt;8.9 MVVM 单元测试要点&lt;/h3&gt;
&lt;p&gt;MVVM 最大的优势之一是 ViewModel 易于测试：&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;ViewModel 不依赖具体 UI 控件，只依赖接口和模型&lt;/li&gt;
&lt;li&gt;命令逻辑可通过直接调用 &lt;code&gt;Execute&lt;/code&gt; / &lt;code&gt;CanExecute&lt;/code&gt; 测试&lt;/li&gt;
&lt;li&gt;通过 Mock（模拟）服务测试导航、消息弹出等行为&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;示例（伪代码）：&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;[Fact]
public void SaveCommand_Should_Disable_When_Model_Invalid()
{
    var dataService = new FakeDataService();
    var vm = new EditUserViewModel(dataService);

    vm.User.Name = string.Empty;

    Assert.False(vm.SaveCommand.CanExecute(null));
}
&lt;/code&gt;&lt;/pre&gt;
&lt;h2&gt;9. 样式与模板&lt;/h2&gt;
&lt;h3&gt;9.1 样式（Style）基础&lt;/h3&gt;
&lt;p&gt;样式用于定义控件的外观，避免重复设置相同的属性：&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;&amp;lt;Window.Resources&amp;gt;
    &amp;lt;!-- 基本样式 --&amp;gt;
    &amp;lt;Style x:Key=&quot;ButtonStyle&quot; TargetType=&quot;Button&quot;&amp;gt;
        &amp;lt;Setter Property=&quot;Background&quot; Value=&quot;Blue&quot; /&amp;gt;
        &amp;lt;Setter Property=&quot;Foreground&quot; Value=&quot;White&quot; /&amp;gt;
        &amp;lt;Setter Property=&quot;FontSize&quot; Value=&quot;14&quot; /&amp;gt;
        &amp;lt;Setter Property=&quot;Padding&quot; Value=&quot;10,5&quot; /&amp;gt;
        &amp;lt;Setter Property=&quot;Margin&quot; Value=&quot;5&quot; /&amp;gt;
    &amp;lt;/Style&amp;gt;
    
    &amp;lt;!-- 继承样式 --&amp;gt;
    &amp;lt;Style x:Key=&quot;PrimaryButtonStyle&quot; TargetType=&quot;Button&quot; BasedOn=&quot;{StaticResource ButtonStyle}&quot;&amp;gt;
        &amp;lt;Setter Property=&quot;Background&quot; Value=&quot;Green&quot; /&amp;gt;
    &amp;lt;/Style&amp;gt;
    
    &amp;lt;!-- 隐式样式 --&amp;gt;
    &amp;lt;Style TargetType=&quot;TextBlock&quot;&amp;gt;
        &amp;lt;Setter Property=&quot;FontSize&quot; Value=&quot;14&quot; /&amp;gt;
        &amp;lt;Setter Property=&quot;Margin&quot; Value=&quot;5&quot; /&amp;gt;
    &amp;lt;/Style&amp;gt;
&amp;lt;/Window.Resources&amp;gt;

&amp;lt;!-- 使用样式 --&amp;gt;
&amp;lt;Button Content=&quot;普通按钮&quot; Style=&quot;{StaticResource ButtonStyle}&quot; /&amp;gt;
&amp;lt;Button Content=&quot;主要按钮&quot; Style=&quot;{StaticResource PrimaryButtonStyle}&quot; /&amp;gt;
&amp;lt;TextBlock Text=&quot;这是一个文本块&quot; /&amp;gt;
&lt;/code&gt;&lt;/pre&gt;
&lt;h3&gt;9.2 触发器（Trigger）&lt;/h3&gt;
&lt;p&gt;触发器用于响应属性变化，动态改变控件的外观：&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;&amp;lt;Style TargetType=&quot;Button&quot;&amp;gt;
    &amp;lt;Setter Property=&quot;Background&quot; Value=&quot;Blue&quot; /&amp;gt;
    &amp;lt;Setter Property=&quot;Foreground&quot; Value=&quot;White&quot; /&amp;gt;
    &amp;lt;Setter Property=&quot;Padding&quot; Value=&quot;10,5&quot; /&amp;gt;
    &amp;lt;Setter Property=&quot;Margin&quot; Value=&quot;5&quot; /&amp;gt;
    
    &amp;lt;Style.Triggers&amp;gt;
        &amp;lt;!-- 属性触发器 --&amp;gt;
        &amp;lt;Trigger Property=&quot;IsMouseOver&quot; Value=&quot;True&quot;&amp;gt;
            &amp;lt;Setter Property=&quot;Background&quot; Value=&quot;LightBlue&quot; /&amp;gt;
            &amp;lt;Setter Property=&quot;Cursor&quot; Value=&quot;Hand&quot; /&amp;gt;
        &amp;lt;/Trigger&amp;gt;
        
        &amp;lt;!-- 事件触发器 --&amp;gt;
        &amp;lt;EventTrigger RoutedEvent=&quot;MouseDown&quot;&amp;gt;
            &amp;lt;BeginStoryboard&amp;gt;
                &amp;lt;Storyboard&amp;gt;
                    &amp;lt;DoubleAnimation Storyboard.TargetProperty=&quot;Width&quot; From=&quot;100&quot; To=&quot;120&quot; Duration=&quot;0:0:0.2&quot; /&amp;gt;
                &amp;lt;/Storyboard&amp;gt;
            &amp;lt;/BeginStoryboard&amp;gt;
        &amp;lt;/EventTrigger&amp;gt;
        
        &amp;lt;!-- 数据触发器 --&amp;gt;
        &amp;lt;DataTrigger Binding=&quot;{Binding IsEnabled}&quot; Value=&quot;False&quot;&amp;gt;
            &amp;lt;Setter Property=&quot;Background&quot; Value=&quot;Gray&quot; /&amp;gt;
            &amp;lt;Setter Property=&quot;Foreground&quot; Value=&quot;LightGray&quot; /&amp;gt;
        &amp;lt;/DataTrigger&amp;gt;
    &amp;lt;/Style.Triggers&amp;gt;
&amp;lt;/Style&amp;gt;
&lt;/code&gt;&lt;/pre&gt;
&lt;h3&gt;9.3 控件模板（ControlTemplate）&lt;/h3&gt;
&lt;p&gt;控件模板用于完全自定义控件的外观：&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;&amp;lt;ControlTemplate x:Key=&quot;CustomButtonTemplate&quot; TargetType=&quot;Button&quot;&amp;gt;
    &amp;lt;Border x:Name=&quot;Border&quot; 
            Background=&quot;{TemplateBinding Background}&quot;
            BorderBrush=&quot;{TemplateBinding BorderBrush}&quot;
            BorderThickness=&quot;{TemplateBinding BorderThickness}&quot;
            CornerRadius=&quot;5&quot;&amp;gt;
        &amp;lt;StackPanel Orientation=&quot;Horizontal&quot; HorizontalAlignment=&quot;Center&quot; VerticalAlignment=&quot;Center&quot;&amp;gt;
            &amp;lt;ContentPresenter x:Name=&quot;Content&quot; Margin=&quot;{TemplateBinding Padding}&quot; /&amp;gt;
        &amp;lt;/StackPanel&amp;gt;
    &amp;lt;/Border&amp;gt;
    &amp;lt;ControlTemplate.Triggers&amp;gt;
        &amp;lt;Trigger Property=&quot;IsMouseOver&quot; Value=&quot;True&quot;&amp;gt;
            &amp;lt;Setter TargetName=&quot;Border&quot; Property=&quot;Background&quot; Value=&quot;LightBlue&quot; /&amp;gt;
        &amp;lt;/Trigger&amp;gt;
        &amp;lt;Trigger Property=&quot;IsPressed&quot; Value=&quot;True&quot;&amp;gt;
            &amp;lt;Setter TargetName=&quot;Border&quot; Property=&quot;Background&quot; Value=&quot;DarkBlue&quot; /&amp;gt;
            &amp;lt;Setter TargetName=&quot;Content&quot; Property=&quot;Margin&quot; Value=&quot;12,7,8,3&quot; /&amp;gt;
        &amp;lt;/Trigger&amp;gt;
        &amp;lt;Trigger Property=&quot;IsEnabled&quot; Value=&quot;False&quot;&amp;gt;
            &amp;lt;Setter TargetName=&quot;Border&quot; Property=&quot;Background&quot; Value=&quot;Gray&quot; /&amp;gt;
            &amp;lt;Setter TargetName=&quot;Border&quot; Property=&quot;BorderBrush&quot; Value=&quot;LightGray&quot; /&amp;gt;
        &amp;lt;/Trigger&amp;gt;
    &amp;lt;/ControlTemplate.Triggers&amp;gt;
&amp;lt;/ControlTemplate&amp;gt;

&amp;lt;Button Content=&quot;自定义按钮&quot; Template=&quot;{StaticResource CustomButtonTemplate}&quot; Background=&quot;Blue&quot; Foreground=&quot;White&quot; Padding=&quot;10,5&quot; /&amp;gt;
&lt;/code&gt;&lt;/pre&gt;
&lt;h3&gt;9.4 数据模板（DataTemplate）&lt;/h3&gt;
&lt;p&gt;数据模板用于定义数据对象的可视化表示：&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;&amp;lt;DataTemplate x:Key=&quot;PersonTemplate&quot; DataType=&quot;{x:Type local:Person}&quot;&amp;gt;
    &amp;lt;Border BorderBrush=&quot;Gray&quot; BorderThickness=&quot;1&quot; Padding=&quot;10&quot; Margin=&quot;5&quot; CornerRadius=&quot;5&quot;&amp;gt;
        &amp;lt;StackPanel&amp;gt;
            &amp;lt;StackPanel Orientation=&quot;Horizontal&quot;&amp;gt;
                &amp;lt;TextBlock Text=&quot;姓名: &quot; FontWeight=&quot;Bold&quot; /&amp;gt;
                &amp;lt;TextBlock Text=&quot;{Binding Name}&quot; /&amp;gt;
            &amp;lt;/StackPanel&amp;gt;
            &amp;lt;StackPanel Orientation=&quot;Horizontal&quot;&amp;gt;
                &amp;lt;TextBlock Text=&quot;年龄: &quot; FontWeight=&quot;Bold&quot; /&amp;gt;
                &amp;lt;TextBlock Text=&quot;{Binding Age}&quot; /&amp;gt;
            &amp;lt;/StackPanel&amp;gt;
            &amp;lt;StackPanel Orientation=&quot;Horizontal&quot;&amp;gt;
                &amp;lt;TextBlock Text=&quot;邮箱: &quot; FontWeight=&quot;Bold&quot; /&amp;gt;
                &amp;lt;TextBlock Text=&quot;{Binding Email}&quot; /&amp;gt;
            &amp;lt;/StackPanel&amp;gt;
        &amp;lt;/StackPanel&amp;gt;
    &amp;lt;/Border&amp;gt;
&amp;lt;/DataTemplate&amp;gt;

&amp;lt;ContentControl Content=&quot;{Binding SelectedPerson}&quot; ContentTemplate=&quot;{StaticResource PersonTemplate}&quot; /&amp;gt;

&amp;lt;ListBox ItemsSource=&quot;{Binding People}&quot; ItemTemplate=&quot;{StaticResource PersonTemplate}&quot; /&amp;gt;
&lt;/code&gt;&lt;/pre&gt;
&lt;h3&gt;9.5 资源（Resource）与资源字典&lt;/h3&gt;
&lt;p&gt;资源字典用于集中管理和共享资源：&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;&amp;lt;!-- Styles.xaml --&amp;gt;
&amp;lt;ResourceDictionary xmlns=&quot;http://schemas.microsoft.com/winfx/2006/xaml/presentation&quot;
                    xmlns:x=&quot;http://schemas.microsoft.com/winfx/2006/xaml&quot;&amp;gt;
    &amp;lt;SolidColorBrush x:Key=&quot;PrimaryColor&quot; Color=&quot;Blue&quot; /&amp;gt;
    &amp;lt;SolidColorBrush x:Key=&quot;SecondaryColor&quot; Color=&quot;Green&quot; /&amp;gt;
    
    &amp;lt;Style x:Key=&quot;ButtonStyle&quot; TargetType=&quot;Button&quot;&amp;gt;
        &amp;lt;Setter Property=&quot;Background&quot; Value=&quot;{StaticResource PrimaryColor}&quot; /&amp;gt;
        &amp;lt;Setter Property=&quot;Foreground&quot; Value=&quot;White&quot; /&amp;gt;
        &amp;lt;Setter Property=&quot;Padding&quot; Value=&quot;10,5&quot; /&amp;gt;
    &amp;lt;/Style&amp;gt;
&amp;lt;/ResourceDictionary&amp;gt;

&amp;lt;!-- 在 App.xaml 中引用 --&amp;gt;
&amp;lt;Application.Resources&amp;gt;
    &amp;lt;ResourceDictionary&amp;gt;
        &amp;lt;ResourceDictionary.MergedDictionaries&amp;gt;
            &amp;lt;ResourceDictionary Source=&quot;Styles.xaml&quot; /&amp;gt;
        &amp;lt;/ResourceDictionary.MergedDictionaries&amp;gt;
    &amp;lt;/ResourceDictionary&amp;gt;
&amp;lt;/Application.Resources&amp;gt;

&amp;lt;!-- 在窗口中引用 --&amp;gt;
&amp;lt;Window.Resources&amp;gt;
    &amp;lt;ResourceDictionary&amp;gt;
        &amp;lt;ResourceDictionary.MergedDictionaries&amp;gt;
            &amp;lt;ResourceDictionary Source=&quot;Styles.xaml&quot; /&amp;gt;
        &amp;lt;/ResourceDictionary.MergedDictionaries&amp;gt;
        
        &amp;lt;!-- 窗口特定资源 --&amp;gt;
        &amp;lt;Style x:Key=&quot;WindowButtonStyle&quot; TargetType=&quot;Button&quot; BasedOn=&quot;{StaticResource ButtonStyle}&quot;&amp;gt;
            &amp;lt;Setter Property=&quot;Margin&quot; Value=&quot;5&quot; /&amp;gt;
        &amp;lt;/Style&amp;gt;
    &amp;lt;/ResourceDictionary&amp;gt;
&amp;lt;/Window.Resources&amp;gt;
&lt;/code&gt;&lt;/pre&gt;
&lt;h3&gt;9.6 TemplateBinding 与 RelativeSource TemplatedParent&lt;/h3&gt;
&lt;p&gt;在 &lt;code&gt;ControlTemplate&lt;/code&gt; 内部访问控件本身的属性时，常见两种写法：&lt;code&gt;TemplateBinding&lt;/code&gt; 与 &lt;code&gt;RelativeSource TemplatedParent&lt;/code&gt;。&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;&lt;strong&gt;TemplateBinding&lt;/strong&gt;
&lt;ul&gt;
&lt;li&gt;语法简洁，性能较好&lt;/li&gt;
&lt;li&gt;只能用于简单的“一对一属性映射”，不支持转换器、字符串格式等&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;/ol&gt;
&lt;pre&gt;&lt;code&gt;&amp;lt;ControlTemplate TargetType=&quot;Button&quot;&amp;gt;
    &amp;lt;Border Background=&quot;{TemplateBinding Background}&quot;
            BorderBrush=&quot;{TemplateBinding BorderBrush}&quot;
            BorderThickness=&quot;{TemplateBinding BorderThickness}&quot;&amp;gt;
        &amp;lt;ContentPresenter Margin=&quot;{TemplateBinding Padding}&quot;
                          HorizontalAlignment=&quot;{TemplateBinding HorizontalContentAlignment}&quot;
                          VerticalAlignment=&quot;{TemplateBinding VerticalContentAlignment}&quot; /&amp;gt;
    &amp;lt;/Border&amp;gt;
&amp;lt;/ControlTemplate&amp;gt;
&lt;/code&gt;&lt;/pre&gt;
&lt;ol&gt;
&lt;li&gt;&lt;strong&gt;RelativeSource TemplatedParent&lt;/strong&gt;
&lt;ul&gt;
&lt;li&gt;完整的 Binding 语法，支持 Converter、StringFormat 等&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;/ol&gt;
&lt;pre&gt;&lt;code&gt;&amp;lt;ControlTemplate TargetType=&quot;Button&quot;&amp;gt;
    &amp;lt;Border&amp;gt;
        &amp;lt;Border.Background&amp;gt;
            &amp;lt;SolidColorBrush Color=&quot;{Binding RelativeSource={RelativeSource TemplatedParent},
                                             Path=Tag,
                                             Converter={StaticResource TagToColorConverter}}&quot; /&amp;gt;
        &amp;lt;/Border.Background&amp;gt;
        &amp;lt;ContentPresenter /&amp;gt;
    &amp;lt;/Border&amp;gt;
&amp;lt;/ControlTemplate&amp;gt;
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;一般原则：&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;简单属性映射用 &lt;code&gt;TemplateBinding&lt;/code&gt;&lt;/li&gt;
&lt;li&gt;需要转换器、复杂路径、绑定到 DataContext 时用 &lt;code&gt;RelativeSource TemplatedParent&lt;/code&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;h3&gt;9.7 ItemsControl 模板体系：ItemTemplate、ItemsPanel、ItemContainerStyle&lt;/h3&gt;
&lt;p&gt;&lt;code&gt;ItemsControl&lt;/code&gt; 及其子类有一套完整的模板体系：&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;&lt;strong&gt;ItemTemplate（每一行长什么样）&lt;/strong&gt;&lt;/li&gt;
&lt;/ol&gt;
&lt;pre&gt;&lt;code&gt;&amp;lt;ListBox ItemsSource=&quot;{Binding People}&quot;&amp;gt;
    &amp;lt;ListBox.ItemTemplate&amp;gt;
        &amp;lt;DataTemplate&amp;gt;
            &amp;lt;StackPanel Orientation=&quot;Horizontal&quot;&amp;gt;
                &amp;lt;TextBlock Text=&quot;{Binding Name}&quot; Width=&quot;100&quot; /&amp;gt;
                &amp;lt;TextBlock Text=&quot;{Binding Age}&quot; /&amp;gt;
            &amp;lt;/StackPanel&amp;gt;
        &amp;lt;/DataTemplate&amp;gt;
    &amp;lt;/ListBox.ItemTemplate&amp;gt;
&amp;lt;/ListBox&amp;gt;
&lt;/code&gt;&lt;/pre&gt;
&lt;ol&gt;
&lt;li&gt;&lt;strong&gt;ItemsPanel（所有行如何排列）&lt;/strong&gt;&lt;/li&gt;
&lt;/ol&gt;
&lt;pre&gt;&lt;code&gt;&amp;lt;ListBox ItemsSource=&quot;{Binding People}&quot;&amp;gt;
    &amp;lt;ListBox.ItemsPanel&amp;gt;
        &amp;lt;ItemsPanelTemplate&amp;gt;
            &amp;lt;WrapPanel /&amp;gt;
        &amp;lt;/ItemsPanelTemplate&amp;gt;
    &amp;lt;/ListBox.ItemsPanel&amp;gt;
&amp;lt;/ListBox&amp;gt;
&lt;/code&gt;&lt;/pre&gt;
&lt;ol&gt;
&lt;li&gt;&lt;strong&gt;ItemContainerStyle（容器行为与样式）&lt;/strong&gt;&lt;/li&gt;
&lt;/ol&gt;
&lt;pre&gt;&lt;code&gt;&amp;lt;ListBox ItemsSource=&quot;{Binding People}&quot; SelectionMode=&quot;Multiple&quot;&amp;gt;
    &amp;lt;ListBox.ItemContainerStyle&amp;gt;
        &amp;lt;Style TargetType=&quot;ListBoxItem&quot;&amp;gt;
            &amp;lt;Setter Property=&quot;Margin&quot; Value=&quot;2&quot; /&amp;gt;
            &amp;lt;Setter Property=&quot;HorizontalContentAlignment&quot; Value=&quot;Stretch&quot; /&amp;gt;
            &amp;lt;Style.Triggers&amp;gt;
                &amp;lt;Trigger Property=&quot;IsSelected&quot; Value=&quot;True&quot;&amp;gt;
                    &amp;lt;Setter Property=&quot;Background&quot; Value=&quot;#333333&quot; /&amp;gt;
                    &amp;lt;Setter Property=&quot;Foreground&quot; Value=&quot;White&quot; /&amp;gt;
                &amp;lt;/Trigger&amp;gt;
            &amp;lt;/Style.Triggers&amp;gt;
        &amp;lt;/Style&amp;gt;
    &amp;lt;/ListBox.ItemContainerStyle&amp;gt;
&amp;lt;/ListBox&amp;gt;
&lt;/code&gt;&lt;/pre&gt;
&lt;ol&gt;
&lt;li&gt;&lt;strong&gt;HierarchicalDataTemplate（层级数据模板，常用于 TreeView）&lt;/strong&gt;&lt;/li&gt;
&lt;/ol&gt;
&lt;pre&gt;&lt;code&gt;&amp;lt;TreeView ItemsSource=&quot;{Binding Departments}&quot;&amp;gt;
    &amp;lt;TreeView.ItemTemplate&amp;gt;
        &amp;lt;HierarchicalDataTemplate ItemsSource=&quot;{Binding Employees}&quot;&amp;gt;
            &amp;lt;TextBlock Text=&quot;{Binding Name}&quot; /&amp;gt;
        &amp;lt;/HierarchicalDataTemplate&amp;gt;
    &amp;lt;/TreeView.ItemTemplate&amp;gt;
&amp;lt;/TreeView&amp;gt;
&lt;/code&gt;&lt;/pre&gt;
&lt;h3&gt;9.8 VisualStateManager 与控件状态&lt;/h3&gt;
&lt;p&gt;对于复杂控件状态（普通、鼠标悬停、按下、禁用等），可以使用 &lt;code&gt;VisualStateManager&lt;/code&gt; 管理视觉状态：&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;&amp;lt;ControlTemplate x:Key=&quot;StatefulButtonTemplate&quot; TargetType=&quot;Button&quot;&amp;gt;
    &amp;lt;Grid x:Name=&quot;Root&quot;&amp;gt;
        &amp;lt;VisualStateManager.VisualStateGroups&amp;gt;
            &amp;lt;VisualStateGroup x:Name=&quot;CommonStates&quot;&amp;gt;
                &amp;lt;VisualState x:Name=&quot;Normal&quot; /&amp;gt;
                &amp;lt;VisualState x:Name=&quot;MouseOver&quot;&amp;gt;
                    &amp;lt;Storyboard&amp;gt;
                        &amp;lt;ColorAnimation Storyboard.TargetName=&quot;BorderBrush&quot;
                                        Storyboard.TargetProperty=&quot;Color&quot;
                                        To=&quot;Orange&quot; Duration=&quot;0:0:0.2&quot; /&amp;gt;
                    &amp;lt;/Storyboard&amp;gt;
                &amp;lt;/VisualState&amp;gt;
                &amp;lt;VisualState x:Name=&quot;Pressed&quot;&amp;gt;
                    &amp;lt;Storyboard&amp;gt;
                        &amp;lt;DoubleAnimation Storyboard.TargetName=&quot;Root&quot;
                                         Storyboard.TargetProperty=&quot;Opacity&quot;
                                         To=&quot;0.7&quot; Duration=&quot;0:0:0.1&quot; /&amp;gt;
                    &amp;lt;/Storyboard&amp;gt;
                &amp;lt;/VisualState&amp;gt;
                &amp;lt;VisualState x:Name=&quot;Disabled&quot;&amp;gt;
                    &amp;lt;Storyboard&amp;gt;
                        &amp;lt;DoubleAnimation Storyboard.TargetName=&quot;Root&quot;
                                         Storyboard.TargetProperty=&quot;Opacity&quot;
                                         To=&quot;0.4&quot; Duration=&quot;0:0:0.2&quot; /&amp;gt;
                    &amp;lt;/Storyboard&amp;gt;
                &amp;lt;/VisualState&amp;gt;
            &amp;lt;/VisualStateGroup&amp;gt;
        &amp;lt;/VisualStateManager.VisualStateGroups&amp;gt;

        &amp;lt;Border CornerRadius=&quot;4&quot; Padding=&quot;{TemplateBinding Padding}&quot;&amp;gt;
            &amp;lt;Border.BorderBrush&amp;gt;
                &amp;lt;SolidColorBrush x:Name=&quot;BorderBrush&quot; Color=&quot;Gray&quot; /&amp;gt;
            &amp;lt;/Border.BorderBrush&amp;gt;
            &amp;lt;ContentPresenter HorizontalAlignment=&quot;Center&quot; VerticalAlignment=&quot;Center&quot; /&amp;gt;
        &amp;lt;/Border&amp;gt;
    &amp;lt;/Grid&amp;gt;
&amp;lt;/ControlTemplate&amp;gt;

&amp;lt;Button Content=&quot;状态按钮&quot;
        Template=&quot;{StaticResource StatefulButtonTemplate}&quot; /&amp;gt;
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;&lt;code&gt;VisualStateManager&lt;/code&gt; 适合状态较多、需要平滑动画过渡的控件，对复杂 UI 的可维护性帮助较大。&lt;/p&gt;
&lt;h2&gt;10. 动画与图形&lt;/h2&gt;
&lt;h3&gt;10.1 动画基础&lt;/h3&gt;
&lt;p&gt;WPF 提供了强大的动画系统，支持各种类型的动画：&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;&lt;strong&gt;属性动画&lt;/strong&gt;：改变控件的属性值&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;路径动画&lt;/strong&gt;：沿着路径移动元素&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;关键帧动画&lt;/strong&gt;：定义多个关键帧，实现更复杂的动画&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;故事板&lt;/strong&gt;：组合多个动画，实现复杂的动画序列&lt;/li&gt;
&lt;/ol&gt;
&lt;h3&gt;10.2 DoubleAnimation与ColorAnimation&lt;/h3&gt;
&lt;p&gt;DoubleAnimation 用于动画数值类型的属性：&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;&amp;lt;Button Content=&quot;动画按钮&quot; Width=&quot;100&quot; Height=&quot;40&quot;&amp;gt;
    &amp;lt;Button.Triggers&amp;gt;
        &amp;lt;EventTrigger RoutedEvent=&quot;MouseEnter&quot;&amp;gt;
            &amp;lt;BeginStoryboard&amp;gt;
                &amp;lt;Storyboard&amp;gt;
                    &amp;lt;DoubleAnimation Storyboard.TargetProperty=&quot;Width&quot; From=&quot;100&quot; To=&quot;150&quot; Duration=&quot;0:0:0.5&quot; /&amp;gt;
                    &amp;lt;DoubleAnimation Storyboard.TargetProperty=&quot;Height&quot; From=&quot;40&quot; To=&quot;60&quot; Duration=&quot;0:0:0.5&quot; /&amp;gt;
                &amp;lt;/Storyboard&amp;gt;
            &amp;lt;/BeginStoryboard&amp;gt;
        &amp;lt;/EventTrigger&amp;gt;
        &amp;lt;EventTrigger RoutedEvent=&quot;MouseLeave&quot;&amp;gt;
            &amp;lt;BeginStoryboard&amp;gt;
                &amp;lt;Storyboard&amp;gt;
                    &amp;lt;DoubleAnimation Storyboard.TargetProperty=&quot;Width&quot; From=&quot;150&quot; To=&quot;100&quot; Duration=&quot;0:0:0.5&quot; /&amp;gt;
                    &amp;lt;DoubleAnimation Storyboard.TargetProperty=&quot;Height&quot; From=&quot;60&quot; To=&quot;40&quot; Duration=&quot;0:0:0.5&quot; /&amp;gt;
                &amp;lt;/Storyboard&amp;gt;
            &amp;lt;/BeginStoryboard&amp;gt;
        &amp;lt;/EventTrigger&amp;gt;
    &amp;lt;/Button.Triggers&amp;gt;
&amp;lt;/Button&amp;gt;
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;ColorAnimation 用于动画颜色类型的属性：&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;&amp;lt;Rectangle Width=&quot;200&quot; Height=&quot;100&quot;&amp;gt;
    &amp;lt;Rectangle.Fill&amp;gt;
        &amp;lt;SolidColorBrush x:Name=&quot;RectBrush&quot; Color=&quot;Blue&quot; /&amp;gt;
    &amp;lt;/Rectangle.Fill&amp;gt;
    &amp;lt;Rectangle.Triggers&amp;gt;
        &amp;lt;EventTrigger RoutedEvent=&quot;Loaded&quot;&amp;gt;
            &amp;lt;BeginStoryboard&amp;gt;
                &amp;lt;Storyboard RepeatBehavior=&quot;Forever&quot; AutoReverse=&quot;True&quot;&amp;gt;
                    &amp;lt;ColorAnimation Storyboard.TargetName=&quot;RectBrush&quot; 
                                   Storyboard.TargetProperty=&quot;Color&quot; 
                                   From=&quot;Blue&quot; To=&quot;Red&quot; 
                                   Duration=&quot;0:0:2&quot; /&amp;gt;
                &amp;lt;/Storyboard&amp;gt;
            &amp;lt;/BeginStoryboard&amp;gt;
        &amp;lt;/EventTrigger&amp;gt;
    &amp;lt;/Rectangle.Triggers&amp;gt;
&amp;lt;/Rectangle&amp;gt;
&lt;/code&gt;&lt;/pre&gt;
&lt;h3&gt;10.3 Storyboard详解&lt;/h3&gt;
&lt;p&gt;Storyboard 用于组合和控制多个动画：&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;&amp;lt;Storyboard x:Key=&quot;FadeInOutAnimation&quot;&amp;gt;
    &amp;lt;DoubleAnimation Storyboard.TargetProperty=&quot;Opacity&quot; 
                     From=&quot;0&quot; To=&quot;1&quot; 
                     Duration=&quot;0:0:1&quot; 
                     BeginTime=&quot;0:0:0&quot; /&amp;gt;
    &amp;lt;DoubleAnimation Storyboard.TargetProperty=&quot;Opacity&quot; 
                     From=&quot;1&quot; To=&quot;0&quot; 
                     Duration=&quot;0:0:1&quot; 
                     BeginTime=&quot;0:0:2&quot; /&amp;gt;
&amp;lt;/Storyboard&amp;gt;

&amp;lt;TextBlock Text=&quot;淡入淡出动画&quot; Opacity=&quot;0&quot;&amp;gt;
    &amp;lt;TextBlock.Triggers&amp;gt;
        &amp;lt;EventTrigger RoutedEvent=&quot;Loaded&quot;&amp;gt;
            &amp;lt;BeginStoryboard Storyboard=&quot;{StaticResource FadeInOutAnimation}&quot; RepeatBehavior=&quot;Forever&quot; /&amp;gt;
        &amp;lt;/EventTrigger&amp;gt;
    &amp;lt;/TextBlock.Triggers&amp;gt;
&amp;lt;/TextBlock&amp;gt;
&lt;/code&gt;&lt;/pre&gt;
&lt;h3&gt;10.4 绘图与形状&lt;/h3&gt;
&lt;p&gt;WPF 提供了丰富的绘图和形状功能：&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;&amp;lt;Canvas Width=&quot;400&quot; Height=&quot;300&quot; Background=&quot;LightGray&quot;&amp;gt;
    &amp;lt;!-- 基本形状 --&amp;gt;
    &amp;lt;Rectangle Width=&quot;100&quot; Height=&quot;100&quot; Fill=&quot;Red&quot; Canvas.Left=&quot;50&quot; Canvas.Top=&quot;50&quot; /&amp;gt;
    &amp;lt;Ellipse Width=&quot;100&quot; Height=&quot;100&quot; Fill=&quot;Blue&quot; Canvas.Left=&quot;200&quot; Canvas.Top=&quot;50&quot; /&amp;gt;
    &amp;lt;Line X1=&quot;50&quot; Y1=&quot;200&quot; X2=&quot;150&quot; Y2=&quot;250&quot; Stroke=&quot;Green&quot; StrokeThickness=&quot;2&quot; /&amp;gt;
    &amp;lt;Path Data=&quot;M 50,200 L 150,200 L 100,150 Z&quot; Fill=&quot;Yellow&quot; /&amp;gt;
    
    &amp;lt;!-- 复杂路径 --&amp;gt;
    &amp;lt;Path Data=&quot;M 200,200 C 250,150 350,150 350,200 S 250,250 200,200&quot; 
          Stroke=&quot;Purple&quot; StrokeThickness=&quot;2&quot; Fill=&quot;Transparent&quot; /&amp;gt;
    
    &amp;lt;!-- 文本 --&amp;gt;
    &amp;lt;TextBlock Text=&quot;WPF 绘图示例&quot; FontSize=&quot;16&quot; FontWeight=&quot;Bold&quot; Canvas.Left=&quot;100&quot; Canvas.Top=&quot;250&quot; /&amp;gt;
&amp;lt;/Canvas&amp;gt;
&lt;/code&gt;&lt;/pre&gt;
&lt;h2&gt;11. 路由事件与命令系统&lt;/h2&gt;
&lt;h3&gt;11.1 路由事件的机制&lt;/h3&gt;
&lt;p&gt;WPF 的路由事件系统允许事件从子元素冒泡到父元素，或从父元素隧道到子元素：&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;&lt;strong&gt;冒泡事件（Bubbling）&lt;/strong&gt;：从事件源向上传播到视觉树的根&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;隧道事件（Tunneling）&lt;/strong&gt;：从视觉树的根向下传播到事件源&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;直接事件（Direct）&lt;/strong&gt;：只在事件源上触发，不传播&lt;/li&gt;
&lt;/ol&gt;
&lt;p&gt;路由事件的处理：&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;// 处理冒泡事件
private void Button_Click(object sender, RoutedEventArgs e)
{
    // 处理点击事件
    e.Handled = true; // 阻止事件继续传播
}

// 处理隧道事件
private void Grid_PreviewMouseDown(object sender, MouseButtonEventArgs e)
{
    // 处理鼠标按下事件
}
&lt;/code&gt;&lt;/pre&gt;
&lt;h3&gt;11.2 冒泡与隧道事件&lt;/h3&gt;
&lt;pre&gt;&lt;code&gt;&amp;lt;Grid PreviewMouseDown=&quot;Grid_PreviewMouseDown&quot; MouseDown=&quot;Grid_MouseDown&quot; Background=&quot;LightGray&quot; Padding=&quot;20&quot;&amp;gt;
    &amp;lt;StackPanel PreviewMouseDown=&quot;StackPanel_PreviewMouseDown&quot; MouseDown=&quot;StackPanel_MouseDown&quot; Background=&quot;LightBlue&quot; Padding=&quot;10&quot;&amp;gt;
        &amp;lt;Button Content=&quot;点击我&quot; PreviewMouseDown=&quot;Button_PreviewMouseDown&quot; Click=&quot;Button_Click&quot; /&amp;gt;
    &amp;lt;/StackPanel&amp;gt;
&amp;lt;/Grid&amp;gt;
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;事件触发顺序：&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;Grid.PreviewMouseDown（隧道）&lt;/li&gt;
&lt;li&gt;StackPanel.PreviewMouseDown（隧道）&lt;/li&gt;
&lt;li&gt;Button.PreviewMouseDown（隧道）&lt;/li&gt;
&lt;li&gt;Button.Click（直接）&lt;/li&gt;
&lt;li&gt;Button.MouseDown（冒泡）&lt;/li&gt;
&lt;li&gt;StackPanel.MouseDown（冒泡）&lt;/li&gt;
&lt;li&gt;Grid.MouseDown（冒泡）&lt;/li&gt;
&lt;/ol&gt;
&lt;h3&gt;11.3 命令（Command）机制&lt;/h3&gt;
&lt;p&gt;WPF 的命令系统提供了一种将用户操作与业务逻辑分离的方式：&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;&lt;strong&gt;命令源（Command Source）&lt;/strong&gt;：触发命令的元素，如 Button、MenuItem&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;命令目标（Command Target）&lt;/strong&gt;：命令的执行目标，如 TextBox&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;命令绑定（Command Binding）&lt;/strong&gt;：将命令与处理方法关联&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;命令（Command）&lt;/strong&gt;：表示要执行的操作，如 ApplicationCommands.Copy&lt;/li&gt;
&lt;/ol&gt;
&lt;pre&gt;&lt;code&gt;&amp;lt;Window.CommandBindings&amp;gt;
    &amp;lt;CommandBinding Command=&quot;ApplicationCommands.Open&quot; 
                    Executed=&quot;OpenCommand_Executed&quot; 
                    CanExecute=&quot;OpenCommand_CanExecute&quot; /&amp;gt;
    &amp;lt;CommandBinding Command=&quot;ApplicationCommands.Save&quot; 
                    Executed=&quot;SaveCommand_Executed&quot; 
                    CanExecute=&quot;SaveCommand_CanExecute&quot; /&amp;gt;
&amp;lt;/Window.CommandBindings&amp;gt;

&amp;lt;StackPanel&amp;gt;
    &amp;lt;MenuItem Header=&quot;文件&quot;&amp;gt;
        &amp;lt;MenuItem Header=&quot;打开&quot; Command=&quot;ApplicationCommands.Open&quot; /&amp;gt;
        &amp;lt;MenuItem Header=&quot;保存&quot; Command=&quot;ApplicationCommands.Save&quot; /&amp;gt;
    &amp;lt;/MenuItem&amp;gt;
    &amp;lt;Button Content=&quot;打开&quot; Command=&quot;ApplicationCommands.Open&quot; /&amp;gt;
    &amp;lt;Button Content=&quot;保存&quot; Command=&quot;ApplicationCommands.Save&quot; /&amp;gt;
&amp;lt;/StackPanel&amp;gt;
&lt;/code&gt;&lt;/pre&gt;
&lt;pre&gt;&lt;code&gt;private void OpenCommand_Executed(object sender, ExecutedRoutedEventArgs e)
{
    // 执行打开操作
}

private void OpenCommand_CanExecute(object sender, CanExecuteRoutedEventArgs e)
{
    e.CanExecute = true; // 检查是否可以执行
}

private void SaveCommand_Executed(object sender, ExecutedRoutedEventArgs e)
{
    // 执行保存操作
}

private void SaveCommand_CanExecute(object sender, CanExecuteRoutedEventArgs e)
{
    e.CanExecute = true; // 检查是否可以执行
}
&lt;/code&gt;&lt;/pre&gt;
&lt;h3&gt;11.4 自定义命令&lt;/h3&gt;
&lt;p&gt;除了使用内置命令，还可以创建自定义命令：&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;public static class CustomCommands
{
    public static readonly RoutedUICommand Exit = new RoutedUICommand(
        &quot;退出&quot;, &quot;Exit&quot;, typeof(CustomCommands),
        new InputGestureCollection { new KeyGesture(Key.F4, ModifierKeys.Alt) });
    
    public static readonly RoutedUICommand About = new RoutedUICommand(
        &quot;关于&quot;, &quot;About&quot;, typeof(CustomCommands));
}
&lt;/code&gt;&lt;/pre&gt;
&lt;pre&gt;&lt;code&gt;&amp;lt;Window.CommandBindings&amp;gt;
    &amp;lt;CommandBinding Command=&quot;local:CustomCommands.Exit&quot; 
                    Executed=&quot;ExitCommand_Executed&quot; 
                    CanExecute=&quot;ExitCommand_CanExecute&quot; /&amp;gt;
    &amp;lt;CommandBinding Command=&quot;local:CustomCommands.About&quot; 
                    Executed=&quot;AboutCommand_Executed&quot; 
                    CanExecute=&quot;AboutCommand_CanExecute&quot; /&amp;gt;
&amp;lt;/Window.CommandBindings&amp;gt;

&amp;lt;MenuItem Header=&quot;帮助&quot;&amp;gt;
    &amp;lt;MenuItem Header=&quot;关于&quot; Command=&quot;local:CustomCommands.About&quot; /&amp;gt;
    &amp;lt;Separator /&amp;gt;
    &amp;lt;MenuItem Header=&quot;退出&quot; Command=&quot;local:CustomCommands.Exit&quot; /&amp;gt;
&amp;lt;/MenuItem&amp;gt;
&lt;/code&gt;&lt;/pre&gt;
&lt;h2&gt;12. WPF性能优化与最佳实践&lt;/h2&gt;
&lt;h3&gt;12.1 资源管理优化&lt;/h3&gt;
&lt;ol&gt;
&lt;li&gt;&lt;strong&gt;合理使用资源字典&lt;/strong&gt;：将资源集中管理，避免重复定义&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;使用静态资源&lt;/strong&gt;：对于不会变化的资源，使用 StaticResource&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;延迟加载资源&lt;/strong&gt;：对于大型资源，使用延迟加载&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;释放未使用的资源&lt;/strong&gt;：及时释放不再使用的资源&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;避免内存泄漏&lt;/strong&gt;：正确处理事件订阅和资源引用&lt;/li&gt;
&lt;/ol&gt;
&lt;h3&gt;12.2 虚拟化与延迟加载&lt;/h3&gt;
&lt;ol&gt;
&lt;li&gt;
&lt;p&gt;&lt;strong&gt;列表虚拟化&lt;/strong&gt;：对于大量数据的列表，使用虚拟化&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;&amp;lt;ListBox ItemsSource=&quot;{Binding Items}&quot;
         VirtualizingStackPanel.IsVirtualizing=&quot;True&quot;
         VirtualizingStackPanel.VirtualizationMode=&quot;Recycling&quot; /&amp;gt;
&lt;/code&gt;&lt;/pre&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;&lt;strong&gt;数据虚拟化&lt;/strong&gt;：对于非常大的数据集，使用数据虚拟化&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;&lt;strong&gt;延迟加载&lt;/strong&gt;：对于复杂的 UI 元素，使用延迟加载&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;&amp;lt;ContentControl Content=&quot;{Binding ComplexContent}&quot; IsAsync=&quot;True&quot; /&amp;gt;
&lt;/code&gt;&lt;/pre&gt;
&lt;/li&gt;
&lt;/ol&gt;
&lt;h3&gt;12.3 绑定性能优化&lt;/h3&gt;
&lt;ol&gt;
&lt;li&gt;&lt;strong&gt;使用合适的绑定模式&lt;/strong&gt;：根据需要选择合适的绑定模式&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;优化绑定路径&lt;/strong&gt;：避免深层嵌套的绑定路径&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;使用 OneWay 绑定&lt;/strong&gt;：对于不需要双向绑定的场景，使用 OneWay 绑定&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;使用 ObservableCollection&lt;/strong&gt;：对于动态集合，使用 ObservableCollection&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;避免不必要的绑定更新&lt;/strong&gt;：合理设置 UpdateSourceTrigger&lt;/li&gt;
&lt;/ol&gt;
&lt;h3&gt;12.4 UI线程与异步操作&lt;/h3&gt;
&lt;ol&gt;
&lt;li&gt;
&lt;p&gt;&lt;strong&gt;使用异步操作&lt;/strong&gt;：对于耗时操作，使用异步处理&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;private async void LoadDataButton_Click(object sender, RoutedEventArgs e)
{
    IsBusy = true;
    try
    {
        await Task.Run(() =&amp;gt; LoadData());
    }
    finally
    {
        IsBusy = false;
    }
}
&lt;/code&gt;&lt;/pre&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;&lt;strong&gt;使用 Dispatcher&lt;/strong&gt;：在非 UI 线程中更新 UI 时，使用 Dispatcher&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;private void BackgroundOperation()
{
    // 执行后台操作
    
    // 更新 UI
    Application.Current.Dispatcher.Invoke(() =&amp;gt;
    {
        StatusText = &quot;操作完成&quot;;
    });
}
&lt;/code&gt;&lt;/pre&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;&lt;strong&gt;避免阻塞 UI 线程&lt;/strong&gt;：不要在 UI 线程中执行耗时操作&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;&lt;strong&gt;使用进度报告&lt;/strong&gt;：对于长时间运行的操作，使用进度报告&lt;/p&gt;
&lt;/li&gt;
&lt;/ol&gt;
&lt;h3&gt;12.5 性能监控与调试&lt;/h3&gt;
&lt;ol&gt;
&lt;li&gt;
&lt;p&gt;&lt;strong&gt;使用性能分析工具&lt;/strong&gt;：&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;Visual Studio 性能分析器&lt;/li&gt;
&lt;li&gt;WPF 性能工具（PerfView）&lt;/li&gt;
&lt;li&gt;Snoop（WPF UI 调试工具）&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;&lt;strong&gt;监控视觉树复杂度&lt;/strong&gt;：避免过度复杂的视觉树&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;&lt;strong&gt;监控绑定更新&lt;/strong&gt;：避免不必要的绑定更新&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;&lt;strong&gt;使用日志&lt;/strong&gt;：记录性能关键路径的执行时间&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;&lt;strong&gt;定期测试&lt;/strong&gt;：定期进行性能测试，发现问题及时优化&lt;/p&gt;
&lt;/li&gt;
&lt;/ol&gt;
&lt;h2&gt;13. MVVM模式进阶与常用框架&lt;/h2&gt;
&lt;h3&gt;13.1 课外阅读推荐与架构演进&lt;/h3&gt;
&lt;p&gt;在深入 MVVM 之前，了解经典架构设计的演进过程是非常有必要的，这有助于我们理解 MVVM 为什么会在 WPF 中成为主流。&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;strong&gt;MVC (Model-View-Controller)&lt;/strong&gt;：模型、视图和控制器。最初的 UI 架构，控制器处理用户输入逻辑，然后更新模型，模型改变后通知视图更新。缺点在于控制器往往会变得非常臃肿，且与视图耦合较深。&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;MVP (Model-View-Presenter)&lt;/strong&gt;：为了解决 MVC 耦合问题引入了 Presenter。视图将所有交互委托给 Presenter，Presenter 处理业务逻辑后通过接口被动更新视图。实现了完全解耦，代价是接口方法数量激增，且依然需要手动编写大量更新界面的代码。&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;MVVM (Model-View-ViewModel)&lt;/strong&gt;：WPF 开发的核心架构模式。ViewModel 代替了 Presenter，不持有视图的任何引用。核心在于利用 WPF 强大的**数据绑定（Data Binding）**引擎和数据模板。ViewModel 专注于把控状态，只要 ViewModel 中的数据发生了变化，视图就会自动更新，极大地解放了生产力。&lt;/li&gt;
&lt;/ul&gt;
&lt;blockquote&gt;
&lt;p&gt;&lt;strong&gt;课外阅读推荐：&lt;/strong&gt;&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;a href=&quot;https://blog.csdn.net/fageaaa/article/details/145936426&quot;&gt;MVP，MVC，MVVM 深度参考 1&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;https://baijiahao.baidu.com/s?id=1855075764046835653&quot;&gt;MVP，MVC，MVVM 深度参考 2&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;/blockquote&gt;
&lt;h3&gt;13.2 INotifyPropertyChanged 与 ICommand 核心原理解析&lt;/h3&gt;
&lt;p&gt;这两个接口是撑起 WPF 响应式 MVVM 架构的绝对基石。不论使用什么第三方框架，底层一定是对这两者的封装。&lt;/p&gt;
&lt;h4&gt;1. INotifyPropertyChanged：数据的“广播站”&lt;/h4&gt;
&lt;ul&gt;
&lt;li&gt;&lt;strong&gt;主要作用&lt;/strong&gt;：当属性发生变化时发起通知。WPF 中为了给控件绑定动态属性，使用了绑定语法 &lt;code&gt;{Binding PropertyName}&lt;/code&gt;。&lt;code&gt;INotifyPropertyChanged&lt;/code&gt; 负责在后台变量发生变化时，准确地通知视图重绘。&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;如果不实现它会怎样？&lt;/strong&gt;：如果绑定的后台属性变了，但没有触发 &lt;code&gt;PropertyChanged&lt;/code&gt; 事件，UI 界面就不会发生任何变化。&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;原始代码实现示例&lt;/strong&gt;：&lt;/li&gt;
&lt;/ul&gt;
&lt;pre&gt;&lt;code&gt;// 经典的 INotifyPropertyChanged 手写实现非常繁琐
public class PersonModel : INotifyPropertyChanged
{
    private string _name;
    public string Name
    {
        get { return _name; }
        set
        {
            if (_name != value)
            {
                _name = value;
                // 数据发生变化，必须手动调用通知方法
                OnPropertyChanged(nameof(Name)); 
            }
        }
    }

    public event PropertyChangedEventHandler PropertyChanged;
    protected virtual void OnPropertyChanged(string propertyName)
    {
        PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(propertyName));
    }
}
&lt;/code&gt;&lt;/pre&gt;
&lt;blockquote&gt;
&lt;p&gt;&lt;strong&gt;课外阅读推荐：&lt;/strong&gt;&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;a href=&quot;https://learn.microsoft.com/zh-cn/dotnet/desktop/wpf/data/how-to-implement-property-change-notification&quot;&gt;如何实现 INotifyPropertyChanged 属性更改通知&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;/blockquote&gt;
&lt;h4&gt;2. ICommand：行为交互的“遥控器”&lt;/h4&gt;
&lt;ul&gt;
&lt;li&gt;&lt;strong&gt;主要作用&lt;/strong&gt;：为了给页面元素（XAML 标签，比如 Button 的 Click）绑定事件参数，&lt;code&gt;ICommand&lt;/code&gt; 取代了传统的 Code-Behind 中双击生成的事件处理器。实现了前台交互设计与后台业务逻辑完全分离。&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;原始接口核心要素&lt;/strong&gt;：
&lt;ul&gt;
&lt;li&gt;&lt;code&gt;Execute(object parameter)&lt;/code&gt;：点击时执行的核心业务代码。&lt;/li&gt;
&lt;li&gt;&lt;code&gt;CanExecute(object parameter)&lt;/code&gt;：返回布尔值，决定当前命令状态。如果返回 &lt;code&gt;false&lt;/code&gt;，界面绑定的按钮组件会自动变为灰色&lt;strong&gt;禁用状态&lt;/strong&gt;。&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;&lt;strong&gt;原始的手写 ICommand 实现示例：&lt;/strong&gt;&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;// 通常我们需要自定义一个实现了 ICommand 的 RelayCommand 类
public class RelayCommand : ICommand
{
    private readonly Action&amp;lt;object&amp;gt; _execute;
    private readonly Predicate&amp;lt;object&amp;gt; _canExecute;

    public RelayCommand(Action&amp;lt;object&amp;gt; execute, Predicate&amp;lt;object&amp;gt; canExecute = null)
    {
        _execute = execute ?? throw new ArgumentNullException(nameof(execute));
        _canExecute = canExecute;
    }

    // CommandManager 会在界面发生交互时评估命令是否可用
    public event EventHandler CanExecuteChanged
    {
        add { CommandManager.RequerySuggested += value; }
        remove { CommandManager.RequerySuggested -= value; }
    }

    public bool CanExecute(object parameter)
    {
        return _canExecute == null || _canExecute(parameter);
    }

    public void Execute(object parameter)
    {
        _execute(parameter);
    }
}

// 在 ViewModel 中使用
public class MainViewModel
{
    public ICommand SaveCommand { get; }

    public MainViewModel()
    {
        // 只有当名字不为空时才能点击保存
        SaveCommand = new RelayCommand(ExecuteSave, CanSave);
    }

    private void ExecuteSave(object obj) { /* 模拟保存逻辑 */ }
    private bool CanSave(object obj) { return true; } 
}
&lt;/code&gt;&lt;/pre&gt;
&lt;blockquote&gt;
&lt;p&gt;&lt;strong&gt;课外阅读推荐：&lt;/strong&gt;&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;a href=&quot;https://www.cnblogs.com/wzzkaifa/p/19126253&quot;&gt;深入理解 ICommand 参考 1&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;https://www.cnblogs.com/yanwuming/p/18929337&quot;&gt;深入理解 ICommand 参考 2&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;/blockquote&gt;
&lt;h3&gt;13.3 WPF 中三大重要的 MVVM 框架评测&lt;/h3&gt;
&lt;p&gt;社区和官方封装了现成的 MVVM 框架，免去了我们每次手写上述恶心累赘的实现的折磨。&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;&lt;strong&gt;MVVMLight&lt;/strong&gt;
&lt;ul&gt;
&lt;li&gt;&lt;strong&gt;概况&lt;/strong&gt;：诞生最早且最为知名的轻量级框架，微软出品。&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;状态&lt;/strong&gt;：&lt;strong&gt;目前已官方标记为过时（Deprecated），不再推荐使用。&lt;/strong&gt; 它的历史使命已经完成。&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;CommunityToolkit.Mvvm&lt;/strong&gt;
&lt;ul&gt;
&lt;li&gt;&lt;strong&gt;概况&lt;/strong&gt;：由微软官方主推的现代 MVVM 工具包，可以视为 MVVMLight精神续作（API 高度兼容）。&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;特点&lt;/strong&gt;：依托于源生器（Source Generators）机制和现代特性，能成倍减少代码编写量，性能极佳。&lt;strong&gt;目前 WPF / MAUI / WinUI3 开发绝对的首选。&lt;/strong&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Prism&lt;/strong&gt;
&lt;ul&gt;
&lt;li&gt;&lt;strong&gt;概况&lt;/strong&gt;：微软推出的重量级老牌框架，被称为“三棱镜”万花筒。&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;特点&lt;/strong&gt;：专为大型企业级复合应用设计。内置了极其复杂的模块化、高级区域导航（Region Navigation）、对话框服务等。对于中小型管理系统显得笨重庞大。&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;/ol&gt;
&lt;hr /&gt;
&lt;h2&gt;14. CommunityToolkit.Mvvm 框架详解&lt;/h2&gt;
&lt;p&gt;&lt;strong&gt;进阶学习参考资料：&lt;/strong&gt;&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;a href=&quot;https://learn.microsoft.com/zh-cn/dotnet/communitytoolkit/mvvm/&quot;&gt;官方文档 - 微软 Learn&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;https://github.com/CommunityToolkit&quot;&gt;GitHub 开源仓库首页&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;https://www.cnblogs.com/7bao3shu/articles/19420516&quot;&gt;博客参考 1：核心原理解析&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;https://www.cnblogs.com/cdaniu/p/16848821.html&quot;&gt;博客参考 2：入门综合指南&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;https://blog.csdn.net/JetRaven12/article/details/156069254&quot;&gt;博客参考 3：信使高级模块&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;https://blog.csdn.net/u010975589/article/details/155889321&quot;&gt;博客参考 4：视图模型定位器的封装艺术&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;h3&gt;14.1 什么是 CommunityToolkit.Mvvm？&lt;/h3&gt;
&lt;p&gt;&lt;code&gt;CommunityToolkit.Mvvm&lt;/code&gt; 包（曾经名叫 &lt;code&gt;Microsoft.Toolkit.Mvvm&lt;/code&gt;）是一个现代、快速、独立且模块化的 MVVM 库。&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;strong&gt;Community（社区）&lt;/strong&gt;：由社区和微软官方联合开源维护。&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Toolkit（工具包）&lt;/strong&gt;：它不强制采用某种特定的工程项目架构，开发者可以当做一个百宝箱随取随用。&lt;/li&gt;
&lt;/ul&gt;
&lt;h3&gt;14.2 工具包中的核心骨架对象&lt;/h3&gt;
&lt;p&gt;熟悉以下几组对象，你就能快速驾驭此框架：&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;&lt;strong&gt;视图模型基类与命令&lt;/strong&gt;：
&lt;ul&gt;
&lt;li&gt;&lt;code&gt;ObservableObject&lt;/code&gt;：实现 &lt;code&gt;INotifyPropertyChanged&lt;/code&gt; 的基类，内置最高效的通知逻辑。&lt;/li&gt;
&lt;li&gt;&lt;code&gt;RelayCommand&lt;/code&gt; / &lt;code&gt;AsyncRelayCommand&lt;/code&gt;：实现了 &lt;code&gt;ICommand&lt;/code&gt;，专门用来绑定界面上的同步点击或是耗时的异步等待事件（如 API 数据请求）。&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;依赖注入（IoC）服务集成&lt;/strong&gt;：
&lt;ul&gt;
&lt;li&gt;&lt;code&gt;IServiceProvider&lt;/code&gt;：获取服务的核心接口。&lt;/li&gt;
&lt;li&gt;&lt;code&gt;ServiceCollection&lt;/code&gt;：用来注册、配置生命周期服务的容器组件。&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;信使（消息通知交互机制）&lt;/strong&gt;：
&lt;ul&gt;
&lt;li&gt;&lt;code&gt;IMessenger&lt;/code&gt;：消息总线接口基类。&lt;/li&gt;
&lt;li&gt;&lt;code&gt;WeakReferenceMessenger&lt;/code&gt;：弱引用信使（官方最推荐的默认实现）。&lt;/li&gt;
&lt;li&gt;&lt;code&gt;StrongReferenceMessenger&lt;/code&gt;：强引用信使（性能最好，但容易造成内存泄漏）。&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;/ol&gt;
&lt;h3&gt;14.3 项目实战：标准使用三步曲&lt;/h3&gt;
&lt;ol&gt;
&lt;li&gt;&lt;strong&gt;安装依赖&lt;/strong&gt;：通过 NuGet 包管理器安装 &lt;code&gt;CommunityToolkit.Mvvm&lt;/code&gt; 包。如果您还需要依赖注入，可以一并安装 &lt;code&gt;Microsoft.Extensions.DependencyInjection&lt;/code&gt;。&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;规整架构&lt;/strong&gt;：在项目中按规范规划文件夹。推荐的最基础的三层/四层结构：&lt;code&gt;Models&lt;/code&gt;, &lt;code&gt;Views&lt;/code&gt;, &lt;code&gt;ViewModels&lt;/code&gt;, &lt;code&gt;Services&lt;/code&gt;。&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;编码开发&lt;/strong&gt;：继承基础对象编写业务逻辑，时刻注意使用框架提供的特性来简化代码。&lt;/li&gt;
&lt;/ol&gt;
&lt;h3&gt;14.4 优雅架构：视图模型定位器（ViewModelLocator）的封装与扩展&lt;/h3&gt;
&lt;p&gt;为了实现视图与模型依赖关系的进一步解耦并彻底拥抱依赖注入（DI），构建一个全局的 &lt;code&gt;ViewModelLocator&lt;/code&gt; 充当创建 VM 和注入服务的引擎是非常有必要的。&lt;/p&gt;
&lt;p&gt;这本质就是把 ASP.NET Core 的那一整套启动配置注入机制搬到了 WPF：&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;// App.xaml.cs 启动配置
public partial class App : Application
{
    // 全局服务提供者
    public static IServiceProvider Services { get; private set; }

    public App()
    {
        Services = ConfigureServices();
    }

    private static IServiceProvider ConfigureServices()
    {
        var services = new ServiceCollection();

        // 1. 注册所有的服务层
        services.AddSingleton&amp;lt;IDataService, SqlDataService&amp;gt;();
        services.AddSingleton&amp;lt;IDialogService, DefaultDialogService&amp;gt;();

        // 2. 注册所有的 ViewModel (一般为瞬时对象 Transient)
        services.AddTransient&amp;lt;MainViewModel&amp;gt;();
        services.AddTransient&amp;lt;LoginViewModel&amp;gt;();

        return services.BuildServiceProvider();
    }
}
&lt;/code&gt;&lt;/pre&gt;
&lt;pre&gt;&lt;code&gt;// ViewModelLocator.cs 的标准设计
public class ViewModelLocator
{
    public MainViewModel Main =&amp;gt; App.Services.GetService&amp;lt;MainViewModel&amp;gt;();
    public LoginViewModel Login =&amp;gt; App.Services.GetService&amp;lt;LoginViewModel&amp;gt;();
}
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;&lt;strong&gt;在项目中扩展页面的开发流（以新建注册页为例）：&lt;/strong&gt;&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;&lt;strong&gt;第一步&lt;/strong&gt;：在 &lt;code&gt;Views&lt;/code&gt; 文件夹中新建视图 &lt;code&gt;RegisterView.xaml&lt;/code&gt;。&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;第二步&lt;/strong&gt;：在 &lt;code&gt;ViewModels&lt;/code&gt; 文件夹中新建视图模型 &lt;code&gt;RegisterViewModel.cs&lt;/code&gt;。&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;第三步&lt;/strong&gt;：在 &lt;code&gt;App.xaml.cs&lt;/code&gt; 中添加 &lt;code&gt;services.AddTransient&amp;lt;RegisterViewModel&amp;gt;();&lt;/code&gt;。然后在 &lt;code&gt;ViewModelLocator.cs&lt;/code&gt; 中添加一个只读属性 &lt;code&gt;Register&lt;/code&gt;。&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;第四步&lt;/strong&gt;：通过 Locator 为视图绑定它的 ViewModel (DataContext 挂载)：&lt;pre&gt;&lt;code&gt;&amp;lt;!-- App.xaml 中已提前将 Locator 声明为全家静态资源 &amp;lt;local:ViewModelLocator x:Key=&quot;Locator&quot; /&amp;gt; --&amp;gt;
&amp;lt;Window x:Class=&quot;MyApp.Views.RegisterView&quot;
        DataContext=&quot;{Binding Source={StaticResource Locator}, Path=Register}&quot;&amp;gt;
        ...
&lt;/code&gt;&lt;/pre&gt;
&lt;/li&gt;
&lt;/ol&gt;
&lt;h3&gt;14.5 核心工具：源生成器（Source Generators）深度剖析&lt;/h3&gt;
&lt;p&gt;&lt;code&gt;CommunityToolkit.Mvvm&lt;/code&gt; 包之所以让人上瘾，核心就在于这套从版本 8.0 引入的基于 Roslyn 的&lt;strong&gt;源生成器&lt;/strong&gt;（Source Generators）。&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;源生成器的本质：它是 C# 编译期的高效代码生成机制。&lt;/strong&gt;
你只需要写极简的一两句代码定好标记（特性的使用），编译时，编译器检测到这些类，会在内存中动态为你生成上百行标准的样板代码。它是在编译阶段重写源代码，而不是反射，所以具有&lt;strong&gt;绝对零性能损耗&lt;/strong&gt;。&lt;/p&gt;
&lt;h4&gt;源生器使用的前提与特点：&lt;/h4&gt;
&lt;ol&gt;
&lt;li&gt;&lt;strong&gt;必须声明为部分类 (&lt;code&gt;partial class&lt;/code&gt;)&lt;/strong&gt;：
&lt;ul&gt;
&lt;li&gt;&lt;strong&gt;原因与原理&lt;/strong&gt;：系统生成的那一大堆通知代码、绑定逻辑也叫这个类名，只有加上 &lt;code&gt;partial&lt;/code&gt; 才能让它们在编译结果中合二为一，缺少了将会直接编译报错。&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;平台严重限制支持&lt;/strong&gt;：
&lt;ul&gt;
&lt;li&gt;由于依赖 Roslyn 最新底层特性，&lt;strong&gt;它仅支持 .NET Core / .NET 5+ 以上。绝对不支持传统的 .NET Framework （如 .net framework 4.7.2）&lt;/strong&gt;。如果不幸使用了老旧框架环境，只能乖乖手写完整的实现。&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;/ol&gt;
&lt;h4&gt;对比：不使用源生器 VS 使用源生器&lt;/h4&gt;
&lt;p&gt;&lt;strong&gt;【纯手写实现（老式的MVVMLight风格）】 (需要大约 30 行恶心的重复代码)：&lt;/strong&gt;&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;public class UserViewModel : ObservableObject
{
    private string _userName;
    public string UserName
    {
        get =&amp;gt; _userName;
        set =&amp;gt; SetProperty(ref _userName, value); // 必须逐个属性写重写调用通知
    }

    public ICommand LoginCommand { get; }

    public UserViewModel()
    {
        LoginCommand = new RelayCommand(OnLogin, CanLogin);
    }

    private void OnLogin()
    {
        // 登陆业务逻辑
    }

    private bool CanLogin()
    {
        return !string.IsNullOrEmpty(UserName);
    }
}
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;&lt;strong&gt;【使用 CommunityToolkit.Mvvm 源生器后】 (精简至极，仅仅只需几行！)：&lt;/strong&gt;&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;// 必须是 partial class
public partial class UserViewModel : ObservableObject
{
    // C# 特性：自动生成拥有通知能力的公共属性 public string UserName { get; set; }
    // 并且当你按下了保存键保存这个底层字段时，系统甚至替你通知了 CanExecuteChanged ！
    // 注意：字段建议用小写下划线开头
    [ObservableProperty]
    [NotifyCanExecuteChangedFor(nameof(LoginCommand))] 
    private string _userName;

    // C# 特性：自动将当前普通方法包装为 public IAsyncRelayCommand LoginCommand { get; }
    // 如果带了 CanExecute 参数，自动识别执行限制
    [RelayCommand(CanExecute = nameof(CanLogin))]
    private async Task LoginAsync()
    {
        // 业务逻辑变得清爽无比
        await Task.Delay(1000); 
    }

    private bool CanLogin() =&amp;gt; !string.IsNullOrWhiteSpace(UserName);
}
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;&lt;strong&gt;源生器通过以上数个 C# 属性特征魔法标签，帮助我们把繁琐的日常操作彻底埋藏，开发者几乎只需要专注核心业务。&lt;/strong&gt;&lt;/p&gt;
&lt;h3&gt;14.6 信使机制（Messenger）：打通组件任督二脉&lt;/h3&gt;
&lt;p&gt;在一个绝对解耦、采用顶级 MVVM 划分标准的项目系统中，页面 A 是绝不可能持有 页面 B 的代码上下文引用的。它们必须要沟通（刷新列表、传递用户资料、发送弹窗触发等），通常采用中转站发送消息的模式。&lt;/p&gt;
&lt;h4&gt;弱引用 vs 强引用（避免内存泄漏的抉择）&lt;/h4&gt;
&lt;ul&gt;
&lt;li&gt;&lt;strong&gt;WeakReferenceMessenger（官方默认推荐实现）&lt;/strong&gt;：内部使用&lt;strong&gt;弱引用&lt;/strong&gt;来跟踪维护收件人的订阅注册。
&lt;ul&gt;
&lt;li&gt;&lt;strong&gt;优点为安全省心&lt;/strong&gt;：如果你使用它，即使一个弹出子级 Window 已经关闭，但因为忘记注销对一条消息的订阅事件也没关系。因为是弱引用，.NET 的垃圾回收机制依旧可以完美销毁掉那个 Window。&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;StrongReferenceMessenger&lt;/strong&gt;：内部使用传统的&lt;strong&gt;强引用&lt;/strong&gt;。
&lt;ul&gt;
&lt;li&gt;&lt;strong&gt;它的优点为极限性能&lt;/strong&gt;：因为无需追踪和维护弱引用的回收耗时，它的派发吞吐性能远超前者。&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;它的代价是严苛的开发意识&lt;/strong&gt;：使用它，在那个窗口关闭时&lt;strong&gt;必须绝对手动去取消订阅收件人（Unregister）&lt;/strong&gt;。少写一句注销代码而强引用绑架着目标对象，那么那个窗口就永远不会被内存回收掉，最终导致客户端崩溃。只有对游戏渲染、大量毫秒级消息通讯的应用才需考虑性能取舍而去使用它。&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;h4&gt;实战三段式必考题：如何操作信使？（代码示例）&lt;/h4&gt;
&lt;p&gt;&lt;strong&gt;1. 如何创建一个消息类型？（用来承载传递内容的数据包）&lt;/strong&gt;
通常派生自 &lt;code&gt;ValueChangedMessage&amp;lt;T&amp;gt;&lt;/code&gt;，用来代表这是一个明确携带值改变的消息类型。&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;using CommunityToolkit.Mvvm.Messaging.Messages;

// 创建一个专门用来跨ViewModel传递“系统通知推送文本”的快递包
public class SystemAlertMessage : ValueChangedMessage&amp;lt;string&amp;gt;
{
    // 构造函数传入消息内容，供基类承载
    public SystemAlertMessage(string messageText) : base(messageText)
    {
    }
}
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;&lt;strong&gt;2. 如何注册消息（订阅监听消息）？&lt;/strong&gt;
在需要接收通知处理事件的（或者是弹出框）ViewModel 的构造函数中进行事件注册：&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;public partial class MainDashboardViewModel : ObservableObject
{
    [ObservableProperty]
    private string _alertFooterText;

    public MainDashboardViewModel()
    {
        // WeakReferenceMessenger.Default 是一个全局的中转站单例实体
        // Register 参数讲解：1. 接收者就是我自己 (this) 
        // 2. 只有发送了类型为 SystemAlertMessage 包裹的人发的消息，我这才能监听到，然后进入 Lambda 回调。
        WeakReferenceMessenger.Default.Register&amp;lt;SystemAlertMessage&amp;gt;(this, (recipient, message) =&amp;gt;
        {
            // 当消息到达时，提取其内置的消息承载文本，刷新我这个页面的属性。
            recipient.AlertFooterText = $&quot;收到警告广播：{message.Value}&quot;;
            
            // 如果你想用强引用信使机制： StrongReferenceMessenger.Default.Register... 
        });
    }

    // 严谨写法如果是强引用信使，还需要手动写重构注销，避免内存炸弹：
    // public void Cleanup() {  StrongReferenceMessenger.Default.UnregisterAll(this);  }
}
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;&lt;strong&gt;3. 如何发送消息广播？&lt;/strong&gt;
例如在系统中某一个设置界面，保存成功后想通知整个框架全局弹出或者更新数据，只需要一句话：&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;public partial class SettingsViewModel : ObservableObject
{
    [RelayCommand]
    private void SaveSettings()
    {
        // ... 执行写入到本地数据库配置的漫长逻辑保存 ..

        // 将自己新做好的快递包寄件扔到中转站。只要注册过对相同类型的数据包感冒的接收者，统统立刻执行回调！
        WeakReferenceMessenger.Default.Send(new SystemAlertMessage(&quot;恭喜：当前系统配置数据已极速保存成功！&quot;));
    }
}
&lt;/code&gt;&lt;/pre&gt;
&lt;h3&gt;14.7 架构进阶：依赖注入（DI）与多窗口自动注册的最佳实践&lt;/h3&gt;
&lt;p&gt;在实际的企业级 WPF 应用开发中，仅仅通过 &lt;code&gt;ViewModelLocator&lt;/code&gt; 绑定单个的主窗体是不够的。我们通常会面临大量子窗口（如登录、设置、编辑弹窗）的跳转需求。如何在完全隔离 View 和 ViewModel 的前提下，还能&lt;strong&gt;优雅地传递参数、打开新窗口，并且自动享受依赖注入的红利（比如 EF Core上下文）呢？&lt;/strong&gt;&lt;/p&gt;
&lt;p&gt;通常的做法是封装一个 &lt;code&gt;WindowRegister&lt;/code&gt; 窗口注册/导航器，结合 Microsoft 的通用主机构建器（&lt;code&gt;Microsoft.Extensions.Hosting&lt;/code&gt;），实现全自动的容器装配与窗口管理。&lt;/p&gt;
&lt;h4&gt;1. 核心导航引擎：&lt;code&gt;WindowRegister&lt;/code&gt; 解析&lt;/h4&gt;
&lt;p&gt;这是一份非常典型的工业级封装代码。它接管了原本散落在各处的 &lt;code&gt;new Window().Show()&lt;/code&gt; 逻辑：&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;using System;
using System.Collections.Generic;
using System.Reflection;
using System.Windows;
using Microsoft.Extensions.DependencyInjection;

namespace WpfApp1.Utils;

/// &amp;lt;summary&amp;gt;
/// 窗口注册器，用于在 MVVM 架构中管理 ViewModel 和 View(Window) 的映射关系，并提供打开窗口的方法。
/// 配合 Microsoft.Extensions.DependencyInjection (DI) 容器结合使用。
/// &amp;lt;/summary&amp;gt;
public class WindowRegister
{
    private static IServiceProvider _serviceProvider;
    // 存储 ViewModel 类型到 Window 类型的映射表
    private static readonly Dictionary&amp;lt;Type, Type&amp;gt; _mappings = new Dictionary&amp;lt;Type, Type&amp;gt;();

    /// &amp;lt;summary&amp;gt;
    /// ① 自动扫描装配：通过反射扫描所有标有 [RegisterViewModel] 的 View，并将对应的 VM和视图批量丢入 DI 容器。
    /// &amp;lt;/summary&amp;gt;
    public static void AddWindowsFromAssembly(IServiceCollection services, Assembly assembly)
    {
        var types = assembly.GetTypes();
        foreach (var viewType in types)
        {
            var attr = viewType.GetCustomAttribute&amp;lt;RegisterViewModelAttribute&amp;gt;();
            if (attr != null)
            {
                Type viewModelType = attr.ViewModelType;

                // 统一注册到生命周期容器中 (推荐使用 Transient 瞬态，每次打开新窗口都是独立的)
                if (services != null)
                {
                    services.AddTransient(viewModelType);
                    services.AddTransient(viewType);
                }

                // 登记字典映射表，方便后续只传泛型 ViewModel 就能反推找到应该弹出哪个 View。
                if (!_mappings.ContainsKey(viewModelType))
                {
                    _mappings.Add(viewModelType, viewType);
                }
            }
        }
    }

    /// &amp;lt;summary&amp;gt;
    /// 初始化：在 App 启动时将全局 DI 供应商注入进来。
    /// &amp;lt;/summary&amp;gt;
    public static void Initialize(IServiceProvider serviceProvider)
    {
        _serviceProvider = serviceProvider;
    }

    /// &amp;lt;summary&amp;gt;
    /// ② 核心跳转逻辑：只认 ViewModel，绝对不碰 View 实现代码，强制解耦。
    /// &amp;lt;/summary&amp;gt;
    public static void ShowWindow&amp;lt;TViewModel&amp;gt;()
    {
        if (_serviceProvider == null)
            throw new InvalidOperationException(&quot;请先调用 Initialize 注入 IServiceProvider！&quot;);

        Type viewModelType = typeof(TViewModel);
        if (_mappings.TryGetValue(viewModelType, out Type viewType))
        {
            // 通过 DI 容器解析得到 Window (这能保证 Window 构造函数如果有参数需求，会被容器自动搞定)
            Window window = (Window)_serviceProvider.GetRequiredService(viewType);  

            // 【防重复挂接】检查窗口是否已经通过 DI 自动注入了 DataContext
            if (window.DataContext == null || window.DataContext.GetType() != viewModelType)
            {
                object viewModel = _serviceProvider.GetRequiredService(viewModelType);
                window.DataContext = viewModel; // 手动挂载上下文
            }

            // 【防止内存泄漏】必须拦截它的关闭事件
            AttachDisposeToWindow(window);

            window.Show(); // 打开非模态窗口
        }
        else
        {
            throw new InvalidOperationException($&quot;未找到对应的视图窗口类型，是否漏加了标签？&quot;);
        }
    }
    
    // ... 可以进一步扩展诸如 ShowWindow&amp;lt;TViewModel&amp;gt;(TViewModel viewModel) 传参打开
    // ... 和 ShowDialog&amp;lt;TViewModel&amp;gt; 模态打开的方法。

    /// &amp;lt;summary&amp;gt;
    /// ③ 保驾护航的灵魂：自动内存清理 (Dispose) 防止瞬态对象堆积爆内存
    /// &amp;lt;/summary&amp;gt;
    private static void AttachDisposeToWindow(Window window)
    {
        window.Closed += (sender, e) =&amp;gt;
        {
            // 只要你编写的 ViewModel 里面继承接管了 IDisposable 接口
            // 窗口被×掉的时候，注册器会强制帮你调用析构清理（比如断开特定的 Socket 或网络连接实例）
            if (window.DataContext is IDisposable disposableVm)
            {
                disposableVm.Dispose();
            }

            // 暴力切断 View 和 ViewModel 之间的树形绑定联系，帮助 GC (垃圾回收器) 加速回收内存空间
            window.DataContext = null;
        };
    }
}
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;&lt;strong&gt;上述设计带来的三大核心优势：&lt;/strong&gt;&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;&lt;strong&gt;完全消灭了类似 &lt;code&gt;new LoginWindow().Show()&lt;/code&gt; 的强耦合恶习&lt;/strong&gt;。现在，如果要在主控制台打开登录界面，只有纯粹清爽的 &lt;code&gt;WindowRegister.ShowWindow&amp;lt;LoginViewModel&amp;gt;();&lt;/code&gt; 一行代码，符合 MVVM 规范且极度方便单元测试。&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;避免忘记写 GC 释放&lt;/strong&gt;。因为瞬态窗体每次打开都会吃掉一份常驻内存，&lt;code&gt;AttachDisposeToWindow&lt;/code&gt; 是守护内存暴涨的最后防线。&lt;/li&gt;
&lt;li&gt;只需要在 XAML 的后台代码顶部加一句 &lt;code&gt;[RegisterViewModel(typeof(LoginViewModel))]&lt;/code&gt;，它就能通过反射自动接管全世界的页面映射。&lt;/li&gt;
&lt;/ol&gt;
&lt;h4&gt;2. 全局承载容器：标准的 &lt;code&gt;App.xaml.cs&lt;/code&gt; 配置模板&lt;/h4&gt;
&lt;p&gt;最后，我们需要将前面学习的“数据库服务”、“弹窗服务”、以及刚刚讲解的“窗体自动注册”全部汇总进 &lt;code&gt;App.xaml.cs&lt;/code&gt;。现在的 WPF 项目基本上都会借用经典的 .NET 通用主机（&lt;code&gt;IHost&lt;/code&gt;） 启动管道：&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;using System.Windows;
using Microsoft.EntityFrameworkCore;
using Microsoft.Extensions.Configuration;
using Microsoft.Extensions.DependencyInjection;
using Microsoft.Extensions.Hosting;
using DAL;
using WpfApp1.Utils;
using WpfApp1.ViewModel;

namespace WpfApp1;

public partial class App : Application
{
    // 全局通用的 Application Host
    public static IHost? AppHost { get; private set; }

    public App()
    {
        AppHost = Host.CreateDefaultBuilder()
            .ConfigureServices((context, services) =&amp;gt;
            {
                // 1. 注册全局/单例的核心服务 (比如弹窗提示服务、用户的权限Token会话)
                services.AddSingleton&amp;lt;IDialogService, DefaultDialogService&amp;gt;();
                services.AddSingleton&amp;lt;IUserSession, UserSession&amp;gt;();

                // 2. 注入前面封装的注册器：自动扫描自身程序集，批量把几百个窗口丢进 DI 容器
                WindowRegister.AddWindowsFromAssembly(services, System.Reflection.Assembly.GetExecutingAssembly());

                // 3. (高阶) 注册 EF Core ORM数据库上下文，并且读取工程根目录下的 json 数据库连接符
                var connectionString = context.Configuration.GetConnectionString(&quot;DefaultConnection&quot;);
                services.AddDbContext&amp;lt;DAL.Models.StudentManagementContext&amp;gt;(options =&amp;gt;
                    options.UseSqlServer(connectionString), ServiceLifetime.Transient);

                // 4. 注册标准的仓储(Repository)与业务逻辑(BLL)层接口
                services.AddTransient(typeof(IRepository&amp;lt;&amp;gt;), typeof(BaseRepository&amp;lt;&amp;gt;));
                services.AddTransient&amp;lt;BLL.IStudentService, BLL.StudentService&amp;gt;();
                services.AddTransient&amp;lt;BLL.ILoginService, BLL.LoginService&amp;gt;();
            })
            .Build();
    }

    protected override async void OnStartup(StartupEventArgs e)
    {
        // 启动后台通用主机的系统监听
        await AppHost!.StartAsync();
        base.OnStartup(e);
        
        // 【关键装配点】把 DI 大管家(IServiceProvider)递交给静态的窗体导航注器
        WindowRegister.Initialize(AppHost.Services);

        // 利用纯粹的泛型 ViewModel 类型，向用户弹出软件的第一个起点界面 (登陆页面)
        WindowRegister.ShowWindow&amp;lt;LoginViewModel&amp;gt;();
    }

    protected override async void OnExit(ExitEventArgs e)
    {
        // 软件关闭时平滑地结束并归还底层依赖主机的一切句柄
        await AppHost!.StopAsync();
        base.OnExit(e);
    }
}
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;&lt;strong&gt;这段配置使得 WPF 的生命周期彻底跟现代 .NET 挂轨！&lt;/strong&gt;
今后，例如当你的 &lt;code&gt;LoginViewModel&lt;/code&gt; 构造函数配置为：&lt;code&gt;public LoginViewModel(ILoginService loginService)&lt;/code&gt;，开发者无需手动 &lt;code&gt;new&lt;/code&gt; 实例化业务服务，底层的依赖注入容器 &lt;code&gt;AppHost&lt;/code&gt; 会自动完成组件树的解析并注入 &lt;code&gt;LoginService&lt;/code&gt; 实例。这是现代大型客户端应用中实现控制反转的标准系统规范。&lt;/p&gt;
&lt;h3&gt;14.8 现代 WPF 配置管理：&lt;code&gt;appsettings.json&lt;/code&gt; 的深度使用&lt;/h3&gt;
&lt;p&gt;在传统的 .NET Framework 时代，WPF 依靠繁琐的 &lt;code&gt;App.config&lt;/code&gt; 和 &lt;code&gt;ConfigurationManager&lt;/code&gt; 来读取 XML 配置。
而在全新的 .NET Core / .NET 5+ 架构下（尤其是当我们引入了 &lt;code&gt;Microsoft.Extensions.Hosting&lt;/code&gt; 后），WPF 可以像 ASP.NET Core Web API 一样，完美拥抱现代化的 JSON 配置文件管理。&lt;/p&gt;
&lt;h4&gt;1. 典型的 &lt;code&gt;appsettings.json&lt;/code&gt; 配置示例&lt;/h4&gt;
&lt;p&gt;首先，在 WPF 项目的根目录新建一个 &lt;code&gt;appsettings.json&lt;/code&gt; 文件。&lt;strong&gt;（注意：必须在文件属性中将其设置为“如果较新则复制”或“始终复制”到输出目录，否则程序将找不到该文件）&lt;/strong&gt;：&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;{
  &quot;ConnectionStrings&quot;: {
    &quot;DefaultConnection&quot;: &quot;Server=(localdb)\\mssqllocaldb;Database=StudentManagement;User ID=sa;Password=123456;Trusted_Connection=False;MultipleActiveResultSets=true;TrustServerCertificate=True;&quot;
  },
  &quot;Logging&quot;: {
    &quot;LogLevel&quot;: {
      &quot;Default&quot;: &quot;Information&quot;,
      &quot;Microsoft.Hosting.Lifetime&quot;: &quot;Information&quot;,
      &quot;Microsoft.EntityFrameworkCore.Database.Command&quot;: &quot;Warning&quot;
    }
  },
  &quot;AppPreferences&quot;: {
    &quot;MaxRecentFiles&quot;: 10,
    &quot;Theme&quot;: &quot;Dark&quot;,
    &quot;ApiBaseUrl&quot;: &quot;https://api.example.com/v1/&quot;
  }
}
&lt;/code&gt;&lt;/pre&gt;
&lt;h4&gt;2. 在不同场景下读取配置的方法&lt;/h4&gt;
&lt;p&gt;当你在 &lt;code&gt;App.xaml.cs&lt;/code&gt; 中使用了 &lt;code&gt;Host.CreateDefaultBuilder()&lt;/code&gt; 后，系统会在底层自动搜寻并加载运行目录下的 &lt;code&gt;appsettings.json&lt;/code&gt; 文件并将它们构建为 &lt;code&gt;IConfiguration&lt;/code&gt; 树。&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;场景 A：在 &lt;code&gt;App.xaml.cs&lt;/code&gt; (DI 容器注册阶段) 直接获取&lt;/strong&gt;&lt;/p&gt;
&lt;p&gt;在这个阶段，容器还没有建立完毕，但我们可以通过 &lt;code&gt;HostBuilderContext&lt;/code&gt; 提前取出配置：&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;AppHost = Host.CreateDefaultBuilder()
    .ConfigureServices((context, services) =&amp;gt;
    {
        // 核心方法：context.Configuration
        
        // 【1】获取专属的数据库连接字符串 (内部本质是读取 &quot;ConnectionStrings:DefaultConnection&quot;)
        var connectionString = context.Configuration.GetConnectionString(&quot;DefaultConnection&quot;);
        services.AddDbContext&amp;lt;StudentManagementContext&amp;gt;(options =&amp;gt;
            options.UseSqlServer(connectionString));

        // 【2】获取某个普通字符串片段
        string theme = context.Configuration[&quot;AppPreferences:Theme&quot;];

        // 【3】将整块配置强类型绑定到 C# 类，并注入给需要的地方 (IOptions 模式)
        // 例如：services.Configure&amp;lt;AppPreferencesOptions&amp;gt;(context.Configuration.GetSection(&quot;AppPreferences&quot;));
    })
    .Build();
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;&lt;strong&gt;场景 B：在普通的服务（Service）或 ViewModel 中获取&lt;/strong&gt;&lt;/p&gt;
&lt;p&gt;既然全局主机引擎已经建立，一切对象都处于依赖注入之中。我们只需要在构造函数里&lt;strong&gt;索要 &lt;code&gt;IConfiguration&lt;/code&gt;&lt;/strong&gt;，底层就会完美地把它递给你：&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;using Microsoft.Extensions.Configuration;

public class LoginService : ILoginService
{
    private readonly IConfiguration _config;
    private readonly string _apiBaseUrl;

    // 构造函数注入 IConfiguration 依赖
    public LoginService(IConfiguration config)
    {
        _config = config;
        
        // 通过冒号 &apos;:&apos; 来逐级读取 JSON 树中的键值
        _apiBaseUrl = _config[&quot;AppPreferences:ApiBaseUrl&quot;]; 
    }

    public async Task&amp;lt;bool&amp;gt; Authenticate(string user, string pass)
    {
        // ... 使用 _apiBaseUrl 拼接请求进行网络验证 ...
        return true;
    }
}
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;&lt;strong&gt;场景 C：非主流场景——在没有 DI 构造函数的静态类/扩展方法中获取&lt;/strong&gt;&lt;/p&gt;
&lt;p&gt;如果因为历史包袱，某些静态工具类无法使用构造函数依赖注入，可以从我们之前建立好的全局静态 &lt;code&gt;AppHost&lt;/code&gt; 中硬取（这也就是所谓的&lt;strong&gt;服务定位器模式&lt;/strong&gt;，非必要不推荐，但有时很救急）：&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;using Microsoft.Extensions.DependencyInjection;
using Microsoft.Extensions.Configuration;

public static class GlobalConfigHelper
{
    public static int GetMaxRecentFiles()
    {
        // 直接找 App.AppHost 提供商索要配置服务实体
        var config = App.AppHost.Services.GetRequiredService&amp;lt;IConfiguration&amp;gt;();
        
        // 读取并转换类型 (也可以用 config.GetValue&amp;lt;int&amp;gt;(&quot;AppPreferences:MaxRecentFiles&quot;))
        int limit = int.Parse(config[&quot;AppPreferences:MaxRecentFiles&quot;]);
        return limit;
    }
}
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;&lt;strong&gt;总结：&lt;/strong&gt;
采用这一套 &lt;code&gt;appsettings.json&lt;/code&gt; + &lt;code&gt;IConfiguration&lt;/code&gt; 大一统模型，可以让 WPF 前端轻松与公司后端的 C# Web 服务保持统一的开发体验，配置文件不仅结构更清晰，反序列化绑定也更安全。&lt;/p&gt;
&lt;hr /&gt;
&lt;h2&gt;15. 轻量级国产 ORM 神器：SqlSugar 详细使用指南&lt;/h2&gt;
&lt;p&gt;&lt;strong&gt;官方文档&lt;/strong&gt;：&lt;a href=&quot;https://www.donet5.com/Home/Doc&quot;&gt;https://www.donet5.com/Home/Doc&lt;/a&gt;&lt;/p&gt;
&lt;p&gt;在 WPF 开发中，虽然微软官方主推 Entity Framework Core (EF Core)，但在遇到一些老旧系统、国产数据库或者需要轻量、极速开发的桌面场景时，&lt;strong&gt;SqlSugar&lt;/strong&gt; 凭借其极其平缓的学习曲线、原生支持几十种主流及国产数据库、优异的性能，成为了很多团队的首选。&lt;/p&gt;
&lt;h3&gt;17.1 引入 SqlSugar 与基础对象映射 (DbFirst/CodeFirst)&lt;/h3&gt;
&lt;p&gt;通过 NuGet 安装包：&lt;code&gt;SqlSugarCore&lt;/code&gt;（注意，不是旧版的 &lt;code&gt;sqlSugar&lt;/code&gt;）。&lt;/p&gt;
&lt;h4&gt;步骤一：创建实体类模型&lt;/h4&gt;
&lt;p&gt;SqlSugar 通过简单的 C# 特性（Attributes）即可完成实体与数据库表的映射。&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;using SqlSugar;
using System;

// SugarTable 映射数据库表名
[SugarTable(&quot;SysUser&quot;)]
public class UserEntity
{
    // ISPrimaryKey 标记主键，IsIdentity 标记自增列
    [SugarColumn(IsPrimaryKey = true, IsIdentity = true)]
    public int Id { get; set; }

    [SugarColumn(ColumnName = &quot;UserName&quot;, Length = 50)]
    public string Name { get; set; }

    // 可空类型会被映射成数据库中的 NULL 允许列
    public DateTime? CreateTime { get; set; }

    // IsIgnore 标记在数据库中不存在该列，纯粹用于程序的临时业务逻辑
    [SugarColumn(IsIgnore = true)]
    public string TempToken { get; set; }
}
&lt;/code&gt;&lt;/pre&gt;
&lt;h4&gt;步骤二：实例化 SqlSugarClient 连接&lt;/h4&gt;
&lt;p&gt;创建一个简单的 SqlSugar 客户端是非常轻量的，它是线程安全的，官方推荐在每次使用时实例化或者按需采用单例。&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;using SqlSugar;

public class SqlSugarHelper
{
    public static SqlSugarClient GetInstance()
    {
        return new SqlSugarClient(new ConnectionConfig()
        {
            ConnectionString = &quot;Server=(local);Database=TestDb;UID=sa;PWD=123&quot;,
            DbType = DbType.SqlServer,       // 必须指定数据库类型 (MySQL, Oracle, Sqlite 等支持多达几十种)
            IsAutoCloseConnection = true,    // 自动释放关闭数据库连接，不用手工 using
            InitKeyType = InitKeyType.Attribute // 告诉框架通过特性去读取主键
        });
    }
}
&lt;/code&gt;&lt;/pre&gt;
&lt;h3&gt;17.2 进阶操作：结合依赖注入 (DI) 在 WPF 中的应用&lt;/h3&gt;
&lt;p&gt;如果你的 WPF 程序像前面的章节一样，集成了现代化的 &lt;code&gt;Host.CreateDefaultBuilder()&lt;/code&gt; 和依赖注入，这里有官方推荐的单例注入方案 &lt;code&gt;ISqlSugarClient&lt;/code&gt;。&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;在 &lt;code&gt;App.xaml.cs&lt;/code&gt; 中注册：&lt;/strong&gt;&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;services.AddSingleton&amp;lt;ISqlSugarClient&amp;gt;(s =&amp;gt;
{
    var config = s.GetRequiredService&amp;lt;IConfiguration&amp;gt;();
    // 需要通过 NuGet 额外安装 SqlSugar.IOC
    var client = new SqlSugarScope(new ConnectionConfig()
    {
        ConnectionString = config.GetConnectionString(&quot;DefaultConnection&quot;),
        DbType = DbType.SqlServer,
        IsAutoCloseConnection = true
    });
    
    // 配置 AOP 机制，用来在控制台打印生成的真实 SQL 语句，方便调试追踪。
    client.Aop.OnLogExecuting = (sql, pars) =&amp;gt;
    {
        Console.WriteLine(sql); // 打印出来的SQL可以复制并在 SSMS 运行
    };
    
    return client;
});

// 你也可以注册泛型仓储 SimpleClient&amp;lt;T&amp;gt; 来提供更面向对象的玩法
services.AddTransient(typeof(SimpleClient&amp;lt;&amp;gt;));
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;然后，在你的 ViewModel 中直接构造函数注入即可：&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;public partial class UserViewModel : ObservableObject
{
    private readonly ISqlSugarClient _db;

    public UserViewModel(ISqlSugarClient db)
    {
        _db = db;
    }

    [RelayCommand]
    private async Task QueryUsersAsync()
    {
        // ... 使用 _db 查询操作 ...
    }
}
&lt;/code&gt;&lt;/pre&gt;
&lt;h3&gt;17.3 经典 CRUD（增删改查）语法实战&lt;/h3&gt;
&lt;p&gt;&lt;strong&gt;1. 高级查询 (Queryable)：&lt;/strong&gt;
SqlSugar 的 Lambda 查询表达式支持极其丰富。&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;// 【基础查询】获取列表
var list = _db.Queryable&amp;lt;UserEntity&amp;gt;().ToList();

// 【条件查询】Where
var list2 = _db.Queryable&amp;lt;UserEntity&amp;gt;()
               .Where(it =&amp;gt; it.Name == &quot;admin&quot; &amp;amp;&amp;amp; it.Id &amp;gt; 10)
               .ToList();

// 【模糊查询】类似于 LIKE &apos;%keyword%&apos;
var keyword = &quot;jack&quot;;
var list3 = _db.Queryable&amp;lt;UserEntity&amp;gt;()
               .Where(it =&amp;gt; it.Name.Contains(keyword))
               .ToList();

// 【分页查询】
int totalCount = 0;
// 查第 1 页，每页 20 条，带出总行数
var pageList = _db.Queryable&amp;lt;UserEntity&amp;gt;()
                  .OrderBy(it =&amp;gt; it.CreateTime, OrderByType.Desc)
                  .ToPageList(1, 20, ref totalCount);

// 【连表查询】
var joinList = _db.Queryable&amp;lt;UserEntity, DeptEntity&amp;gt;((user, dept) =&amp;gt; new JoinQueryInfos(
    JoinType.Left, user.DeptId == dept.Id))
    .Select((user, dept) =&amp;gt; new 
    {
        user.Name,
        DeptName = dept.Name
    })
    .ToList();
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;&lt;strong&gt;2. 插入 (Insertable)：&lt;/strong&gt;&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;var newUser = new UserEntity { Name = &quot;NewUser&quot;, CreateTime = DateTime.Now };

// 插入单条：返回影响行数
int count = _db.Insertable(newUser).ExecuteCommand(); 

// 插入并返回库里自动生成的主键 Id
int insertId = _db.Insertable(newUser).ExecuteReturnIdentity(); 

// 极速批量插入百万级别数据 (内部自动采用 SqlBulkCopy 高性能模式)
var listToInsert = new List&amp;lt;UserEntity&amp;gt; { ... };
_db.Fastest&amp;lt;UserEntity&amp;gt;().BulkCopy(listToInsert);
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;&lt;strong&gt;3. 更新 (Updateable)：&lt;/strong&gt;&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;var user = _db.Queryable&amp;lt;UserEntity&amp;gt;().InSingle(1);
user.Name = &quot;UpdatedName&quot;;

// 【按实体更新】会自动根据主键更新那些变化了的列
_db.Updateable(user).ExecuteCommand();

// 【批量条件更新】这比 EF Core 要写原生的 ExecuteSqlRaw 方便太多了
_db.Updateable&amp;lt;UserEntity&amp;gt;()
   .SetColumns(it =&amp;gt; it.Name == &quot;禁止登陆&quot;)
   .Where(it =&amp;gt; it.CreateTime &amp;lt; DateTime.Now.AddYears(-1))
   .ExecuteCommand();
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;&lt;strong&gt;4. 删除 (Deleteable)：&lt;/strong&gt;&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;// 【根据主键实体删除】
_db.Deleteable(user).ExecuteCommand();

// 【根据主键Id集合进行 IN 操作批量删除】
int[] idsToDelete = new int[] { 1, 2, 3 };
_db.Deleteable&amp;lt;UserEntity&amp;gt;().In(idsToDelete).ExecuteCommand();

// 【根据表达式多条件复杂删除】
_db.Deleteable&amp;lt;UserEntity&amp;gt;().Where(it =&amp;gt; it.Name == &quot;Test&quot;).ExecuteCommand();
&lt;/code&gt;&lt;/pre&gt;
&lt;hr /&gt;
&lt;h2&gt;16. 全球性能第一的微型 ORM：Dapper 全场景 API 使用大全&lt;/h2&gt;
&lt;p&gt;&lt;strong&gt;官方/GitHub地址&lt;/strong&gt;：&lt;a href=&quot;https://github.com/DapperLib/Dapper&quot;&gt;https://github.com/DapperLib/Dapper&lt;/a&gt;&lt;/p&gt;
&lt;p&gt;Dapper 由 StackOverflow 团队开发并开源。它是市面上名副其实&lt;strong&gt;性能最高的 ORM（仅比纯原生的 Ado.Net &lt;code&gt;DataReader&lt;/code&gt; 慢极短暂的微秒量级，完全秒杀 EF Core 等重型框架）&lt;/strong&gt;。&lt;/p&gt;
&lt;p&gt;Dapper 的本质是一个&lt;strong&gt;对象映射器（Object Mapper）&lt;/strong&gt;，它以 &lt;code&gt;IDbConnection&lt;/code&gt; 扩展方法的形式存在。
&lt;strong&gt;核心理念&lt;/strong&gt;：Dapper &lt;strong&gt;完全不负责帮你生成 SQL 语句&lt;/strong&gt;，你需要自己手写 原汁原味的 SQL 或调用存储过程。Dapper 只负责一件事：&lt;strong&gt;将你手写的 SQL 扔给数据库，并极其狂暴地把返回的死表格结果集反射实例化成你想要的 C# 面向对象集合&lt;/strong&gt;。&lt;/p&gt;
&lt;p&gt;如果你或你的公司有严苛复杂的 SQL（几百行连表报表查询），在 EF Core 编写 Lambda 觉得费劲，并且极度在乎查询的绝对响应毫秒数，那么 Dapper 是你的不二之选。&lt;/p&gt;
&lt;h3&gt;16.1 核心 API 全家桶解析 (Query / Execute 家族)&lt;/h3&gt;
&lt;p&gt;通过 NuGet 安装包：&lt;code&gt;Dapper&lt;/code&gt;。如果操作 SQL Server，请一并安装官方驱动包 &lt;code&gt;Microsoft.Data.SqlClient&lt;/code&gt;。
&lt;em&gt;(注意：所有的同步方法，Dapper 都提供了对应的异步 &lt;code&gt;*Async&lt;/code&gt; 版本，在 WPF 开发中，&lt;strong&gt;强烈建议全部使用 Async 版本以防止界面卡死&lt;/strong&gt;。)&lt;/em&gt;&lt;/p&gt;
&lt;h4&gt;1. Query&amp;lt;T&amp;gt; 系列：一切查询语句的基石&lt;/h4&gt;
&lt;p&gt;用于执行 &lt;code&gt;SELECT&lt;/code&gt; 语句，并将结果映射到指定的类型的集合。&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;using Dapper;
using Microsoft.Data.SqlClient;
using System.Data;

public class DapperQueryDemo
{
    private string connStr = &quot;Server=(local);Database=TestDb;UID=sa;PWD=123&quot;;

    public async Task GetAllUsers()
    {
        using (IDbConnection db = new SqlConnection(connStr))
        {
            // 1. 查询全部列表
            // Dapper 会自动忽略字段大小写，甚至把下划线 user_name 自动映射到 UserName 属性上
            IEnumerable&amp;lt;UserEntity&amp;gt; list = await db.QueryAsync&amp;lt;UserEntity&amp;gt;(&quot;SELECT * FROM SysUser&quot;);

            // 2. 防注入参数化查询 (绝不准用字符串 + 号拼接 SQL！)
            // 只要传入一个匿名对象 new { 变量名 = 值 }，Dapper 会自动转化为等价的 SqlParameter
            string sql = &quot;SELECT * FROM SysUser WHERE Age &amp;gt; @MinAge AND Status = @State&quot;;
            var filteredList = await db.QueryAsync&amp;lt;UserEntity&amp;gt;(sql, new { MinAge = 25, State = 1 });

            // 3. 【极高频需求】获取单条记录的 4 兄弟：
            
            // QueryFirst：查不到直接抛红字报错崩溃！查到多行拿第一行。
            var u1 = await db.QueryFirstAsync&amp;lt;UserEntity&amp;gt;(&quot;SELECT * FROM SysUser WHERE Id = @id&quot;, new { id = 1 });
            
            // QueryFirstOrDefault：查不到优雅返回 null。查到多行拿第一行。(最常用)
            var u2 = await db.QueryFirstOrDefaultAsync&amp;lt;UserEntity&amp;gt;(&quot;...&quot;, new { id = 9999 });

            // QuerySingle：查不到报错！查到了但居然有两条以上也报错！（逼迫底层保证数据的绝对唯一性）
            var u3 = await db.QuerySingleAsync&amp;lt;UserEntity&amp;gt;(&quot;...&quot;);
            
            // QuerySingleOrDefault：查不到返回 null。查到了两条以上报错违规！
            var u4 = await db.QuerySingleOrDefaultAsync&amp;lt;UserEntity&amp;gt;(&quot;...&quot;);
        }
    }
}
&lt;/code&gt;&lt;/pre&gt;
&lt;h4&gt;2. Execute：无情的数据绞肉机 (增删改)&lt;/h4&gt;
&lt;p&gt;&lt;code&gt;Execute&lt;/code&gt; 用于运行根本不返回结果集列表的 &lt;code&gt;INSERT&lt;/code&gt;, &lt;code&gt;UPDATE&lt;/code&gt;, &lt;code&gt;DELETE&lt;/code&gt;。它的返回值永远是：&lt;strong&gt;受影响的行数 (int)&lt;/strong&gt;。&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;public async Task DapperExecuteDemo(UserEntity user)
{
    using (IDbConnection db = new SqlConnection(connStr))
    {
        // 1. 单条猛烈插入
        string sql = &quot;INSERT INTO SysUser (UserName, Age) VALUES (@UserName, @Age)&quot;;
        // 直接把整个实体对象 user 扔给 Dapper，它会自动剥离出里面的 UserName 和 Age 属性凑成参数包！
        int rows = await db.ExecuteAsync(sql, user);

        // 2. 批量高速操作 (Bulk Execution)
        // Dapper 有个黑魔法：如果传给它的 参数 不是单体模型，而是一个 List&amp;lt;T&amp;gt; 集合
        // 它会在底层自动展开，帮你循环对这批数据进行极其快速的多次插入/更新动作！
        var hugeList = new List&amp;lt;UserEntity&amp;gt; { new UserEntity{...}, new UserEntity{...} };
        int massiveRows = await db.ExecuteAsync(
            &quot;UPDATE SysUser SET Status = 1 WHERE Id = @Id&quot;, 
            hugeList  // 传进去的是个集合！
        );
    }
}
&lt;/code&gt;&lt;/pre&gt;
&lt;h4&gt;3. ExecuteScalar&amp;lt;T&amp;gt;：获取标量第一行第一列的王者&lt;/h4&gt;
&lt;p&gt;专门用来搞定像 &lt;code&gt;SELECT COUNT(*)&lt;/code&gt;、&lt;code&gt;SELECT SUM(Salary)&lt;/code&gt; 或者刚 &lt;code&gt;INSERT&lt;/code&gt; 完直接查询全局生成 ID 的那【唯一一个单独数字/字符串】。&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;public async Task&amp;lt;int&amp;gt; GetUserCountAndInsert()
{
    using (IDbConnection db = new SqlConnection(connStr))
    {
        // 获取总数
        int total = await db.ExecuteScalarAsync&amp;lt;int&amp;gt;(&quot;SELECT COUNT(1) FROM SysUser WHERE Status = 1&quot;);

        // 极其常见的连环魔法：插入一条数据后，由于 SQL 末尾带了 SELECT SCOPE_IDENTITY()
        // ExecuteScalar 会瞬间捕获这个新生成的自增 ID 返回回来给你！
        string insertSql = @&quot;
             INSERT INTO SysUser (UserName) VALUES (@Name);
             SELECT CAST(SCOPE_IDENTITY() AS INT);&quot;;
        
        int newlyBornId = await db.ExecuteScalarAsync&amp;lt;int&amp;gt;(insertSql, new { Name = &quot;Jack&quot; });
        return newlyBornId;
    }
}
&lt;/code&gt;&lt;/pre&gt;
&lt;h3&gt;16.2 进阶用法：让 Dapper 与传统 ORM 并驾齐驱&lt;/h3&gt;
&lt;p&gt;很多新手觉得既然手写 SQL 爽，那怎么解决 &lt;code&gt;IN&lt;/code&gt; 数组查找？又怎么解决多张表格 &lt;code&gt;JOIN&lt;/code&gt; 回来拆分给不同的实体类的情况？Dapper 全都有对策！&lt;/p&gt;
&lt;h4&gt;1. IN 语法的动态数组大挪移&lt;/h4&gt;
&lt;p&gt;在传统的原生 ADO.Net 中，要对 &lt;code&gt;WHERE Id IN (1, 2, 3)&lt;/code&gt; 传参是人间炼狱（你要手动拼接几百个参数符）。
但在 Dapper 里，它聪明到了极点：&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;public async Task&amp;lt;IEnumerable&amp;lt;UserEntity&amp;gt;&amp;gt; GetUsersByIds(int[] searchIds)
{
    using (IDbConnection db = new SqlConnection(connStr))
    {
        // SQL 里只有一个普通的 @Ids
        string sql = &quot;SELECT * FROM SysUser WHERE Id IN @Ids&quot;;
        
        // 但你传进来的 new { Ids = searchIds } 其实是一个完整的 int 数组 或 List&amp;lt;int&amp;gt;
        // Dapper 会在底层把它大卸八块，自动翻译扩写成： IN (@Ids1, @Ids2, @Ids3)
        return await db.QueryAsync&amp;lt;UserEntity&amp;gt;(sql, new { Ids = searchIds });
    }
}
&lt;/code&gt;&lt;/pre&gt;
&lt;h4&gt;2. QueryMultiple：一网打尽（多结果集）&lt;/h4&gt;
&lt;p&gt;想要展示一个大型看板，既要用户列表，又要部门列表，还要今日订单总数。
以前的做法是发起 3 次对数据库的网络 TCP 握手请求。
现在，用 &lt;code&gt;QueryMultiple&lt;/code&gt; 发一次包含 3 条句子的超级长报文，一次性全拿回来，网络 I/O 耗电直降到底：&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;public async Task GetDashboardData()
{
    using (IDbConnection db = new SqlConnection(connStr))
    {
        string sql = @&quot;
            SELECT * FROM SysUser;
            SELECT * FROM Department;
            SELECT COUNT(1) FROM Orders WHERE CreateDate = CAST(GETDATE() AS DATE);
        &quot;;
        
        // 通过网格阅读器拿到了从数据库端一次性打回的这个包含 3 个大包裹的包裹箱
        using (var gridReader = await db.QueryMultipleAsync(sql))
        {
            // 注意：Read 的顺序必须与你写的 SQL 查询顺序严丝合缝的对应上！
            var users = (await gridReader.ReadAsync&amp;lt;UserEntity&amp;gt;()).ToList();
            var depts = (await gridReader.ReadAsync&amp;lt;DepartmentEntity&amp;gt;()).ToList();
            var todaysOrders = await gridReader.ReadFirstAsync&amp;lt;int&amp;gt;();
            
            // 数据在极度极速的网络吞吐下全部拼装完毕发往前台。
        }
    }
}
&lt;/code&gt;&lt;/pre&gt;
&lt;h4&gt;3. 动态超阶参数神器：DynamicParameters&lt;/h4&gt;
&lt;p&gt;如果你是在跟 DBA 写的&lt;strong&gt;存储过程 (Stored Procedure)&lt;/strong&gt; 对接，经常会遇到恶心的 &lt;code&gt;OUTPUT&lt;/code&gt; 参数（输出包裹）、或者要求抛回返回值状态（Return Value）。这个时候普通的 &lt;code&gt;new { aaa = 1 }&lt;/code&gt; 这种傻瓜匿名类抗不住了，必须上纯粹的重武器：&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;public async Task CallStoredProcedure()
{
    using (IDbConnection db = new SqlConnection(connStr))
    {
        // 1. 手动声明一个高度自定义的参数组大杂烩容器
        var parameters = new DynamicParameters();
        parameters.Add(&quot;@InName&quot;, &quot;DapperFan&quot;);
        parameters.Add(&quot;@InAge&quot;, 28);
        
        // 2. 指定它这个是个暗器(OUTPUT)：跑完存储过程你要往外往这个格子里倒东西的！
        parameters.Add(&quot;@OutNewId&quot;, dbType: DbType.Int32, direction: ParameterDirection.Output);
        
        // 3. 执行存储过程 (切记 CommandType 参数务必标明这是存储过程不是普通常规代码)
        await db.ExecuteAsync(
            &quot;sp_CreateUserAndReturnId&quot;, 
            parameters, 
            commandType: CommandType.StoredProcedure
        );

        // 4. 从大杂烩容器里把暗盒里的结果取走掏出来
        int resultingId = parameters.Get&amp;lt;int&amp;gt;(&quot;@OutNewId&quot;);
    }
}
&lt;/code&gt;&lt;/pre&gt;
&lt;h4&gt;4. 神魔一念：多重映射连表查拆分 (Multi-Mapping)&lt;/h4&gt;
&lt;p&gt;这是所有轻量化 ORM 的门槛噩梦。一句话 &lt;code&gt;SELECT * FROM User u INNER JOIN Dept d ON u.DeptId = d.Id&lt;/code&gt;。这返回的一行横向结果里既有人的字段又有部门的字段，这可怎么往内存实体类里塞？&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;场景 A：1 对 1 (一对一的挂靠)&lt;/strong&gt;
我们希望拿出的 &lt;code&gt;UserEntity&lt;/code&gt; 模型身体里，那个 &lt;code&gt;public DeptEntity Department {get;set;}&lt;/code&gt; 关联属性能被 Dapper 自动填充。&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;public async Task&amp;lt;List&amp;lt;UserEntity&amp;gt;&amp;gt; GetUsersWithDept()
{
    using (IDbConnection db = new SqlConnection(connStr))
    {
        // 注意列的顺序：前面全是人的属性，直到 d.Id 开始，全变成部门属性
        string sql = @&quot;
            SELECT u.Id, u.UserName, u.DeptId, d.Id, d.DeptName 
            FROM SysUser u 
            INNER JOIN Department d ON u.DeptId = d.Id&quot;;

        // QueryAsync&amp;lt;目标1, 目标2, 最终怎么揉在一块的返回值类型&amp;gt;
        var result = await db.QueryAsync&amp;lt;UserEntity, DepartmentEntity, UserEntity&amp;gt;(
            sql,
            (user, dept) =&amp;gt; 
            {
                // Dapper 已经聪明的把刚才那么极其长的一行字符串，以 splitOn 切分出的人和部门单独给你了。
                // 你只需要在这一个 Lambda 里面，手动帮他们系上鞋带关联起来。
                user.Department = dept;
                // 最终还是返回包装好的人
                return user; 
            },
            // 【极其关键的劈砍点】：告诉 Dapper 框架，“兄弟，当你扫到名叫 Id 的这列时（指的是 d.Id），前面的全是用户部分，从这里开始后面都是部门部分的数据。”
            splitOn: &quot;Id&quot; 
        );
        
        return result.ToList();
    }
}
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;&lt;strong&gt;场景 B：1 对 多 (如获取某部门及其旗下的所有几百个员工)&lt;/strong&gt;
（由于 Dapper 遇到十行相同部门、不同人的记录时，会触发十次回调，我们需要自己用字典做消除重复缓存）。&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;public async Task&amp;lt;List&amp;lt;DepartmentEntity&amp;gt;&amp;gt; GetDeptsWithHugeUsers()
{
    using (IDbConnection db = new SqlConnection(connStr))
    {
        string sql = @&quot;
            SELECT d.Id, d.DeptName, u.Id, u.UserName 
            FROM Department d 
            LEFT JOIN SysUser u ON d.Id = u.DeptId&quot;;

        // 建立一个只存在于这短暂一瞬查询中的缓存池
        var deptCache = new Dictionary&amp;lt;int, DepartmentEntity&amp;gt;();

        var result = await db.QueryAsync&amp;lt;DepartmentEntity, UserEntity, DepartmentEntity&amp;gt;(
            sql,
            (dept, user) =&amp;gt; 
            {
                // 1. 如果字典里没这个头目部门，就加进去先
                if (!deptCache.TryGetValue(dept.Id, out var currentDept))
                {
                    currentDept = dept;
                    currentDept.Users = new List&amp;lt;UserEntity&amp;gt;(); // 初始化他的手下数组
                    deptCache.Add(currentDept.Id, currentDept);
                }

                // 2. 如果这行连表的人真的扫出来活人了(由于是 LEFT JOIN，可能是 Null)，就把人拉进数组
                if (user != null)
                {
                    currentDept.Users.Add(user);
                }

                // 随便返回，其实我们最终想要的是外面的纯天然被折叠过的字典值
                return currentDept; 
            },
            splitOn: &quot;Id&quot; // 切分点
        );
        
        // 放弃他直接 Select 回来的充满重复项的 List 结局。单独抽离出我们缓存池的纯净被压合数据。
        return deptCache.Values.ToList();
    }
}
&lt;/code&gt;&lt;/pre&gt;
&lt;h3&gt;16.3 与 WPF 及现代依赖注入 (DI) 的彻底融合&lt;/h3&gt;
&lt;p&gt;在上一章的 &lt;code&gt;WPF App.xaml.cs&lt;/code&gt; 构建中，我们不需要去像 EFCore 那样注册一个巨大的 &lt;code&gt;DbContext&lt;/code&gt;。对于 Dapper，我们只需要把最底层的原生发动机 &lt;strong&gt;&lt;code&gt;IDbConnection&lt;/code&gt;&lt;/strong&gt; 交给容器。&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;1. 在 App.xaml.cs 中注册你的纯血统连接&lt;/strong&gt;&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;AppHost = Host.CreateDefaultBuilder()
    .ConfigureServices((context, services) =&amp;gt;
    {
        // 拿到我们在 appsettings.json 里写好的数据库账号密码
        var connStr = context.Configuration.GetConnectionString(&quot;DefaultConnection&quot;);

        // 注册底层的 IDbConnection。（如果你的软件高发频跳转，使用 Transient 提供瞬态新建链接最好！因为底层 SqlClient 天生就有巨型连接池担保，绝不会拖拉）
        services.AddTransient&amp;lt;IDbConnection&amp;gt;(provider =&amp;gt; 
        {
            return new SqlConnection(connStr);
        });

        // 也可以顺带把你手写了各种 CRUD 封装出来的通用服务层装载进去
        services.AddTransient&amp;lt;IUserService, UserService&amp;gt;();
    })
    .Build();
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;&lt;strong&gt;2. 在 Service 层中优雅地取出享受&lt;/strong&gt;&lt;/p&gt;
&lt;p&gt;当你能在构造函数里直接提取 &lt;code&gt;_db&lt;/code&gt; 并无缝开启带有事务 (&lt;code&gt;Transaction&lt;/code&gt;) 控制的业务代码操作，这才是真正的大师级客户端应用体验。&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;public class UserService : IUserService
{
    private readonly IDbConnection _db;

    public UserService(IDbConnection db)
    {
        _db = db;
    }

    // 一个真实的转账事务例子：演示 Dapper 也可以极速带 Transaction
    public async Task&amp;lt;bool&amp;gt; ProcessSalaryTransferAsync(int companyId, int employeeId, decimal amount)
    {
        // 【关键】：Dapper 有个神器的检测，你无需显式调用 _db.Open()，只要你发起 Query，它自动开门。
        // 但如果要使用 Transaction，必须强行手动先扯开大门！
        if (_db.State != ConnectionState.Open)
            _db.Open();

        using (var trans = _db.BeginTransaction())
        {
            try
            {
                // 注意第二个参数和 第三个 transaction 锁定护身符参数的传递
                await _db.ExecuteAsync(&quot;UPDATE Accounts SET Bal = Bal - @amount WHERE Id = @cId&quot;, 
                    new { amount, cId = companyId }, transaction: trans);
                    
                await _db.ExecuteAsync(&quot;UPDATE Accounts SET Bal = Bal + @amount WHERE Id = @eId&quot;, 
                    new { amount, eId = employeeId }, transaction: trans);

                trans.Commit();
                return true;
            }
            catch (Exception)
            {
                trans.Rollback();
                return false;
            }
        } // 离开 using 块，如果忘关底端链接池会自动进行处置终结。
    }
}
&lt;/code&gt;&lt;/pre&gt;
&lt;h3&gt;16.4 SqlSugar vs Dapper 的使用抉择总结&lt;/h3&gt;
&lt;p&gt;在 WPF 的开发框架选型时，这里给出一个标准客观的抉择建议：&lt;/p&gt;
&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;特性评估项&lt;/th&gt;
&lt;th&gt;SqlSugar (最强全自动化国产)&lt;/th&gt;
&lt;th&gt;Dapper (纯手动硬核最强王者)&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;性能测试&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;中上 (优于 EFCore)&lt;/td&gt;
&lt;td&gt;&lt;strong&gt;快到令人窒息&lt;/strong&gt; (贴身肉搏纯低级 Ado.Net Reader)&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;学习门槛&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;极低（配置好特性直接无脑 &lt;code&gt;.Where()&lt;/code&gt; &lt;code&gt;.ToPageList()&lt;/code&gt;)）&lt;/td&gt;
&lt;td&gt;高（需要极度扎实且严谨的手写大部头复合超长 SQL 能力）&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;多库兼容切换&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;&lt;strong&gt;极强&lt;/strong&gt; (底层自带翻译引擎，一键转 MySQL/Oracle)&lt;/td&gt;
&lt;td&gt;极弱（如果里面写了死锁和 SqlServer 方言函数，一旦切库就会立刻集体报废）&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;复杂树结构查询&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;原生拥抱主外键导航属性，几层级联查询全部 Lambda 包裹全自动&lt;/td&gt;
&lt;td&gt;难受至极，多对多需要手写复杂的 &lt;code&gt;Dictionary&lt;/code&gt; 和 &lt;code&gt;Multi-Mapping&lt;/code&gt; 分裂点来擦屁股&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;
&lt;p&gt;&lt;strong&gt;究极推荐开发姿势 (CQRS 混流分治)：&lt;/strong&gt;
不要让这俩打架，在超大型商业级 WPF 应用中，绝大部分团队会采用&lt;strong&gt;读写分离&lt;/strong&gt;：&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;所有涉及到修改、插入、带验证逻辑的写业务 (&lt;code&gt;Command&lt;/code&gt;)：使用 &lt;strong&gt;SqlSugar&lt;/strong&gt; 的全自动增删改查。&lt;/li&gt;
&lt;li&gt;所有涉及到百万级别长时耗大数据展现渲染统计表格、超复杂三十张表关联汇总的大数据报表 (&lt;code&gt;Query&lt;/code&gt;)：纯手写最高效精妙的大范围 SQL，用 &lt;strong&gt;Dapper&lt;/strong&gt; 去 &lt;code&gt;QueryMultiple&lt;/code&gt; 直接闪电抽拉带走结果。&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;把这套组合拳打在 WPF 和依赖注入系统中，这个桌面应用的框架底层就算是彻底无懈可击了。&lt;/p&gt;
&lt;hr /&gt;
&lt;h2&gt;17. WPF 现代 UI 控件库详解：HandyControl 全场景实战&lt;/h2&gt;
&lt;p&gt;WPF 原生的控件虽然强大，但在 2026 年现代 UI 审美标准下，原生的基础控件视觉效果显得非常老旧（类似于 Windows 7/8 风格）。为了实现扁平化、流线型以及具有高阶交互动画的现代化界面，在商业项目中我们极少原生地从零手搓圆角和阴影，而是全栈引入成熟的开源 UI 框架。&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;HandyControl&lt;/strong&gt; 是 WPF 领域知名度极高、极其轻量且组件极为丰富的开源 UI 框架。如果你不需要像 MaterialDesignInXAML 那样强制束缚在谷歌的 Material 规范下，HandyControl 将为你打开一扇绝对自由且极为奢华漂亮的 UI 大门。&lt;/p&gt;
&lt;p&gt;&lt;em&gt;官方仓库：&lt;a href=&quot;https://github.com/HandyOrg/HandyControl&quot;&gt;https://github.com/HandyOrg/HandyControl&lt;/a&gt;&lt;/em&gt;&lt;/p&gt;
&lt;h3&gt;17.1 引入与全局配置初始化&lt;/h3&gt;
&lt;ol&gt;
&lt;li&gt;&lt;strong&gt;安装 NuGet 包&lt;/strong&gt;：在项目中搜索并安装 &lt;code&gt;HandyControl&lt;/code&gt;。&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;全局字典注水 (App.xaml)&lt;/strong&gt;：
要想让全工程的按钮、文本框一夜之间变成现代化的精美风格，我们需要在应用的启动根节点将 HandyControl 的基础样式大血脉全部导入。&lt;/li&gt;
&lt;/ol&gt;
&lt;pre&gt;&lt;code&gt;&amp;lt;!-- App.xaml --&amp;gt;
&amp;lt;Application x:Class=&quot;WpfAppApp.App&quot;
             xmlns=&quot;http://schemas.microsoft.com/winfx/2006/xaml/presentation&quot;
             xmlns:x=&quot;http://schemas.microsoft.com/winfx/2006/xaml&quot;
             StartupUri=&quot;MainWindow.xaml&quot;&amp;gt;
    &amp;lt;Application.Resources&amp;gt;
        &amp;lt;!-- 将 HandyControl 主题皮肤强行注入到程序的血液中 --&amp;gt;
        &amp;lt;ResourceDictionary&amp;gt;
            &amp;lt;ResourceDictionary.MergedDictionaries&amp;gt;
                &amp;lt;!-- 基础主题颜色和控件统一样式 --&amp;gt;
                &amp;lt;ResourceDictionary Source=&quot;pack://application:,,,/HandyControl;component/Themes/SkinDefault.xaml&quot;/&amp;gt;
                &amp;lt;ResourceDictionary Source=&quot;pack://application:,,,/HandyControl;component/Themes/Theme.xaml&quot;/&amp;gt;
            &amp;lt;/ResourceDictionary.MergedDictionaries&amp;gt;
        &amp;lt;/ResourceDictionary&amp;gt;
    &amp;lt;/Application.Resources&amp;gt;
&amp;lt;/Application&amp;gt;
&lt;/code&gt;&lt;/pre&gt;
&lt;ol&gt;
&lt;li&gt;&lt;strong&gt;在窗体中声明命名空间 (MainWindow.xaml)&lt;/strong&gt;：
必须在 XML 头部引入 &lt;code&gt;hc&lt;/code&gt; 前缀，才能使用其数百个独创的现代控件。&lt;/li&gt;
&lt;/ol&gt;
&lt;pre&gt;&lt;code&gt;&amp;lt;Window x:Class=&quot;WpfAppApp.MainWindow&quot;
        xmlns=&quot;http://schemas.microsoft.com/winfx/2006/xaml/presentation&quot;
        xmlns:x=&quot;http://schemas.microsoft.com/winfx/2006/xaml&quot;
        xmlns:hc=&quot;https://handyorg.github.io/handycontrol&quot;
        Title=&quot;HandyControl 演练场&quot; Height=&quot;600&quot; Width=&quot;800&quot;&amp;gt;
    &amp;lt;!-- 主体代码 --&amp;gt;
&amp;lt;/Window&amp;gt;
&lt;/code&gt;&lt;/pre&gt;
&lt;hr /&gt;
&lt;h3&gt;17.2 基础输入控件的彻底进化 (按钮与输入框)&lt;/h3&gt;
&lt;p&gt;HandyControl 接管了 WPF 原生 &lt;code&gt;Button&lt;/code&gt; 和 &lt;code&gt;TextBox&lt;/code&gt; 等基础控件，并提供了海量的开箱即用的优美样式。&lt;/p&gt;
&lt;h4&gt;1. 语义化按钮 (Semantic Buttons)&lt;/h4&gt;
&lt;p&gt;在现代 Web 框架 (如 Vue Element, React AntDesign) 中非常流行的“主配色、成功、警告、危险”语义颜色按钮，现在在 WPF 也能通过一行 &lt;code&gt;Style&lt;/code&gt; 调用：&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;&amp;lt;StackPanel Orientation=&quot;Horizontal&quot; HorizontalAlignment=&quot;Center&quot; Margin=&quot;20&quot;&amp;gt;
    &amp;lt;!-- 原生默认按钮，已经被 HC 自动渲染成了带动画圆角的扁平按钮 --&amp;gt;
    &amp;lt;Button Content=&quot;普通按钮&quot; Margin=&quot;5&quot;/&amp;gt;
    
    &amp;lt;!-- 蓝色首要强调按钮 --&amp;gt;
    &amp;lt;Button Content=&quot;主要操作 Primary&quot; Style=&quot;{StaticResource ButtonPrimary}&quot; Margin=&quot;5&quot;/&amp;gt;
    
    &amp;lt;!-- 绿色成功按钮 --&amp;gt;
    &amp;lt;Button Content=&quot;成功 Success&quot; Style=&quot;{StaticResource ButtonSuccess}&quot; Margin=&quot;5&quot;/&amp;gt;
    
    &amp;lt;!-- 黄色警告按钮 --&amp;gt;
    &amp;lt;Button Content=&quot;警告 Warning&quot; Style=&quot;{StaticResource ButtonWarning}&quot; Margin=&quot;5&quot;/&amp;gt;
    
    &amp;lt;!-- 红色危险按钮 (带悬浮涟漪动画) --&amp;gt;
    &amp;lt;Button Content=&quot;绝密删除 Danger&quot; Style=&quot;{StaticResource ButtonDanger}&quot; Margin=&quot;5&quot;/&amp;gt;
    
    &amp;lt;!-- 虚线边框外轮廓按钮 --&amp;gt;
    &amp;lt;Button Content=&quot;幽灵虚线 Dashed&quot; Style=&quot;{StaticResource ButtonDashed}&quot; Margin=&quot;5&quot;/&amp;gt;
&amp;lt;/StackPanel&amp;gt;
&lt;/code&gt;&lt;/pre&gt;
&lt;h4&gt;2. “霸道”的现代文本框 (支持占位符和一键清除)&lt;/h4&gt;
&lt;p&gt;WPF 原生最让人诟病的是 &lt;code&gt;TextBox&lt;/code&gt; 竟然没有 &lt;code&gt;Placeholder&lt;/code&gt;（暗淡提示修饰词）属性！想要一键清空的 &lt;code&gt;X&lt;/code&gt; 按钮更是需要手写大量附加属性。现在有了 HC 的强力扩展属性（通过 &lt;code&gt;hc:InfoElement&lt;/code&gt; 追加的各类特性）：&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;&amp;lt;StackPanel Width=&quot;300&quot; Margin=&quot;20&quot; Spacing=&quot;15&quot;&amp;gt;
    &amp;lt;!-- 1. 带有 Placeholder 和自带清空叉号的文本框 --&amp;gt;
    &amp;lt;TextBox hc:InfoElement.Placeholder=&quot;请输入您的登录账号...&quot;
             hc:InfoElement.Necessary=&quot;True&quot; 
             Style=&quot;{StaticResource TextBoxExtend}&quot;
             hc:InfoElement.ShowClearButton=&quot;True&quot; /&amp;gt;
             
    &amp;lt;!-- 2. 完全现代化的密码框 (自带右侧小眼睛点击显示明文的防偷窥功能) --&amp;gt;
    &amp;lt;PasswordBox hc:InfoElement.Placeholder=&quot;请输入严格的大小写密码...&quot; 
                 Style=&quot;{StaticResource PasswordBoxExtend}&quot;
                 hc:InfoElement.ShowClearButton=&quot;True&quot; /&amp;gt;
                 
    &amp;lt;!-- 3. 数字微调器 (替代 TextBox 的纯数字输入限制，原生 WPF 没有这个！) --&amp;gt;
    &amp;lt;hc:NumericUpDown Minimum=&quot;0&quot; Maximum=&quot;100&quot; Value=&quot;25&quot; 
                      hc:InfoElement.Placeholder=&quot;年龄上限&quot; /&amp;gt;
                      
    &amp;lt;!-- 4. 极致漂亮的纯正搜索框 --&amp;gt;
    &amp;lt;hc:SearchBar hc:InfoElement.Placeholder=&quot;全网搜素您的报表...&quot; Style=&quot;{StaticResource SearchBarPlus}&quot; /&amp;gt;
&amp;lt;/StackPanel&amp;gt;
&lt;/code&gt;&lt;/pre&gt;
&lt;hr /&gt;
&lt;h3&gt;17.3 进阶视觉/布局组件&lt;/h3&gt;
&lt;h4&gt;1. 加载等待动画 (Loading &amp;amp; Progress)&lt;/h4&gt;
&lt;p&gt;当界面后台发起异步数据库查询时，锁定 UI 并转圈必不可少。&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;&amp;lt;WrapPanel Margin=&quot;20&quot;&amp;gt;
    &amp;lt;!-- 环形动态大平滑光晕进度条 --&amp;gt;
    &amp;lt;hc:CircleProgressBar Value=&quot;65&quot; ArcThickness=&quot;5&quot; Margin=&quot;20&quot; Width=&quot;80&quot; Height=&quot;80&quot; Text=&quot;65%&quot;/&amp;gt;
    
    &amp;lt;!-- 无尽旋转的双轨道波浪动画等待圈 --&amp;gt;
    &amp;lt;hc:LoadingCircle Foreground=&quot;{DynamicResource PrimaryBrush}&quot; Margin=&quot;20&quot; Width=&quot;60&quot; Height=&quot;60&quot;/&amp;gt;
    
    &amp;lt;!-- 骨架屏占位符 (网速慢时常用的块状渐变闪烁等待替代物) --&amp;gt;
    &amp;lt;hc:Skeleton Margin=&quot;20&quot; Width=&quot;200&quot; Height=&quot;80&quot; Active=&quot;True&quot; Skin=&quot;Line&quot;/&amp;gt;
&amp;lt;/WrapPanel&amp;gt;
&lt;/code&gt;&lt;/pre&gt;
&lt;h4&gt;2. 分段器与徽标 (Pagination &amp;amp; Badge)&lt;/h4&gt;
&lt;p&gt;针对列表和提醒的小部件，让页面专业性陡增。&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;&amp;lt;!-- 1. 红点/数字角标 (常用于右上角提醒新消息 99+) --&amp;gt;
&amp;lt;hc:Badge Value=&quot;99+&quot; BadgeType=&quot;Badge&quot; Margin=&quot;20&quot;&amp;gt;
    &amp;lt;Button Content=&quot;你有未读审批&quot; Style=&quot;{StaticResource ButtonPrimary}&quot;/&amp;gt;
&amp;lt;/hc:Badge&amp;gt;

&amp;lt;!-- 2. 全功能底部分页控制器 --&amp;gt;
&amp;lt;!-- 极其容易跟后端 MVVM 进行绑定控制总页数与当前页跳跃触发 --&amp;gt;
&amp;lt;hc:Pagination MaxPageCount=&quot;100&quot; PageIndex=&quot;3&quot; DataCountPerPage=&quot;20&quot; IsJumpEnabled=&quot;True&quot; Margin=&quot;20&quot;/&amp;gt;
&lt;/code&gt;&lt;/pre&gt;
&lt;hr /&gt;
&lt;h3&gt;17.4 灵魂功能：Growl (全局极简屏显通知)&lt;/h3&gt;
&lt;p&gt;这绝对是 HandyControl 最核心必杀的控件：&lt;strong&gt;绝不要再去用又丑又打断用户的原生弹窗 &lt;code&gt;MessageBox.Show()&lt;/code&gt; 了&lt;/strong&gt;！
就像在现代 Web 里一样，操作成功后在屏幕顶部或右下角轻柔地浮现一个渐渐滑出色带气泡，三秒后自动溶解消失。这就叫做 &lt;strong&gt;Growl&lt;/strong&gt;。&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;它的使用纯粹依靠 C# 后端代码触发（无论在普通的 ViewModel 还是窗口后台 cs），不需要在 XAML 埋点任何东西。&lt;/strong&gt;&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;using HandyControl.Controls;

public class TestViewModel : ObservableObject
{
    [RelayCommand]
    private void SaveData()
    {
        // ... (假装在这里调用了刚才 Dapper 章节的数据库 Execute 增加) ...
        bool dbSuccess = true;

        if (dbSuccess)
        {
            // 在屏幕右上角或中上方浮现一层绿色的悦耳成功提示框，2秒后自动透明消失（不抢夺鼠标焦点，用户体验极佳）
            Growl.Success(&quot;薪水调优及数据报表保存成功！&quot;);
        }
        else
        {
            // 在屏幕右上角强力震动弹出的红色危险错误通知
            Growl.Fatal(&quot;致命错误：底层数据库引擎断开链接，请检查网络！&quot;);
        }
        
        // 还有蓝色打底的 Growl.Info(&quot;我只是个单纯通知&quot;) 以及橙色打底的 Growl.Warning(&quot;服务器负载预警&quot;)
    }
}
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;&lt;em&gt;进阶特性&lt;/em&gt;：如果不喜欢右上角堆叠（类似 Mac 的系统通知机制），甚至可以通过 &lt;code&gt;Growl.SetGrowlParent()&lt;/code&gt; 把这些气泡死死锁进屏幕的主布局容器，限制他们在特定容器中央漂浮！&lt;/p&gt;
&lt;h3&gt;17.5 现代弹窗系统 (Dialog)&lt;/h3&gt;
&lt;p&gt;有时候必须要打断用户进行输入，但自带的 Windows MessageBox 不仅难看还会导致进程强行切挂，甚至在带遮罩的异步编程下极其难受。HC 提供了纯净原生渲染在你的 UI 里（而非新建独立进程悬浮框）层的对话框。&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;using HandyControl.Controls;

// 极其方便强硬的原生对话框平替
MessageBox.Show(&quot;您确认要清空本年度的财会账本并且删除 50 亿条归档吗？&quot;, 
                &quot;危险最高级红线警告&quot;, 
                MessageBoxButton.YesNo, 
                MessageBoxImage.Warning);
                
// 注意：HandyControl 已经使用样式覆盖了这个静态 MessageBox 调用，
// 所以只需触发这句普通代码，跳出来的就是带着极为酷炫高雅的纯扁平磨砂玻璃样式的警告弹框。
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;而在更复杂的“里面不是一行字，我要弹出一个带着极其复杂的验证码输入、多层下拉框选人的巨大子窗口”时：&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;// 【极客高发】展示极其复杂的自定义子视图弹层：
// 假设你有一个提前画好的独立 UserControl 叫 MyCustomAddUserDetailView (里面全是填表栏)

var dialog = Dialog.Show(new MyCustomAddUserDetailView())
                   .Initialize&amp;lt;TestViewModel&amp;gt;(vm =&amp;gt; 
                   {
                         // 可以在这里瞬间把数据从母窗口强硬插进被弹出的子窗口 ViewModel 里面去！
                         vm.IsAdminPass = true;
                   })
                   .GetHalfWindow(); // 拿到一个受控制的内部独立框实例句柄
                   
// 上述代码不会引起跨线程崩溃，而且能得到极其完美的黑色半透明遮罩背景。
&lt;/code&gt;&lt;/pre&gt;
&lt;h3&gt;17.6 将 HandyControl 收为己有的总结&lt;/h3&gt;
&lt;p&gt;在 WPF 的征途上，拥有了 &lt;strong&gt;CommunityToolkit.Mvvm 管理逻辑大动脉&lt;/strong&gt;，拥有了 &lt;strong&gt;Dapper / SqlSugar 做底层钢铁引擎&lt;/strong&gt;，加上现在由 &lt;strong&gt;HandyControl 为你的软件披上了如同现代宇宙战舰一般的极简科幻皮肤&lt;/strong&gt;。&lt;/p&gt;
&lt;p&gt;您目前所掌握的技术版图，已经彻底抹除了“WPF 软件长得像上个世纪老财务报表工具”的陈旧偏见。只需要合理的组合搭配，便能在几天时间内靠一人之力，爆发出媲美一整个五十人专业 Web 前端加设计团队制作出的大型多端适配应用级别的震撼桌面视觉体验方案。&lt;/p&gt;
</content:encoded></item><item><title>C#进阶特性详解</title><link>https://meteor-comet.github.io/posts/csharp-advanced/</link><guid isPermaLink="true">https://meteor-comet.github.io/posts/csharp-advanced/</guid><description>泛型/委托与事件/反射与特性</description><pubDate>Fri, 15 Mar 2024 00:00:00 GMT</pubDate><content:encoded>&lt;h2&gt;目录&lt;/h2&gt;
&lt;ol&gt;
&lt;li&gt;&lt;a href=&quot;#generics-detail&quot;&gt;C#泛型详解&lt;/a&gt;
&lt;ul&gt;
&lt;li&gt;&lt;a href=&quot;#generics-concept&quot;&gt;泛型的基本概念&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;#generics-class-interface&quot;&gt;泛型类与泛型接口&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;#generics-method&quot;&gt;泛型方法&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;#generics-constraints&quot;&gt;泛型约束&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;#generics-delegate&quot;&gt;泛型委托&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;#generics-variance&quot;&gt;协变与逆变&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;#generics-performance&quot;&gt;泛型的性能优势&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;#generics-scenarios&quot;&gt;泛型的应用场景&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;#delegates-lambda-advanced&quot;&gt;C#委托与Lambda表达式进阶&lt;/a&gt;
&lt;ul&gt;
&lt;li&gt;&lt;a href=&quot;#anonymous-function-lambda&quot;&gt;匿名函数与Lambda表达式&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;#func-delegate-advanced&quot;&gt;Func委托详解&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;#action-delegate-advanced&quot;&gt;Action委托详解&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;#predicate-delegate-advanced&quot;&gt;Predicate委托详解&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;#delegate-chain-event&quot;&gt;委托链与事件&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;#expression-trees&quot;&gt;表达式树&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;#linq-detail&quot;&gt;C# LINQ详解&lt;/a&gt;
&lt;ul&gt;
&lt;li&gt;&lt;a href=&quot;#linq-concept&quot;&gt;LINQ的基本概念&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;#linq-query-syntax&quot;&gt;LINQ查询语法&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;#linq-method-syntax&quot;&gt;LINQ方法语法&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;#standard-query-operators&quot;&gt;标准查询操作符&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;#linq-to-objects&quot;&gt;LINQ to Objects&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;#linq-to-sql&quot;&gt;LINQ to SQL&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;#linq-to-xml&quot;&gt;LINQ to XML&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;#linq-performance&quot;&gt;LINQ性能优化&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;#linq-scenarios&quot;&gt;LINQ应用场景&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;#orm-frameworks&quot;&gt;C# ORM框架详解&lt;/a&gt;
&lt;ul&gt;
&lt;li&gt;&lt;a href=&quot;#ado-net-basics&quot;&gt;ADO.NET基础&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;#ado-net-connection&quot;&gt;ADO.NET连接管理&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;#ado-net-commands&quot;&gt;ADO.NET命令执行&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;#ado-net-data-reader&quot;&gt;ADO.NET数据读取&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;#ado-net-parameters&quot;&gt;ADO.NET参数化查询&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;#ado-net-transactions&quot;&gt;ADO.NET事务处理&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;#ado-net-stored-procedures&quot;&gt;ADO.NET存储过程&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;#ef6-basics&quot;&gt;Entity Framework 6基础&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;#dbcontext-detail&quot;&gt;DbContext详解&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;#dbset-detail&quot;&gt;DbSet详解&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;#entity-configuration&quot;&gt;实体配置&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;#relationship-configuration&quot;&gt;关系配置&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;#ef6-querying&quot;&gt;数据查询&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;#ef6-modifying&quot;&gt;数据修改&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;#change-tracking&quot;&gt;变更跟踪&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;#concurrency-control&quot;&gt;并发控制&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;#migrations&quot;&gt;迁移（Migrations）&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;#ef6-performance&quot;&gt;性能优化&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;#ef6-advanced&quot;&gt;高级特性&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;#design-patterns&quot;&gt;C#设计模式详解&lt;/a&gt;
&lt;ul&gt;
&lt;li&gt;&lt;a href=&quot;#singleton-pattern&quot;&gt;单例模式&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;#factory-pattern&quot;&gt;工厂模式&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;#observer-pattern&quot;&gt;观察者模式&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;#strategy-pattern&quot;&gt;策略模式&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;#adapter-pattern&quot;&gt;适配器模式&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;#decorator-pattern&quot;&gt;装饰器模式&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;#dependency-injection-pattern&quot;&gt;依赖注入模式&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;#communication-programming&quot;&gt;C#通信编程详解&lt;/a&gt;
&lt;ul&gt;
&lt;li&gt;&lt;a href=&quot;#tcp-ip-basics&quot;&gt;TCP/IP通信基础&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;#socket-programming&quot;&gt;Socket编程&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;#tcpclient-tcplistener&quot;&gt;TcpClient与TcpListener&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;#udpclient-programming&quot;&gt;UdpClient编程&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;#http-communication&quot;&gt;HTTP通信&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;#websocket-communication&quot;&gt;WebSocket通信&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;#named-pipes&quot;&gt;命名管道通信&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;#wcf-communication&quot;&gt;WCF通信&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;#grpc-communication&quot;&gt;gRPC通信&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;#message-queues&quot;&gt;消息队列&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;#serial-communication&quot;&gt;上位机串口通信&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;#attributes-detail&quot;&gt;C#特性（Attributes）详解&lt;/a&gt;
&lt;ul&gt;
&lt;li&gt;&lt;a href=&quot;#attributes-concept&quot;&gt;特性的基本概念&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;#attributes-usage&quot;&gt;特性的定义与使用&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;#common-attributes&quot;&gt;常见内置特性&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;#custom-attributes&quot;&gt;自定义特性&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;#attributes-reflection&quot;&gt;特性的反射访问&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;#attributes-scenarios&quot;&gt;特性的应用场景&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;#reflection-detail&quot;&gt;C#反射（Reflection）详解&lt;/a&gt;
&lt;ul&gt;
&lt;li&gt;&lt;a href=&quot;#reflection-concept&quot;&gt;反射的基本概念&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;#type-class&quot;&gt;Type类型详解&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;#assembly-operations&quot;&gt;程序集（Assembly）操作&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;#type-members&quot;&gt;类型成员访问&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;#dynamic-object-creation&quot;&gt;动态创建对象&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;#method-property-access&quot;&gt;方法调用与属性访问&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;#generic-reflection&quot;&gt;泛型类型的反射&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;#reflection-performance&quot;&gt;反射的性能优化&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;#reflection-scenarios&quot;&gt;反射的应用场景&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;#async-programming&quot;&gt;C#异步编程详解&lt;/a&gt;
&lt;ul&gt;
&lt;li&gt;&lt;a href=&quot;#async-basics&quot;&gt;异步编程基础&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;#async-await&quot;&gt;async/await关键字&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;#task-types&quot;&gt;Task与Task&amp;lt;T&amp;gt;&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;#async-best-practices&quot;&gt;异步方法最佳实践&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;#async-winform&quot;&gt;异步编程与WinForm集成&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;#file-stream&quot;&gt;C#文件操作与流处理&lt;/a&gt;
&lt;ul&gt;
&lt;li&gt;&lt;a href=&quot;#file-basics&quot;&gt;文件操作基础&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;#stream-overview&quot;&gt;流（Stream）概述&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;#filestream&quot;&gt;FileStream文件流&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;#stream-reader-writer&quot;&gt;StreamReader和StreamWriter&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;#memorystream&quot;&gt;MemoryStream内存流&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;#file-winform&quot;&gt;文件操作与WinForm集成&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;#multithreading&quot;&gt;C#多线程编程详解&lt;/a&gt;&lt;/li&gt;
&lt;/ol&gt;
&lt;ul&gt;
&lt;li&gt;&lt;a href=&quot;#multithreading-basics&quot;&gt;多线程基础概念&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;#thread-class&quot;&gt;Thread类详解&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;#threadpool&quot;&gt;ThreadPool线程池&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;#thread-synchronization&quot;&gt;线程同步机制&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;#thread-safe-collections&quot;&gt;线程安全集合&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;#parallel-programming&quot;&gt;并行编程（Parallel类）&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;#task-parallel-library&quot;&gt;任务并行库（TPL）&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;#thread-communication&quot;&gt;线程间通信&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;#deadlock-race-condition&quot;&gt;死锁与竞态条件&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;#multithreading-best-practices&quot;&gt;多线程最佳实践&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;h2&gt;&amp;lt;a id=&quot;generics-detail&quot;&amp;gt;&amp;lt;/a&amp;gt;C#泛型详解&lt;/h2&gt;
&lt;p&gt;泛型是C#中一项强大的特性，它允许在编写代码时不指定具体类型，而是在使用时再指定类型参数。泛型提供了类型安全、代码重用和性能优化等好处，是现代C#开发中不可或缺的一部分。&lt;/p&gt;
&lt;h3&gt;&amp;lt;a id=&quot;generics-concept&quot;&amp;gt;&amp;lt;/a&amp;gt;泛型的基本概念&lt;/h3&gt;
&lt;p&gt;泛型的核心思想是&quot;参数化类型&quot;，即把类型作为参数传递给类、接口、方法等。通过泛型，可以创建适用于多种数据类型的可重用组件，同时保持类型安全。&lt;/p&gt;
&lt;h4&gt;泛型的语法表示&lt;/h4&gt;
&lt;p&gt;在C#中，泛型通过尖括号&lt;code&gt;&amp;lt;&amp;gt;&lt;/code&gt;和类型参数来表示。例如：&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;// 泛型类
class GenericClass&amp;lt;T&amp;gt;
{
    // 类型参数T可以在类内部作为类型使用
    private T _value;
    
    public void SetValue(T value)
    {
        _value = value;
    }
    
    public T GetValue()
    {
        return _value;
    }
}
&lt;/code&gt;&lt;/pre&gt;
&lt;h4&gt;泛型的优势&lt;/h4&gt;
&lt;ol&gt;
&lt;li&gt;&lt;strong&gt;类型安全&lt;/strong&gt;：泛型在编译时进行类型检查，避免了运行时的类型转换错误&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;代码重用&lt;/strong&gt;：一套泛型代码可以适用于多种数据类型&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;性能优化&lt;/strong&gt;：减少了装箱和拆箱操作，提高了性能&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;可读性&lt;/strong&gt;：代码更加清晰，类型意图明确&lt;/li&gt;
&lt;/ol&gt;
&lt;h3&gt;&amp;lt;a id=&quot;generics-class-interface&quot;&amp;gt;&amp;lt;/a&amp;gt;泛型类与泛型接口&lt;/h3&gt;
&lt;p&gt;泛型类和泛型接口是C#中最常用的泛型形式，它们允许在定义时指定类型参数，在实例化时提供具体类型。&lt;/p&gt;
&lt;h4&gt;泛型类&lt;/h4&gt;
&lt;p&gt;泛型类的定义与普通类类似，但需要在类名后添加类型参数：&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;// 单类型参数泛型类
public class Stack&amp;lt;T&amp;gt;
{
    private T[] _items;
    private int _count;
    
    public Stack(int capacity)
    {
        _items = new T[capacity];
        _count = 0;
    }
    
    public void Push(T item)
    {
        if (_count == _items.Length)
        {
            Array.Resize(ref _items, _items.Length * 2);
        }
        _items[_count++] = item;
    }
    
    public T Pop()
    {
        if (_count == 0)
        {
            throw new InvalidOperationException(&quot;Stack is empty&quot;);
        }
        return _items[--_count];
    }
    
    public bool IsEmpty =&amp;gt; _count == 0;
}

// 使用泛型类
var intStack = new Stack&amp;lt;int&amp;gt;(5);
intStack.Push(10);
intStack.Push(20);
int value = intStack.Pop(); // 20

var stringStack = new Stack&amp;lt;string&amp;gt;(5);
stringStack.Push(&quot;Hello&quot;);
stringStack.Push(&quot;World&quot;);
string text = stringStack.Pop(); // &quot;World&quot;
&lt;/code&gt;&lt;/pre&gt;
&lt;h4&gt;泛型接口&lt;/h4&gt;
&lt;p&gt;泛型接口的定义与泛型类类似，在接口名后添加类型参数：&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;// 泛型接口
public interface IRepository&amp;lt;T&amp;gt;
{
    T GetById(int id);
    void Add(T entity);
    void Update(T entity);
    void Delete(T entity);
    IEnumerable&amp;lt;T&amp;gt; GetAll();
}

// 实现泛型接口
public class UserRepository : IRepository&amp;lt;User&amp;gt;
{
    private List&amp;lt;User&amp;gt; _users = new List&amp;lt;User&amp;gt;();
    
    public User GetById(int id)
    {
        return _users.FirstOrDefault(u =&amp;gt; u.Id == id);
    }
    
    public void Add(User entity)
    {
        _users.Add(entity);
    }
    
    public void Update(User entity)
    {
        var user = GetById(entity.Id);
        if (user != null)
        {
            user.Name = entity.Name;
            user.Email = entity.Email;
        }
    }
    
    public void Delete(User entity)
    {
        _users.Remove(entity);
    }
    
    public IEnumerable&amp;lt;User&amp;gt; GetAll()
    {
        return _users;
    }
}

public class User
{
    public int Id { get; set; }
    public string Name { get; set; }
    public string Email { get; set; }
}
&lt;/code&gt;&lt;/pre&gt;
&lt;h3&gt;&amp;lt;a id=&quot;generics-method&quot;&amp;gt;&amp;lt;/a&amp;gt;泛型方法&lt;/h3&gt;
&lt;p&gt;泛型方法是在方法级别定义类型参数的方法，它可以是泛型类的成员，也可以是普通类的成员。&lt;/p&gt;
&lt;h4&gt;泛型方法的定义与使用&lt;/h4&gt;
&lt;pre&gt;&lt;code&gt;public class GenericMethods
{
    // 泛型方法
    public T Max&amp;lt;T&amp;gt;(T a, T b) where T : IComparable&amp;lt;T&amp;gt;
    {
        return a.CompareTo(b) &amp;gt; 0 ? a : b;
    }
    
    // 静态泛型方法
    public static void Swap&amp;lt;T&amp;gt;(ref T a, ref T b)
    {
        T temp = a;
        a = b;
        b = temp;
    }
    
    // 泛型方法与类型推断
    public void Print&amp;lt;T&amp;gt;(T value)
    {
        Console.WriteLine($&quot;Value: {value}, Type: {typeof(T).Name}&quot;);
    }
}

// 使用泛型方法
var genericMethods = new GenericMethods();

// 显式指定类型参数
int maxInt = genericMethods.Max&amp;lt;int&amp;gt;(10, 20); // 20
string maxString = genericMethods.Max&amp;lt;string&amp;gt;(&quot;apple&quot;, &quot;banana&quot;); // &quot;banana&quot;

// 类型推断
int a = 10, b = 20;
GenericMethods.Swap(ref a, ref b); // a=20, b=10

// 类型推断
genericMethods.Print(10); // Value: 10, Type: Int32
genericMethods.Print(&quot;Hello&quot;); // Value: Hello, Type: String
genericMethods.Print(3.14); // Value: 3.14, Type: Double
&lt;/code&gt;&lt;/pre&gt;
&lt;h4&gt;泛型方法的类型推断&lt;/h4&gt;
&lt;p&gt;C#编译器可以根据方法参数的类型自动推断泛型方法的类型参数，这使得代码更加简洁。例如：&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;// 编译器推断T为int
genericMethods.Print(10);

// 编译器推断T为string
genericMethods.Print(&quot;Hello&quot;);
&lt;/code&gt;&lt;/pre&gt;
&lt;h3&gt;&amp;lt;a id=&quot;generics-constraints&quot;&amp;gt;&amp;lt;/a&amp;gt;泛型约束&lt;/h3&gt;
&lt;p&gt;泛型约束允许限制泛型类型参数可以接受的类型，增加了泛型代码的安全性和功能性。C#提供了多种类型的泛型约束：&lt;/p&gt;
&lt;h4&gt;约束类型&lt;/h4&gt;
&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;约束&lt;/th&gt;
&lt;th&gt;说明&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;where T : struct&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;T必须是值类型&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;where T : class&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;T必须是引用类型&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;where T : new()&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;T必须有一个无参数的公共构造函数&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;where T : &amp;lt;base class name&amp;gt;&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;T必须是指定基类或派生自指定基类&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;where T : &amp;lt;interface name&amp;gt;&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;T必须实现指定接口&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;where T : U&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;T必须是另一个类型参数U或派生自U&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;
&lt;h4&gt;约束的使用&lt;/h4&gt;
&lt;pre&gt;&lt;code&gt;// 多个约束，用逗号分隔
public class GenericConstraintExample&amp;lt;T, U&amp;gt;
    where T : class, IComparable&amp;lt;T&amp;gt;, new()
    where U : struct
{
    public T CreateInstance()
    {
        // 使用new()约束创建实例
        return new T();
    }
    
    public int Compare(T a, T b)
    {
        // 使用IComparable&amp;lt;T&amp;gt;约束调用CompareTo方法
        return a.CompareTo(b);
    }
    
    public void Process(U value)
    {
        // U是值类型
        Console.WriteLine($&quot;Processing value: {value}&quot;);
    }
}

// 实现IComparable接口的类
public class Person : IComparable&amp;lt;Person&amp;gt;
{
    public string Name { get; set; }
    public int Age { get; set; }
    
    public int CompareTo(Person other)
    {
        if (other == null) return 1;
        return Age.CompareTo(other.Age);
    }
}

// 使用带约束的泛型类
var example = new GenericConstraintExample&amp;lt;Person, int&amp;gt;();
Person person = example.CreateInstance();
person.Name = &quot;Alice&quot;;
person.Age = 30;

example.Process(42); // Processing value: 42
&lt;/code&gt;&lt;/pre&gt;
&lt;h3&gt;&amp;lt;a id=&quot;generics-delegate&quot;&amp;gt;&amp;lt;/a&amp;gt;泛型委托&lt;/h3&gt;
&lt;p&gt;泛型委托允许定义可以处理不同类型参数的委托，提高了委托的灵活性和重用性。&lt;/p&gt;
&lt;h4&gt;泛型委托的定义与使用&lt;/h4&gt;
&lt;pre&gt;&lt;code&gt;// 泛型委托定义
public delegate TResult Func&amp;lt;T, TResult&amp;gt;(T arg);
public delegate void Action&amp;lt;T&amp;gt;(T arg);
public delegate bool Predicate&amp;lt;T&amp;gt;(T obj);

// 自定义泛型委托
public delegate T Output&amp;lt;T, U, V&amp;gt;(U input1, V input2);

// 使用泛型委托
Func&amp;lt;int, int&amp;gt; square = x =&amp;gt; x * x;
int result = square(5); // 25

Action&amp;lt;string&amp;gt; print = s =&amp;gt; Console.WriteLine(s);
print(&quot;Hello World&quot;); // 输出: Hello World

Predicate&amp;lt;int&amp;gt; isEven = x =&amp;gt; x % 2 == 0;
bool even = isEven(4); // true

// 使用自定义泛型委托
Output&amp;lt;int, int, int&amp;gt; add = (a, b) =&amp;gt; a + b;
int sum = add(3, 5); // 8
&lt;/code&gt;&lt;/pre&gt;
&lt;h4&gt;泛型委托与事件&lt;/h4&gt;
&lt;p&gt;泛型委托也可以用于定义事件：&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;// 泛型事件参数
public class GenericEventArgs&amp;lt;T&amp;gt; : EventArgs
{
    public T Data { get; }
    
    public GenericEventArgs(T data)
    {
        Data = data;
    }
}

// 使用泛型委托的事件
public class Publisher&amp;lt;T&amp;gt;
{
    // 定义泛型事件
    public event EventHandler&amp;lt;GenericEventArgs&amp;lt;T&amp;gt;&amp;gt; GenericEvent;
    
    protected virtual void OnGenericEvent(T data)
    {
        GenericEvent?.Invoke(this, new GenericEventArgs&amp;lt;T&amp;gt;(data));
    }
    
    public void Publish(T data)
    {
        Console.WriteLine($&quot;Publishing: {data}&quot;);
        OnGenericEvent(data);
    }
}

// 订阅泛型事件
public class Subscriber
{
    public void Subscribe&amp;lt;T&amp;gt;(Publisher&amp;lt;T&amp;gt; publisher)
    {
        publisher.GenericEvent += (sender, e) =&amp;gt; 
            Console.WriteLine($&quot;Received: {e.Data}, Type: {typeof(T).Name}&quot;);
    }
}

// 使用泛型事件
var publisher = new Publisher&amp;lt;int&amp;gt;();
var subscriber = new Subscriber();
subscriber.Subscribe(publisher);
publisher.Publish(42); // 输出: Publishing: 42  Received: 42, Type: Int32

var stringPublisher = new Publisher&amp;lt;string&amp;gt;();
subscriber.Subscribe(stringPublisher);
stringPublisher.Publish(&quot;Hello&quot;); // 输出: Publishing: Hello  Received: Hello, Type: String
&lt;/code&gt;&lt;/pre&gt;
&lt;h3&gt;&amp;lt;a id=&quot;generics-variance&quot;&amp;gt;&amp;lt;/a&amp;gt;协变与逆变&lt;/h3&gt;
&lt;p&gt;协变和逆变是C# 4.0引入的特性，它们允许在一定条件下将泛型类型参数的派生类型赋值给基类型，或基类型赋值给派生类型。&lt;/p&gt;
&lt;h4&gt;协变（Covariance）&lt;/h4&gt;
&lt;p&gt;协变使用&lt;code&gt;out&lt;/code&gt;关键字标记类型参数，表示该类型参数只能作为方法的返回值或只读属性使用。协变允许将派生类型的泛型实例赋值给基类型的泛型变量。&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;// 协变接口定义
public interface ICovariant&amp;lt;out T&amp;gt;
{
    T GetItem();
    // 以下方法会导致编译错误，因为T是协变的，不能作为参数
    // void SetItem(T item);
}

// 实现协变接口
public class CovariantImplementation&amp;lt;T&amp;gt; : ICovariant&amp;lt;T&amp;gt;
{
    private T _item;
    
    public CovariantImplementation(T item)
    {
        _item = item;
    }
    
    public T GetItem()
    {
        return _item;
    }
}

// 协变的使用
ICovariant&amp;lt;string&amp;gt; stringCovariant = new CovariantImplementation&amp;lt;string&amp;gt;(&quot;Hello&quot;);
// 协变：可以将ICovariant&amp;lt;string&amp;gt;赋值给ICovariant&amp;lt;object&amp;gt;
ICovariant&amp;lt;object&amp;gt; objectCovariant = stringCovariant;
object item = objectCovariant.GetItem(); // &quot;Hello&quot;

// .NET中的协变示例
IEnumerable&amp;lt;string&amp;gt; strings = new List&amp;lt;string&amp;gt; { &quot;a&quot;, &quot;b&quot;, &quot;c&quot; };
IEnumerable&amp;lt;object&amp;gt; objects = strings; // 协变
&lt;/code&gt;&lt;/pre&gt;
&lt;h4&gt;逆变（Contravariance）&lt;/h4&gt;
&lt;p&gt;逆变使用&lt;code&gt;in&lt;/code&gt;关键字标记类型参数，表示该类型参数只能作为方法的参数使用。逆变允许将基类型的泛型实例赋值给派生类型的泛型变量。&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;// 逆变接口定义
public interface IContravariant&amp;lt;in T&amp;gt;
{
    void SetItem(T item);
    // 以下方法会导致编译错误，因为T是逆变的，不能作为返回值
    // T GetItem();
}

// 实现逆变接口
public class ContravariantImplementation&amp;lt;T&amp;gt; : IContravariant&amp;lt;T&amp;gt;
{
    public void SetItem(T item)
    {
        Console.WriteLine($&quot;Set item: {item}&quot;);
    }
}

// 逆变的使用
IContravariant&amp;lt;object&amp;gt; objectContravariant = new ContravariantImplementation&amp;lt;object&amp;gt;();
// 逆变：可以将IContravariant&amp;lt;object&amp;gt;赋值给IContravariant&amp;lt;string&amp;gt;
IContravariant&amp;lt;string&amp;gt; stringContravariant = objectContravariant;
stringContravariant.SetItem(&quot;Hello&quot;); // Set item: Hello

// .NET中的逆变示例
Action&amp;lt;object&amp;gt; objectAction = o =&amp;gt; Console.WriteLine(o);
Action&amp;lt;string&amp;gt; stringAction = objectAction; // 逆变
stringAction(&quot;Hello&quot;); // Hello
&lt;/code&gt;&lt;/pre&gt;
&lt;h4&gt;协变与逆变的总结&lt;/h4&gt;
&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;特性&lt;/th&gt;
&lt;th&gt;关键字&lt;/th&gt;
&lt;th&gt;适用场景&lt;/th&gt;
&lt;th&gt;示例&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;协变&lt;/td&gt;
&lt;td&gt;&lt;code&gt;out&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;返回类型&lt;/td&gt;
&lt;td&gt;&lt;code&gt;IEnumerable&amp;lt;out T&amp;gt;&lt;/code&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;逆变&lt;/td&gt;
&lt;td&gt;&lt;code&gt;in&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;参数类型&lt;/td&gt;
&lt;td&gt;&lt;code&gt;Action&amp;lt;in T&amp;gt;&lt;/code&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;
&lt;h3&gt;&amp;lt;a id=&quot;generics-performance&quot;&amp;gt;&amp;lt;/a&amp;gt;泛型的性能优势&lt;/h3&gt;
&lt;p&gt;泛型提供了显著的性能优势，主要体现在以下几个方面：&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;&lt;strong&gt;减少装箱和拆箱操作&lt;/strong&gt;：
&lt;ul&gt;
&lt;li&gt;非泛型集合（如&lt;code&gt;ArrayList&lt;/code&gt;）存储值类型时需要装箱，取出时需要拆箱&lt;/li&gt;
&lt;li&gt;泛型集合（如&lt;code&gt;List&amp;lt;T&amp;gt;&lt;/code&gt;）直接存储值类型，避免了装箱和拆箱操作&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;/ol&gt;
&lt;pre&gt;&lt;code&gt;// 使用ArrayList（需要装箱和拆箱）
ArrayList arrayList = new ArrayList();
arrayList.Add(10); // 装箱：int -&amp;gt; object
int value1 = (int)arrayList[0]; // 拆箱：object -&amp;gt; int

// 使用List&amp;lt;T&amp;gt;（避免装箱和拆箱）
List&amp;lt;int&amp;gt; list = new List&amp;lt;int&amp;gt;();
list.Add(10); // 直接存储int
int value2 = list[0]; // 直接获取int
&lt;/code&gt;&lt;/pre&gt;
&lt;ol&gt;
&lt;li&gt;
&lt;p&gt;&lt;strong&gt;编译时类型检查&lt;/strong&gt;：&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;泛型在编译时进行类型检查，避免了运行时类型转换错误&lt;/li&gt;
&lt;li&gt;非泛型集合需要在运行时进行类型检查和转换&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;&lt;strong&gt;代码重用&lt;/strong&gt;：&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;一套泛型代码可以适用于多种数据类型&lt;/li&gt;
&lt;li&gt;减少了重复代码，提高了代码维护性&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;/ol&gt;
&lt;h3&gt;&amp;lt;a id=&quot;generics-scenarios&quot;&amp;gt;&amp;lt;/a&amp;gt;泛型的应用场景&lt;/h3&gt;
&lt;p&gt;泛型在C#开发中有广泛的应用场景，以下是一些常见的例子：&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;
&lt;p&gt;&lt;strong&gt;集合类&lt;/strong&gt;：&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;.NET Framework提供的大部分集合类都是泛型的，如&lt;code&gt;List&amp;lt;T&amp;gt;&lt;/code&gt;、&lt;code&gt;Dictionary&amp;lt;TKey, TValue&amp;gt;&lt;/code&gt;、&lt;code&gt;HashSet&amp;lt;T&amp;gt;&lt;/code&gt;等&lt;/li&gt;
&lt;li&gt;这些泛型集合提供了类型安全和性能优势&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;&lt;strong&gt;数据访问层&lt;/strong&gt;：&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;使用泛型创建通用的数据访问组件，如&lt;code&gt;Repository&amp;lt;T&amp;gt;&lt;/code&gt;模式&lt;/li&gt;
&lt;li&gt;一套数据访问代码可以适用于多种实体类型&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;&lt;strong&gt;工具类和扩展方法&lt;/strong&gt;：&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;使用泛型创建通用的工具类和扩展方法&lt;/li&gt;
&lt;li&gt;提高代码的重用性和灵活性&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;&lt;strong&gt;委托和事件&lt;/strong&gt;：&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;使用泛型委托和事件创建类型安全的回调机制&lt;/li&gt;
&lt;li&gt;如&lt;code&gt;Func&amp;lt;T, TResult&amp;gt;&lt;/code&gt;、&lt;code&gt;Action&amp;lt;T&amp;gt;&lt;/code&gt;等&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;&lt;strong&gt;算法实现&lt;/strong&gt;：&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;使用泛型实现通用算法，如排序、搜索等&lt;/li&gt;
&lt;li&gt;一套算法代码可以适用于多种数据类型&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;/ol&gt;
&lt;h2&gt;总结&lt;/h2&gt;
&lt;p&gt;泛型是C#中一项强大的特性，它提供了类型安全、代码重用和性能优化等好处。通过泛型类、泛型接口、泛型方法和泛型委托，可以创建适用于多种数据类型的可重用组件。泛型约束、协变和逆变等高级特性进一步增强了泛型的灵活性和功能性。&lt;/p&gt;
&lt;h2&gt;&amp;lt;a id=&quot;delegates-lambda-advanced&quot;&amp;gt;&amp;lt;/a&amp;gt;C#委托与Lambda表达式进阶&lt;/h2&gt;
&lt;p&gt;委托和Lambda表达式是C#中支持函数式编程的核心特性。它们允许将方法作为参数传递、创建匿名函数以及实现回调机制。在现代C#开发中，这些特性被广泛应用于LINQ、异步编程和事件处理等场景。&lt;/p&gt;
&lt;h3&gt;&amp;lt;a id=&quot;anonymous-function-lambda&quot;&amp;gt;&amp;lt;/a&amp;gt;匿名函数与Lambda表达式&lt;/h3&gt;
&lt;p&gt;匿名函数是没有名称的函数，它们可以作为参数传递给其他方法或存储在变量中。在C#中，匿名函数可以通过两种方式实现：传统的&lt;code&gt;delegate&lt;/code&gt;语法和更简洁的Lambda表达式语法。&lt;/p&gt;
&lt;h4&gt;传统匿名函数（Delegate语法）&lt;/h4&gt;
&lt;p&gt;传统匿名函数使用&lt;code&gt;delegate&lt;/code&gt;关键字定义，语法相对冗长但功能完整。&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;// 基本使用
Func&amp;lt;int, int, int&amp;gt; add = delegate(int a, int b)
{
    return a + b;
};
int result = add(3, 5); // 结果为8

// 作为参数传递
List&amp;lt;int&amp;gt; numbers = new List&amp;lt;int&amp;gt; { 1, 2, 3, 4, 5 };
List&amp;lt;int&amp;gt; evenNumbers = numbers.FindAll(delegate(int x) { return x % 2 == 0; });

// 闭包特性
int factor = 2;
Func&amp;lt;int, int&amp;gt; multiplyByFactor = delegate(int x) { return x * factor; };
int result2 = multiplyByFactor(5); // 结果为10，因为factor=2
factor = 3;
int result3 = multiplyByFactor(5); // 结果为15，因为factor=3（闭包捕获的是变量引用）
&lt;/code&gt;&lt;/pre&gt;
&lt;h4&gt;Lambda表达式&lt;/h4&gt;
&lt;p&gt;Lambda表达式提供了更简洁的语法来定义匿名函数，它使用&lt;code&gt;=&amp;gt;&lt;/code&gt;（读作&quot;goes to&quot;）操作符分隔参数列表和函数体。&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;// 基本语法
// (参数列表) =&amp;gt; 表达式
// 或
// (参数列表) =&amp;gt; { 语句块 }

// 单行Lambda表达式
Func&amp;lt;int, int, int&amp;gt; add = (a, b) =&amp;gt; a + b;
Func&amp;lt;string, int&amp;gt; getLength = s =&amp;gt; s.Length;
Action&amp;lt;string&amp;gt; print = s =&amp;gt; Console.WriteLine(s);

// 多行Lambda表达式
Func&amp;lt;int, int&amp;gt; calculateSquare = x =&amp;gt;
{
    Console.WriteLine($&quot;计算 {x} 的平方&quot;);
    return x * x;
};

// 各种简写形式
Func&amp;lt;int, bool&amp;gt; isEven = x =&amp;gt; x % 2 == 0; // 单个参数可省略括号
Action printHello = () =&amp;gt; Console.WriteLine(&quot;Hello&quot;); // 无参数
Func&amp;lt;int, int, bool&amp;gt; isGreater = (a, b) =&amp;gt; a &amp;gt; b; // 多个参数
&lt;/code&gt;&lt;/pre&gt;
&lt;h4&gt;匿名函数与Lambda表达式的区别&lt;/h4&gt;
&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;特性&lt;/th&gt;
&lt;th&gt;传统匿名函数（delegate）&lt;/th&gt;
&lt;th&gt;Lambda表达式&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;语法&lt;/td&gt;
&lt;td&gt;更冗长&lt;/td&gt;
&lt;td&gt;更简洁&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;类型推断&lt;/td&gt;
&lt;td&gt;支持有限&lt;/td&gt;
&lt;td&gt;更强大&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;表达式树&lt;/td&gt;
&lt;td&gt;不支持&lt;/td&gt;
&lt;td&gt;支持（使用Expression&amp;lt;TDelegate&amp;gt;）&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;目标类型&lt;/td&gt;
&lt;td&gt;必须是委托类型&lt;/td&gt;
&lt;td&gt;可以是委托类型或表达式树类型&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;可读性&lt;/td&gt;
&lt;td&gt;较低&lt;/td&gt;
&lt;td&gt;较高&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;
&lt;h3&gt;&amp;lt;a id=&quot;func-delegate-advanced&quot;&amp;gt;&amp;lt;/a&amp;gt;Func委托详解&lt;/h3&gt;
&lt;p&gt;&lt;code&gt;Func&lt;/code&gt;是C#中预定义的泛型委托，用于表示具有返回值的方法。&lt;code&gt;Func&lt;/code&gt;委托可以接受0到16个输入参数，并始终有一个返回类型（最后一个类型参数）。&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;// Func委托示例

// 无参数，返回string
Func&amp;lt;string&amp;gt; getGreeting = () =&amp;gt; &quot;Hello, World!&quot;;
string greeting = getGreeting();

// 一个参数，返回bool
Func&amp;lt;int, bool&amp;gt; isEven = (x) =&amp;gt; x % 2 == 0;
bool result1 = isEven(4); // 结果为true

// 两个参数，返回int
Func&amp;lt;int, int, int&amp;gt; subtract = (a, b) =&amp;gt; a - b;
int result2 = subtract(10, 3); // 结果为7

// 三个参数，返回string
Func&amp;lt;int, int, int, string&amp;gt; formatSum = (a, b, c) =&amp;gt; $&quot;{a} + {b} + {c} = {a + b + c}&quot;;
string message = formatSum(1, 2, 3); // 结果为&quot;1 + 2 + 3 = 6&quot;
&lt;/code&gt;&lt;/pre&gt;
&lt;h4&gt;Func的高级应用&lt;/h4&gt;
&lt;ol&gt;
&lt;li&gt;&lt;strong&gt;复杂数据转换&lt;/strong&gt;&lt;/li&gt;
&lt;/ol&gt;
&lt;pre&gt;&lt;code&gt;// 将Person对象转换为匿名类型
Func&amp;lt;Person, object&amp;gt; toAnonymousType = p =&amp;gt; new { p.Name, AgeGroup = p.Age &amp;gt;= 18 ? &quot;Adult&quot; : &quot;Minor&quot; };

// 组合多个Func
Func&amp;lt;int, int&amp;gt; doubleIt = x =&amp;gt; x * 2;
Func&amp;lt;int, int&amp;gt; addFive = x =&amp;gt; x + 5;
Func&amp;lt;int, int&amp;gt; processNumber = x =&amp;gt; addFive(doubleIt(x)); // 先乘2再加5
int result = processNumber(10); // 结果为25
&lt;/code&gt;&lt;/pre&gt;
&lt;ol&gt;
&lt;li&gt;&lt;strong&gt;作为方法返回值&lt;/strong&gt;&lt;/li&gt;
&lt;/ol&gt;
&lt;pre&gt;&lt;code&gt;// 根据条件返回不同的处理函数
Func&amp;lt;int, int&amp;gt; GetProcessor(bool useAddition)
{
    if (useAddition)
        return x =&amp;gt; x + 10;
    else
        return x =&amp;gt; x * 10;
}

// 使用
var processor = GetProcessor(true);
int result = processor(5); // 结果为15
&lt;/code&gt;&lt;/pre&gt;
&lt;h3&gt;&amp;lt;a id=&quot;action-delegate-advanced&quot;&amp;gt;&amp;lt;/a&amp;gt;Action委托详解&lt;/h3&gt;
&lt;p&gt;&lt;code&gt;Action&lt;/code&gt;是C#中预定义的泛型委托，用于表示没有返回值（void）的方法。&lt;code&gt;Action&lt;/code&gt;委托可以接受0到16个输入参数。&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;// Action委托示例

// 无参数
Action printMessage = () =&amp;gt; Console.WriteLine(&quot;Hello from Action!&quot;);
printMessage();

// 一个参数
Action&amp;lt;string&amp;gt; greet = (name) =&amp;gt; Console.WriteLine($&quot;Hello, {name}!&quot;);
greet(&quot;Alice&quot;);

// 两个参数
Action&amp;lt;string, int&amp;gt; displayInfo = (name, age) =&amp;gt; Console.WriteLine($&quot;Name: {name}, Age: {age}&quot;);
displayInfo(&quot;Bob&quot;, 30);

// 三个参数
Action&amp;lt;int, int, int&amp;gt; printSum = (a, b, c) =&amp;gt; Console.WriteLine($&quot;Sum: {a + b + c}&quot;);
printSum(1, 2, 3);
&lt;/code&gt;&lt;/pre&gt;
&lt;h4&gt;Action的高级应用&lt;/h4&gt;
&lt;ol&gt;
&lt;li&gt;&lt;strong&gt;批量操作&lt;/strong&gt;&lt;/li&gt;
&lt;/ol&gt;
&lt;pre&gt;&lt;code&gt;// 对集合中的每个元素执行相同操作
void ProcessCollection&amp;lt;T&amp;gt;(IEnumerable&amp;lt;T&amp;gt; collection, Action&amp;lt;T&amp;gt; action)
{
    foreach (var item in collection)
    {
        action(item);
    }
}

// 使用
List&amp;lt;string&amp;gt; names = new List&amp;lt;string&amp;gt; { &quot;Alice&quot;, &quot;Bob&quot;, &quot;Charlie&quot; };
ProcessCollection(names, name =&amp;gt; Console.WriteLine(name.ToUpper()));
&lt;/code&gt;&lt;/pre&gt;
&lt;ol&gt;
&lt;li&gt;&lt;strong&gt;复合操作&lt;/strong&gt;&lt;/li&gt;
&lt;/ol&gt;
&lt;pre&gt;&lt;code&gt;// 创建一个包含多个操作的复合Action
Action&amp;lt;string&amp;gt; log = s =&amp;gt; Console.WriteLine($&quot;[{DateTime.Now}] {s}&quot;);
Action&amp;lt;string&amp;gt; validate = s =&amp;gt; 
{
    if (string.IsNullOrWhiteSpace(s))
        throw new ArgumentException(&quot;输入不能为空&quot;);
};
Action&amp;lt;string&amp;gt; process = s =&amp;gt; 
{
    log($&quot;开始处理: {s}&quot;);
    validate(s);
    log($&quot;处理完成: {s}&quot;);
};

// 使用
process(&quot;有效输入&quot;);
&lt;/code&gt;&lt;/pre&gt;
&lt;h3&gt;&amp;lt;a id=&quot;predicate-delegate-advanced&quot;&amp;gt;&amp;lt;/a&amp;gt;Predicate委托详解&lt;/h3&gt;
&lt;p&gt;&lt;code&gt;Predicate&lt;/code&gt;是C#中预定义的泛型委托，专门用于表示返回bool值的方法，通常用于条件判断。&lt;code&gt;Predicate&lt;/code&gt;委托只接受一个输入参数，并始终返回bool类型。&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;// Predicate委托示例

// 检查整数是否为正数
Predicate&amp;lt;int&amp;gt; isPositive = (x) =&amp;gt; x &amp;gt; 0;
bool result1 = isPositive(5);  // 结果为true
bool result2 = isPositive(-3); // 结果为false

// 检查字符串是否为空或 null
Predicate&amp;lt;string&amp;gt; isNullOrEmpty = (str) =&amp;gt; string.IsNullOrEmpty(str);
bool result3 = isNullOrEmpty(&quot;&quot;);     // 结果为true
bool result4 = isNullOrEmpty(&quot;Hello&quot;); // 结果为false

// 检查列表是否包含元素
Predicate&amp;lt;List&amp;lt;int&amp;gt;&amp;gt; hasElements = (list) =&amp;gt; list != null &amp;amp;&amp;amp; list.Count &amp;gt; 0;
List&amp;lt;int&amp;gt; numbers = new List&amp;lt;int&amp;gt; { 1, 2, 3 };
bool result5 = hasElements(numbers); // 结果为true
&lt;/code&gt;&lt;/pre&gt;
&lt;h4&gt;Predicate的高级应用&lt;/h4&gt;
&lt;ol&gt;
&lt;li&gt;&lt;strong&gt;自定义对象筛选&lt;/strong&gt;&lt;/li&gt;
&lt;/ol&gt;
&lt;pre&gt;&lt;code&gt;public class Product
{
    public string Name { get; set; }
    public decimal Price { get; set; }
    public bool IsInStock { get; set; }
}

// 创建产品列表
List&amp;lt;Product&amp;gt; products = new List&amp;lt;Product&amp;gt;
{
    new Product { Name = &quot;笔记本电脑&quot;, Price = 5999.99m, IsInStock = true },
    new Product { Name = &quot;智能手机&quot;, Price = 3999.99m, IsInStock = true },
    new Product { Name = &quot;平板电脑&quot;, Price = 2999.99m, IsInStock = false },
    new Product { Name = &quot;无线耳机&quot;, Price = 999.99m, IsInStock = true }
};

// 定义复杂筛选条件
Predicate&amp;lt;Product&amp;gt; affordableInStockProducts = p =&amp;gt; 
    p.IsInStock &amp;amp;&amp;amp; p.Price &amp;lt; 3000;

// 使用Predicate筛选
List&amp;lt;Product&amp;gt; filteredProducts = products.FindAll(affordableInStockProducts);
&lt;/code&gt;&lt;/pre&gt;
&lt;ol&gt;
&lt;li&gt;&lt;strong&gt;链式条件&lt;/strong&gt;&lt;/li&gt;
&lt;/ol&gt;
&lt;pre&gt;&lt;code&gt;// 组合多个条件
Predicate&amp;lt;int&amp;gt; isEven = x =&amp;gt; x % 2 == 0;
Predicate&amp;lt;int&amp;gt; isGreaterThan5 = x =&amp;gt; x &amp;gt; 5;
Predicate&amp;lt;int&amp;gt; isEvenAndGreaterThan5 = x =&amp;gt; isEven(x) &amp;amp;&amp;amp; isGreaterThan5(x);

// 使用
List&amp;lt;int&amp;gt; numbers = new List&amp;lt;int&amp;gt; { 1, 2, 3, 4, 5, 6, 7, 8, 9, 10 };
List&amp;lt;int&amp;gt; filtered = numbers.FindAll(isEvenAndGreaterThan5);
&lt;/code&gt;&lt;/pre&gt;
&lt;h3&gt;&amp;lt;a id=&quot;delegate-chain-event&quot;&amp;gt;&amp;lt;/a&amp;gt;委托链与事件&lt;/h3&gt;
&lt;p&gt;委托链允许将多个委托实例组合在一起，形成一个委托列表。当调用这个委托链时，列表中的所有委托都会被依次调用。事件是基于委托的一种特殊机制，它允许对象在发生特定事件时通知其他对象。&lt;/p&gt;
&lt;h4&gt;委托链&lt;/h4&gt;
&lt;pre&gt;&lt;code&gt;// 定义委托类型
delegate void NumberHandler(int number);

// 创建委托实例
NumberHandler printNumber = n =&amp;gt; Console.WriteLine($&quot;数字: {n}&quot;);
NumberHandler doubleNumber = n =&amp;gt; Console.WriteLine($&quot;两倍: {n * 2}&quot;);
NumberHandler squareNumber = n =&amp;gt; Console.WriteLine($&quot;平方: {n * n}&quot;);

// 组合委托链
NumberHandler chain = printNumber + doubleNumber + squareNumber;

// 调用委托链
chain(5);
// 输出:
// 数字: 5
// 两倍: 10
// 平方: 25

// 移除委托
chain -= doubleNumber;
chain(5);
// 输出:
// 数字: 5
// 平方: 25
&lt;/code&gt;&lt;/pre&gt;
&lt;h5&gt;委托链的高级特性&lt;/h5&gt;
&lt;ol&gt;
&lt;li&gt;&lt;strong&gt;空委托处理&lt;/strong&gt;&lt;/li&gt;
&lt;/ol&gt;
&lt;pre&gt;&lt;code&gt;// 安全调用委托链
NumberHandler safeChain = null;
// 直接调用空委托会引发NullReferenceException
try
{
    safeChain(5);
} catch (NullReferenceException)
{
    Console.WriteLine(&quot;直接调用空委托引发异常&quot;);
}

// 使用?.Invoke()安全调用
safeChain?.Invoke(5); // 不会引发异常

// 初始化时提供一个空委托，避免空检查
safeChain = delegate { };
// 或者使用Lambda表达式
safeChain = _ =&amp;gt; { };
safeChain(5); // 安全调用
&lt;/code&gt;&lt;/pre&gt;
&lt;ol&gt;
&lt;li&gt;&lt;strong&gt;多播委托的返回值&lt;/strong&gt;&lt;/li&gt;
&lt;/ol&gt;
&lt;pre&gt;&lt;code&gt;// 委托链返回值
Func&amp;lt;int, int&amp;gt; func1 = x =&amp;gt; x + 1;
Func&amp;lt;int, int&amp;gt; func2 = x =&amp;gt; x * 2;
Func&amp;lt;int, int&amp;gt; funcChain = func1 + func2;

// 调用委托链，只会返回最后一个委托的结果
int result = funcChain(5); // 结果为10，即(5 * 2)
Console.WriteLine(result);
&lt;/code&gt;&lt;/pre&gt;
&lt;ol&gt;
&lt;li&gt;&lt;strong&gt;委托链的执行顺序&lt;/strong&gt;&lt;/li&gt;
&lt;/ol&gt;
&lt;pre&gt;&lt;code&gt;// 委托链的执行顺序是添加顺序
Action step1 = () =&amp;gt; Console.WriteLine(&quot;步骤1&quot;);
Action step2 = () =&amp;gt; Console.WriteLine(&quot;步骤2&quot;);
Action step3 = () =&amp;gt; Console.WriteLine(&quot;步骤3&quot;);

Action workflow = step1 + step2 + step3;
workflow(); // 按顺序执行步骤1、步骤2、步骤3

// 更改执行顺序
workflow = step3 + step1 + step2;
workflow(); // 按顺序执行步骤3、步骤1、步骤2
&lt;/code&gt;&lt;/pre&gt;
&lt;h4&gt;事件&lt;/h4&gt;
&lt;p&gt;事件使用&lt;code&gt;event&lt;/code&gt;关键字声明，它是对委托的封装，提供了更安全的访问控制。&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;// 定义事件参数类
public class OrderEventArgs : EventArgs
{
    public int OrderId { get; }
    public decimal TotalAmount { get; }
    public OrderEventArgs(int orderId, decimal totalAmount)
    {
        OrderId = orderId;
        TotalAmount = totalAmount;
    }
}

// 定义发布者类
public class OrderService
{
    // 声明事件
    public event EventHandler&amp;lt;OrderEventArgs&amp;gt; OrderPlaced;
    public event EventHandler&amp;lt;OrderEventArgs&amp;gt; OrderShipped;

    // 触发事件的方法
    protected virtual void OnOrderPlaced(OrderEventArgs e)
    {
        // 检查是否有订阅者
        OrderPlaced?.Invoke(this, e);
    }

    protected virtual void OnOrderShipped(OrderEventArgs e)
    {
        OrderShipped?.Invoke(this, e);
    }

    // 业务方法
    public void PlaceOrder(int orderId, decimal amount)
    {
        Console.WriteLine($&quot;订单 {orderId} 已创建&quot;);
        // 触发事件
        OnOrderPlaced(new OrderEventArgs(orderId, amount));
    }

    public void ShipOrder(int orderId, decimal amount)
    {
        Console.WriteLine($&quot;订单 {orderId} 已发货&quot;);
        // 触发事件
        OnOrderShipped(new OrderEventArgs(orderId, amount));
    }
}

// 定义订阅者类
public class EmailService
{
    public void HandleOrderPlaced(object sender, OrderEventArgs e)
    {
        Console.WriteLine($&quot;发送确认邮件: 订单 {e.OrderId} (金额: {e.TotalAmount})&quot;);
    }

    public void HandleOrderShipped(object sender, OrderEventArgs e)
    {
        Console.WriteLine($&quot;发送发货邮件: 订单 {e.OrderId} (金额: {e.TotalAmount})&quot;);
    }
}

// 使用示例
var orderService = new OrderService();
var emailService = new EmailService();

// 订阅事件
orderService.OrderPlaced += emailService.HandleOrderPlaced;
orderService.OrderShipped += emailService.HandleOrderShipped;

// 触发事件
orderService.PlaceOrder(1001, 99.99m);
orderService.ShipOrder(1001, 99.99m);

// 输出:
// 订单 1001 已创建
// 发送确认邮件: 订单 1001 (金额: 99.99)
// 订单 1001 已发货
// 发送发货邮件: 订单 1001 (金额: 99.99)
&lt;/code&gt;&lt;/pre&gt;
&lt;h5&gt;事件的高级用法&lt;/h5&gt;
&lt;ol&gt;
&lt;li&gt;&lt;strong&gt;自定义事件访问器&lt;/strong&gt;&lt;/li&gt;
&lt;/ol&gt;
&lt;pre&gt;&lt;code&gt;public class CustomEventPublisher
{
    // 自定义事件存储
    private EventHandler&amp;lt;EventArgs&amp;gt; _customEvent;
    
    // 自定义事件访问器
    public event EventHandler&amp;lt;EventArgs&amp;gt; CustomEvent
    {
        add
        {
            Console.WriteLine(&quot;订阅者添加了事件处理程序&quot;);
            _customEvent += value;
        }
        remove
        {
            Console.WriteLine(&quot;订阅者移除了事件处理程序&quot;);
            _customEvent -= value;
        }
    }
    
    public void RaiseEvent()
    {
        _customEvent?.Invoke(this, EventArgs.Empty);
    }
}

// 使用自定义事件访问器
var publisher = new CustomEventPublisher();
publisher.CustomEvent += (sender, e) =&amp;gt; Console.WriteLine(&quot;事件处理1&quot;);
publisher.CustomEvent += (sender, e) =&amp;gt; Console.WriteLine(&quot;事件处理2&quot;);
publisher.RaiseEvent();
publisher.CustomEvent -= (sender, e) =&amp;gt; Console.WriteLine(&quot;事件处理1&quot;); // 注意：这种方式无法移除匿名方法
&lt;/code&gt;&lt;/pre&gt;
&lt;ol&gt;
&lt;li&gt;&lt;strong&gt;静态事件&lt;/strong&gt;&lt;/li&gt;
&lt;/ol&gt;
&lt;pre&gt;&lt;code&gt;public static class GlobalEvents
{
    public static event EventHandler ApplicationStarted;
    public static event EventHandler ApplicationStopped;
    
    public static void OnApplicationStarted()
    {
        ApplicationStarted?.Invoke(null, EventArgs.Empty);
    }
    
    public static void OnApplicationStopped()
    {
        ApplicationStopped?.Invoke(null, EventArgs.Empty);
    }
}

// 使用静态事件
GlobalEvents.ApplicationStarted += (sender, e) =&amp;gt; Console.WriteLine(&quot;应用程序已启动&quot;);
GlobalEvents.OnApplicationStarted();
&lt;/code&gt;&lt;/pre&gt;
&lt;ol&gt;
&lt;li&gt;&lt;strong&gt;线程安全的事件&lt;/strong&gt;&lt;/li&gt;
&lt;/ol&gt;
&lt;pre&gt;&lt;code&gt;public class ThreadSafeEventPublisher
{
    private readonly object _eventLock = new object();
    private EventHandler&amp;lt;EventArgs&amp;gt; _threadSafeEvent;
    
    public event EventHandler&amp;lt;EventArgs&amp;gt; ThreadSafeEvent
    {
        add
        {
            lock (_eventLock)
            {
                _threadSafeEvent += value;
            }
        }
        remove
        {
            lock (_eventLock)
            {
                _threadSafeEvent -= value;
            }
        }
    }
    
    public void RaiseEvent()
    {
        // 复制到局部变量，避免多线程问题
        EventHandler&amp;lt;EventArgs&amp;gt; handler;
        lock (_eventLock)
        {
            handler = _threadSafeEvent;
        }
        handler?.Invoke(this, EventArgs.Empty);
    }
}
&lt;/code&gt;&lt;/pre&gt;
&lt;ol&gt;
&lt;li&gt;&lt;strong&gt;弱事件模式&lt;/strong&gt;&lt;/li&gt;
&lt;/ol&gt;
&lt;pre&gt;&lt;code&gt;// 弱事件模式可以避免内存泄漏，当订阅者被销毁时，会自动从事件中移除
public class WeakEventPublisher
{
    // 使用WeakReference存储订阅者
    private List&amp;lt;WeakReference&amp;lt;EventHandler&amp;lt;EventArgs&amp;gt;&amp;gt;&amp;gt; _weakHandlers = new List&amp;lt;WeakReference&amp;lt;EventHandler&amp;lt;EventArgs&amp;gt;&amp;gt;&amp;gt;();
    
    public event EventHandler&amp;lt;EventArgs&amp;gt; WeakEvent
    {
        add
        {
            _weakHandlers.Add(new WeakReference&amp;lt;EventHandler&amp;lt;EventArgs&amp;gt;&amp;gt;(value));
        }
        remove
        {
            // 实际实现中需要遍历并比较委托
        }
    }
    
    public void RaiseEvent()
    {
        var handlersToRemove = new List&amp;lt;WeakReference&amp;lt;EventHandler&amp;lt;EventArgs&amp;gt;&amp;gt;&amp;gt;();
        
        foreach (var weakHandler in _weakHandlers)
        {
            if (weakHandler.TryGetTarget(out var handler))
            {
                handler?.Invoke(this, EventArgs.Empty);
            }
            else
            {
                // 订阅者已被垃圾回收，标记移除
                handlersToRemove.Add(weakHandler);
            }
        }
        
        // 清理已被回收的订阅者
        foreach (var handlerToRemove in handlersToRemove)
        {
            _weakHandlers.Remove(handlerToRemove);
        }
    }
}
&lt;/code&gt;&lt;/pre&gt;
&lt;h5&gt;事件的最佳实践&lt;/h5&gt;
&lt;ol&gt;
&lt;li&gt;&lt;strong&gt;遵循.NET事件命名约定&lt;/strong&gt;&lt;/li&gt;
&lt;/ol&gt;
&lt;pre&gt;&lt;code&gt;// 事件命名：动词或动词短语
public event EventHandler Started;
public event EventHandler&amp;lt;DataReceivedEventArgs&amp;gt; DataReceived;

// 触发事件的方法命名：On+事件名
protected virtual void OnStarted(EventArgs e)
{
    Started?.Invoke(this, e);
}

protected virtual void OnDataReceived(DataReceivedEventArgs e)
{
    DataReceived?.Invoke(this, e);
}
&lt;/code&gt;&lt;/pre&gt;
&lt;ol&gt;
&lt;li&gt;&lt;strong&gt;使用EventHandler和EventHandler&amp;lt;TEventArgs&amp;gt;&lt;/strong&gt;&lt;/li&gt;
&lt;/ol&gt;
&lt;pre&gt;&lt;code&gt;// 推荐：使用标准的EventHandler委托
event EventHandler SimpleEvent;

// 推荐：使用泛型EventHandler&amp;lt;TEventArgs&amp;gt;委托
event EventHandler&amp;lt;CustomEventArgs&amp;gt; CustomEvent;

// 不推荐：自定义委托类型（除非有特殊需求）
delegate void MyCustomEventHandler(object sender, CustomEventArgs e);
event MyCustomEventHandler MyCustomEvent;
&lt;/code&gt;&lt;/pre&gt;
&lt;ol&gt;
&lt;li&gt;&lt;strong&gt;使用不可变的事件参数&lt;/strong&gt;&lt;/li&gt;
&lt;/ol&gt;
&lt;pre&gt;&lt;code&gt;// 不可变事件参数
public class ImmutableEventArgs : EventArgs
{
    public int Id { get; }
    public string Name { get; }
    
    public ImmutableEventArgs(int id, string name)
    {
        Id = id;
        Name = name;
    }
}
&lt;/code&gt;&lt;/pre&gt;
&lt;ol&gt;
&lt;li&gt;&lt;strong&gt;避免在事件处理程序中抛出异常&lt;/strong&gt;&lt;/li&gt;
&lt;/ol&gt;
&lt;pre&gt;&lt;code&gt;// 发布者应该考虑订阅者抛出异常的情况
public void SafeRaiseEvent()
{
    var handler = MyEvent;
    if (handler == null) return;
    
    // 逐一调用每个事件处理程序，捕获异常
    foreach (Delegate d in handler.GetInvocationList())
    {
        try
        {
            d.DynamicInvoke(this, EventArgs.Empty);
        }
        catch (Exception ex)
        {
            Console.WriteLine($&quot;事件处理程序抛出异常: {ex.Message}&quot;);
            // 可以选择记录日志，但不应该中断其他订阅者
        }
    }
}
&lt;/code&gt;&lt;/pre&gt;
&lt;ol&gt;
&lt;li&gt;&lt;strong&gt;事件聚合器模式&lt;/strong&gt;&lt;/li&gt;
&lt;/ol&gt;
&lt;pre&gt;&lt;code&gt;// 事件聚合器用于解耦事件发布者和订阅者
public class EventAggregator
{
    private readonly Dictionary&amp;lt;Type, List&amp;lt;WeakReference&amp;gt;&amp;gt; _handlers = new Dictionary&amp;lt;Type, List&amp;lt;WeakReference&amp;gt;&amp;gt;();
    
    public void Subscribe&amp;lt;T&amp;gt;(Action&amp;lt;T&amp;gt; handler) where T : EventArgs
    {
        var eventType = typeof(T);
        if (!_handlers.ContainsKey(eventType))
        {
            _handlers[eventType] = new List&amp;lt;WeakReference&amp;gt;();
        }
        _handlers[eventType].Add(new WeakReference(handler));
    }
    
    public void Publish&amp;lt;T&amp;gt;(T eventArgs) where T : EventArgs
    {
        var eventType = typeof(T);
        if (!_handlers.ContainsKey(eventType)) return;
        
        var handlersToRemove = new List&amp;lt;WeakReference&amp;gt;();
        
        foreach (var weakRef in _handlers[eventType])
        {
            if (weakRef.TryGetTarget(out var handlerObj) &amp;amp;&amp;amp; handlerObj is Action&amp;lt;T&amp;gt; handler)
            {
                try
                {
                    handler(eventArgs);
                }
                catch (Exception ex)
                {
                    Console.WriteLine($&quot;事件处理程序抛出异常: {ex.Message}&quot;);
                }
            }
            else
            {
                handlersToRemove.Add(weakRef);
            }
        }
        
        foreach (var handlerToRemove in handlersToRemove)
        {
            _handlers[eventType].Remove(handlerToRemove);
        }
    }
}

// 使用事件聚合器
var aggregator = new EventAggregator();

// 订阅事件
aggregator.Subscribe&amp;lt;OrderEventArgs&amp;gt;(args =&amp;gt;
{
    Console.WriteLine($&quot;订单 {args.OrderId} 已创建，金额: {args.TotalAmount}&quot;);
});

// 发布事件
aggregator.Publish(new OrderEventArgs(1001, 99.99m));
&lt;/code&gt;&lt;/pre&gt;
&lt;h5&gt;事件的应用场景&lt;/h5&gt;
&lt;ol&gt;
&lt;li&gt;&lt;strong&gt;UI事件处理&lt;/strong&gt;&lt;/li&gt;
&lt;/ol&gt;
&lt;pre&gt;&lt;code&gt;// WinForm或WPF中的事件处理
public partial class MainForm : Form
{
    public MainForm()
    {
        InitializeComponent();
        
        // 订阅按钮点击事件
        btnSubmit.Click += BtnSubmit_Click;
        
        // 订阅文本框文本改变事件
        txtInput.TextChanged += TxtInput_TextChanged;
    }
    
    private void BtnSubmit_Click(object sender, EventArgs e)
    {
        // 处理按钮点击
    }
    
    private void TxtInput_TextChanged(object sender, EventArgs e)
    {
        // 处理文本改变
    }
}
&lt;/code&gt;&lt;/pre&gt;
&lt;ol&gt;
&lt;li&gt;&lt;strong&gt;状态变化通知&lt;/strong&gt;&lt;/li&gt;
&lt;/ol&gt;
&lt;pre&gt;&lt;code&gt;// 状态机状态变化事件
public class StateMachine
{
    public enum State { Idle, Running, Paused, Completed }
    
    private State _currentState;
    
    public event EventHandler&amp;lt;StateChangedEventArgs&amp;gt; StateChanged;
    
    public State CurrentState
    {
        get =&amp;gt; _currentState;
        private set
        {
            if (_currentState != value)
            {
                var oldState = _currentState;
                _currentState = value;
                OnStateChanged(new StateChangedEventArgs(oldState, value));
            }
        }
    }
    
    protected virtual void OnStateChanged(StateChangedEventArgs e)
    {
        StateChanged?.Invoke(this, e);
    }
    
    public void Start() =&amp;gt; CurrentState = State.Running;
    public void Pause() =&amp;gt; CurrentState = State.Paused;
    public void Resume() =&amp;gt; CurrentState = State.Running;
    public void Stop() =&amp;gt; CurrentState = State.Completed;
}

public class StateChangedEventArgs : EventArgs
{
    public StateMachine.State OldState { get; }
    public StateMachine.State NewState { get; }
    
    public StateChangedEventArgs(StateMachine.State oldState, StateMachine.State newState)
    {
        OldState = oldState;
        NewState = newState;
    }
}

// 使用状态机事件
var stateMachine = new StateMachine();
stateMachine.StateChanged += (sender, e) =&amp;gt;
{
    Console.WriteLine($&quot;状态从 {e.OldState} 变为 {e.NewState}&quot;);
};
stateMachine.Start(); // 状态从 Idle 变为 Running
stateMachine.Pause(); // 状态从 Running 变为 Paused
stateMachine.Stop();  // 状态从 Paused 变为 Completed
&lt;/code&gt;&lt;/pre&gt;
&lt;ol&gt;
&lt;li&gt;&lt;strong&gt;消息通知系统&lt;/strong&gt;&lt;/li&gt;
&lt;/ol&gt;
&lt;pre&gt;&lt;code&gt;// 简单的消息总线
public static class MessageBus
{
    public static event Action&amp;lt;string, object&amp;gt; MessagePublished;
    
    public static void Publish(string messageType, object payload)
    {
        MessagePublished?.Invoke(messageType, payload);
    }
    
    public static void Subscribe(string messageType, Action&amp;lt;string, object&amp;gt; handler)
    {
        MessagePublished += (type, payload) =&amp;gt;
        {
            if (type == messageType)
            {
                handler(type, payload);
            }
        };
    }
}

// 使用消息总线
MessageBus.Subscribe(&quot;UserCreated&quot;, (type, payload) =&amp;gt;
{
    if (payload is User user)
    {
        Console.WriteLine($&quot;用户 {user.Name} 已创建&quot;);
    }
});

// 发布消息
MessageBus.Publish(&quot;UserCreated&quot;, new User { Id = 1, Name = &quot;张三&quot; });

public class User
{
    public int Id { get; set; }
    public string Name { get; set; }
}
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;通过理解委托和事件的高级特性和最佳实践，可以编写出更加灵活、可维护和高性能的事件驱动代码。事件驱动编程是现代C#开发的核心模式之一，广泛应用于UI开发、分布式系统和异步编程等领域。&lt;/p&gt;
&lt;h3&gt;&amp;lt;a id=&quot;expression-trees&quot;&amp;gt;&amp;lt;/a&amp;gt;表达式树&lt;/h3&gt;
&lt;p&gt;表达式树是一种表示代码结构的数据结构，它允许在运行时检查、修改和执行代码。Lambda表达式可以隐式转换为表达式树，这使得LINQ to SQL、Entity Framework等ORM框架能够将C#代码转换为SQL查询。&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;// 创建表达式树
Expression&amp;lt;Func&amp;lt;int, int, int&amp;gt;&amp;gt; addExpression = (a, b) =&amp;gt; a + b;

// 解析表达式树
BinaryExpression body = (BinaryExpression)addExpression.Body;
ParameterExpression leftParam = (ParameterExpression)body.Left;
ParameterExpression rightParam = (ParameterExpression)body.Right;

Console.WriteLine($&quot;表达式类型: {body.NodeType}&quot;);
Console.WriteLine($&quot;左操作数: {leftParam.Name}&quot;);
Console.WriteLine($&quot;右操作数: {rightParam.Name}&quot;);

// 编译表达式树
Func&amp;lt;int, int, int&amp;gt; addFunc = addExpression.Compile();
int result = addFunc(3, 5); // 结果为8
&lt;/code&gt;&lt;/pre&gt;
&lt;h4&gt;表达式树的应用&lt;/h4&gt;
&lt;ol&gt;
&lt;li&gt;&lt;strong&gt;动态查询构建&lt;/strong&gt;&lt;/li&gt;
&lt;/ol&gt;
&lt;pre&gt;&lt;code&gt;// 构建动态查询
Expression&amp;lt;Func&amp;lt;Product, bool&amp;gt;&amp;gt; BuildProductQuery(decimal maxPrice, bool inStock)
{
    ParameterExpression productParam = Expression.Parameter(typeof(Product), &quot;p&quot;);
    Expression priceCondition = Expression.LessThan(Expression.Property(productParam, &quot;Price&quot;), Expression.Constant(maxPrice));
    Expression stockCondition = Expression.Equal(Expression.Property(productParam, &quot;IsInStock&quot;), Expression.Constant(inStock));
    Expression combinedCondition = Expression.AndAlso(priceCondition, stockCondition);
    return Expression.Lambda&amp;lt;Func&amp;lt;Product, bool&amp;gt;&amp;gt;(combinedCondition, productParam);
}

// 使用
var query = BuildProductQuery(3000, true);
var compiledQuery = query.Compile();
List&amp;lt;Product&amp;gt; filteredProducts = products.Where(compiledQuery).ToList();
&lt;/code&gt;&lt;/pre&gt;
&lt;ol&gt;
&lt;li&gt;&lt;strong&gt;ORM框架中的应用&lt;/strong&gt;&lt;/li&gt;
&lt;/ol&gt;
&lt;pre&gt;&lt;code&gt;// Entity Framework使用表达式树将LINQ转换为SQL
using (var context = new MyDbContext())
{
    // 这是一个表达式树，会被转换为SQL
    Expression&amp;lt;Func&amp;lt;Customer, bool&amp;gt;&amp;gt; filter = c =&amp;gt; c.City == &quot;北京&quot; &amp;amp;&amp;amp; c.Age &amp;gt; 18;
    var customers = context.Customers.Where(filter).ToList();
    // 生成的SQL类似: SELECT * FROM Customers WHERE City = &apos;北京&apos; AND Age &amp;gt; 18
}
&lt;/code&gt;&lt;/pre&gt;
&lt;h2&gt;&amp;lt;a id=&quot;linq-detail&quot;&amp;gt;&amp;lt;/a&amp;gt;C# LINQ详解&lt;/h2&gt;
&lt;p&gt;LINQ（Language Integrated Query，语言集成查询）是C#中的强大查询功能，它提供了一种统一的语法来查询各种数据源（集合、数据库、XML等）。LINQ将查询语法直接集成到C#语言中，使得数据查询变得简洁、类型安全和易于维护。&lt;/p&gt;
&lt;h3&gt;&amp;lt;a id=&quot;linq-concept&quot;&amp;gt;&amp;lt;/a&amp;gt;LINQ的基本概念&lt;/h3&gt;
&lt;h4&gt;什么是LINQ？&lt;/h4&gt;
&lt;p&gt;LINQ是一组技术和语言扩展，允许在C#中编写类似SQL的查询语句来查询数据。LINQ支持多种数据源：&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;strong&gt;LINQ to Objects&lt;/strong&gt;：查询内存中的集合&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;LINQ to SQL&lt;/strong&gt;：查询关系数据库&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;LINQ to XML&lt;/strong&gt;：查询XML文档&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;LINQ to Entities&lt;/strong&gt;：查询Entity Framework实体&lt;/li&gt;
&lt;/ul&gt;
&lt;h4&gt;LINQ的优势&lt;/h4&gt;
&lt;ol&gt;
&lt;li&gt;&lt;strong&gt;统一语法&lt;/strong&gt;：使用相同的语法查询不同的数据源&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;类型安全&lt;/strong&gt;：编译时类型检查，减少运行时错误&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;IntelliSense支持&lt;/strong&gt;：IDE自动完成和类型提示&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;延迟执行&lt;/strong&gt;：查询在需要时才执行，提高性能&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;声明式编程&lt;/strong&gt;：描述&quot;做什么&quot;而不是&quot;怎么做&quot;&lt;/li&gt;
&lt;/ol&gt;
&lt;h4&gt;LINQ的两种语法&lt;/h4&gt;
&lt;p&gt;LINQ支持两种语法形式：&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;// 1. 查询语法（Query Syntax）- 类似SQL
var query1 = from p in products
             where p.Price &amp;gt; 100
             select p.Name;

// 2. 方法语法（Method Syntax）- 链式方法调用
var query2 = products
    .Where(p =&amp;gt; p.Price &amp;gt; 100)
    .Select(p =&amp;gt; p.Name);
&lt;/code&gt;&lt;/pre&gt;
&lt;h3&gt;&amp;lt;a id=&quot;linq-query-syntax&quot;&amp;gt;&amp;lt;/a&amp;gt;LINQ查询语法&lt;/h3&gt;
&lt;p&gt;查询语法使用类似SQL的关键字，更加直观和易读。&lt;/p&gt;
&lt;h4&gt;基本查询语法&lt;/h4&gt;
&lt;pre&gt;&lt;code&gt;using System.Linq;

public class Product
{
    public int Id { get; set; }
    public string Name { get; set; }
    public decimal Price { get; set; }
    public string Category { get; set; }
    public bool InStock { get; set; }
}

List&amp;lt;Product&amp;gt; products = new List&amp;lt;Product&amp;gt;
{
    new Product { Id = 1, Name = &quot;笔记本电脑&quot;, Price = 5999.99m, Category = &quot;电子产品&quot;, InStock = true },
    new Product { Id = 2, Name = &quot;智能手机&quot;, Price = 3999.99m, Category = &quot;电子产品&quot;, InStock = true },
    new Product { Id = 3, Name = &quot;办公桌&quot;, Price = 899.99m, Category = &quot;家具&quot;, InStock = false },
    new Product { Id = 4, Name = &quot;椅子&quot;, Price = 299.99m, Category = &quot;家具&quot;, InStock = true }
};

// from：指定数据源
var query1 = from p in products
             select p;

// where：过滤条件
var query2 = from p in products
             where p.Price &amp;gt; 1000
             select p;

// select：选择字段
var query3 = from p in products
             select p.Name;

// 选择多个字段（匿名类型）
var query4 = from p in products
             select new { p.Name, p.Price };

// orderby：排序
var query5 = from p in products
             orderby p.Price ascending
             select p;

// orderby descending：降序排序
var query6 = from p in products
             orderby p.Price descending
             select p;

// 多个排序条件
var query7 = from p in products
             orderby p.Category, p.Price descending
             select p;

// group by：分组
var query8 = from p in products
             group p by p.Category into g
             select new { Category = g.Key, Products = g };

// join：连接
List&amp;lt;Category&amp;gt; categories = new List&amp;lt;Category&amp;gt;
{
    new Category { Id = 1, Name = &quot;电子产品&quot; },
    new Category { Id = 2, Name = &quot;家具&quot; }
};

var query9 = from p in products
             join c in categories on p.Category equals c.Name
             select new { ProductName = p.Name, CategoryName = c.Name };

// let：创建临时变量
var query10 = from p in products
              let discountPrice = p.Price * 0.9m
              where discountPrice &amp;gt; 500
              select new { p.Name, OriginalPrice = p.Price, DiscountPrice = discountPrice };

// into：继续查询
var query11 = from p in products
              group p by p.Category into categoryGroup
              where categoryGroup.Count() &amp;gt; 1
              select new { Category = categoryGroup.Key, Count = categoryGroup.Count() };
&lt;/code&gt;&lt;/pre&gt;
&lt;h3&gt;&amp;lt;a id=&quot;linq-method-syntax&quot;&amp;gt;&amp;lt;/a&amp;gt;LINQ方法语法&lt;/h3&gt;
&lt;p&gt;方法语法使用扩展方法和Lambda表达式，更加灵活和功能强大。&lt;/p&gt;
&lt;h4&gt;基本方法语法&lt;/h4&gt;
&lt;pre&gt;&lt;code&gt;// Where：过滤
var query1 = products.Where(p =&amp;gt; p.Price &amp;gt; 1000);

// Select：投影
var query2 = products.Select(p =&amp;gt; p.Name);
var query3 = products.Select(p =&amp;gt; new { p.Name, p.Price });

// OrderBy / OrderByDescending：排序
var query4 = products.OrderBy(p =&amp;gt; p.Price);
var query5 = products.OrderByDescending(p =&amp;gt; p.Price);

// ThenBy / ThenByDescending：多级排序
var query6 = products
    .OrderBy(p =&amp;gt; p.Category)
    .ThenByDescending(p =&amp;gt; p.Price);

// GroupBy：分组
var query7 = products.GroupBy(p =&amp;gt; p.Category);

// SelectMany：展平嵌套集合
List&amp;lt;Order&amp;gt; orders = new List&amp;lt;Order&amp;gt;
{
    new Order { Id = 1, Items = new List&amp;lt;OrderItem&amp;gt; { new OrderItem { ProductName = &quot;A&quot; }, new OrderItem { ProductName = &quot;B&quot; } } },
    new Order { Id = 2, Items = new List&amp;lt;OrderItem&amp;gt; { new OrderItem { ProductName = &quot;C&quot; } } }
};

var allItems = orders.SelectMany(o =&amp;gt; o.Items);

// Join：连接
var query8 = products.Join(
    categories,
    p =&amp;gt; p.Category,
    c =&amp;gt; c.Name,
    (p, c) =&amp;gt; new { ProductName = p.Name, CategoryName = c.Name }
);

// Take / Skip：分页
var query9 = products.OrderBy(p =&amp;gt; p.Price).Take(10);      // 前10条
var query10 = products.OrderBy(p =&amp;gt; p.Price).Skip(10).Take(10); // 第11-20条

// First / FirstOrDefault：获取第一个元素
var firstProduct = products.First(p =&amp;gt; p.Price &amp;gt; 1000);
var firstOrNull = products.FirstOrDefault(p =&amp;gt; p.Price &amp;gt; 10000); // 找不到返回null

// Last / LastOrDefault：获取最后一个元素
var lastProduct = products.Last(p =&amp;gt; p.InStock);

// Single / SingleOrDefault：获取唯一元素
var singleProduct = products.Single(p =&amp;gt; p.Id == 1);
var singleOrNull = products.SingleOrDefault(p =&amp;gt; p.Id == 999);

// Any / All：存在性检查
bool hasExpensive = products.Any(p =&amp;gt; p.Price &amp;gt; 5000);
bool allInStock = products.All(p =&amp;gt; p.InStock);

// Count / Sum / Average / Min / Max：聚合
int count = products.Count();
int expensiveCount = products.Count(p =&amp;gt; p.Price &amp;gt; 1000);
decimal totalPrice = products.Sum(p =&amp;gt; p.Price);
decimal avgPrice = products.Average(p =&amp;gt; p.Price);
decimal minPrice = products.Min(p =&amp;gt; p.Price);
decimal maxPrice = products.Max(p =&amp;gt; p.Price);

// Distinct：去重
var distinctCategories = products.Select(p =&amp;gt; p.Category).Distinct();

// Contains：包含检查
bool containsProduct = products.Select(p =&amp;gt; p.Name).Contains(&quot;笔记本电脑&quot;);

// Concat / Union：合并
var allItems1 = products.Select(p =&amp;gt; p.Name);
var allItems2 = new List&amp;lt;string&amp;gt; { &quot;新产品1&quot;, &quot;新产品2&quot; };
var combined = allItems1.Concat(allItems2);
var unioned = allItems1.Union(allItems2); // 去重合并

// Intersect / Except：集合运算
var set1 = new List&amp;lt;int&amp;gt; { 1, 2, 3, 4 };
var set2 = new List&amp;lt;int&amp;gt; { 3, 4, 5, 6 };
var intersection = set1.Intersect(set2); // {3, 4}
var except = set1.Except(set2);          // {1, 2}

// ToList / ToArray / ToDictionary：转换为集合
List&amp;lt;Product&amp;gt; productList = products.Where(p =&amp;gt; p.InStock).ToList();
Product[] productArray = products.ToArray();
Dictionary&amp;lt;int, Product&amp;gt; productDict = products.ToDictionary(p =&amp;gt; p.Id);
Dictionary&amp;lt;string, List&amp;lt;Product&amp;gt;&amp;gt; categoryDict = products
    .GroupBy(p =&amp;gt; p.Category)
    .ToDictionary(g =&amp;gt; g.Key, g =&amp;gt; g.ToList());
&lt;/code&gt;&lt;/pre&gt;
&lt;h3&gt;&amp;lt;a id=&quot;standard-query-operators&quot;&amp;gt;&amp;lt;/a&amp;gt;标准查询操作符&lt;/h3&gt;
&lt;h4&gt;筛选操作符（Filtering）&lt;/h4&gt;
&lt;pre&gt;&lt;code&gt;// Where：根据条件筛选
var inStockProducts = products.Where(p =&amp;gt; p.InStock);
var expensiveProducts = products.Where(p =&amp;gt; p.Price &amp;gt; 1000 &amp;amp;&amp;amp; p.Category == &quot;电子产品&quot;);

// 使用索引的Where重载
var productsByIndex = products.Where((p, index) =&amp;gt; index % 2 == 0); // 偶数索引
&lt;/code&gt;&lt;/pre&gt;
&lt;h4&gt;投影操作符（Projection）&lt;/h4&gt;
&lt;pre&gt;&lt;code&gt;// Select：选择单个字段
var names = products.Select(p =&amp;gt; p.Name);

// Select：创建匿名类型
var productInfo = products.Select(p =&amp;gt; new { p.Name, p.Price, IsExpensive = p.Price &amp;gt; 1000 });

// Select：使用索引
var indexedProducts = products.Select((p, index) =&amp;gt; new { Index = index, p.Name });

// SelectMany：展平嵌套集合
var allOrderItems = orders.SelectMany(o =&amp;gt; o.Items);
var allOrderItemsWithOrder = orders.SelectMany(o =&amp;gt; o.Items, (o, item) =&amp;gt; new { OrderId = o.Id, Item = item });
&lt;/code&gt;&lt;/pre&gt;
&lt;h4&gt;排序操作符（Sorting）&lt;/h4&gt;
&lt;pre&gt;&lt;code&gt;// OrderBy / OrderByDescending
var sortedByPrice = products.OrderBy(p =&amp;gt; p.Price);
var sortedByPriceDesc = products.OrderByDescending(p =&amp;gt; p.Price);

// ThenBy / ThenByDescending（多级排序）
var sorted = products
    .OrderBy(p =&amp;gt; p.Category)
    .ThenByDescending(p =&amp;gt; p.Price)
    .ThenBy(p =&amp;gt; p.Name);

// Reverse：反转顺序
var reversed = products.Reverse();
&lt;/code&gt;&lt;/pre&gt;
&lt;h4&gt;分组操作符（Grouping）&lt;/h4&gt;
&lt;pre&gt;&lt;code&gt;// GroupBy：分组
var groupedByCategory = products.GroupBy(p =&amp;gt; p.Category);

foreach (var group in groupedByCategory)
{
    Console.WriteLine($&quot;类别: {group.Key}&quot;);
    foreach (var product in group)
    {
        Console.WriteLine($&quot;  - {product.Name}&quot;);
    }
}

// GroupBy：多个键
var groupedByMultiple = products.GroupBy(p =&amp;gt; new { p.Category, p.InStock });

// GroupBy：使用结果选择器
var categoryStats = products.GroupBy(
    p =&amp;gt; p.Category,
    (key, items) =&amp;gt; new 
    { 
        Category = key, 
        Count = items.Count(), 
        TotalPrice = items.Sum(p =&amp;gt; p.Price),
        Products = items.ToList()
    }
);
&lt;/code&gt;&lt;/pre&gt;
&lt;h4&gt;连接操作符（Joining）&lt;/h4&gt;
&lt;pre&gt;&lt;code&gt;// Join：内连接
var joinQuery = products.Join(
    categories,
    product =&amp;gt; product.Category,
    category =&amp;gt; category.Name,
    (product, category) =&amp;gt; new 
    { 
        ProductName = product.Name, 
        CategoryName = category.Name 
    }
);

// GroupJoin：分组连接
var groupJoinQuery = categories.GroupJoin(
    products,
    category =&amp;gt; category.Name,
    product =&amp;gt; product.Category,
    (category, products) =&amp;gt; new 
    { 
        Category = category.Name, 
        Products = products 
    }
);

// Zip：合并两个序列
var numbers = new List&amp;lt;int&amp;gt; { 1, 2, 3 };
var letters = new List&amp;lt;string&amp;gt; { &quot;A&quot;, &quot;B&quot;, &quot;C&quot; };
var zipped = numbers.Zip(letters, (n, l) =&amp;gt; $&quot;{n}{l}&quot;); // {&quot;1A&quot;, &quot;2B&quot;, &quot;3C&quot;}
&lt;/code&gt;&lt;/pre&gt;
&lt;h4&gt;聚合操作符（Aggregation）&lt;/h4&gt;
&lt;pre&gt;&lt;code&gt;// Count：计数
int totalCount = products.Count();
int inStockCount = products.Count(p =&amp;gt; p.InStock);

// Sum：求和
decimal totalPrice = products.Sum(p =&amp;gt; p.Price);
decimal categoryTotal = products.Where(p =&amp;gt; p.Category == &quot;电子产品&quot;).Sum(p =&amp;gt; p.Price);

// Average：平均值
decimal avgPrice = products.Average(p =&amp;gt; p.Price);

// Min / Max：最小/最大值
decimal minPrice = products.Min(p =&amp;gt; p.Price);
decimal maxPrice = products.Max(p =&amp;gt; p.Price);
Product cheapestProduct = products.OrderBy(p =&amp;gt; p.Price).First();

// Aggregate：自定义聚合
decimal total = products.Aggregate(0m, (sum, p) =&amp;gt; sum + p.Price);
string allNames = products.Aggregate(&quot;&quot;, (current, p) =&amp;gt; current + p.Name + &quot;, &quot;);
&lt;/code&gt;&lt;/pre&gt;
&lt;h4&gt;元素操作符（Element）&lt;/h4&gt;
&lt;pre&gt;&lt;code&gt;// First / FirstOrDefault
Product first = products.First();
Product firstExpensive = products.First(p =&amp;gt; p.Price &amp;gt; 5000);
Product firstOrNull = products.FirstOrDefault(p =&amp;gt; p.Price &amp;gt; 10000);

// Last / LastOrDefault
Product last = products.Last();
Product lastExpensive = products.LastOrDefault(p =&amp;gt; p.Price &amp;gt; 5000);

// Single / SingleOrDefault
Product single = products.Single(p =&amp;gt; p.Id == 1);
Product singleOrNull = products.SingleOrDefault(p =&amp;gt; p.Id == 999);

// ElementAt / ElementAtOrDefault
Product atIndex = products.ElementAt(2);
Product atIndexOrNull = products.ElementAtOrDefault(100);
&lt;/code&gt;&lt;/pre&gt;
&lt;h4&gt;集合操作符（Set）&lt;/h4&gt;
&lt;pre&gt;&lt;code&gt;// Distinct：去重
var distinctCategories = products.Select(p =&amp;gt; p.Category).Distinct();

// Distinct：使用自定义比较器
var distinctProducts = products.Distinct(new ProductComparer());

// Union：并集（去重）
var set1 = new List&amp;lt;int&amp;gt; { 1, 2, 3 };
var set2 = new List&amp;lt;int&amp;gt; { 3, 4, 5 };
var union = set1.Union(set2); // {1, 2, 3, 4, 5}

// Intersect：交集
var intersection = set1.Intersect(set2); // {3}

// Except：差集
var except = set1.Except(set2); // {1, 2}

// Concat：连接（不去重）
var concat = set1.Concat(set2); // {1, 2, 3, 3, 4, 5}
&lt;/code&gt;&lt;/pre&gt;
&lt;h4&gt;分区操作符（Partitioning）&lt;/h4&gt;
&lt;pre&gt;&lt;code&gt;// Take：取前N个
var first10 = products.Take(10);

// Skip：跳过前N个
var after10 = products.Skip(10);

// TakeWhile：满足条件时取
var takeWhile = products.TakeWhile(p =&amp;gt; p.Price &amp;lt; 1000);

// SkipWhile：满足条件时跳过
var skipWhile = products.SkipWhile(p =&amp;gt; p.Price &amp;lt; 1000);

// 分页示例
int pageSize = 10;
int pageNumber = 2;
var page = products
    .OrderBy(p =&amp;gt; p.Id)
    .Skip((pageNumber - 1) * pageSize)
    .Take(pageSize);
&lt;/code&gt;&lt;/pre&gt;
&lt;h4&gt;转换操作符（Conversion）&lt;/h4&gt;
&lt;pre&gt;&lt;code&gt;// ToList
List&amp;lt;Product&amp;gt; productList = products.ToList();

// ToArray
Product[] productArray = products.ToArray();

// ToDictionary
Dictionary&amp;lt;int, Product&amp;gt; dictById = products.ToDictionary(p =&amp;gt; p.Id);
Dictionary&amp;lt;int, string&amp;gt; dictIdToName = products.ToDictionary(p =&amp;gt; p.Id, p =&amp;gt; p.Name);

// ToLookup：类似Dictionary，但一个键可以对应多个值
ILookup&amp;lt;string, Product&amp;gt; lookupByCategory = products.ToLookup(p =&amp;gt; p.Category);
var electronics = lookupByCategory[&quot;电子产品&quot;];

// AsEnumerable / AsQueryable：延迟执行
IEnumerable&amp;lt;Product&amp;gt; enumerable = products.AsEnumerable();
IQueryable&amp;lt;Product&amp;gt; queryable = products.AsQueryable();

// Cast：类型转换
IEnumerable&amp;lt;object&amp;gt; objects = new List&amp;lt;object&amp;gt; { 1, 2, 3 };
IEnumerable&amp;lt;int&amp;gt; integers = objects.Cast&amp;lt;int&amp;gt;();

// OfType：类型过滤
IEnumerable&amp;lt;object&amp;gt; mixed = new List&amp;lt;object&amp;gt; { 1, &quot;hello&quot;, 2, &quot;world&quot; };
IEnumerable&amp;lt;int&amp;gt; numbers = mixed.OfType&amp;lt;int&amp;gt;(); // {1, 2}
&lt;/code&gt;&lt;/pre&gt;
&lt;h3&gt;&amp;lt;a id=&quot;linq-to-objects&quot;&amp;gt;&amp;lt;/a&amp;gt;LINQ to Objects&lt;/h3&gt;
&lt;p&gt;LINQ to Objects用于查询内存中的集合（如List、Array等）。&lt;/p&gt;
&lt;h4&gt;复杂查询示例&lt;/h4&gt;
&lt;pre&gt;&lt;code&gt;// 示例1：查找价格最高的前3个产品
var top3Products = products
    .OrderByDescending(p =&amp;gt; p.Price)
    .Take(3)
    .Select(p =&amp;gt; new { p.Name, p.Price });

// 示例2：按类别分组，计算每类的平均价格
var categoryAvgPrice = products
    .GroupBy(p =&amp;gt; p.Category)
    .Select(g =&amp;gt; new 
    { 
        Category = g.Key, 
        AvgPrice = g.Average(p =&amp;gt; p.Price),
        Count = g.Count()
    });

// 示例3：查找有库存且价格在指定范围内的产品
decimal minPrice = 100;
decimal maxPrice = 5000;
var filteredProducts = products
    .Where(p =&amp;gt; p.InStock &amp;amp;&amp;amp; p.Price &amp;gt;= minPrice &amp;amp;&amp;amp; p.Price &amp;lt;= maxPrice)
    .OrderBy(p =&amp;gt; p.Price)
    .ToList();

// 示例4：计算总价值
decimal totalValue = products
    .Where(p =&amp;gt; p.InStock)
    .Sum(p =&amp;gt; p.Price);

// 示例5：查找产品名称中包含特定关键字的产品
string keyword = &quot;电脑&quot;;
var searchResults = products
    .Where(p =&amp;gt; p.Name.Contains(keyword))
    .ToList();

// 示例6：判断是否存在符合条件的产品
bool hasExpensiveElectronics = products
    .Any(p =&amp;gt; p.Category == &quot;电子产品&quot; &amp;amp;&amp;amp; p.Price &amp;gt; 5000);

// 示例7：获取所有类别及其产品数量
var categoryCounts = products
    .GroupBy(p =&amp;gt; p.Category)
    .Select(g =&amp;gt; new { Category = g.Key, ProductCount = g.Count() })
    .OrderByDescending(x =&amp;gt; x.ProductCount);

// 示例8：查找价格最高的产品
var mostExpensive = products
    .OrderByDescending(p =&amp;gt; p.Price)
    .FirstOrDefault();

// 示例9：获取产品名称列表（去重）
var uniqueNames = products
    .Select(p =&amp;gt; p.Name)
    .Distinct()
    .ToList();
&lt;/code&gt;&lt;/pre&gt;
&lt;h3&gt;&amp;lt;a id=&quot;linq-to-sql&quot;&amp;gt;&amp;lt;/a&amp;gt;LINQ to SQL&lt;/h3&gt;
&lt;p&gt;LINQ to SQL用于查询SQL Server数据库。&lt;/p&gt;
&lt;h4&gt;Entity Framework Core示例&lt;/h4&gt;
&lt;pre&gt;&lt;code&gt;using Microsoft.EntityFrameworkCore;

public class ApplicationDbContext : DbContext
{
    public DbSet&amp;lt;Product&amp;gt; Products { get; set; }
    public DbSet&amp;lt;Category&amp;gt; Categories { get; set; }
    
    protected override void OnConfiguring(DbContextOptionsBuilder optionsBuilder)
    {
        optionsBuilder.UseSqlServer(&quot;connection string&quot;);
    }
}

// 使用LINQ查询数据库
using (var context = new ApplicationDbContext())
{
    // 基本查询（延迟执行，转换为SQL）
    var query = from p in context.Products
                where p.Price &amp;gt; 1000
                select p;
    
    // 执行查询（此时才执行SQL）
    List&amp;lt;Product&amp;gt; products = query.ToList();
    
    // 方法语法
    var expensiveProducts = context.Products
        .Where(p =&amp;gt; p.Price &amp;gt; 1000)
        .OrderByDescending(p =&amp;gt; p.Price)
        .ToList();
    
    // 连接查询
    var productsWithCategory = from p in context.Products
                               join c in context.Categories on p.CategoryId equals c.Id
                               select new { p.Name, CategoryName = c.Name };
    
    var result = productsWithCategory.ToList();
    
    // 分组查询
    var categoryStats = context.Products
        .GroupBy(p =&amp;gt; p.CategoryId)
        .Select(g =&amp;gt; new 
        { 
            CategoryId = g.Key, 
            Count = g.Count(),
            AvgPrice = g.Average(p =&amp;gt; p.Price)
        })
        .ToList();
    
    // 聚合查询
    decimal totalValue = context.Products.Sum(p =&amp;gt; p.Price);
    int productCount = context.Products.Count();
    
    // 分页查询
    int pageSize = 10;
    int pageNumber = 1;
    var pagedProducts = context.Products
        .OrderBy(p =&amp;gt; p.Id)
        .Skip((pageNumber - 1) * pageSize)
        .Take(pageSize)
        .ToList();
}
&lt;/code&gt;&lt;/pre&gt;
&lt;h3&gt;&amp;lt;a id=&quot;linq-to-xml&quot;&amp;gt;&amp;lt;/a&amp;gt;LINQ to XML&lt;/h3&gt;
&lt;p&gt;LINQ to XML用于查询和操作XML文档。&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;using System.Xml.Linq;

// 创建XML文档
XDocument xml = new XDocument(
    new XElement(&quot;Products&quot;,
        new XElement(&quot;Product&quot;,
            new XAttribute(&quot;Id&quot;, 1),
            new XElement(&quot;Name&quot;, &quot;笔记本电脑&quot;),
            new XElement(&quot;Price&quot;, 5999.99)
        ),
        new XElement(&quot;Product&quot;,
            new XAttribute(&quot;Id&quot;, 2),
            new XElement(&quot;Name&quot;, &quot;智能手机&quot;),
            new XElement(&quot;Price&quot;, 3999.99)
        )
    )
);

// 查询XML
var products = from p in xml.Descendants(&quot;Product&quot;)
               where (decimal)p.Element(&quot;Price&quot;) &amp;gt; 1000
               select new 
               { 
                   Id = (int)p.Attribute(&quot;Id&quot;),
                   Name = (string)p.Element(&quot;Name&quot;),
                   Price = (decimal)p.Element(&quot;Price&quot;)
               };

// 方法语法
var expensiveProducts = xml.Descendants(&quot;Product&quot;)
    .Where(p =&amp;gt; (decimal)p.Element(&quot;Price&quot;) &amp;gt; 1000)
    .Select(p =&amp;gt; new 
    { 
        Id = (int)p.Attribute(&quot;Id&quot;),
        Name = (string)p.Element(&quot;Name&quot;),
        Price = (decimal)p.Element(&quot;Price&quot;)
    })
    .ToList();

// 修改XML
XElement product = xml.Descendants(&quot;Product&quot;).First(p =&amp;gt; (int)p.Attribute(&quot;Id&quot;) == 1);
product.Element(&quot;Price&quot;).Value = &quot;5499.99&quot;;

// 添加元素
XElement newProduct = new XElement(&quot;Product&quot;,
    new XAttribute(&quot;Id&quot;, 3),
    new XElement(&quot;Name&quot;, &quot;平板电脑&quot;),
    new XElement(&quot;Price&quot;, 2999.99)
);
xml.Root.Add(newProduct);

// 删除元素
xml.Descendants(&quot;Product&quot;)
    .Where(p =&amp;gt; (int)p.Attribute(&quot;Id&quot;) == 2)
    .Remove();
&lt;/code&gt;&lt;/pre&gt;
&lt;h3&gt;&amp;lt;a id=&quot;linq-performance&quot;&amp;gt;&amp;lt;/a&amp;gt;LINQ性能优化&lt;/h3&gt;
&lt;h4&gt;延迟执行（Deferred Execution）&lt;/h4&gt;
&lt;pre&gt;&lt;code&gt;// LINQ查询默认延迟执行
var query = products.Where(p =&amp;gt; p.Price &amp;gt; 1000); // 此时不执行查询

// 只有在迭代或调用ToList、ToArray等时才执行
List&amp;lt;Product&amp;gt; result = query.ToList(); // 此时才执行查询

// 多次迭代会多次执行查询
foreach (var item in query) { } // 执行查询
foreach (var item in query) { } // 再次执行查询

// 缓存结果避免重复执行
var cachedResult = query.ToList(); // 执行一次并缓存
foreach (var item in cachedResult) { } // 使用缓存
foreach (var item in cachedResult) { } // 使用缓存
&lt;/code&gt;&lt;/pre&gt;
&lt;h4&gt;及早执行（Immediate Execution）&lt;/h4&gt;
&lt;pre&gt;&lt;code&gt;// 这些操作会立即执行
int count = products.Count();
bool any = products.Any();
Product first = products.First();
decimal sum = products.Sum(p =&amp;gt; p.Price);
List&amp;lt;Product&amp;gt; list = products.ToList();
Product[] array = products.ToArray();
&lt;/code&gt;&lt;/pre&gt;
&lt;h4&gt;性能优化技巧&lt;/h4&gt;
&lt;pre&gt;&lt;code&gt;// 1. 使用索引而不是顺序查找
// ❌ 较慢
var result1 = products.Where(p =&amp;gt; p.Name == &quot;笔记本电脑&quot;).ToList();

// ✅ 较快（如果有索引）
var result2 = products.Where(p =&amp;gt; p.Id == 1).ToList();

// 2. 在Where之前进行排序（如果只需要Top N）
// ❌ 较慢：先排序全部，再取前10
var result3 = products.OrderBy(p =&amp;gt; p.Price).Take(10).ToList();

// ✅ 较快：使用索引或较小的数据集
var result4 = products.Where(p =&amp;gt; p.InStock).OrderBy(p =&amp;gt; p.Price).Take(10).ToList();

// 3. 避免不必要的多次迭代
// ❌ 多次执行查询
if (products.Any(p =&amp;gt; p.Price &amp;gt; 1000))
{
    var expensive = products.Where(p =&amp;gt; p.Price &amp;gt; 1000).ToList();
}

// ✅ 执行一次
var expensive = products.Where(p =&amp;gt; p.Price &amp;gt; 1000).ToList();
if (expensive.Any())
{
    // 使用expensive
}

// 4. 使用正确的集合类型
// ✅ ToList：需要列表操作时
var list = products.ToList();

// ✅ ToArray：需要数组时
var array = products.ToArray();

// ✅ ToDictionary：需要按键查找时
var dict = products.ToDictionary(p =&amp;gt; p.Id);

// 5. 避免Select的重复调用
// ❌ 多次Select
var names1 = products.Select(p =&amp;gt; p.Name);
var prices1 = products.Select(p =&amp;gt; p.Price);

// ✅ 一次Select选择多个字段
var namesAndPrices = products.Select(p =&amp;gt; new { p.Name, p.Price });
var names2 = namesAndPrices.Select(x =&amp;gt; x.Name);
var prices2 = namesAndPrices.Select(x =&amp;gt; x.Price);
&lt;/code&gt;&lt;/pre&gt;
&lt;h3&gt;&amp;lt;a id=&quot;linq-scenarios&quot;&amp;gt;&amp;lt;/a&amp;gt;LINQ应用场景&lt;/h3&gt;
&lt;h4&gt;1. 数据筛选和排序&lt;/h4&gt;
&lt;pre&gt;&lt;code&gt;// 电商网站：筛选和排序商品
public class ProductService
{
    public IEnumerable&amp;lt;Product&amp;gt; GetProducts(
        string category = null, 
        decimal? minPrice = null, 
        decimal? maxPrice = null,
        bool? inStock = null,
        string sortBy = &quot;Price&quot;,
        bool ascending = true)
    {
        var query = products.AsQueryable();
        
        if (!string.IsNullOrEmpty(category))
            query = query.Where(p =&amp;gt; p.Category == category);
        
        if (minPrice.HasValue)
            query = query.Where(p =&amp;gt; p.Price &amp;gt;= minPrice.Value);
        
        if (maxPrice.HasValue)
            query = query.Where(p =&amp;gt; p.Price &amp;lt;= maxPrice.Value);
        
        if (inStock.HasValue)
            query = query.Where(p =&amp;gt; p.InStock == inStock.Value);
        
        query = sortBy switch
        {
            &quot;Price&quot; =&amp;gt; ascending ? query.OrderBy(p =&amp;gt; p.Price) : query.OrderByDescending(p =&amp;gt; p.Price),
            &quot;Name&quot; =&amp;gt; ascending ? query.OrderBy(p =&amp;gt; p.Name) : query.OrderByDescending(p =&amp;gt; p.Name),
            _ =&amp;gt; query
        };
        
        return query.ToList();
    }
}
&lt;/code&gt;&lt;/pre&gt;
&lt;h4&gt;2. 数据统计和分析&lt;/h4&gt;
&lt;pre&gt;&lt;code&gt;// 销售报表：统计各类别销售额
public class SalesReport
{
    public class CategorySales
    {
        public string Category { get; set; }
        public int ProductCount { get; set; }
        public decimal TotalValue { get; set; }
        public decimal AveragePrice { get; set; }
    }
    
    public List&amp;lt;CategorySales&amp;gt; GetCategoryStatistics(List&amp;lt;Product&amp;gt; products)
    {
        return products
            .GroupBy(p =&amp;gt; p.Category)
            .Select(g =&amp;gt; new CategorySales
            {
                Category = g.Key,
                ProductCount = g.Count(),
                TotalValue = g.Sum(p =&amp;gt; p.Price),
                AveragePrice = g.Average(p =&amp;gt; p.Price)
            })
            .OrderByDescending(x =&amp;gt; x.TotalValue)
            .ToList();
    }
}
&lt;/code&gt;&lt;/pre&gt;
&lt;h4&gt;3. 数据转换和映射&lt;/h4&gt;
&lt;pre&gt;&lt;code&gt;// DTO映射
public class ProductDto
{
    public int Id { get; set; }
    public string Name { get; set; }
    public string FormattedPrice { get; set; }
    public string Status { get; set; }
}

public List&amp;lt;ProductDto&amp;gt; MapToDto(List&amp;lt;Product&amp;gt; products)
{
    return products.Select(p =&amp;gt; new ProductDto
    {
        Id = p.Id,
        Name = p.Name,
        FormattedPrice = $&quot;¥{p.Price:N2}&quot;,
        Status = p.InStock ? &quot;有库存&quot; : &quot;缺货&quot;
    }).ToList();
}
&lt;/code&gt;&lt;/pre&gt;
&lt;h4&gt;4. 数据分组和聚合&lt;/h4&gt;
&lt;pre&gt;&lt;code&gt;// 按日期分组统计
public class DailySales
{
    public DateTime Date { get; set; }
    public decimal TotalSales { get; set; }
    public int OrderCount { get; set; }
}

public List&amp;lt;DailySales&amp;gt; GetDailySales(List&amp;lt;Order&amp;gt; orders)
{
    return orders
        .GroupBy(o =&amp;gt; o.OrderDate.Date)
        .Select(g =&amp;gt; new DailySales
        {
            Date = g.Key,
            TotalSales = g.Sum(o =&amp;gt; o.TotalAmount),
            OrderCount = g.Count()
        })
        .OrderBy(x =&amp;gt; x.Date)
        .ToList();
}
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;LINQ是C#中强大的查询工具，它提供了一种统一、类型安全、声明式的方式来查询各种数据源。通过合理使用LINQ，可以编写出简洁、易读、高效的数据处理代码。&lt;/p&gt;
&lt;h2&gt;&amp;lt;a id=&quot;orm-frameworks&quot;&amp;gt;&amp;lt;/a&amp;gt;C# ORM框架详解&lt;/h2&gt;
&lt;p&gt;ORM（Object-Relational Mapping，对象关系映射）是一种编程技术，用于在面向对象编程语言和关系数据库之间建立映射关系。在C#中，从基础的ADO.NET到现代的Entity Framework，提供了多种数据访问方式。本章将从ADO.NET基础开始，逐步介绍Entity Framework 6的完整功能。&lt;/p&gt;
&lt;h3&gt;&amp;lt;a id=&quot;ado-net-basics&quot;&amp;gt;&amp;lt;/a&amp;gt;ADO.NET基础&lt;/h3&gt;
&lt;p&gt;ADO.NET是.NET Framework中用于访问数据库的核心技术，它提供了直接访问数据库的能力，是其他ORM框架的基础。&lt;/p&gt;
&lt;h4&gt;ADO.NET架构&lt;/h4&gt;
&lt;p&gt;ADO.NET采用断开式架构，主要组件包括：&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;ADO.NET架构
├── .NET Framework数据提供程序
│   ├── SqlClient（SQL Server）
│   ├── OleDb（OLE DB数据源）
│   ├── Odbc（ODBC数据源）
│   └── OracleClient（Oracle）
├── DataSet和DataTable（断开式数据）
└── 数据访问类
    ├── Connection（连接）
    ├── Command（命令）
    ├── DataReader（数据读取器）
    └── DataAdapter（数据适配器）
&lt;/code&gt;&lt;/pre&gt;
&lt;h4&gt;命名空间&lt;/h4&gt;
&lt;pre&gt;&lt;code&gt;using System.Data;                    // 核心数据类
using System.Data.SqlClient;          // SQL Server提供程序
using System.Data.Common;             // 通用数据访问类
using System.Data.SqlTypes;           // SQL Server数据类型
&lt;/code&gt;&lt;/pre&gt;
&lt;h3&gt;&amp;lt;a id=&quot;ado-net-connection&quot;&amp;gt;&amp;lt;/a&amp;gt;ADO.NET连接管理&lt;/h3&gt;
&lt;h4&gt;SqlConnection类&lt;/h4&gt;
&lt;p&gt;&lt;code&gt;SqlConnection&lt;/code&gt;用于建立与SQL Server数据库的连接。&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;using System.Data.SqlClient;

// 连接字符串
string connectionString = &quot;Server=localhost;Database=MyDB;User Id=sa;Password=123456;&quot;;
// 或者使用集成安全
string connectionString2 = &quot;Server=localhost;Database=MyDB;Integrated Security=True;&quot;;
// 或者使用连接字符串构建器
SqlConnectionStringBuilder builder = new SqlConnectionStringBuilder
{
    DataSource = &quot;localhost&quot;,
    InitialCatalog = &quot;MyDB&quot;,
    UserID = &quot;sa&quot;,
    Password = &quot;123456&quot;,
    ConnectTimeout = 30,
    Encrypt = true,
    TrustServerCertificate = false
};
string connectionString3 = builder.ConnectionString;

// 创建连接
SqlConnection connection = new SqlConnection(connectionString);

// 打开连接
try
{
    connection.Open();
    Console.WriteLine(&quot;连接成功&quot;);
    Console.WriteLine($&quot;数据库: {connection.Database}&quot;);
    Console.WriteLine($&quot;服务器: {connection.DataSource}&quot;);
    Console.WriteLine($&quot;状态: {connection.State}&quot;);
    Console.WriteLine($&quot;服务器版本: {connection.ServerVersion}&quot;);
}
catch (SqlException ex)
{
    Console.WriteLine($&quot;连接失败: {ex.Message}&quot;);
}
finally
{
    // 关闭连接
    if (connection.State == ConnectionState.Open)
    {
        connection.Close();
    }
    connection.Dispose();
}

// 使用using语句（推荐）
using (SqlConnection conn = new SqlConnection(connectionString))
{
    conn.Open();
    // 使用连接
    // 自动关闭和释放
}
&lt;/code&gt;&lt;/pre&gt;
&lt;h4&gt;连接字符串详解&lt;/h4&gt;
&lt;pre&gt;&lt;code&gt;// 基本连接字符串
string basic = &quot;Server=localhost;Database=MyDB;User Id=sa;Password=123456;&quot;;

// 完整连接字符串（包含所有常用选项）
string full = @&quot;Server=localhost,1433;
                Database=MyDB;
                User Id=sa;
                Password=123456;
                Connect Timeout=30;
                Encrypt=True;
                TrustServerCertificate=False;
                Integrated Security=False;
                MultipleActiveResultSets=True;
                Pooling=True;
                Min Pool Size=5;
                Max Pool Size=100;
                Connection Lifetime=0;
                Application Name=MyApp&quot;;

// 使用连接字符串构建器（类型安全）
SqlConnectionStringBuilder csb = new SqlConnectionStringBuilder
{
    // 服务器和数据库
    DataSource = &quot;localhost&quot;,
    InitialCatalog = &quot;MyDB&quot;,
    
    // 身份验证
    IntegratedSecurity = false,
    UserID = &quot;sa&quot;,
    Password = &quot;123456&quot;,
    
    // 连接选项
    ConnectTimeout = 30,
    Encrypt = true,
    TrustServerCertificate = false,
    MultipleActiveResultSets = true,
    
    // 连接池
    Pooling = true,
    MinPoolSize = 5,
    MaxPoolSize = 100,
    ConnectionLifetime = 0,
    
    // 应用程序信息
    ApplicationName = &quot;MyApplication&quot;
};

string connectionString = csb.ConnectionString;
&lt;/code&gt;&lt;/pre&gt;
&lt;h4&gt;连接池管理&lt;/h4&gt;
&lt;p&gt;ADO.NET自动管理连接池，提高性能：&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;// 连接池默认启用
// 相同连接字符串的连接会被放入池中重用

// 检查连接池状态
string poolInfo = $&quot;当前连接池中的连接数: {GetConnectionPoolInfo()}&quot;;

// 手动控制连接池
SqlConnectionStringBuilder builder = new SqlConnectionStringBuilder
{
    DataSource = &quot;localhost&quot;,
    InitialCatalog = &quot;MyDB&quot;,
    IntegratedSecurity = true,
    Pooling = true,        // 启用连接池（默认）
    MinPoolSize = 5,       // 最小连接数
    MaxPoolSize = 100,     // 最大连接数
    ConnectionLifetime = 0 // 连接生命周期（0表示不限制）
};

// 清空连接池（谨慎使用）
SqlConnection.ClearPool(connection);
SqlConnection.ClearAllPools(); // 清空所有连接池
&lt;/code&gt;&lt;/pre&gt;
&lt;h3&gt;&amp;lt;a id=&quot;ado-net-commands&quot;&amp;gt;&amp;lt;/a&amp;gt;ADO.NET命令执行&lt;/h3&gt;
&lt;h4&gt;SqlCommand类&lt;/h4&gt;
&lt;p&gt;&lt;code&gt;SqlCommand&lt;/code&gt;用于执行SQL命令。&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;using (SqlConnection connection = new SqlConnection(connectionString))
{
    connection.Open();
    
    // 方式1：使用构造函数
    SqlCommand command = new SqlCommand(&quot;SELECT * FROM Users&quot;, connection);
    
    // 方式2：使用CommandText属性
    SqlCommand command2 = new SqlCommand();
    command2.Connection = connection;
    command2.CommandText = &quot;SELECT * FROM Users&quot;;
    
    // 方式3：使用CommandType
    SqlCommand command3 = new SqlCommand(&quot;GetAllUsers&quot;, connection);
    command3.CommandType = CommandType.StoredProcedure; // 存储过程
    
    // 执行命令
    // ExecuteNonQuery：执行不返回数据的命令（INSERT、UPDATE、DELETE）
    int rowsAffected = command.ExecuteNonQuery();
    
    // ExecuteScalar：执行返回单个值的命令
    object result = command.ExecuteScalar();
    
    // ExecuteReader：执行返回数据集的命令（SELECT）
    SqlDataReader reader = command.ExecuteReader();
}
&lt;/code&gt;&lt;/pre&gt;
&lt;h4&gt;执行不同类型的命令&lt;/h4&gt;
&lt;pre&gt;&lt;code&gt;using (SqlConnection connection = new SqlConnection(connectionString))
{
    connection.Open();
    
    // 1. SELECT查询（使用ExecuteReader）
    SqlCommand selectCmd = new SqlCommand(&quot;SELECT Id, Name, Email FROM Users&quot;, connection);
    using (SqlDataReader reader = selectCmd.ExecuteReader())
    {
        while (reader.Read())
        {
            int id = reader.GetInt32(0);
            string name = reader.GetString(1);
            string email = reader.GetString(2);
            Console.WriteLine($&quot;ID: {id}, Name: {name}, Email: {email}&quot;);
        }
    }
    
    // 2. INSERT命令（使用ExecuteNonQuery）
    SqlCommand insertCmd = new SqlCommand(
        &quot;INSERT INTO Users (Name, Email) VALUES (@Name, @Email)&quot;, 
        connection);
    insertCmd.Parameters.AddWithValue(&quot;@Name&quot;, &quot;张三&quot;);
    insertCmd.Parameters.AddWithValue(&quot;@Email&quot;, &quot;zhangsan@example.com&quot;);
    int insertedRows = insertCmd.ExecuteNonQuery();
    Console.WriteLine($&quot;插入了 {insertedRows} 行&quot;);
    
    // 3. UPDATE命令
    SqlCommand updateCmd = new SqlCommand(
        &quot;UPDATE Users SET Email = @Email WHERE Id = @Id&quot;, 
        connection);
    updateCmd.Parameters.AddWithValue(&quot;@Email&quot;, &quot;newemail@example.com&quot;);
    updateCmd.Parameters.AddWithValue(&quot;@Id&quot;, 1);
    int updatedRows = updateCmd.ExecuteNonQuery();
    Console.WriteLine($&quot;更新了 {updatedRows} 行&quot;);
    
    // 4. DELETE命令
    SqlCommand deleteCmd = new SqlCommand(&quot;DELETE FROM Users WHERE Id = @Id&quot;, connection);
    deleteCmd.Parameters.AddWithValue(&quot;@Id&quot;, 1);
    int deletedRows = deleteCmd.ExecuteNonQuery();
    Console.WriteLine($&quot;删除了 {deletedRows} 行&quot;);
    
    // 5. 返回单个值（使用ExecuteScalar）
    SqlCommand countCmd = new SqlCommand(&quot;SELECT COUNT(*) FROM Users&quot;, connection);
    int userCount = (int)countCmd.ExecuteScalar();
    Console.WriteLine($&quot;用户总数: {userCount}&quot;);
    
    // 6. 返回OUTPUT参数
    SqlCommand outputCmd = new SqlCommand(
        &quot;INSERT INTO Users (Name, Email) OUTPUT INSERTED.Id VALUES (@Name, @Email)&quot;, 
        connection);
    outputCmd.Parameters.AddWithValue(&quot;@Name&quot;, &quot;李四&quot;);
    outputCmd.Parameters.AddWithValue(&quot;@Email&quot;, &quot;lisi@example.com&quot;);
    int newId = (int)outputCmd.ExecuteScalar();
    Console.WriteLine($&quot;新插入的ID: {newId}&quot;);
}
&lt;/code&gt;&lt;/pre&gt;
&lt;h3&gt;&amp;lt;a id=&quot;ado-net-data-reader&quot;&amp;gt;&amp;lt;/a&amp;gt;ADO.NET数据读取&lt;/h3&gt;
&lt;h4&gt;SqlDataReader类&lt;/h4&gt;
&lt;p&gt;&lt;code&gt;SqlDataReader&lt;/code&gt;提供只进、只读的数据流访问。&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;using (SqlConnection connection = new SqlConnection(connectionString))
{
    connection.Open();
    
    SqlCommand command = new SqlCommand(&quot;SELECT * FROM Users&quot;, connection);
    
    // 执行查询并获取DataReader
    using (SqlDataReader reader = command.ExecuteReader())
    {
        // 检查是否有数据
        if (reader.HasRows)
        {
            // 读取列信息
            for (int i = 0; i &amp;lt; reader.FieldCount; i++)
            {
                Console.WriteLine($&quot;列 {i}: {reader.GetName(i)}, 类型: {reader.GetFieldType(i)}&quot;);
            }
            
            // 逐行读取数据
            while (reader.Read())
            {
                // 方式1：使用索引（最快）
                int id = reader.GetInt32(0);
                string name = reader.GetString(1);
                
                // 方式2：使用列名（更安全）
                int id2 = reader.GetInt32(&quot;Id&quot;);
                string name2 = reader.GetString(&quot;Name&quot;);
                
                // 方式3：使用IsDBNull检查空值
                string email = reader.IsDBNull(&quot;Email&quot;) 
                    ? null 
                    : reader.GetString(&quot;Email&quot;);
                
                // 方式4：使用索引器
                object idObj = reader[&quot;Id&quot;];
                object nameObj = reader[&quot;Name&quot;];
                
                Console.WriteLine($&quot;ID: {id}, Name: {name}, Email: {email}&quot;);
            }
        }
    }
}
&lt;/code&gt;&lt;/pre&gt;
&lt;h4&gt;DataReader的常用方法&lt;/h4&gt;
&lt;pre&gt;&lt;code&gt;using (SqlDataReader reader = command.ExecuteReader())
{
    while (reader.Read())
    {
        // 获取值的方法（按类型）
        int intValue = reader.GetInt32(&quot;Id&quot;);
        long longValue = reader.GetInt64(&quot;BigId&quot;);
        string stringValue = reader.GetString(&quot;Name&quot;);
        bool boolValue = reader.GetBoolean(&quot;IsActive&quot;);
        DateTime dateValue = reader.GetDateTime(&quot;CreateDate&quot;);
        decimal decimalValue = reader.GetDecimal(&quot;Price&quot;);
        double doubleValue = reader.GetDouble(&quot;Rate&quot;);
        float floatValue = reader.GetFloat(&quot;Score&quot;);
        byte[] bytes = (byte[])reader[&quot;ImageData&quot;];
        Guid guidValue = reader.GetGuid(&quot;GuidId&quot;);
        
        // 获取值（通用方法）
        object value = reader.GetValue(&quot;ColumnName&quot;);
        string valueAsString = reader.GetValue(&quot;ColumnName&quot;).ToString();
        
        // 检查是否为NULL
        if (!reader.IsDBNull(&quot;Email&quot;))
        {
            string email = reader.GetString(&quot;Email&quot;);
        }
        
        // 获取列索引
        int columnIndex = reader.GetOrdinal(&quot;Name&quot;);
        string name = reader.GetString(columnIndex);
        
        // 获取列名
        string columnName = reader.GetName(0);
        
        // 获取列类型
        Type columnType = reader.GetFieldType(0);
        
        // 获取数据类型名称
        string dataTypeName = reader.GetDataTypeName(0);
    }
}
&lt;/code&gt;&lt;/pre&gt;
&lt;h4&gt;读取多个结果集&lt;/h4&gt;
&lt;pre&gt;&lt;code&gt;using (SqlConnection connection = new SqlConnection(connectionString))
{
    connection.Open();
    
    SqlCommand command = new SqlCommand(
        &quot;SELECT * FROM Users; SELECT * FROM Orders;&quot;, 
        connection);
    
    using (SqlDataReader reader = command.ExecuteReader())
    {
        // 第一个结果集：Users
        Console.WriteLine(&quot;=== 用户列表 ===&quot;);
        while (reader.Read())
        {
            Console.WriteLine($&quot;用户: {reader.GetString(&quot;Name&quot;)}&quot;);
        }
        
        // 移动到下一个结果集
        if (reader.NextResult())
        {
            Console.WriteLine(&quot;=== 订单列表 ===&quot;);
            while (reader.Read())
            {
                Console.WriteLine($&quot;订单ID: {reader.GetInt32(&quot;Id&quot;)}&quot;);
            }
        }
    }
}
&lt;/code&gt;&lt;/pre&gt;
&lt;h3&gt;&amp;lt;a id=&quot;ado-net-parameters&quot;&amp;gt;&amp;lt;/a&amp;gt;ADO.NET参数化查询&lt;/h3&gt;
&lt;p&gt;参数化查询是防止SQL注入攻击的最佳实践，同时提高查询性能。&lt;/p&gt;
&lt;h4&gt;SqlParameter类&lt;/h4&gt;
&lt;pre&gt;&lt;code&gt;using (SqlConnection connection = new SqlConnection(connectionString))
{
    connection.Open();
    
    // 方式1：使用AddWithValue（简单但不推荐用于生产环境）
    SqlCommand command1 = new SqlCommand(
        &quot;SELECT * FROM Users WHERE Name = @Name AND Age &amp;gt; @Age&quot;, 
        connection);
    command1.Parameters.AddWithValue(&quot;@Name&quot;, &quot;张三&quot;);
    command1.Parameters.AddWithValue(&quot;@Age&quot;, 18);
    
    // 方式2：使用Add方法（推荐）
    SqlCommand command2 = new SqlCommand(
        &quot;INSERT INTO Users (Name, Email, Age, CreateDate) VALUES (@Name, @Email, @Age, @CreateDate)&quot;, 
        connection);
    
    // 添加参数并指定类型和值
    command2.Parameters.Add(&quot;@Name&quot;, SqlDbType.NVarChar, 100).Value = &quot;张三&quot;;
    command2.Parameters.Add(&quot;@Email&quot;, SqlDbType.NVarChar, 200).Value = &quot;zhangsan@example.com&quot;;
    command2.Parameters.Add(&quot;@Age&quot;, SqlDbType.Int).Value = 25;
    command2.Parameters.Add(&quot;@CreateDate&quot;, SqlDbType.DateTime).Value = DateTime.Now;
    
    // 方式3：创建SqlParameter对象
    SqlParameter nameParam = new SqlParameter(&quot;@Name&quot;, SqlDbType.NVarChar, 100)
    {
        Value = &quot;张三&quot;,
        Direction = ParameterDirection.Input
    };
    command2.Parameters.Add(nameParam);
    
    // 执行命令
    int rowsAffected = command2.ExecuteNonQuery();
}
&lt;/code&gt;&lt;/pre&gt;
&lt;h4&gt;参数类型和方向&lt;/h4&gt;
&lt;pre&gt;&lt;code&gt;using (SqlConnection connection = new SqlConnection(connectionString))
{
    connection.Open();
    
    // 输入参数（默认）
    SqlCommand command = new SqlCommand(
        &quot;INSERT INTO Users (Name, Email) VALUES (@Name, @Email); SELECT SCOPE_IDENTITY();&quot;, 
        connection);
    
    SqlParameter nameParam = new SqlParameter(&quot;@Name&quot;, SqlDbType.NVarChar, 100)
    {
        Value = &quot;张三&quot;,
        Direction = ParameterDirection.Input // 输入参数
    };
    command.Parameters.Add(nameParam);
    
    // 输出参数
    SqlParameter idParam = new SqlParameter(&quot;@NewId&quot;, SqlDbType.Int)
    {
        Direction = ParameterDirection.Output // 输出参数
    };
    command.Parameters.Add(idParam);
    
    // 输入输出参数
    SqlParameter countParam = new SqlParameter(&quot;@Count&quot;, SqlDbType.Int)
    {
        Value = 0,
        Direction = ParameterDirection.InputOutput // 输入输出参数
    };
    command.Parameters.Add(countParam);
    
    // 返回值参数
    SqlParameter returnParam = new SqlParameter(&quot;@ReturnValue&quot;, SqlDbType.Int)
    {
        Direction = ParameterDirection.ReturnValue // 返回值
    };
    command.Parameters.Add(returnParam);
    
    command.ExecuteNonQuery();
    
    // 获取输出参数值
    int newId = (int)idParam.Value;
    int count = (int)countParam.Value;
    int returnValue = (int)returnParam.Value;
}
&lt;/code&gt;&lt;/pre&gt;
&lt;h4&gt;参数化查询完整示例&lt;/h4&gt;
&lt;pre&gt;&lt;code&gt;// 用户实体类
public class User
{
    public int Id { get; set; }
    public string Name { get; set; }
    public string Email { get; set; }
    public int Age { get; set; }
    public DateTime CreateDate { get; set; }
}

// 查询示例
public List&amp;lt;User&amp;gt; GetUsers(string nameFilter, int minAge)
{
    List&amp;lt;User&amp;gt; users = new List&amp;lt;User&amp;gt;();
    
    using (SqlConnection connection = new SqlConnection(connectionString))
    {
        connection.Open();
        
        SqlCommand command = new SqlCommand(
            &quot;SELECT Id, Name, Email, Age FROM Users WHERE Name LIKE @Name AND Age &amp;gt;= @MinAge&quot;, 
            connection);
        
        // 使用LIKE时，通配符在值中
        command.Parameters.Add(&quot;@Name&quot;, SqlDbType.NVarChar, 100).Value = $&quot;%{nameFilter}%&quot;;
        command.Parameters.Add(&quot;@MinAge&quot;, SqlDbType.Int).Value = minAge;
        
        using (SqlDataReader reader = command.ExecuteReader())
        {
            while (reader.Read())
            {
                users.Add(new User
                {
                    Id = reader.GetInt32(&quot;Id&quot;),
                    Name = reader.GetString(&quot;Name&quot;),
                    Email = reader.GetString(&quot;Email&quot;),
                    Age = reader.GetInt32(&quot;Age&quot;)
                });
            }
        }
    }
    
    return users;
}

// 插入示例
public int InsertUser(User user)
{
    using (SqlConnection connection = new SqlConnection(connectionString))
    {
        connection.Open();
        
        SqlCommand command = new SqlCommand(
            &quot;INSERT INTO Users (Name, Email, Age, CreateDate) &quot; +
            &quot;VALUES (@Name, @Email, @Age, @CreateDate); &quot; +
            &quot;SELECT SCOPE_IDENTITY();&quot;, 
            connection);
        
        command.Parameters.Add(&quot;@Name&quot;, SqlDbType.NVarChar, 100).Value = user.Name;
        command.Parameters.Add(&quot;@Email&quot;, SqlDbType.NVarChar, 200).Value = user.Email;
        command.Parameters.Add(&quot;@Age&quot;, SqlDbType.Int).Value = user.Age;
        command.Parameters.Add(&quot;@CreateDate&quot;, SqlDbType.DateTime).Value = DateTime.Now;
        
        object result = command.ExecuteScalar();
        return Convert.ToInt32(result);
    }
}

// 更新示例
public int UpdateUser(User user)
{
    using (SqlConnection connection = new SqlConnection(connectionString))
    {
        connection.Open();
        
        SqlCommand command = new SqlCommand(
            &quot;UPDATE Users SET Name = @Name, Email = @Email, Age = @Age WHERE Id = @Id&quot;, 
            connection);
        
        command.Parameters.Add(&quot;@Id&quot;, SqlDbType.Int).Value = user.Id;
        command.Parameters.Add(&quot;@Name&quot;, SqlDbType.NVarChar, 100).Value = user.Name;
        command.Parameters.Add(&quot;@Email&quot;, SqlDbType.NVarChar, 200).Value = user.Email;
        command.Parameters.Add(&quot;@Age&quot;, SqlDbType.Int).Value = user.Age;
        
        return command.ExecuteNonQuery();
    }
}

// 删除示例
public int DeleteUser(int userId)
{
    using (SqlConnection connection = new SqlConnection(connectionString))
    {
        connection.Open();
        
        SqlCommand command = new SqlCommand(&quot;DELETE FROM Users WHERE Id = @Id&quot;, connection);
        command.Parameters.Add(&quot;@Id&quot;, SqlDbType.Int).Value = userId;
        
        return command.ExecuteNonQuery();
    }
}
&lt;/code&gt;&lt;/pre&gt;
&lt;h3&gt;&amp;lt;a id=&quot;ado-net-transactions&quot;&amp;gt;&amp;lt;/a&amp;gt;ADO.NET事务处理&lt;/h3&gt;
&lt;p&gt;事务确保数据库操作的原子性、一致性、隔离性和持久性（ACID特性）。&lt;/p&gt;
&lt;h4&gt;基本事务&lt;/h4&gt;
&lt;pre&gt;&lt;code&gt;using (SqlConnection connection = new SqlConnection(connectionString))
{
    connection.Open();
    
    // 开始事务
    SqlTransaction transaction = connection.BeginTransaction();
    
    try
    {
        // 命令1：插入用户
        SqlCommand cmd1 = new SqlCommand(
            &quot;INSERT INTO Users (Name, Email) VALUES (@Name, @Email)&quot;, 
            connection, 
            transaction);
        cmd1.Parameters.AddWithValue(&quot;@Name&quot;, &quot;张三&quot;);
        cmd1.Parameters.AddWithValue(&quot;@Email&quot;, &quot;zhangsan@example.com&quot;);
        cmd1.ExecuteNonQuery();
        
        // 命令2：插入订单
        SqlCommand cmd2 = new SqlCommand(
            &quot;INSERT INTO Orders (UserId, TotalAmount) VALUES (@UserId, @TotalAmount)&quot;, 
            connection, 
            transaction);
        cmd2.Parameters.AddWithValue(&quot;@UserId&quot;, 1);
        cmd2.Parameters.AddWithValue(&quot;@TotalAmount&quot;, 100.00m);
        cmd2.ExecuteNonQuery();
        
        // 提交事务
        transaction.Commit();
        Console.WriteLine(&quot;事务提交成功&quot;);
    }
    catch (Exception ex)
    {
        // 回滚事务
        transaction.Rollback();
        Console.WriteLine($&quot;事务回滚: {ex.Message}&quot;);
    }
}
&lt;/code&gt;&lt;/pre&gt;
&lt;h4&gt;事务隔离级别&lt;/h4&gt;
&lt;pre&gt;&lt;code&gt;using (SqlConnection connection = new SqlConnection(connectionString))
{
    connection.Open();
    
    // 设置事务隔离级别
    SqlTransaction transaction = connection.BeginTransaction(IsolationLevel.ReadCommitted);
    
    // 隔离级别选项：
    // ReadUncommitted：读取未提交的数据（最低隔离级别，可能脏读）
    // ReadCommitted：读取已提交的数据（默认，SQL Server，避免脏读）
    // RepeatableRead：可重复读（避免脏读和不可重复读）
    // Serializable：序列化（最高隔离级别，避免所有并发问题，但性能最低）
    // Snapshot：快照隔离（SQL Server 2005+，使用行版本控制）
    
    try
    {
        SqlCommand command = new SqlCommand(&quot;UPDATE Users SET Name = @Name WHERE Id = @Id&quot;, 
            connection, transaction);
        command.Parameters.AddWithValue(&quot;@Name&quot;, &quot;新名称&quot;);
        command.Parameters.AddWithValue(&quot;@Id&quot;, 1);
        command.ExecuteNonQuery();
        
        transaction.Commit();
    }
    catch
    {
        transaction.Rollback();
        throw;
    }
}
&lt;/code&gt;&lt;/pre&gt;
&lt;h4&gt;嵌套事务（保存点）&lt;/h4&gt;
&lt;pre&gt;&lt;code&gt;using (SqlConnection connection = new SqlConnection(connectionString))
{
    connection.Open();
    SqlTransaction transaction = connection.BeginTransaction();
    
    try
    {
        // 第一个操作
        SqlCommand cmd1 = new SqlCommand(&quot;INSERT INTO Users (Name) VALUES (&apos;用户1&apos;)&quot;, 
            connection, transaction);
        cmd1.ExecuteNonQuery();
        
        // 创建保存点
        transaction.Save(&quot;SavePoint1&quot;);
        
        try
        {
            // 第二个操作（可能失败）
            SqlCommand cmd2 = new SqlCommand(&quot;INSERT INTO Users (Name) VALUES (&apos;用户2&apos;)&quot;, 
                connection, transaction);
            cmd2.ExecuteNonQuery();
        }
        catch
        {
            // 回滚到保存点（只回滚保存点之后的操作）
            transaction.Rollback(&quot;SavePoint1&quot;);
            Console.WriteLine(&quot;回滚到保存点，第一个操作仍然有效&quot;);
        }
        
        // 提交事务
        transaction.Commit();
    }
    catch
    {
        transaction.Rollback();
        throw;
    }
}
&lt;/code&gt;&lt;/pre&gt;
&lt;h3&gt;&amp;lt;a id=&quot;ado-net-stored-procedures&quot;&amp;gt;&amp;lt;/a&amp;gt;ADO.NET存储过程&lt;/h3&gt;
&lt;p&gt;存储过程是预编译的SQL代码，可以提高性能、安全性和代码重用性。&lt;/p&gt;
&lt;h4&gt;执行存储过程&lt;/h4&gt;
&lt;pre&gt;&lt;code&gt;// 创建存储过程（SQL Server示例）
/*
CREATE PROCEDURE GetUserById
    @UserId INT
AS
BEGIN
    SELECT Id, Name, Email, Age, CreateDate
    FROM Users 
    WHERE Id = @UserId
END

CREATE PROCEDURE InsertUser
    @Name NVARCHAR(100),
    @Email NVARCHAR(200),
    @Age INT,
    @NewId INT OUTPUT
AS
BEGIN
    INSERT INTO Users (Name, Email, Age, CreateDate)
    VALUES (@Name, @Email, @Age, GETDATE())
    
    SET @NewId = SCOPE_IDENTITY()
END

CREATE PROCEDURE GetUsersByAgeRange
    @MinAge INT,
    @MaxAge INT
AS
BEGIN
    SELECT Id, Name, Email, Age
    FROM Users
    WHERE Age BETWEEN @MinAge AND @MaxAge
    ORDER BY Age
END
*/

// 执行存储过程（查询）
public User GetUserById(int userId)
{
    using (SqlConnection connection = new SqlConnection(connectionString))
    {
        connection.Open();
        
        SqlCommand command = new SqlCommand(&quot;GetUserById&quot;, connection);
        command.CommandType = CommandType.StoredProcedure;
        command.Parameters.Add(&quot;@UserId&quot;, SqlDbType.Int).Value = userId;
        
        using (SqlDataReader reader = command.ExecuteReader())
        {
            if (reader.Read())
            {
                return new User
                {
                    Id = reader.GetInt32(&quot;Id&quot;),
                    Name = reader.GetString(&quot;Name&quot;),
                    Email = reader.GetString(&quot;Email&quot;),
                    Age = reader.GetInt32(&quot;Age&quot;),
                    CreateDate = reader.GetDateTime(&quot;CreateDate&quot;)
                };
            }
        }
    }
    return null;
}

// 执行存储过程（带输出参数）
public int InsertUserWithStoredProcedure(User user)
{
    using (SqlConnection connection = new SqlConnection(connectionString))
    {
        connection.Open();
        
        SqlCommand command = new SqlCommand(&quot;InsertUser&quot;, connection);
        command.CommandType = CommandType.StoredProcedure;
        
        // 输入参数
        command.Parameters.Add(&quot;@Name&quot;, SqlDbType.NVarChar, 100).Value = user.Name;
        command.Parameters.Add(&quot;@Email&quot;, SqlDbType.NVarChar, 200).Value = user.Email;
        command.Parameters.Add(&quot;@Age&quot;, SqlDbType.Int).Value = user.Age;
        
        // 输出参数
        SqlParameter newIdParam = new SqlParameter(&quot;@NewId&quot;, SqlDbType.Int)
        {
            Direction = ParameterDirection.Output
        };
        command.Parameters.Add(newIdParam);
        
        command.ExecuteNonQuery();
        
        // 获取输出参数值
        return (int)newIdParam.Value;
    }
}

// 执行存储过程（返回多个结果集）
public void GetUsersAndOrders(int userId)
{
    using (SqlConnection connection = new SqlConnection(connectionString))
    {
        connection.Open();
        
        SqlCommand command = new SqlCommand(&quot;GetUserWithOrders&quot;, connection);
        command.CommandType = CommandType.StoredProcedure;
        command.Parameters.Add(&quot;@UserId&quot;, SqlDbType.Int).Value = userId;
        
        using (SqlDataReader reader = command.ExecuteReader())
        {
            // 第一个结果集：用户信息
            if (reader.Read())
            {
                Console.WriteLine($&quot;用户: {reader.GetString(&quot;Name&quot;)}&quot;);
            }
            
            // 第二个结果集：订单信息
            if (reader.NextResult())
            {
                while (reader.Read())
                {
                    Console.WriteLine($&quot;订单: {reader.GetInt32(&quot;Id&quot;)}&quot;);
                }
            }
        }
    }
}
&lt;/code&gt;&lt;/pre&gt;
&lt;h4&gt;使用DataAdapter填充DataSet&lt;/h4&gt;
&lt;pre&gt;&lt;code&gt;using System.Data;

// 使用DataAdapter填充DataSet（断开式数据访问）
public DataSet GetUsersDataSet()
{
    DataSet dataSet = new DataSet();
    
    using (SqlConnection connection = new SqlConnection(connectionString))
    {
        connection.Open();
        
        SqlDataAdapter adapter = new SqlDataAdapter(&quot;SELECT * FROM Users&quot;, connection);
        
        // 填充DataSet
        adapter.Fill(dataSet, &quot;Users&quot;);
        
        // 可以填充多个表
        adapter.SelectCommand.CommandText = &quot;SELECT * FROM Orders&quot;;
        adapter.Fill(dataSet, &quot;Orders&quot;);
    }
    
    return dataSet;
}

// 使用DataAdapter更新数据
public void UpdateUsersWithAdapter(DataTable dataTable)
{
    using (SqlConnection connection = new SqlConnection(connectionString))
    {
        connection.Open();
        
        SqlDataAdapter adapter = new SqlDataAdapter();
        
        // 配置SelectCommand
        adapter.SelectCommand = new SqlCommand(&quot;SELECT * FROM Users&quot;, connection);
        
        // 配置InsertCommand
        adapter.InsertCommand = new SqlCommand(
            &quot;INSERT INTO Users (Name, Email, Age, CreateDate) VALUES (@Name, @Email, @Age, @CreateDate)&quot;, 
            connection);
        adapter.InsertCommand.Parameters.Add(&quot;@Name&quot;, SqlDbType.NVarChar, 100, &quot;Name&quot;);
        adapter.InsertCommand.Parameters.Add(&quot;@Email&quot;, SqlDbType.NVarChar, 200, &quot;Email&quot;);
        adapter.InsertCommand.Parameters.Add(&quot;@Age&quot;, SqlDbType.Int, 0, &quot;Age&quot;);
        adapter.InsertCommand.Parameters.Add(&quot;@CreateDate&quot;, SqlDbType.DateTime, 0, &quot;CreateDate&quot;);
        
        // 配置UpdateCommand
        adapter.UpdateCommand = new SqlCommand(
            &quot;UPDATE Users SET Name = @Name, Email = @Email, Age = @Age WHERE Id = @Id&quot;, 
            connection);
        adapter.UpdateCommand.Parameters.Add(&quot;@Name&quot;, SqlDbType.NVarChar, 100, &quot;Name&quot;);
        adapter.UpdateCommand.Parameters.Add(&quot;@Email&quot;, SqlDbType.NVarChar, 200, &quot;Email&quot;);
        adapter.UpdateCommand.Parameters.Add(&quot;@Age&quot;, SqlDbType.Int, 0, &quot;Age&quot;);
        SqlParameter idParam = adapter.UpdateCommand.Parameters.Add(&quot;@Id&quot;, SqlDbType.Int, 0, &quot;Id&quot;);
        idParam.SourceVersion = DataRowVersion.Original; // 使用原始值
        
        // 配置DeleteCommand
        adapter.DeleteCommand = new SqlCommand(&quot;DELETE FROM Users WHERE Id = @Id&quot;, connection);
        SqlParameter deleteIdParam = adapter.DeleteCommand.Parameters.Add(&quot;@Id&quot;, SqlDbType.Int, 0, &quot;Id&quot;);
        deleteIdParam.SourceVersion = DataRowVersion.Original;
        
        // 执行更新
        int rowsAffected = adapter.Update(dataTable);
        Console.WriteLine($&quot;更新了 {rowsAffected} 行&quot;);
    }
}
&lt;/code&gt;&lt;/pre&gt;
&lt;h3&gt;&amp;lt;a id=&quot;ef6-basics&quot;&amp;gt;&amp;lt;/a&amp;gt;Entity Framework 6基础&lt;/h3&gt;
&lt;p&gt;Entity Framework 6（EF6）是Microsoft开发的ORM框架，它允许开发者使用面向对象的方式操作数据库，而无需编写大量的SQL代码。&lt;/p&gt;
&lt;h4&gt;Entity Framework 6概述&lt;/h4&gt;
&lt;p&gt;Entity Framework 6的主要特点：&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;strong&gt;Code First&lt;/strong&gt;：通过代码定义模型，EF自动创建数据库&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Database First&lt;/strong&gt;：从现有数据库生成模型&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Model First&lt;/strong&gt;：使用设计器创建模型&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;LINQ支持&lt;/strong&gt;：使用LINQ查询数据库&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;变更跟踪&lt;/strong&gt;：自动跟踪实体状态变化&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;迁移支持&lt;/strong&gt;：数据库架构版本管理&lt;/li&gt;
&lt;/ul&gt;
&lt;h4&gt;安装Entity Framework 6&lt;/h4&gt;
&lt;pre&gt;&lt;code&gt;// 通过NuGet包管理器安装
// Install-Package EntityFramework

// 或者在Package Manager Console中执行：
// PM&amp;gt; Install-Package EntityFramework -Version 6.4.4

// 安装后会自动添加引用：
// using System.Data.Entity;
&lt;/code&gt;&lt;/pre&gt;
&lt;h4&gt;Code First开发流程（代码先行）&lt;/h4&gt;
&lt;p&gt;Code First是Entity Framework的一种开发模式，开发者先定义实体类和DbContext，然后由EF自动创建数据库。这种方式适合新项目开发，可以完全通过代码控制数据库结构。&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;Code First开发步骤：&lt;/strong&gt;&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;步骤1：定义实体类&lt;/strong&gt;&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;using System;
using System.ComponentModel.DataAnnotations;
using System.ComponentModel.DataAnnotations.Schema;

public class User
{
    [Key]
    public int Id { get; set; }
    
    [Required]
    [MaxLength(100)]
    public string Name { get; set; }
    
    [Required]
    [MaxLength(200)]
    [Index(IsUnique = true)]
    public string Email { get; set; }
    
    public int Age { get; set; }
    
    public DateTime CreateDate { get; set; }
    
    public virtual ICollection&amp;lt;Order&amp;gt; Orders { get; set; }
    
    public User()
    {
        Orders = new HashSet&amp;lt;Order&amp;gt;();
        CreateDate = DateTime.Now;
    }
}
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;&lt;strong&gt;步骤2：定义DbContext&lt;/strong&gt;&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;using System.Data.Entity;

public class MyDbContext : DbContext
{
    public MyDbContext() : base(&quot;DefaultConnection&quot;)
    {
        // 开发阶段：自动创建数据库（如果不存在）
        Database.SetInitializer(new CreateDatabaseIfNotExists&amp;lt;MyDbContext&amp;gt;());
        
        // 或者：删除并重新创建数据库（仅开发环境，会丢失数据）
        // Database.SetInitializer(new DropCreateDatabaseIfModelChanges&amp;lt;MyDbContext&amp;gt;());
        
        // 生产环境：禁用自动创建
        // Database.SetInitializer&amp;lt;MyDbContext&amp;gt;(null);
    }
    
    public DbSet&amp;lt;User&amp;gt; Users { get; set; }
    public DbSet&amp;lt;Order&amp;gt; Orders { get; set; }
    
    protected override void OnModelCreating(DbModelBuilder modelBuilder)
    {
        base.OnModelCreating(modelBuilder);
        
        // 使用Fluent API进行额外配置
        modelBuilder.Entity&amp;lt;User&amp;gt;()
            .HasMany(u =&amp;gt; u.Orders)
            .WithRequired(o =&amp;gt; o.User)
            .HasForeignKey(o =&amp;gt; o.UserId)
            .WillCascadeOnDelete(true);
    }
}
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;&lt;strong&gt;步骤3：配置连接字符串（App.config或Web.config）&lt;/strong&gt;&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;&amp;lt;?xml version=&quot;1.0&quot; encoding=&quot;utf-8&quot;?&amp;gt;
&amp;lt;configuration&amp;gt;
  &amp;lt;connectionStrings&amp;gt;
    &amp;lt;add name=&quot;DefaultConnection&quot; 
         connectionString=&quot;Server=localhost;Database=MyCodeFirstDB;Integrated Security=True;&quot; 
         providerName=&quot;System.Data.SqlClient&quot; /&amp;gt;
  &amp;lt;/connectionStrings&amp;gt;
  
  &amp;lt;entityFramework&amp;gt;
    &amp;lt;defaultConnectionFactory type=&quot;System.Data.Entity.Infrastructure.SqlConnectionFactory, EntityFramework&quot; /&amp;gt;
    &amp;lt;providers&amp;gt;
      &amp;lt;provider invariantName=&quot;System.Data.SqlClient&quot; 
                type=&quot;System.Data.Entity.SqlServer.SqlProviderServices, EntityFramework.SqlServer&quot; /&amp;gt;
    &amp;lt;/providers&amp;gt;
  &amp;lt;/entityFramework&amp;gt;
&amp;lt;/configuration&amp;gt;
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;&lt;strong&gt;步骤4：启用迁移（Migration）&lt;/strong&gt;&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;# 在Package Manager Console中执行：
# 1. 启用迁移（首次）
PM&amp;gt; Enable-Migrations

# 这会创建一个Migrations文件夹和Configuration.cs文件
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;&lt;strong&gt;步骤5：创建初始迁移&lt;/strong&gt;&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;# 2. 创建初始迁移
PM&amp;gt; Add-Migration InitialCreate

# 这会创建一个迁移文件，包含创建数据库的代码
# 文件名格式：时间戳_InitialCreate.cs
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;&lt;strong&gt;步骤6：查看迁移SQL（可选）&lt;/strong&gt;&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;# 查看将要执行的SQL语句
PM&amp;gt; Update-Database -Script

# 这会生成SQL脚本，但不执行
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;&lt;strong&gt;步骤7：应用迁移到数据库&lt;/strong&gt;&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;# 应用迁移，创建或更新数据库
PM&amp;gt; Update-Database

# 这会：
# - 创建数据库（如果不存在）
# - 创建表结构
# - 创建索引和约束
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;&lt;strong&gt;步骤8：使用DbContext&lt;/strong&gt;&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;using (var context = new MyDbContext())
{
    // 第一次运行时会自动创建数据库（如果启用了自动创建）
    // 或者手动执行迁移：Update-Database
    
    // 添加数据
    var user = new User
    {
        Name = &quot;张三&quot;,
        Email = &quot;zhangsan@example.com&quot;,
        Age = 25
    };
    context.Users.Add(user);
    context.SaveChanges(); // 此时数据库和表已创建
}
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;&lt;strong&gt;Code First迁移工作流：&lt;/strong&gt;&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;# 1. 修改实体类（添加属性、修改属性等）
# 例如：在User类中添加Phone属性

# 2. 创建新的迁移
PM&amp;gt; Add-Migration AddPhoneToUser

# 3. 查看SQL（可选）
PM&amp;gt; Update-Database -Script

# 4. 应用迁移
PM&amp;gt; Update-Database

# 5. 回滚迁移（如果需要）
PM&amp;gt; Update-Database -TargetMigration:PreviousMigrationName
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;&lt;strong&gt;Code First数据库初始化策略：&lt;/strong&gt;&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;// 1. CreateDatabaseIfNotExists（默认，如果数据库不存在则创建）
Database.SetInitializer(new CreateDatabaseIfNotExists&amp;lt;MyDbContext&amp;gt;());

// 2. DropCreateDatabaseIfModelChanges（模型改变时删除并重建，仅开发环境）
Database.SetInitializer(new DropCreateDatabaseIfModelChanges&amp;lt;MyDbContext&amp;gt;());

// 3. DropCreateDatabaseAlways（总是删除并重建，仅开发环境）
Database.SetInitializer(new DropCreateDatabaseAlways&amp;lt;MyDbContext&amp;gt;());

// 4. 自定义初始化器
public class MyDbInitializer : CreateDatabaseIfNotExists&amp;lt;MyDbContext&amp;gt;
{
    protected override void Seed(MyDbContext context)
    {
        // 初始化种子数据
        context.Users.Add(new User { Name = &quot;管理员&quot;, Email = &quot;admin@example.com&quot; });
        context.SaveChanges();
        base.Seed(context);
    }
}

Database.SetInitializer(new MyDbInitializer());

// 5. 禁用初始化（生产环境）
Database.SetInitializer&amp;lt;MyDbContext&amp;gt;(null);
&lt;/code&gt;&lt;/pre&gt;
&lt;h4&gt;Database First开发流程（数据库先行）&lt;/h4&gt;
&lt;p&gt;Database First是Entity Framework的另一种开发模式，开发者先创建数据库，然后使用EF工具从数据库生成实体类和DbContext。这种方式适合已有数据库或数据库优先的场景。&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;Database First开发步骤：&lt;/strong&gt;&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;步骤1：创建数据库&lt;/strong&gt;&lt;/p&gt;
&lt;p&gt;在SQL Server Management Studio中创建数据库和表：&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;-- 创建数据库
CREATE DATABASE MyDatabaseFirstDB;
GO

USE MyDatabaseFirstDB;
GO

-- 创建Users表
CREATE TABLE Users (
    Id INT PRIMARY KEY IDENTITY(1,1),
    Name NVARCHAR(100) NOT NULL,
    Email NVARCHAR(200) NOT NULL UNIQUE,
    Age INT NOT NULL,
    CreateDate DATETIME NOT NULL DEFAULT GETDATE()
);

-- 创建Orders表
CREATE TABLE Orders (
    Id INT PRIMARY KEY IDENTITY(1,1),
    OrderDate DATETIME NOT NULL,
    TotalAmount DECIMAL(18,2) NOT NULL,
    UserId INT NOT NULL,
    FOREIGN KEY (UserId) REFERENCES Users(Id)
);
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;&lt;strong&gt;步骤2：在Visual Studio中添加ADO.NET Entity Data Model&lt;/strong&gt;&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;右键点击项目 → 添加 → 新建项&lt;/li&gt;
&lt;li&gt;选择&quot;数据&quot; → &quot;ADO.NET Entity Data Model&quot;&lt;/li&gt;
&lt;li&gt;输入模型名称（如：MyModel.edmx）&lt;/li&gt;
&lt;li&gt;点击&quot;添加&quot;&lt;/li&gt;
&lt;/ol&gt;
&lt;p&gt;&lt;strong&gt;步骤3：选择&quot;来自数据库的EF设计器&quot;&lt;/strong&gt;&lt;/p&gt;
&lt;p&gt;在&quot;Entity Data Model向导&quot;中选择：&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;strong&gt;从数据库生成&lt;/strong&gt;（Database First）&lt;/li&gt;
&lt;li&gt;点击&quot;下一步&quot;&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;&lt;strong&gt;步骤4：配置数据库连接&lt;/strong&gt;&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;选择&quot;新建连接&quot;&lt;/li&gt;
&lt;li&gt;配置连接：
&lt;ul&gt;
&lt;li&gt;服务器名：localhost&lt;/li&gt;
&lt;li&gt;数据库名：MyDatabaseFirstDB&lt;/li&gt;
&lt;li&gt;身份验证方式（Windows或SQL Server）&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;测试连接&lt;/li&gt;
&lt;li&gt;勾选&quot;将App.config中的实体连接设置另存为&quot;&lt;/li&gt;
&lt;li&gt;连接字符串名称：MyDatabaseFirstDBEntities（或自定义）&lt;/li&gt;
&lt;li&gt;点击&quot;下一步&quot;&lt;/li&gt;
&lt;/ol&gt;
&lt;p&gt;&lt;strong&gt;步骤5：选择数据库对象&lt;/strong&gt;&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;选择要包含在模型中的表、视图、存储过程&lt;/li&gt;
&lt;li&gt;勾选：
&lt;ul&gt;
&lt;li&gt;✅ Users（表）&lt;/li&gt;
&lt;li&gt;✅ Orders（表）&lt;/li&gt;
&lt;li&gt;✅ 存储过程（可选）&lt;/li&gt;
&lt;li&gt;✅ 视图（可选）&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;输入模型命名空间（如：MyModel）&lt;/li&gt;
&lt;li&gt;勾选&quot;确定所生成对象名称的单复数形式&quot;&lt;/li&gt;
&lt;li&gt;勾选&quot;包含外键列&quot;&lt;/li&gt;
&lt;li&gt;勾选&quot;导入所选存储过程和函数到实体模型&quot;&lt;/li&gt;
&lt;li&gt;点击&quot;完成&quot;&lt;/li&gt;
&lt;/ol&gt;
&lt;p&gt;&lt;strong&gt;步骤6：查看生成的模型&lt;/strong&gt;&lt;/p&gt;
&lt;p&gt;EF会生成以下文件：&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;MyModel.edmx          // 实体数据模型文件（设计器）
MyModel.edmx.diagram  // 模型关系图
MyModel.Designer.cs   // 设计器生成的代码（包含实体类和DbContext）
MyModel.Context.tt    // T4模板
MyModel.tt            // T4模板
MyModel.Context.cs    // DbContext类（由T4生成）
User.cs               // User实体类（由T4生成）
Order.cs              // Order实体类（由T4生成）
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;&lt;strong&gt;步骤7：查看生成的代码&lt;/strong&gt;&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;生成的DbContext（MyModel.Context.cs）：&lt;/strong&gt;&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;public partial class MyDatabaseFirstDBEntities : DbContext
{
    public MyDatabaseFirstDBEntities()
        : base(&quot;name=MyDatabaseFirstDBEntities&quot;)
    {
    }
    
    protected override void OnModelCreating(DbModelBuilder modelBuilder)
    {
        throw new UnintentionalCodeFirstException();
    }
    
    public virtual DbSet&amp;lt;User&amp;gt; Users { get; set; }
    public virtual DbSet&amp;lt;Order&amp;gt; Orders { get; set; }
}
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;&lt;strong&gt;生成的实体类（User.cs）：&lt;/strong&gt;&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;public partial class User
{
    [System.Diagnostics.CodeAnalysis.SuppressMessage(&quot;Microsoft.Usage&quot;, 
        &quot;CA2214:DoNotCallOverridableMethodsInConstructors&quot;)]
    public User()
    {
        this.Orders = new HashSet&amp;lt;Order&amp;gt;();
    }
    
    public int Id { get; set; }
    public string Name { get; set; }
    public string Email { get; set; }
    public int Age { get; set; }
    public System.DateTime CreateDate { get; set; }
    
    [System.Diagnostics.CodeAnalysis.SuppressMessage(&quot;Microsoft.Usage&quot;, 
        &quot;CA2227:CollectionPropertiesShouldBeReadOnly&quot;)]
    public virtual ICollection&amp;lt;Order&amp;gt; Orders { get; set; }
}
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;&lt;strong&gt;步骤8：使用生成的DbContext&lt;/strong&gt;&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;using (var context = new MyDatabaseFirstDBEntities())
{
    // 查询数据
    var users = context.Users.ToList();
    var userById = context.Users.Find(1);
    
    // 添加数据
    var newUser = new User
    {
        Name = &quot;李四&quot;,
        Email = &quot;lisi@example.com&quot;,
        Age = 30,
        CreateDate = DateTime.Now
    };
    context.Users.Add(newUser);
    context.SaveChanges();
    
    // 更新数据
    var user = context.Users.Find(1);
    if (user != null)
    {
        user.Name = &quot;更新的名称&quot;;
        context.SaveChanges();
    }
    
    // 删除数据
    var userToDelete = context.Users.Find(2);
    if (userToDelete != null)
    {
        context.Users.Remove(userToDelete);
        context.SaveChanges();
    }
}
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;&lt;strong&gt;步骤9：更新模型（当数据库结构改变时）&lt;/strong&gt;&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;在数据库中修改表结构（添加列、修改列等）&lt;/li&gt;
&lt;li&gt;在Visual Studio中，右键点击.edmx文件&lt;/li&gt;
&lt;li&gt;选择&quot;从数据库更新模型...&quot;&lt;/li&gt;
&lt;li&gt;点击&quot;刷新&quot;标签&lt;/li&gt;
&lt;li&gt;选择要更新的表&lt;/li&gt;
&lt;li&gt;点击&quot;完成&quot;&lt;/li&gt;
&lt;li&gt;保存.edmx文件（会自动重新生成代码）&lt;/li&gt;
&lt;/ol&gt;
&lt;p&gt;&lt;strong&gt;Database First与数据库同步：&lt;/strong&gt;&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;# 如果数据库结构改变了：
# 1. 右键.edmx → 从数据库更新模型
# 2. 或者删除.edmx，重新生成
# 3. 注意：Database First不支持迁移（Migration），需要手动更新模型

# 如果模型改变了（但不推荐，Database First应该从数据库同步）：
# 右键.edmx → 从模型生成数据库
# 这会生成SQL脚本，可以执行更新数据库
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;&lt;strong&gt;Database First注意事项：&lt;/strong&gt;&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;&lt;strong&gt;不支持迁移&lt;/strong&gt;：Database First不支持Code First的迁移功能，数据库结构改变时需要手动更新模型&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;代码会被覆盖&lt;/strong&gt;：.Designer.cs和.tt生成的代码在更新模型时会被覆盖&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;自定义代码&lt;/strong&gt;：将自定义代码放在Partial Class中，避免被覆盖&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;配置文件&lt;/strong&gt;：连接字符串保存在App.config或Web.config中&lt;/li&gt;
&lt;/ol&gt;
&lt;p&gt;&lt;strong&gt;创建Partial Class扩展实体：&lt;/strong&gt;&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;// 创建新文件：UserExtensions.cs
// 不会被覆盖，可以添加自定义属性和方法

public partial class User
{
    // 计算属性
    public string FullInfo =&amp;gt; $&quot;{Name} ({Email}) - {Age}岁&quot;;
    
    // 自定义方法
    public bool IsAdult =&amp;gt; Age &amp;gt;= 18;
    
    // 业务逻辑方法
    public void UpdateAge(int newAge)
    {
        if (newAge &amp;lt; 0 || newAge &amp;gt; 150)
            throw new ArgumentException(&quot;年龄无效&quot;);
        Age = newAge;
    }
}
&lt;/code&gt;&lt;/pre&gt;
&lt;h4&gt;Code First vs Database First对比&lt;/h4&gt;
&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;特性&lt;/th&gt;
&lt;th&gt;Code First&lt;/th&gt;
&lt;th&gt;Database First&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;适用场景&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;新项目开发&lt;/td&gt;
&lt;td&gt;已有数据库&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;控制权&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;代码优先&lt;/td&gt;
&lt;td&gt;数据库优先&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;迁移支持&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;✅ 支持（Migration）&lt;/td&gt;
&lt;td&gt;❌ 不支持&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;版本控制&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;✅ 代码控制，便于版本管理&lt;/td&gt;
&lt;td&gt;⚠️ 需要手动同步&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;灵活性&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;✅ 高，完全由代码控制&lt;/td&gt;
&lt;td&gt;⚠️ 受限于数据库结构&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;开发效率&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;✅ 高（修改代码即可）&lt;/td&gt;
&lt;td&gt;⚠️ 需要更新模型&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;学习曲线&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;中等&lt;/td&gt;
&lt;td&gt;较低&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;维护成本&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;较低（代码统一管理）&lt;/td&gt;
&lt;td&gt;较高（需要同步数据库和模型）&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;
&lt;p&gt;&lt;strong&gt;选择建议：&lt;/strong&gt;&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;strong&gt;选择Code First&lt;/strong&gt;：新项目、需要版本控制、团队协作、需要频繁修改模型&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;选择Database First&lt;/strong&gt;：已有数据库、数据库由DBA管理、不需要频繁修改模型、快速原型开发&lt;/li&gt;
&lt;/ul&gt;
&lt;h4&gt;基本实体类定义&lt;/h4&gt;
&lt;pre&gt;&lt;code&gt;using System;
using System.ComponentModel.DataAnnotations;
using System.ComponentModel.DataAnnotations.Schema;

// 用户实体
public class User
{
    [Key]
    public int Id { get; set; }
    
    [Required]
    [MaxLength(100)]
    public string Name { get; set; }
    
    [Required]
    [MaxLength(200)]
    [Index(IsUnique = true)] // 唯一索引
    public string Email { get; set; }
    
    public int Age { get; set; }
    
    public DateTime CreateDate { get; set; }
    
    // 导航属性（一对多）
    public virtual ICollection&amp;lt;Order&amp;gt; Orders { get; set; }
    
    public User()
    {
        Orders = new HashSet&amp;lt;Order&amp;gt;();
        CreateDate = DateTime.Now;
    }
}

// 订单实体
public class Order
{
    [Key]
    public int Id { get; set; }
    
    [Required]
    public DateTime OrderDate { get; set; }
    
    [Required]
    [Column(TypeName = &quot;decimal(18,2)&quot;)]
    public decimal TotalAmount { get; set; }
    
    // 外键
    [Required]
    [ForeignKey(&quot;User&quot;)]
    public int UserId { get; set; }
    
    // 导航属性（多对一）
    public virtual User User { get; set; }
    
    // 导航属性（一对多）
    public virtual ICollection&amp;lt;OrderItem&amp;gt; OrderItems { get; set; }
    
    public Order()
    {
        OrderItems = new HashSet&amp;lt;OrderItem&amp;gt;();
        OrderDate = DateTime.Now;
    }
}

// 订单项实体
public class OrderItem
{
    [Key]
    public int Id { get; set; }
    
    [Required]
    [MaxLength(200)]
    public string ProductName { get; set; }
    
    [Required]
    public int Quantity { get; set; }
    
    [Required]
    [Column(TypeName = &quot;decimal(18,2)&quot;)]
    public decimal UnitPrice { get; set; }
    
    [Required]
    [ForeignKey(&quot;Order&quot;)]
    public int OrderId { get; set; }
    
    // 导航属性（多对一）
    public virtual Order Order { get; set; }
}
&lt;/code&gt;&lt;/pre&gt;
&lt;h4&gt;DbContext类定义&lt;/h4&gt;
&lt;pre&gt;&lt;code&gt;using System.Data.Entity;

public class MyDbContext : DbContext
{
    // 构造函数：指定连接字符串名称（从App.config或Web.config读取）
    public MyDbContext() : base(&quot;DefaultConnection&quot;)
    {
        // 禁用数据库初始化器（生产环境）
        // Database.SetInitializer&amp;lt;MyDbContext&amp;gt;(null);
        
        // 或者使用自定义初始化器
        Database.SetInitializer(new CreateDatabaseIfNotExists&amp;lt;MyDbContext&amp;gt;());
    }
    
    // 构造函数：直接指定连接字符串
    public MyDbContext(string connectionString) : base(connectionString)
    {
    }
    
    // DbSet属性：表示数据库中的表
    public DbSet&amp;lt;User&amp;gt; Users { get; set; }
    public DbSet&amp;lt;Order&amp;gt; Orders { get; set; }
    public DbSet&amp;lt;OrderItem&amp;gt; OrderItems { get; set; }
    
    // 重写OnModelCreating方法进行Fluent API配置
    protected override void OnModelCreating(DbModelBuilder modelBuilder)
    {
        base.OnModelCreating(modelBuilder);
        
        // 可以在这里进行额外的配置
        // 详见&quot;实体配置&quot;章节
    }
}
&lt;/code&gt;&lt;/pre&gt;
&lt;h4&gt;配置文件（App.config或Web.config）&lt;/h4&gt;
&lt;pre&gt;&lt;code&gt;&amp;lt;?xml version=&quot;1.0&quot; encoding=&quot;utf-8&quot;?&amp;gt;
&amp;lt;configuration&amp;gt;
  &amp;lt;configSections&amp;gt;
    &amp;lt;section name=&quot;entityFramework&quot; 
             type=&quot;System.Data.Entity.Internal.ConfigFile.EntityFrameworkSection, 
                   EntityFramework, Version=6.0.0.0, Culture=neutral, 
                   PublicKeyToken=b77a5c561934e089&quot; 
             requirePermission=&quot;false&quot; /&amp;gt;
  &amp;lt;/configSections&amp;gt;
  
  &amp;lt;connectionStrings&amp;gt;
    &amp;lt;add name=&quot;DefaultConnection&quot; 
         connectionString=&quot;Server=localhost;Database=MyDB;Integrated Security=True;&quot; 
         providerName=&quot;System.Data.SqlClient&quot; /&amp;gt;
  &amp;lt;/connectionStrings&amp;gt;
  
  &amp;lt;entityFramework&amp;gt;
    &amp;lt;defaultConnectionFactory type=&quot;System.Data.Entity.Infrastructure.SqlConnectionFactory, EntityFramework&quot; /&amp;gt;
    &amp;lt;providers&amp;gt;
      &amp;lt;provider invariantName=&quot;System.Data.SqlClient&quot; 
                type=&quot;System.Data.Entity.SqlServer.SqlProviderServices, EntityFramework.SqlServer&quot; /&amp;gt;
    &amp;lt;/providers&amp;gt;
  &amp;lt;/entityFramework&amp;gt;
&amp;lt;/configuration&amp;gt;
&lt;/code&gt;&lt;/pre&gt;
&lt;h4&gt;基本使用示例&lt;/h4&gt;
&lt;pre&gt;&lt;code&gt;using (var context = new MyDbContext())
{
    // 1. 添加数据
    var user = new User
    {
        Name = &quot;张三&quot;,
        Email = &quot;zhangsan@example.com&quot;,
        Age = 25
    };
    context.Users.Add(user);
    context.SaveChanges(); // 保存到数据库
    
    // 2. 查询数据
    var users = context.Users.ToList(); // 查询所有用户
    var userById = context.Users.Find(1); // 根据主键查找
    var userByEmail = context.Users.FirstOrDefault(u =&amp;gt; u.Email == &quot;zhangsan@example.com&quot;);
    
    // 3. 更新数据
    if (userById != null)
    {
        userById.Name = &quot;李四&quot;;
        userById.Age = 30;
        context.SaveChanges();
    }
    
    // 4. 删除数据
    var userToDelete = context.Users.Find(2);
    if (userToDelete != null)
    {
        context.Users.Remove(userToDelete);
        context.SaveChanges();
    }
}
&lt;/code&gt;&lt;/pre&gt;
&lt;h3&gt;&amp;lt;a id=&quot;dbcontext-detail&quot;&amp;gt;&amp;lt;/a&amp;gt;DbContext详解&lt;/h3&gt;
&lt;p&gt;&lt;code&gt;DbContext&lt;/code&gt;是Entity Framework的核心类，它代表与数据库的会话，负责跟踪实体变化、管理连接和执行数据库操作。&lt;/p&gt;
&lt;h4&gt;DbContext的主要属性和方法&lt;/h4&gt;
&lt;pre&gt;&lt;code&gt;public class MyDbContext : DbContext
{
    public MyDbContext() : base(&quot;DefaultConnection&quot;)
    {
    }
    
    public DbSet&amp;lt;User&amp;gt; Users { get; set; }
    public DbSet&amp;lt;Order&amp;gt; Orders { get; set; }
    
    // Database属性：提供对数据库的访问
    // context.Database.Connection - 获取数据库连接
    // context.Database.ExecuteSqlCommand() - 执行SQL命令
    // context.Database.SqlQuery&amp;lt;T&amp;gt;() - 执行SQL查询
    
    // ChangeTracker属性：跟踪实体状态变化
    // context.ChangeTracker.Entries() - 获取所有被跟踪的实体
    
    // Configuration属性：配置选项
    // context.Configuration.LazyLoadingEnabled - 延迟加载
    // context.Configuration.ProxyCreationEnabled - 代理创建
    // context.Configuration.ValidateOnSaveEnabled - 保存时验证
}
&lt;/code&gt;&lt;/pre&gt;
&lt;h4&gt;DbContext的常用方法&lt;/h4&gt;
&lt;pre&gt;&lt;code&gt;using (var context = new MyDbContext())
{
    // ========== 查询方法 ==========
    
    // Find：根据主键查找（同步）
    var user1 = context.Users.Find(1);
    
    // FindAsync：根据主键查找（异步）
    var user2 = await context.Users.FindAsync(1);
    
    // Set&amp;lt;T&amp;gt;：获取指定类型的DbSet
    var users = context.Set&amp;lt;User&amp;gt;();
    
    // Entry：获取实体的跟踪信息
    var entry = context.Entry(user1);
    Console.WriteLine($&quot;状态: {entry.State}&quot;); // Added, Modified, Deleted, Unchanged, Detached
    
    // ========== 保存方法 ==========
    
    // SaveChanges：保存所有更改（同步）
    int count = context.SaveChanges();
    
    // SaveChangesAsync：保存所有更改（异步）
    int count2 = await context.SaveChangesAsync();
    
    // ========== 数据库方法 ==========
    
    // Database.ExecuteSqlCommand：执行SQL命令
    int rowsAffected = context.Database.ExecuteSqlCommand(
        &quot;UPDATE Users SET Age = Age + 1 WHERE Age &amp;lt; 18&quot;);
    
    // Database.SqlQuery：执行SQL查询
    var users2 = context.Database.SqlQuery&amp;lt;User&amp;gt;(
        &quot;SELECT * FROM Users WHERE Age &amp;gt; @Age&quot;, 
        new SqlParameter(&quot;@Age&quot;, 18)).ToList();
    
    // Database.BeginTransaction：开始事务
    using (var transaction = context.Database.BeginTransaction())
    {
        try
        {
            // 执行操作
            context.Users.Add(new User { Name = &quot;新用户&quot; });
            context.SaveChanges();
            
            transaction.Commit();
        }
        catch
        {
            transaction.Rollback();
            throw;
        }
    }
    
    // ========== 变更跟踪方法 ==========
    
    // ChangeTracker.Entries：获取所有被跟踪的实体
    var allEntries = context.ChangeTracker.Entries();
    foreach (var entry in allEntries)
    {
        Console.WriteLine($&quot;实体: {entry.Entity.GetType().Name}, 状态: {entry.State}&quot;);
    }
    
    // ChangeTracker.Entries&amp;lt;T&amp;gt;：获取指定类型的被跟踪实体
    var userEntries = context.ChangeTracker.Entries&amp;lt;User&amp;gt;();
    
    // ========== 配置方法 ==========
    
    // 禁用延迟加载
    context.Configuration.LazyLoadingEnabled = false;
    
    // 禁用代理创建
    context.Configuration.ProxyCreationEnabled = false;
    
    // 禁用保存时验证
    context.Configuration.ValidateOnSaveEnabled = false;
    
    // ========== 其他方法 ==========
    
    // Dispose：释放资源（通常由using语句自动调用）
    // context.Dispose();
}
&lt;/code&gt;&lt;/pre&gt;
&lt;h4&gt;DbContext的生命周期管理&lt;/h4&gt;
&lt;pre&gt;&lt;code&gt;// 方式1：使用using语句（推荐）
using (var context = new MyDbContext())
{
    // 使用context
    var users = context.Users.ToList();
} // 自动释放资源

// 方式2：手动管理（不推荐）
var context = new MyDbContext();
try
{
    var users = context.Users.ToList();
}
finally
{
    context.Dispose();
}

// 方式3：依赖注入（推荐用于Web应用）
public class UserService
{
    private readonly MyDbContext _context;
    
    public UserService(MyDbContext context)
    {
        _context = context;
    }
    
    public List&amp;lt;User&amp;gt; GetUsers()
    {
        return _context.Users.ToList();
    }
}
&lt;/code&gt;&lt;/pre&gt;
&lt;h4&gt;DbContext的配置选项&lt;/h4&gt;
&lt;pre&gt;&lt;code&gt;public class MyDbContext : DbContext
{
    public MyDbContext() : base(&quot;DefaultConnection&quot;)
    {
        // 配置选项
        
        // 禁用延迟加载（提高性能，但需要显式加载关联数据）
        this.Configuration.LazyLoadingEnabled = false;
        
        // 禁用代理创建（提高性能，但失去某些EF功能）
        this.Configuration.ProxyCreationEnabled = false;
        
        // 启用保存时验证（默认启用）
        this.Configuration.ValidateOnSaveEnabled = true;
        
        // 启用自动检测更改（默认启用，可关闭以提高性能）
        this.Configuration.AutoDetectChangesEnabled = true;
        
        // 日志记录（开发环境）
        #if DEBUG
        this.Database.Log = s =&amp;gt; System.Diagnostics.Debug.WriteLine(s);
        #endif
    }
    
    public DbSet&amp;lt;User&amp;gt; Users { get; set; }
    public DbSet&amp;lt;Order&amp;gt; Orders { get; set; }
}
&lt;/code&gt;&lt;/pre&gt;
&lt;h3&gt;&amp;lt;a id=&quot;dbset-detail&quot;&amp;gt;&amp;lt;/a&amp;gt;DbSet详解&lt;/h3&gt;
&lt;p&gt;&lt;code&gt;DbSet&amp;lt;T&amp;gt;&lt;/code&gt;表示数据库中的表，提供了查询、添加、更新、删除等操作。&lt;/p&gt;
&lt;h4&gt;DbSet的主要方法&lt;/h4&gt;
&lt;pre&gt;&lt;code&gt;using (var context = new MyDbContext())
{
    // ========== 查询方法 ==========
    
    // ToList：转换为列表（立即执行）
    var allUsers = context.Users.ToList();
    
    // ToArray：转换为数组
    var usersArray = context.Users.ToArray();
    
    // First：获取第一个元素（如果不存在会抛异常）
    var firstUser = context.Users.First();
    var firstUserWithCondition = context.Users.First(u =&amp;gt; u.Age &amp;gt; 18);
    
    // FirstOrDefault：获取第一个元素（如果不存在返回null）
    var firstUserOrNull = context.Users.FirstOrDefault();
    var firstUserOrNullWithCondition = context.Users.FirstOrDefault(u =&amp;gt; u.Age &amp;gt; 100);
    
    // Single：获取唯一元素（如果不存在或存在多个会抛异常）
    var singleUser = context.Users.Single(u =&amp;gt; u.Id == 1);
    
    // SingleOrDefault：获取唯一元素（如果不存在返回null，存在多个抛异常）
    var singleUserOrNull = context.Users.SingleOrDefault(u =&amp;gt; u.Id == 999);
    
    // Find：根据主键查找（同步）
    var userById = context.Users.Find(1);
    
    // FindAsync：根据主键查找（异步）
    var userByIdAsync = await context.Users.FindAsync(1);
    
    // Where：过滤
    var adultUsers = context.Users.Where(u =&amp;gt; u.Age &amp;gt;= 18).ToList();
    
    // OrderBy / OrderByDescending：排序
    var sortedUsers = context.Users.OrderBy(u =&amp;gt; u.Name).ToList();
    var sortedUsersDesc = context.Users.OrderByDescending(u =&amp;gt; u.Age).ToList();
    
    // ThenBy / ThenByDescending：多级排序
    var multiSorted = context.Users
        .OrderBy(u =&amp;gt; u.Age)
        .ThenByDescending(u =&amp;gt; u.Name)
        .ToList();
    
    // Skip / Take：分页
    var pagedUsers = context.Users
        .OrderBy(u =&amp;gt; u.Id)
        .Skip(10)
        .Take(20)
        .ToList();
    
    // Count：计数
    int userCount = context.Users.Count();
    int adultCount = context.Users.Count(u =&amp;gt; u.Age &amp;gt;= 18);
    
    // Any：是否存在
    bool hasUsers = context.Users.Any();
    bool hasAdults = context.Users.Any(u =&amp;gt; u.Age &amp;gt;= 18);
    
    // All：是否全部满足条件
    bool allAdults = context.Users.All(u =&amp;gt; u.Age &amp;gt;= 18);
    
    // Sum / Average / Min / Max：聚合
    int totalAge = context.Users.Sum(u =&amp;gt; u.Age);
    double avgAge = context.Users.Average(u =&amp;gt; u.Age);
    int minAge = context.Users.Min(u =&amp;gt; u.Age);
    int maxAge = context.Users.Max(u =&amp;gt; u.Age);
    
    // Select：投影
    var userNames = context.Users.Select(u =&amp;gt; u.Name).ToList();
    var userInfo = context.Users.Select(u =&amp;gt; new { u.Id, u.Name, u.Email }).ToList();
    
    // Include：预加载关联数据
    var usersWithOrders = context.Users
        .Include(u =&amp;gt; u.Orders)
        .ToList();
    
    var usersWithOrdersAndItems = context.Users
        .Include(u =&amp;gt; u.Orders.Select(o =&amp;gt; o.OrderItems))
        .ToList();
    
    // ========== 添加方法 ==========
    // Add(T entity)：添加单个实体到DbSet
    // AddRange(IEnumerable&amp;lt;T&amp;gt; entities)：添加多个实体到DbSet
    // 注意：添加后需要调用SaveChanges()才会保存到数据库
    // 详细示例请参考&quot;数据修改&quot;章节
    
    // ========== 更新方法 ==========
    // DbSet没有Update方法，更新操作通过以下方式实现：
    // 1. 修改已跟踪的实体（通过查询获得的实体），然后SaveChanges()
    // 2. 使用Attach()附加实体，然后设置EntityState.Modified
    // 3. 直接使用Entry().State = EntityState.Modified
    // 详细示例请参考&quot;数据修改&quot;章节
    
    // ========== 删除方法 ==========
    // Remove(T entity)：删除单个实体
    // RemoveRange(IEnumerable&amp;lt;T&amp;gt; entities)：删除多个实体
    // 注意：删除后需要调用SaveChanges()才会保存到数据库
    // 也可以通过设置Entry().State = EntityState.Deleted来删除
    // 详细示例请参考&quot;数据修改&quot;章节
    
    // ========== 其他方法 ==========
    
    // Attach：附加实体到上下文（不跟踪）
    var existingUser = new User { Id = 1, Name = &quot;现有用户&quot; };
    context.Users.Attach(existingUser);
    
    // Local：获取本地（内存中）的实体
    var localUsers = context.Users.Local.ToList();
    
    // AsNoTracking：禁用变更跟踪（提高查询性能）
    var usersNoTracking = context.Users.AsNoTracking().ToList();
    
    // AsQueryable：转换为IQueryable（用于动态查询）
    IQueryable&amp;lt;User&amp;gt; query = context.Users.AsQueryable();
    if (someCondition)
    {
        query = query.Where(u =&amp;gt; u.Age &amp;gt; 18);
    }
    var result = query.ToList();
    
    // 保存更改
    context.SaveChanges();
}
&lt;/code&gt;&lt;/pre&gt;
&lt;h4&gt;DbSet的异步方法&lt;/h4&gt;
&lt;pre&gt;&lt;code&gt;using (var context = new MyDbContext())
{
    // 异步查询
    var users = await context.Users.ToListAsync();
    var user = await context.Users.FirstOrDefaultAsync(u =&amp;gt; u.Id == 1);
    var count = await context.Users.CountAsync();
    var hasUsers = await context.Users.AnyAsync();
    
    // 异步查找
    var userById = await context.Users.FindAsync(1);
    
    // 异步添加
    var newUser = new User { Name = &quot;新用户&quot;, Email = &quot;new@example.com&quot;, Age = 25 };
    context.Users.Add(newUser);
    await context.SaveChangesAsync();
    
    // 异步删除
    var userToDelete = await context.Users.FindAsync(1);
    if (userToDelete != null)
    {
        context.Users.Remove(userToDelete);
        await context.SaveChangesAsync();
    }
}
&lt;/code&gt;&lt;/pre&gt;
&lt;h3&gt;&amp;lt;a id=&quot;entity-configuration&quot;&amp;gt;&amp;lt;/a&amp;gt;实体配置&lt;/h3&gt;
&lt;p&gt;Entity Framework提供了两种方式来配置实体：Data Annotations（数据注解）和Fluent API（流式API）。&lt;/p&gt;
&lt;h4&gt;Data Annotations（数据注解）&lt;/h4&gt;
&lt;p&gt;数据注解是直接在实体类上使用特性来配置：&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;using System.ComponentModel.DataAnnotations;
using System.ComponentModel.DataAnnotations.Schema;

public class User
{
    // [Key]：指定主键
    [Key]
    public int Id { get; set; }
    
    // [Required]：必填字段
    [Required]
    [MaxLength(100)] // 最大长度
    public string Name { get; set; }
    
    // [StringLength]：字符串长度限制
    [StringLength(200, MinimumLength = 5)]
    public string Email { get; set; }
    
    // [Column]：指定列名和类型
    [Column(&quot;UserAge&quot;, TypeName = &quot;int&quot;)]
    public int Age { get; set; }
    
    // [Index]：创建索引
    [Index(IsUnique = true)]
    public string Email { get; set; }
    
    // [Index]：复合索引
    [Index(&quot;IX_Name_Age&quot;, IsUnique = false)]
    public string Name { get; set; }
    
    // [Table]：指定表名
    [Table(&quot;Users&quot;, Schema = &quot;dbo&quot;)]
    public class User { }
    
    // [NotMapped]：不映射到数据库
    [NotMapped]
    public string FullName =&amp;gt; $&quot;{FirstName} {LastName}&quot;;
    
    // [DatabaseGenerated]：数据库生成的值
    [DatabaseGenerated(DatabaseGeneratedOption.Identity)] // 自增
    public int Id { get; set; }
    
    [DatabaseGenerated(DatabaseGeneratedOption.Computed)] // 计算列
    public DateTime CreateDate { get; set; }
    
    [DatabaseGenerated(DatabaseGeneratedOption.None)] // 手动设置
    public Guid UniqueId { get; set; }
    
    // [Timestamp]：时间戳（并发控制）
    [Timestamp]
    public byte[] RowVersion { get; set; }
    
    // [ConcurrencyCheck]：并发检查
    [ConcurrencyCheck]
    public int Version { get; set; }
}
&lt;/code&gt;&lt;/pre&gt;
&lt;h4&gt;Fluent API（流式API）&lt;/h4&gt;
&lt;p&gt;Fluent API在&lt;code&gt;OnModelCreating&lt;/code&gt;方法中配置，更灵活强大：&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;public class MyDbContext : DbContext
{
    public DbSet&amp;lt;User&amp;gt; Users { get; set; }
    public DbSet&amp;lt;Order&amp;gt; Orders { get; set; }
    
    protected override void OnModelCreating(DbModelBuilder modelBuilder)
    {
        // 配置User实体
        modelBuilder.Entity&amp;lt;User&amp;gt;()
            // 指定表名和架构
            .ToTable(&quot;Users&quot;, &quot;dbo&quot;)
            
            // 配置主键
            .HasKey(u =&amp;gt; u.Id)
            
            // 配置属性
            .Property(u =&amp;gt; u.Id)
                .HasDatabaseGeneratedOption(DatabaseGeneratedOption.Identity) // 自增
                .HasColumnName(&quot;UserId&quot;) // 列名
                .HasColumnOrder(1); // 列顺序
            
            .Property(u =&amp;gt; u.Name)
                .IsRequired() // 必填
                .HasMaxLength(100) // 最大长度
                .IsUnicode(false); // 非Unicode
            
            .Property(u =&amp;gt; u.Email)
                .IsRequired()
                .HasMaxLength(200)
                .HasColumnName(&quot;EmailAddress&quot;);
            
            .Property(u =&amp;gt; u.Age)
                .IsOptional() // 可选
                .HasColumnType(&quot;int&quot;);
            
            .Property(u =&amp;gt; u.CreateDate)
                .IsRequired()
                .HasColumnType(&quot;datetime2&quot;)
                .HasDatabaseGeneratedOption(DatabaseGeneratedOption.Computed);
            
            // 忽略属性（不映射到数据库）
            .Ignore(u =&amp;gt; u.FullName);
            
            // 配置索引
            .HasIndex(u =&amp;gt; u.Email)
                .IsUnique()
                .HasName(&quot;IX_Users_Email&quot;);
            
            .HasIndex(u =&amp;gt; new { u.Name, u.Age })
                .HasName(&quot;IX_Users_Name_Age&quot;);
    }
}
&lt;/code&gt;&lt;/pre&gt;
&lt;h4&gt;Fluent API详细配置指南（EF6）&lt;/h4&gt;
&lt;p&gt;Fluent API提供了丰富的配置选项，可以精确控制实体映射、属性设置、关系配置和级联行为。&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;1. 实体级别配置&lt;/strong&gt;&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;protected override void OnModelCreating(DbModelBuilder modelBuilder)
{
    // ========== 实体基础配置 ==========
    modelBuilder.Entity&amp;lt;User&amp;gt;(entity =&amp;gt;
    {
        // 表名和架构配置
        entity.ToTable(&quot;Users&quot;, &quot;dbo&quot;); // 指定表名和架构
        
        // 主键配置
        entity.HasKey(u =&amp;gt; u.Id); // 单主键
        entity.HasKey(u =&amp;gt; new { u.Id, u.Code }); // 复合主键
        
        // 忽略实体（不映射到数据库）
        // modelBuilder.Ignore&amp;lt;SomeClass&amp;gt;();
        
        // 实体映射到多个表（Table Splitting）
        entity.Map(m =&amp;gt;
        {
            m.Properties(u =&amp;gt; new { u.Id, u.Name, u.Email });
            m.ToTable(&quot;Users&quot;);
        })
        .Map(m =&amp;gt;
        {
            m.Properties(u =&amp;gt; new { u.Id, u.Address, u.Phone });
            m.ToTable(&quot;UserDetails&quot;);
        });
    });
}
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;&lt;strong&gt;2. 属性详细配置&lt;/strong&gt;&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;protected override void OnModelCreating(DbModelBuilder modelBuilder)
{
    modelBuilder.Entity&amp;lt;User&amp;gt;(entity =&amp;gt;
    {
        // ========== 主键属性配置 ==========
        entity.Property(u =&amp;gt; u.Id)
            .HasColumnName(&quot;UserId&quot;)                    // 列名
            .HasColumnOrder(1)                           // 列顺序
            .HasDatabaseGeneratedOption(DatabaseGeneratedOption.Identity) // 自增
            .IsRequired();                               // 必填
        
        // ========== 字符串属性配置 ==========
        entity.Property(u =&amp;gt; u.Name)
            .IsRequired()                                // 必填
            .HasMaxLength(100)                           // 最大长度
            .HasMinLength(2)                             // 最小长度（EF6不支持，仅用于验证）
            .IsUnicode(true)                             // Unicode字符（nvarchar）
            .IsUnicode(false)                            // 非Unicode（varchar）
            .IsFixedLength()                             // 固定长度（char）
            .IsVariableLength()                          // 可变长度（varchar/nvarchar）
            .HasColumnType(&quot;nvarchar&quot;)                        // 数据库类型
            .HasColumnType(&quot;nvarchar(100)&quot;)               // 完整类型定义
            .HasColumnName(&quot;UserName&quot;);                  // 列名
        
        entity.Property(u =&amp;gt; u.Email)
            .IsRequired()
            .HasMaxLength(200)
            .HasColumnName(&quot;EmailAddress&quot;)
            .IsUnicode(false);                           // varchar(200)
        
        // ========== 数值属性配置 ==========
        entity.Property(u =&amp;gt; u.Age)
            .IsOptional()                                // 可选（可空）
            .HasColumnType(&quot;int&quot;)                             // SQL Server类型
            .HasPrecision(10, 0);                        // 精度（对decimal有效）
        
        entity.Property(u =&amp;gt; u.Salary)
            .HasColumnType(&quot;decimal(18,2)&quot;)                  // decimal类型
            .HasPrecision(18, 2)                         // 精度18，小数位2
            .IsOptional();
        
        entity.Property(u =&amp;gt; u.Rating)
            .HasColumnType(&quot;float&quot;)                          // float类型
            .IsOptional();
        
        // ========== 日期时间属性配置 ==========
        entity.Property(u =&amp;gt; u.CreateDate)
            .IsRequired()
            .HasColumnType(&quot;datetime2&quot;)                      // datetime2类型
            .HasDatabaseGeneratedOption(DatabaseGeneratedOption.Computed) // 计算列
            .HasDefaultValueSql(&quot;GETDATE()&quot;);            // 默认值SQL
        
        entity.Property(u =&amp;gt; u.UpdateDate)
            .HasColumnType(&quot;datetime2&quot;)
            .IsOptional()
            .HasDatabaseGeneratedOption(DatabaseGeneratedOption.None);
        
        // ========== 布尔属性配置 ==========
        entity.Property(u =&amp;gt; u.IsActive)
            .IsRequired()
            .HasColumnType(&quot;bit&quot;)                             // SQL Server bit类型
            .HasDefaultValue(true);                       // 默认值
        
        // ========== 字节数组属性配置 ==========
        entity.Property(u =&amp;gt; u.Avatar)
            .HasColumnType(&quot;varbinary(max)&quot;)                 // varbinary(max)
            .IsOptional();
        
        entity.Property(u =&amp;gt; u.RowVersion)
            .HasColumnType(&quot;timestamp&quot;)                      // timestamp（并发控制）
            .IsRowVersion()                              // 行版本（等同于[Timestamp]）
            .IsConcurrencyToken();                       // 并发令牌
        
        // ========== GUID属性配置 ==========
        entity.Property(u =&amp;gt; u.UniqueId)
            .HasColumnType(&quot;uniqueidentifier&quot;)                // GUID类型
            .HasDatabaseGeneratedOption(DatabaseGeneratedOption.Identity) // 自动生成
            .IsRequired();
        
        // ========== 枚举属性配置 ==========
        entity.Property(u =&amp;gt; u.Status)
            .HasColumnType(&quot;int&quot;)                             // 枚举存储为int
            .IsRequired();
        
        // ========== 忽略属性（不映射到数据库）==========
        entity.Ignore(u =&amp;gt; u.FullName);                  // 计算属性
        entity.Ignore(u =&amp;gt; u.DisplayName);              // 临时属性
        
        // ========== 复杂类型配置 ==========
        // 如果Address是复杂类型
        entity.ComplexProperty(u =&amp;gt; u.Address)
            .Property(a =&amp;gt; a.Street)
            .HasMaxLength(200);
    });
}
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;&lt;strong&gt;3. 索引配置&lt;/strong&gt;&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;protected override void OnModelCreating(DbModelBuilder modelBuilder)
{
    modelBuilder.Entity&amp;lt;User&amp;gt;(entity =&amp;gt;
    {
        // ========== 单列索引 ==========
        entity.HasIndex(u =&amp;gt; u.Email)
            .IsUnique()                                  // 唯一索引
            .HasName(&quot;IX_Users_Email_Unique&quot;);           // 索引名称
        
        // ========== 复合索引 ==========
        entity.HasIndex(u =&amp;gt; new { u.Name, u.Age })
            .HasName(&quot;IX_Users_Name_Age&quot;)
            .IsUnique(false);                            // 非唯一索引
        
        // ========== 包含列索引（SQL Server 2005+）==========
        // EF6不支持包含列，需要在迁移中手动添加
        // CREATE INDEX IX_Users_Email ON Users(Email) INCLUDE (Name, Age)
        
        // ========== 过滤索引（SQL Server 2008+）==========
        // EF6不支持过滤索引，需要在迁移中手动添加
        // CREATE INDEX IX_Users_Active ON Users(Email) WHERE IsActive = 1
    });
}
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;&lt;strong&gt;4. 关系配置详解&lt;/strong&gt;&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;protected override void OnModelCreating(DbModelBuilder modelBuilder)
{
    // ========== 一对多关系配置 ==========
    
    // 方式1：从Order端配置（推荐）
    modelBuilder.Entity&amp;lt;Order&amp;gt;()
        .HasRequired(o =&amp;gt; o.User)                        // 订单必须有用户（必需关系）
        .WithMany(u =&amp;gt; u.Orders)                         // 用户有多个订单
        .HasForeignKey(o =&amp;gt; o.UserId)                    // 外键属性
        .WillCascadeOnDelete(true);                      // 级联删除
    
    // 方式2：从User端配置
    modelBuilder.Entity&amp;lt;User&amp;gt;()
        .HasMany(u =&amp;gt; u.Orders)                          // 用户有多个订单
        .WithRequired(o =&amp;gt; o.User)                       // 订单必须有用户
        .HasForeignKey(o =&amp;gt; o.UserId)
        .WillCascadeOnDelete(true);
    
    // 方式3：可选关系（外键可空）
    modelBuilder.Entity&amp;lt;Order&amp;gt;()
        .HasOptional(o =&amp;gt; o.User)                        // 订单可以没有用户（可选）
        .WithMany(u =&amp;gt; u.Orders)
        .HasForeignKey(o =&amp;gt; o.UserId)
        .WillCascadeOnDelete(false);
    
    // 方式4：指定外键列名
    modelBuilder.Entity&amp;lt;Order&amp;gt;()
        .HasRequired(o =&amp;gt; o.User)
        .WithMany(u =&amp;gt; u.Orders)
        .Map(m =&amp;gt; m.MapKey(&quot;FK_UserId&quot;));                // 指定外键列名
    
    // ========== 一对一关系配置 ==========
    
    // 方式1：UserProfile依赖User（UserId是外键）
    modelBuilder.Entity&amp;lt;UserProfile&amp;gt;()
        .HasRequired(p =&amp;gt; p.User)                       // 用户资料必须有用户
        .WithOptional(u =&amp;gt; u.Profile)                   // 用户可以有可选的资料
        .HasForeignKey(p =&amp;gt; p.UserId)
        .WillCascadeOnDelete(true);
    
    // 方式2：共享主键（UserProfile的主键也是外键）
    modelBuilder.Entity&amp;lt;UserProfile&amp;gt;()
        .HasRequired(p =&amp;gt; p.User)
        .WithOptional(u =&amp;gt; u.Profile)
        .Map(m =&amp;gt; m.MapKey(&quot;UserId&quot;));                   // UserId既是主键也是外键
    
    // 方式3：双向可选
    modelBuilder.Entity&amp;lt;UserProfile&amp;gt;()
        .HasOptional(p =&amp;gt; p.User)
        .WithOptional(u =&amp;gt; u.Profile)
        .Map(m =&amp;gt; m.MapKey(&quot;UserId&quot;));
    
    // ========== 多对多关系配置 ==========
    
    // 方式1：使用默认连接表
    modelBuilder.Entity&amp;lt;User&amp;gt;()
        .HasMany(u =&amp;gt; u.Roles)
        .WithMany(r =&amp;gt; r.Users)
        .Map(m =&amp;gt;
        {
            m.ToTable(&quot;UserRoles&quot;);                      // 连接表名
            m.MapLeftKey(&quot;UserId&quot;);                      // User表的外键
            m.MapRightKey(&quot;RoleId&quot;);                     // Role表的外键
        });
    
    // 方式2：使用中间实体（推荐，更灵活）
    modelBuilder.Entity&amp;lt;UserRole&amp;gt;()
        .HasKey(ur =&amp;gt; new { ur.UserId, ur.RoleId });    // 复合主键
    
    modelBuilder.Entity&amp;lt;UserRole&amp;gt;()
        .HasRequired(ur =&amp;gt; ur.User)
        .WithMany(u =&amp;gt; u.UserRoles)
        .HasForeignKey(ur =&amp;gt; ur.UserId)
        .WillCascadeOnDelete(true);
    
    modelBuilder.Entity&amp;lt;UserRole&amp;gt;()
        .HasRequired(ur =&amp;gt; ur.Role)
        .WithMany(r =&amp;gt; r.UserRoles)
        .HasForeignKey(ur =&amp;gt; ur.RoleId)
        .WillCascadeOnDelete(true);
}
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;&lt;strong&gt;多对多标准开发流程（中间表方案，EF6推荐）&lt;/strong&gt;&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;// 步骤1：创建中间表实体（包含复合主键） 
public class UserRole
{
    public int UserId { get; set; }
    public int RoleId { get; set; }
    public DateTime AssignedDate { get; set; } = DateTime.Now; // 额外字段示例
    
    public virtual User User { get; set; }
    public virtual Role Role { get; set; }
}

// 步骤2：在主实体中添加导航集合
public class User
{
    public int Id { get; set; }
    public string Name { get; set; }
    public virtual ICollection&amp;lt;UserRole&amp;gt; UserRoles { get; set; } = new HashSet&amp;lt;UserRole&amp;gt;();
}

public class Role
{
    public int Id { get; set; }
    public string Name { get; set; }
    public virtual ICollection&amp;lt;UserRole&amp;gt; UserRoles { get; set; } = new HashSet&amp;lt;UserRole&amp;gt;();
}

// 步骤3：在 DbContext 中添加 DbSet
public class MyDbContext : DbContext
{
    public DbSet&amp;lt;User&amp;gt; Users { get; set; }
    public DbSet&amp;lt;Role&amp;gt; Roles { get; set; }
    public DbSet&amp;lt;UserRole&amp;gt; UserRoles { get; set; }

    protected override void OnModelCreating(DbModelBuilder modelBuilder)
    {
        // 3.1 配置中间表主键
        modelBuilder.Entity&amp;lt;UserRole&amp;gt;()
            .HasKey(ur =&amp;gt; new { ur.UserId, ur.RoleId });

        // 3.2 配置 UserRole -&amp;gt; User（一对多）
        modelBuilder.Entity&amp;lt;UserRole&amp;gt;()
            .HasRequired(ur =&amp;gt; ur.User)
            .WithMany(u =&amp;gt; u.UserRoles)
            .HasForeignKey(ur =&amp;gt; ur.UserId)
            .WillCascadeOnDelete(true); // 删除 User 时删除关联

        // 3.3 配置 UserRole -&amp;gt; Role（一对多）
        modelBuilder.Entity&amp;lt;UserRole&amp;gt;()
            .HasRequired(ur =&amp;gt; ur.Role)
            .WithMany(r =&amp;gt; r.UserRoles)
            .HasForeignKey(ur =&amp;gt; ur.RoleId)
            .WillCascadeOnDelete(false); // 避免删除 Role 时误删用户关联

        base.OnModelCreating(modelBuilder);
    }
}

// 步骤4：增删改查示例
using (var context = new MyDbContext())
{
    // 添加关联
    var userRole = new UserRole { UserId = 1, RoleId = 2 };
    context.UserRoles.Add(userRole);
    context.SaveChanges();

    // 查询用户的所有角色
    var roles = context.UserRoles
        .Where(ur =&amp;gt; ur.UserId == 1)
        .Select(ur =&amp;gt; ur.Role)
        .ToList();

    // 移除关联
    var link = context.UserRoles.Find(1, 2);
    if (link != null)
    {
        context.UserRoles.Remove(link);
        context.SaveChanges();
    }
}
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;要点：先建“中间表实体”，然后用两个一对多完成多对多；可在中间表上挂载额外业务字段（如创建时间、操作人），比默认连接表更灵活。&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;5. 级联删除详细配置&lt;/strong&gt;&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;protected override void OnModelCreating(DbModelBuilder modelBuilder)
{
    // ========== 级联删除配置 ==========
    
    // 场景1：删除用户时，自动删除其所有订单（级联删除）
    modelBuilder.Entity&amp;lt;Order&amp;gt;()
        .HasRequired(o =&amp;gt; o.User)
        .WithMany(u =&amp;gt; u.Orders)
        .HasForeignKey(o =&amp;gt; o.UserId)
        .WillCascadeOnDelete(true);                      // 启用级联删除
    
    // 效果：删除User时，会自动删除所有相关的Order记录
    // DELETE FROM Users WHERE Id = 1
    // 会自动执行：DELETE FROM Orders WHERE UserId = 1
    
    // 场景2：删除用户时，不允许删除（如果存在订单）
    modelBuilder.Entity&amp;lt;Order&amp;gt;()
        .HasRequired(o =&amp;gt; o.User)
        .WithMany(u =&amp;gt; u.Orders)
        .HasForeignKey(o =&amp;gt; o.UserId)
        .WillCascadeOnDelete(false);                     // 禁用级联删除
    
    // 效果：如果User有Order，删除User会抛出异常
    // 需要先删除Order，再删除User
    
    // 场景3：可选关系的级联删除
    modelBuilder.Entity&amp;lt;Order&amp;gt;()
        .HasOptional(o =&amp;gt; o.User)
        .WithMany(u =&amp;gt; u.Orders)
        .HasForeignKey(o =&amp;gt; o.UserId)
        .WillCascadeOnDelete(true);                      // 可选关系也可以级联删除
    
    // 场景4：多级级联删除
    // User -&amp;gt; Order -&amp;gt; OrderItem
    // 删除User时，会级联删除Order，Order删除时会级联删除OrderItem
    
    modelBuilder.Entity&amp;lt;Order&amp;gt;()
        .HasRequired(o =&amp;gt; o.User)
        .WithMany(u =&amp;gt; u.Orders)
        .HasForeignKey(o =&amp;gt; o.UserId)
        .WillCascadeOnDelete(true);                      // User -&amp;gt; Order级联
    
    modelBuilder.Entity&amp;lt;OrderItem&amp;gt;()
        .HasRequired(oi =&amp;gt; oi.Order)
        .WithMany(o =&amp;gt; o.OrderItems)
        .HasForeignKey(oi =&amp;gt; oi.OrderId)
        .WillCascadeOnDelete(true);                      // Order -&amp;gt; OrderItem级联
    
    // 场景5：一对一关系的级联删除
    modelBuilder.Entity&amp;lt;UserProfile&amp;gt;()
        .HasRequired(p =&amp;gt; p.User)
        .WithOptional(u =&amp;gt; u.Profile)
        .HasForeignKey(p =&amp;gt; p.UserId)
        .WillCascadeOnDelete(true);                      // 删除User时删除UserProfile
    
    // 场景6：多对多关系的级联删除
    // 删除User时，删除UserRole连接记录，但不删除Role
    modelBuilder.Entity&amp;lt;UserRole&amp;gt;()
        .HasRequired(ur =&amp;gt; ur.User)
        .WithMany(u =&amp;gt; u.UserRoles)
        .HasForeignKey(ur =&amp;gt; ur.UserId)
        .WillCascadeOnDelete(true);                      // 删除User时删除UserRole
    
    modelBuilder.Entity&amp;lt;UserRole&amp;gt;()
        .HasRequired(ur =&amp;gt; ur.Role)
        .WithMany(r =&amp;gt; r.UserRoles)
        .HasForeignKey(ur =&amp;gt; ur.RoleId)
        .WillCascadeOnDelete(false);                     // 删除Role时不删除UserRole（保护User）
    
    // ========== 级联删除注意事项 ==========
    // 1. 级联删除是数据库级别的约束，不是EF代码逻辑
    // 2. 启用级联删除后，数据库会自动创建外键约束
    // 3. 级联删除可能导致意外的数据丢失，需要谨慎使用
    // 4. 生产环境建议禁用级联删除，使用软删除或手动删除
    // 5. 级联删除的性能影响：删除父记录时，需要检查所有子记录
}
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;&lt;strong&gt;6. 并发控制配置&lt;/strong&gt;&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;protected override void OnModelCreating(DbModelBuilder modelBuilder)
{
    modelBuilder.Entity&amp;lt;User&amp;gt;(entity =&amp;gt;
    {
        // ========== 时间戳并发控制 ==========
        entity.Property(u =&amp;gt; u.RowVersion)
            .IsRowVersion()                              // 等同于[Timestamp]
            .IsConcurrencyToken();                       // 并发令牌
        
        // ========== 字段级并发控制 ==========
        entity.Property(u =&amp;gt; u.Version)
            .IsConcurrencyToken();                       // 并发检查字段
        
        // ========== 多字段并发控制 ==========
        entity.Property(u =&amp;gt; u.UpdateDate)
            .IsConcurrencyToken();
        
        entity.Property(u =&amp;gt; u.UpdateBy)
            .IsConcurrencyToken();
    });
}
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;&lt;strong&gt;7. 默认值和计算列配置&lt;/strong&gt;&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;protected override void OnModelCreating(DbModelBuilder modelBuilder)
{
    modelBuilder.Entity&amp;lt;User&amp;gt;(entity =&amp;gt;
    {
        // ========== 默认值配置 ==========
        entity.Property(u =&amp;gt; u.CreateDate)
            .HasDefaultValueSql(&quot;GETDATE()&quot;);            // SQL Server函数
        
        entity.Property(u =&amp;gt; u.IsActive)
            .HasDefaultValue(true);                      // C#默认值
        
        entity.Property(u =&amp;gt; u.Status)
            .HasDefaultValue(0);                         // 枚举默认值
        
        // ========== 计算列配置 ==========
        entity.Property(u =&amp;gt; u.FullName)
            .HasDatabaseGeneratedOption(DatabaseGeneratedOption.Computed)
            .HasColumnType(&quot;nvarchar&quot;)
            .HasMaxLength(200);
        
        // 需要在迁移中手动添加计算列SQL：
        // ALTER TABLE Users ADD FullName AS (Name + &apos; &apos; + Email)
    });
}
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;&lt;strong&gt;8. 完整的Fluent API配置示例&lt;/strong&gt;&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;using System.Data.Entity;
using System.Data.Entity.ModelConfiguration.Configuration;

public class MyDbContext : DbContext
{
    public DbSet&amp;lt;User&amp;gt; Users { get; set; }
    public DbSet&amp;lt;Order&amp;gt; Orders { get; set; }
    public DbSet&amp;lt;OrderItem&amp;gt; OrderItems { get; set; }
    public DbSet&amp;lt;UserProfile&amp;gt; UserProfiles { get; set; }
    public DbSet&amp;lt;Role&amp;gt; Roles { get; set; }
    public DbSet&amp;lt;UserRole&amp;gt; UserRoles { get; set; }
    
    protected override void OnModelCreating(DbModelBuilder modelBuilder)
    {
        // ========== User实体完整配置 ==========
        modelBuilder.Entity&amp;lt;User&amp;gt;(entity =&amp;gt;
        {
            // 表配置
            entity.ToTable(&quot;Users&quot;, &quot;dbo&quot;);
            entity.HasKey(u =&amp;gt; u.Id);
            
            // 主键属性配置
            entity.Property(u =&amp;gt; u.Id)
                .HasColumnName(&quot;UserId&quot;)
                .HasDatabaseGeneratedOption(DatabaseGeneratedOption.Identity)
                .IsRequired();
            
            // 字符串属性配置
            entity.Property(u =&amp;gt; u.Name)
                .IsRequired()
                .HasMaxLength(100)
                .IsUnicode(true)
                .HasColumnName(&quot;UserName&quot;);
            
            entity.Property(u =&amp;gt; u.Email)
                .IsRequired()
                .HasMaxLength(200)
                .IsUnicode(false)
                .HasColumnName(&quot;EmailAddress&quot;);
            
            // 数值属性配置
            entity.Property(u =&amp;gt; u.Age)
                .IsOptional()
                .HasColumnType(&quot;int&quot;);
            
            // 日期时间属性配置
            entity.Property(u =&amp;gt; u.CreateDate)
                .IsRequired()
                .HasColumnType(&quot;datetime2&quot;)
                .HasDefaultValueSql(&quot;GETDATE()&quot;);
            
            entity.Property(u =&amp;gt; u.UpdateDate)
                .IsOptional()
                .HasColumnType(&quot;datetime2&quot;);
            
            // 布尔属性配置
            entity.Property(u =&amp;gt; u.IsActive)
                .IsRequired()
                .HasColumnType(&quot;bit&quot;)
                .HasDefaultValue(true);
            
            // 并发控制
            entity.Property(u =&amp;gt; u.RowVersion)
                .IsRowVersion()
                .IsConcurrencyToken();
            
            // 索引配置
            entity.HasIndex(u =&amp;gt; u.Email)
                .IsUnique()
                .HasName(&quot;IX_Users_Email_Unique&quot;);
            
            entity.HasIndex(u =&amp;gt; new { u.Name, u.Age })
                .HasName(&quot;IX_Users_Name_Age&quot;);
            
            // 忽略属性
            entity.Ignore(u =&amp;gt; u.FullName);
        });
        
        // ========== Order实体配置 ==========
        modelBuilder.Entity&amp;lt;Order&amp;gt;(entity =&amp;gt;
        {
            entity.ToTable(&quot;Orders&quot;);
            entity.HasKey(o =&amp;gt; o.Id);
            
            entity.Property(o =&amp;gt; o.OrderDate)
                .IsRequired()
                .HasColumnType(&quot;datetime2&quot;);
            
            entity.Property(o =&amp;gt; o.TotalAmount)
                .IsRequired()
                .HasColumnType(&quot;decimal(18,2)&quot;)
                .HasPrecision(18, 2);
            
            // 一对多关系：Order -&amp;gt; User（级联删除）
            entity.HasRequired(o =&amp;gt; o.User)
                .WithMany(u =&amp;gt; u.Orders)
                .HasForeignKey(o =&amp;gt; o.UserId)
                .WillCascadeOnDelete(true);
        });
        
        // ========== OrderItem实体配置 ==========
        modelBuilder.Entity&amp;lt;OrderItem&amp;gt;(entity =&amp;gt;
        {
            entity.ToTable(&quot;OrderItems&quot;);
            entity.HasKey(oi =&amp;gt; oi.Id);
            
            entity.Property(oi =&amp;gt; oi.ProductName)
                .IsRequired()
                .HasMaxLength(200);
            
            entity.Property(oi =&amp;gt; oi.Quantity)
                .IsRequired()
                .HasColumnType(&quot;int&quot;);
            
            entity.Property(oi =&amp;gt; oi.UnitPrice)
                .IsRequired()
                .HasColumnType(&quot;decimal(18,2)&quot;)
                .HasPrecision(18, 2);
            
            // 一对多关系：OrderItem -&amp;gt; Order（级联删除）
            entity.HasRequired(oi =&amp;gt; oi.Order)
                .WithMany(o =&amp;gt; o.OrderItems)
                .HasForeignKey(oi =&amp;gt; oi.OrderId)
                .WillCascadeOnDelete(true);
        });
        
        // ========== UserProfile实体配置（一对一）==========
        modelBuilder.Entity&amp;lt;UserProfile&amp;gt;(entity =&amp;gt;
        {
            entity.ToTable(&quot;UserProfiles&quot;);
            entity.HasKey(p =&amp;gt; p.Id);
            
            entity.Property(p =&amp;gt; p.Address)
                .IsOptional()
                .HasMaxLength(500);
            
            entity.Property(p =&amp;gt; p.Phone)
                .IsOptional()
                .HasMaxLength(20);
            
            // 一对一关系：UserProfile -&amp;gt; User（级联删除）
            entity.HasRequired(p =&amp;gt; p.User)
                .WithOptional(u =&amp;gt; u.Profile)
                .HasForeignKey(p =&amp;gt; p.UserId)
                .WillCascadeOnDelete(true);
        });
        
        // ========== Role实体配置 ==========
        modelBuilder.Entity&amp;lt;Role&amp;gt;(entity =&amp;gt;
        {
            entity.ToTable(&quot;Roles&quot;);
            entity.HasKey(r =&amp;gt; r.Id);
            
            entity.Property(r =&amp;gt; r.Name)
                .IsRequired()
                .HasMaxLength(50);
        });
        
        // ========== UserRole中间实体配置（多对多）==========
        modelBuilder.Entity&amp;lt;UserRole&amp;gt;(entity =&amp;gt;
        {
            entity.ToTable(&quot;UserRoles&quot;);
            entity.HasKey(ur =&amp;gt; new { ur.UserId, ur.RoleId }); // 复合主键
            
            entity.Property(ur =&amp;gt; ur.AssignedDate)
                .IsRequired()
                .HasColumnType(&quot;datetime2&quot;)
                .HasDefaultValueSql(&quot;GETDATE()&quot;);
            
            // UserRole -&amp;gt; User（级联删除）
            entity.HasRequired(ur =&amp;gt; ur.User)
                .WithMany(u =&amp;gt; u.UserRoles)
                .HasForeignKey(ur =&amp;gt; ur.UserId)
                .WillCascadeOnDelete(true);
            
            // UserRole -&amp;gt; Role（不级联删除，保护Role）
            entity.HasRequired(ur =&amp;gt; ur.Role)
                .WithMany(r =&amp;gt; r.UserRoles)
                .HasForeignKey(ur =&amp;gt; ur.RoleId)
                .WillCascadeOnDelete(false);
        });
    }
}
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;&lt;strong&gt;9. Fluent API配置最佳实践&lt;/strong&gt;&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;protected override void OnModelCreating(DbModelBuilder modelBuilder)
{
    // ========== 最佳实践 ==========
    
    // 1. 使用EntityTypeConfiguration分离配置（推荐）
    // 创建单独的配置类
    public class UserConfiguration : EntityTypeConfiguration&amp;lt;User&amp;gt;
    {
        public UserConfiguration()
        {
            ToTable(&quot;Users&quot;);
            HasKey(u =&amp;gt; u.Id);
            
            Property(u =&amp;gt; u.Name)
                .IsRequired()
                .HasMaxLength(100);
            
            // ... 其他配置
        }
    }
    
    // 在OnModelCreating中应用配置
    modelBuilder.Configurations.Add(new UserConfiguration());
    
    // 2. 使用配置约定（Convention）
    // 所有字符串属性默认最大长度100
    modelBuilder.Properties&amp;lt;string&amp;gt;()
        .Configure(p =&amp;gt; p.HasMaxLength(100));
    
    // 所有DateTime属性默认类型为datetime2
    modelBuilder.Properties&amp;lt;DateTime&amp;gt;()
        .Configure(p =&amp;gt; p.HasColumnType(&quot;datetime2&quot;));
    
    // 3. 级联删除策略建议
    // - 开发环境：可以启用级联删除，方便测试
    // - 生产环境：建议禁用级联删除，使用软删除或手动删除
    // - 多对多关系：通常不级联删除，保护关联实体
    
    // 4. 性能优化建议
    // - 合理使用索引
    // - 避免过度配置
    // - 使用延迟加载（Lazy Loading）或显式加载（Explicit Loading）
}
&lt;/code&gt;&lt;/pre&gt;
&lt;h3&gt;&amp;lt;a id=&quot;relationship-configuration&quot;&amp;gt;&amp;lt;/a&amp;gt;关系配置&lt;/h3&gt;
&lt;p&gt;Entity Framework支持三种关系类型：一对一、一对多、多对多。&lt;/p&gt;
&lt;h4&gt;一对多关系（One-to-Many）&lt;/h4&gt;
&lt;pre&gt;&lt;code&gt;// 实体定义
public class User
{
    public int Id { get; set; }
    public string Name { get; set; }
    
    // 导航属性：一个用户有多个订单
    public virtual ICollection&amp;lt;Order&amp;gt; Orders { get; set; }
}

public class Order
{
    public int Id { get; set; }
    public DateTime OrderDate { get; set; }
    
    // 外键属性
    public int UserId { get; set; }
    
    // 导航属性：一个订单属于一个用户
    public virtual User User { get; set; }
}

// Fluent API配置
protected override void OnModelCreating(DbModelBuilder modelBuilder)
{
    // 方式1：通过导航属性配置
    modelBuilder.Entity&amp;lt;Order&amp;gt;()
        .HasRequired(o =&amp;gt; o.User) // 订单必须有用户（必需关系）
        .WithMany(u =&amp;gt; u.Orders)  // 用户有多个订单
        .HasForeignKey(o =&amp;gt; o.UserId); // 外键
    
    // 方式2：可选关系
    modelBuilder.Entity&amp;lt;Order&amp;gt;()
        .HasOptional(o =&amp;gt; o.User) // 订单可以没有用户（可选关系）
        .WithMany(u =&amp;gt; u.Orders)
        .HasForeignKey(o =&amp;gt; o.UserId)
        .WillCascadeOnDelete(true); // 级联删除
    
    // 方式3：指定外键名称
    modelBuilder.Entity&amp;lt;Order&amp;gt;()
        .HasRequired(o =&amp;gt; o.User)
        .WithMany(u =&amp;gt; u.Orders)
        .Map(m =&amp;gt; m.MapKey(&quot;FK_UserId&quot;)); // 指定外键列名
}
&lt;/code&gt;&lt;/pre&gt;
&lt;h4&gt;一对一关系（One-to-One）&lt;/h4&gt;
&lt;pre&gt;&lt;code&gt;// 实体定义
public class User
{
    public int Id { get; set; }
    public string Name { get; set; }
    
    // 导航属性：一个用户有一个用户资料
    public virtual UserProfile Profile { get; set; }
}

public class UserProfile
{
    public int Id { get; set; }
    public string Address { get; set; }
    public string Phone { get; set; }
    
    // 外键
    public int UserId { get; set; }
    
    // 导航属性：一个用户资料属于一个用户
    public virtual User User { get; set; }
}

// Fluent API配置
protected override void OnModelCreating(DbModelBuilder modelBuilder)
{
    // 方式1：User是主体，UserProfile是依赖
    modelBuilder.Entity&amp;lt;UserProfile&amp;gt;()
        .HasRequired(p =&amp;gt; p.User) // 用户资料必须有用户
        .WithOptional(u =&amp;gt; u.Profile) // 用户可以有可选的资料
        .HasForeignKey(p =&amp;gt; p.UserId); // 外键在UserProfile中
    
    // 方式2：共享主键（UserProfile的主键也是外键）
    modelBuilder.Entity&amp;lt;UserProfile&amp;gt;()
        .HasRequired(p =&amp;gt; p.User)
        .WithOptional(u =&amp;gt; u.Profile)
        .Map(m =&amp;gt; m.MapKey(&quot;UserId&quot;)); // UserId既是主键也是外键
}
&lt;/code&gt;&lt;/pre&gt;
&lt;h4&gt;多对多关系（Many-to-Many）&lt;/h4&gt;
&lt;pre&gt;&lt;code&gt;// 实体定义
public class User
{
    public int Id { get; set; }
    public string Name { get; set; }
    
    // 导航属性：一个用户有多个角色
    public virtual ICollection&amp;lt;Role&amp;gt; Roles { get; set; }
}

public class Role
{
    public int Id { get; set; }
    public string Name { get; set; }
    
    // 导航属性：一个角色有多个用户
    public virtual ICollection&amp;lt;User&amp;gt; Users { get; set; }
}

// Fluent API配置
protected override void OnModelCreating(DbModelBuilder modelBuilder)
{
    // 方式1：使用默认连接表名（UsersRoles）
    modelBuilder.Entity&amp;lt;User&amp;gt;()
        .HasMany(u =&amp;gt; u.Roles)
        .WithMany(r =&amp;gt; r.Users)
        .Map(m =&amp;gt;
        {
            m.ToTable(&quot;UserRoles&quot;); // 连接表名
            m.MapLeftKey(&quot;UserId&quot;);  // User表的外键
            m.MapRightKey(&quot;RoleId&quot;); // Role表的外键
        });
    
    // 方式2：使用中间实体（更灵活）
    // 定义中间实体
    public class UserRole
    {
        public int UserId { get; set; }
        public int RoleId { get; set; }
        public DateTime AssignedDate { get; set; }
        
        public virtual User User { get; set; }
        public virtual Role Role { get; set; }
    }
    
    // 配置中间实体
    modelBuilder.Entity&amp;lt;UserRole&amp;gt;()
        .HasKey(ur =&amp;gt; new { ur.UserId, ur.RoleId });
    
    modelBuilder.Entity&amp;lt;User&amp;gt;()
        .HasMany(u =&amp;gt; u.Roles)
        .WithMany(r =&amp;gt; r.Users)
        .Map(m =&amp;gt;
        {
            m.ToTable(&quot;UserRoles&quot;);
            m.MapLeftKey(&quot;UserId&quot;);
            m.MapRightKey(&quot;RoleId&quot;);
        });
}
&lt;/code&gt;&lt;/pre&gt;
&lt;h4&gt;级联删除配置详解&lt;/h4&gt;
&lt;p&gt;级联删除（Cascade Delete）是数据库外键约束的一种行为，当删除父记录时，自动删除所有相关的子记录。EF6通过&lt;code&gt;WillCascadeOnDelete()&lt;/code&gt;方法配置级联删除行为。&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;基本级联删除配置：&lt;/strong&gt;&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;protected override void OnModelCreating(DbModelBuilder modelBuilder)
{
    // ========== 启用级联删除 ==========
    // 删除用户时，自动删除其所有订单
    modelBuilder.Entity&amp;lt;Order&amp;gt;()
        .HasRequired(o =&amp;gt; o.User)
        .WithMany(u =&amp;gt; u.Orders)
        .HasForeignKey(o =&amp;gt; o.UserId)
        .WillCascadeOnDelete(true); // 启用级联删除
    
    // 效果：
    // DELETE FROM Users WHERE Id = 1
    // 会自动执行：DELETE FROM Orders WHERE UserId = 1
    
    // ========== 禁用级联删除 ==========
    // 删除用户时，如果有订单则不允许删除（会抛出异常）
    modelBuilder.Entity&amp;lt;Order&amp;gt;()
        .HasRequired(o =&amp;gt; o.User)
        .WithMany(u =&amp;gt; u.Orders)
        .HasForeignKey(o =&amp;gt; o.UserId)
        .WillCascadeOnDelete(false); // 禁用级联删除
    
    // 效果：
    // 如果User有Order，删除User会抛出异常：
    // &quot;The DELETE statement conflicted with the REFERENCE constraint&quot;
    // 需要先手动删除Order，再删除User
}
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;&lt;strong&gt;级联删除的多种场景：&lt;/strong&gt;&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;protected override void OnModelCreating(DbModelBuilder modelBuilder)
{
    // ========== 场景1：一对多关系的级联删除 ==========
    // User -&amp;gt; Order（级联删除）
    modelBuilder.Entity&amp;lt;Order&amp;gt;()
        .HasRequired(o =&amp;gt; o.User)
        .WithMany(u =&amp;gt; u.Orders)
        .HasForeignKey(o =&amp;gt; o.UserId)
        .WillCascadeOnDelete(true);
    
    // ========== 场景2：多级级联删除 ==========
    // User -&amp;gt; Order -&amp;gt; OrderItem
    // 删除User时，会级联删除Order，Order删除时会级联删除OrderItem
    
    modelBuilder.Entity&amp;lt;Order&amp;gt;()
        .HasRequired(o =&amp;gt; o.User)
        .WithMany(u =&amp;gt; u.Orders)
        .HasForeignKey(o =&amp;gt; o.UserId)
        .WillCascadeOnDelete(true); // User -&amp;gt; Order级联
    
    modelBuilder.Entity&amp;lt;OrderItem&amp;gt;()
        .HasRequired(oi =&amp;gt; oi.Order)
        .WithMany(o =&amp;gt; o.OrderItems)
        .HasForeignKey(oi =&amp;gt; oi.OrderId)
        .WillCascadeOnDelete(true); // Order -&amp;gt; OrderItem级联
    
    // 删除User(Id=1)时的执行顺序：
    // 1. DELETE FROM OrderItems WHERE OrderId IN (SELECT Id FROM Orders WHERE UserId = 1)
    // 2. DELETE FROM Orders WHERE UserId = 1
    // 3. DELETE FROM Users WHERE Id = 1
    
    // ========== 场景3：一对一关系的级联删除 ==========
    // User -&amp;gt; UserProfile（级联删除）
    modelBuilder.Entity&amp;lt;UserProfile&amp;gt;()
        .HasRequired(p =&amp;gt; p.User)
        .WithOptional(u =&amp;gt; u.Profile)
        .HasForeignKey(p =&amp;gt; p.UserId)
        .WillCascadeOnDelete(true);
    
    // 删除User时，会自动删除对应的UserProfile
    
    // ========== 场景4：可选关系的级联删除 ==========
    // Order -&amp;gt; User（可选，但可以级联删除）
    modelBuilder.Entity&amp;lt;Order&amp;gt;()
        .HasOptional(o =&amp;gt; o.User)
        .WithMany(u =&amp;gt; u.Orders)
        .HasForeignKey(o =&amp;gt; o.UserId)
        .WillCascadeOnDelete(true); // 可选关系也可以级联删除
    
    // ========== 场景5：多对多关系的级联删除 ==========
    // UserRole中间表：删除User时删除UserRole，但不删除Role
    
    modelBuilder.Entity&amp;lt;UserRole&amp;gt;()
        .HasRequired(ur =&amp;gt; ur.User)
        .WithMany(u =&amp;gt; u.UserRoles)
        .HasForeignKey(ur =&amp;gt; ur.UserId)
        .WillCascadeOnDelete(true); // 删除User时删除UserRole
    
    modelBuilder.Entity&amp;lt;UserRole&amp;gt;()
        .HasRequired(ur =&amp;gt; ur.Role)
        .WithMany(r =&amp;gt; r.UserRoles)
        .HasForeignKey(ur =&amp;gt; ur.RoleId)
        .WillCascadeOnDelete(false); // 删除Role时不删除UserRole（保护User）
    
    // 删除User时，只删除UserRole连接记录，Role不受影响
    // 删除Role时，如果存在UserRole，会抛出异常（需要先删除UserRole）
}
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;&lt;strong&gt;级联删除的实际使用示例：&lt;/strong&gt;&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;using (var context = new MyDbContext())
{
    // ========== 启用级联删除的情况 ==========
    // 删除User，会自动删除其所有Order和OrderItem
    var user = context.Users.Find(1);
    if (user != null)
    {
        context.Users.Remove(user);
        context.SaveChanges(); // 自动删除相关数据，不会抛出异常
    }
    
    // ========== 禁用级联删除的情况 ==========
    // 如果禁用了级联删除，需要手动删除子记录
    var user = context.Users.Find(1);
    if (user != null)
    {
        // 先删除所有订单项
        var orderIds = user.Orders.Select(o =&amp;gt; o.Id).ToList();
        var orderItems = context.OrderItems
            .Where(oi =&amp;gt; orderIds.Contains(oi.OrderId))
            .ToList();
        context.OrderItems.RemoveRange(orderItems);
        
        // 再删除所有订单
        context.Orders.RemoveRange(user.Orders);
        
        // 最后删除用户
        context.Users.Remove(user);
        context.SaveChanges();
    }
    
    // 或者使用数据库的级联删除（推荐）
    // 在EF中启用级联删除，让数据库处理
}
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;&lt;strong&gt;级联删除的注意事项：&lt;/strong&gt;&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;// ========== 注意事项 ==========
// 1. 级联删除是数据库级别的约束，不是EF代码逻辑
//    - 在数据库中创建外键约束时，会设置ON DELETE CASCADE
//    - EF只是配置这个约束，实际删除由数据库执行
//
// 2. 级联删除可能导致意外的数据丢失
//    - 删除父记录时，所有子记录都会被删除
//    - 生产环境需要谨慎使用
//
// 3. 级联删除的性能影响
//    - 删除父记录时，数据库需要检查所有子记录
//    - 如果子记录很多，删除操作可能较慢
//
// 4. 级联删除的限制
//    - SQL Server不允许循环级联删除
//    - 不能创建相互级联删除的关系
//
// 5. 最佳实践建议
//    - 开发环境：可以启用级联删除，方便测试
//    - 生产环境：建议禁用级联删除，使用软删除或手动删除
//    - 多对多关系：通常不级联删除，保护关联实体
//    - 重要数据：禁用级联删除，防止误删
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;&lt;strong&gt;级联删除的替代方案：&lt;/strong&gt;&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;// ========== 替代方案1：软删除 ==========
public class User
{
    public int Id { get; set; }
    public string Name { get; set; }
    public bool IsDeleted { get; set; } // 软删除标记
    public DateTime? DeletedDate { get; set; }
}

// 查询时过滤已删除的记录
var activeUsers = context.Users.Where(u =&amp;gt; !u.IsDeleted).ToList();

// ========== 替代方案2：手动删除 ==========
public void DeleteUser(int userId)
{
    using (var context = new MyDbContext())
    {
        var user = context.Users.Find(userId);
        if (user != null)
        {
            // 手动删除相关数据
            DeleteUserOrders(context, userId);
            DeleteUserProfile(context, userId);
            
            // 最后删除用户
            context.Users.Remove(user);
            context.SaveChanges();
        }
    }
}

// ========== 替代方案3：使用存储过程 ==========
// 在数据库中创建存储过程，控制删除逻辑
// CREATE PROCEDURE DeleteUser
//     @UserId INT
// AS
// BEGIN
//     DELETE FROM OrderItems WHERE OrderId IN (SELECT Id FROM Orders WHERE UserId = @UserId)
//     DELETE FROM Orders WHERE UserId = @UserId
//     DELETE FROM UserProfiles WHERE UserId = @UserId
//     DELETE FROM Users WHERE Id = @UserId
// END
&lt;/code&gt;&lt;/pre&gt;
&lt;h3&gt;&amp;lt;a id=&quot;ef6-querying&quot;&amp;gt;&amp;lt;/a&amp;gt;数据查询&lt;/h3&gt;
&lt;p&gt;Entity Framework支持多种查询方式：LINQ to Entities、原始SQL、存储过程等。&lt;/p&gt;
&lt;h4&gt;LINQ to Entities查询&lt;/h4&gt;
&lt;pre&gt;&lt;code&gt;using (var context = new MyDbContext())
{
    // ========== 基本查询 ==========
    
    // 查询所有用户
    var allUsers = context.Users.ToList();
    
    // 条件查询
    var adultUsers = context.Users.Where(u =&amp;gt; u.Age &amp;gt;= 18).ToList();
    
    // 排序查询
    var sortedUsers = context.Users.OrderBy(u =&amp;gt; u.Name).ToList();
    var sortedUsersDesc = context.Users.OrderByDescending(u =&amp;gt; u.Age).ToList();
    
    // 分页查询
    int pageSize = 10;
    int pageNumber = 2;
    var pagedUsers = context.Users
        .OrderBy(u =&amp;gt; u.Id)
        .Skip((pageNumber - 1) * pageSize)
        .Take(pageSize)
        .ToList();
    
    // 投影查询（只选择需要的字段）
    var userNames = context.Users.Select(u =&amp;gt; u.Name).ToList();
    var userInfo = context.Users.Select(u =&amp;gt; new 
    { 
        u.Id, 
        u.Name, 
        u.Email 
    }).ToList();
    
    // ========== 关联查询 ==========
    
    // Include：预加载关联数据（Eager Loading）
    var usersWithOrders = context.Users
        .Include(u =&amp;gt; u.Orders)
        .ToList();
    
    // 多级Include
    var usersWithOrdersAndItems = context.Users
        .Include(u =&amp;gt; u.Orders.Select(o =&amp;gt; o.OrderItems))
        .ToList();
    
    // 多个Include
    var usersWithAll = context.Users
        .Include(u =&amp;gt; u.Orders)
        .Include(u =&amp;gt; u.Profile)
        .ToList();
    
    // Join查询
    var usersWithOrderCount = context.Users
        .GroupJoin(
            context.Orders,
            u =&amp;gt; u.Id,
            o =&amp;gt; o.UserId,
            (user, orders) =&amp;gt; new 
            { 
                User = user, 
                OrderCount = orders.Count() 
            })
        .ToList();
    
    // ========== 聚合查询 ==========
    
    var userCount = context.Users.Count();
    var adultCount = context.Users.Count(u =&amp;gt; u.Age &amp;gt;= 18);
    var avgAge = context.Users.Average(u =&amp;gt; u.Age);
    var maxAge = context.Users.Max(u =&amp;gt; u.Age);
    var minAge = context.Users.Min(u =&amp;gt; u.Age);
    var totalAge = context.Users.Sum(u =&amp;gt; u.Age);
    
    // 分组查询
    var usersByAge = context.Users
        .GroupBy(u =&amp;gt; u.Age)
        .Select(g =&amp;gt; new 
        { 
            Age = g.Key, 
            Count = g.Count(),
            Users = g.ToList()
        })
        .ToList();
    
    // ========== 复杂查询 ==========
    
    // 子查询
    var usersWithManyOrders = context.Users
        .Where(u =&amp;gt; u.Orders.Count() &amp;gt; 5)
        .ToList();
    
    // 条件查询
    var users = context.Users
        .Where(u =&amp;gt; u.Age &amp;gt;= 18 &amp;amp;&amp;amp; u.Age &amp;lt;= 65)
        .Where(u =&amp;gt; u.Email.Contains(&quot;@example.com&quot;))
        .OrderBy(u =&amp;gt; u.Name)
        .ToList();
    
    // 动态查询
    IQueryable&amp;lt;User&amp;gt; query = context.Users;
    
    if (filterByAge)
    {
        query = query.Where(u =&amp;gt; u.Age &amp;gt;= minAge);
    }
    
    if (filterByName)
    {
        query = query.Where(u =&amp;gt; u.Name.Contains(nameFilter));
    }
    
    var result = query.ToList();
}
&lt;/code&gt;&lt;/pre&gt;
&lt;h4&gt;延迟加载（Lazy Loading）&lt;/h4&gt;
&lt;pre&gt;&lt;code&gt;// 启用延迟加载（默认启用）
public class MyDbContext : DbContext
{
    public MyDbContext() : base(&quot;DefaultConnection&quot;)
    {
        this.Configuration.LazyLoadingEnabled = true; // 启用延迟加载
    }
}

using (var context = new MyDbContext())
{
    var user = context.Users.First();
    
    // 访问导航属性时自动加载（延迟加载）
    var orders = user.Orders.ToList(); // 此时才执行SQL查询Orders表
}
&lt;/code&gt;&lt;/pre&gt;
&lt;h4&gt;显式加载（Explicit Loading）&lt;/h4&gt;
&lt;pre&gt;&lt;code&gt;using (var context = new MyDbContext())
{
    var user = context.Users.First();
    
    // 显式加载关联数据
    context.Entry(user)
        .Collection(u =&amp;gt; u.Orders)
        .Load();
    
    // 显式加载单个关联实体
    context.Entry(user)
        .Reference(u =&amp;gt; u.Profile)
        .Load();
    
    // 条件加载
    context.Entry(user)
        .Collection(u =&amp;gt; u.Orders)
        .Query()
        .Where(o =&amp;gt; o.TotalAmount &amp;gt; 100)
        .Load();
    
    // 检查是否已加载
    bool isLoaded = context.Entry(user)
        .Collection(u =&amp;gt; u.Orders)
        .IsLoaded;
}
&lt;/code&gt;&lt;/pre&gt;
&lt;h4&gt;原始SQL查询&lt;/h4&gt;
&lt;pre&gt;&lt;code&gt;using (var context = new MyDbContext())
{
    // 方式1：SqlQuery（返回实体）
    var users = context.Database.SqlQuery&amp;lt;User&amp;gt;(
        &quot;SELECT * FROM Users WHERE Age &amp;gt; @Age&quot;,
        new SqlParameter(&quot;@Age&quot;, 18))
        .ToList();
    
    // 方式2：SqlQuery（返回匿名类型）
    var userInfo = context.Database.SqlQuery&amp;lt;UserInfo&amp;gt;(
        &quot;SELECT Id, Name, Email FROM Users WHERE Age &amp;gt; @Age&quot;,
        new SqlParameter(&quot;@Age&quot;, 18))
        .ToList();
    
    // 方式3：ExecuteSqlCommand（执行命令）
    int rowsAffected = context.Database.ExecuteSqlCommand(
        &quot;UPDATE Users SET Age = Age + 1 WHERE Age &amp;lt; @Age&quot;,
        new SqlParameter(&quot;@Age&quot;, 18));
    
    // 方式4：使用DbSet的SqlQuery
    var users2 = context.Users.SqlQuery(
        &quot;SELECT * FROM Users WHERE Age &amp;gt; @Age&quot;,
        new SqlParameter(&quot;@Age&quot;, 18))
        .ToList();
}
&lt;/code&gt;&lt;/pre&gt;
&lt;h4&gt;存储过程查询&lt;/h4&gt;
&lt;pre&gt;&lt;code&gt;// 定义存储过程结果类
public class UserOrderSummary
{
    public int UserId { get; set; }
    public string UserName { get; set; }
    public int OrderCount { get; set; }
    public decimal TotalAmount { get; set; }
}

using (var context = new MyDbContext())
{
    // 调用存储过程
    var summaries = context.Database.SqlQuery&amp;lt;UserOrderSummary&amp;gt;(
        &quot;EXEC GetUserOrderSummary @UserId&quot;,
        new SqlParameter(&quot;@UserId&quot;, 1))
        .ToList();
    
    // 带输出参数的存储过程
    var userIdParam = new SqlParameter(&quot;@UserId&quot;, 1);
    var orderCountParam = new SqlParameter(&quot;@OrderCount&quot;, SqlDbType.Int)
    {
        Direction = ParameterDirection.Output
    };
    
    context.Database.ExecuteSqlCommand(
        &quot;EXEC GetUserOrderCount @UserId, @OrderCount OUTPUT&quot;,
        userIdParam,
        orderCountParam);
    
    int orderCount = (int)orderCountParam.Value;
}
&lt;/code&gt;&lt;/pre&gt;
&lt;h3&gt;&amp;lt;a id=&quot;ef6-modifying&quot;&amp;gt;&amp;lt;/a&amp;gt;数据修改&lt;/h3&gt;
&lt;p&gt;Entity Framework提供了多种方式来添加、更新和删除数据。&lt;/p&gt;
&lt;h4&gt;添加数据&lt;/h4&gt;
&lt;pre&gt;&lt;code&gt;using (var context = new MyDbContext())
{
    // 方式1：Add单个实体
    var newUser = new User
    {
        Name = &quot;张三&quot;,
        Email = &quot;zhangsan@example.com&quot;,
        Age = 25
    };
    context.Users.Add(newUser);
    context.SaveChanges(); // 返回受影响的行数
    
    // 方式2：AddRange多个实体
    var newUsers = new List&amp;lt;User&amp;gt;
    {
        new User { Name = &quot;李四&quot;, Email = &quot;lisi@example.com&quot;, Age = 30 },
        new User { Name = &quot;王五&quot;, Email = &quot;wangwu@example.com&quot;, Age = 28 }
    };
    context.Users.AddRange(newUsers);
    context.SaveChanges();
    
    // 方式3：直接设置状态
    var user = new User { Name = &quot;赵六&quot;, Email = &quot;zhaoliu@example.com&quot;, Age = 32 };
    context.Entry(user).State = EntityState.Added;
    context.SaveChanges();
    
    // 获取插入后的ID
    var user2 = new User { Name = &quot;新用户&quot;, Email = &quot;new@example.com&quot;, Age = 20 };
    context.Users.Add(user2);
    context.SaveChanges();
    int newId = user2.Id; // SaveChanges后自动填充ID
}
&lt;/code&gt;&lt;/pre&gt;
&lt;h4&gt;更新数据&lt;/h4&gt;
&lt;pre&gt;&lt;code&gt;using (var context = new MyDbContext())
{
    // 方式1：修改已跟踪的实体（推荐）
    var user = context.Users.Find(1);
    if (user != null)
    {
        user.Name = &quot;更新后的名称&quot;;
        user.Age = 30;
        context.SaveChanges(); // EF自动检测更改
    }
    
    // 方式2：附加并标记为已修改
    var detachedUser = new User 
    { 
        Id = 1, 
        Name = &quot;新名称&quot;, 
        Email = &quot;newemail@example.com&quot;,
        Age = 25
    };
    context.Users.Attach(detachedUser);
    context.Entry(detachedUser).State = EntityState.Modified;
    context.SaveChanges();
    
    // 方式3：只更新特定属性
    var userToUpdate = new User { Id = 1, Name = &quot;只更新名称&quot; };
    context.Users.Attach(userToUpdate);
    context.Entry(userToUpdate).Property(u =&amp;gt; u.Name).IsModified = true;
    context.SaveChanges(); // 只更新Name字段
    
    // 方式4：批量更新（使用原始SQL，性能更好）
    int rowsAffected = context.Database.ExecuteSqlCommand(
        &quot;UPDATE Users SET Age = Age + 1 WHERE Age &amp;lt; @Age&quot;,
        new SqlParameter(&quot;@Age&quot;, 18));
}
&lt;/code&gt;&lt;/pre&gt;
&lt;h4&gt;删除数据&lt;/h4&gt;
&lt;pre&gt;&lt;code&gt;using (var context = new MyDbContext())
{
    // 方式1：Remove单个实体
    var user = context.Users.Find(1);
    if (user != null)
    {
        context.Users.Remove(user);
        context.SaveChanges();
    }
    
    // 方式2：RemoveRange多个实体
    var usersToDelete = context.Users.Where(u =&amp;gt; u.Age &amp;lt; 18).ToList();
    context.Users.RemoveRange(usersToDelete);
    context.SaveChanges();
    
    // 方式3：直接设置状态
    var userToDelete = new User { Id = 2 };
    context.Entry(userToDelete).State = EntityState.Deleted;
    context.SaveChanges();
    
    // 方式4：批量删除（使用原始SQL，性能更好）
    int rowsAffected = context.Database.ExecuteSqlCommand(
        &quot;DELETE FROM Users WHERE Age &amp;lt; @Age&quot;,
        new SqlParameter(&quot;@Age&quot;, 18));
}
&lt;/code&gt;&lt;/pre&gt;
&lt;h4&gt;批量操作&lt;/h4&gt;
&lt;pre&gt;&lt;code&gt;using (var context = new MyDbContext())
{
    // 批量添加
    var users = new List&amp;lt;User&amp;gt;();
    for (int i = 0; i &amp;lt; 1000; i++)
    {
        users.Add(new User 
        { 
            Name = $&quot;User{i}&quot;, 
            Email = $&quot;user{i}@example.com&quot;, 
            Age = 20 + i % 50 
        });
    }
    
    // 方式1：AddRange（EF会分批执行）
    context.Users.AddRange(users);
    context.SaveChanges();
    
    // 方式2：使用BulkInsert扩展（需要安装EntityFramework.BulkInsert）
    // context.BulkInsert(users);
    
    // 批量更新
    var usersToUpdate = context.Users.Where(u =&amp;gt; u.Age &amp;lt; 18).ToList();
    foreach (var user in usersToUpdate)
    {
        user.Age = 18;
    }
    context.SaveChanges();
    
    // 批量删除
    var usersToDelete = context.Users.Where(u =&amp;gt; u.Age &amp;gt; 100).ToList();
    context.Users.RemoveRange(usersToDelete);
    context.SaveChanges();
}
&lt;/code&gt;&lt;/pre&gt;
&lt;h3&gt;&amp;lt;a id=&quot;change-tracking&quot;&amp;gt;&amp;lt;/a&amp;gt;变更跟踪&lt;/h3&gt;
&lt;p&gt;Entity Framework自动跟踪实体的状态变化，这是实现更新和删除的基础。&lt;/p&gt;
&lt;h4&gt;实体状态（EntityState）&lt;/h4&gt;
&lt;pre&gt;&lt;code&gt;using (var context = new MyDbContext())
{
    // 实体状态枚举：
    // Detached：未跟踪
    // Unchanged：未更改
    // Added：已添加
    // Deleted：已删除
    // Modified：已修改
    
    var user = new User { Name = &quot;新用户&quot;, Email = &quot;new@example.com&quot;, Age = 25 };
    
    // 检查状态
    var state1 = context.Entry(user).State; // Detached（未跟踪）
    
    context.Users.Add(user);
    var state2 = context.Entry(user).State; // Added（已添加）
    
    context.SaveChanges();
    var state3 = context.Entry(user).State; // Unchanged（未更改）
    
    user.Name = &quot;更新的名称&quot;;
    var state4 = context.Entry(user).State; // Modified（已修改）
    
    context.Users.Remove(user);
    var state5 = context.Entry(user).State; // Deleted（已删除）
    
    context.SaveChanges();
}
&lt;/code&gt;&lt;/pre&gt;
&lt;h4&gt;获取变更信息&lt;/h4&gt;
&lt;pre&gt;&lt;code&gt;using (var context = new MyDbContext())
{
    var user = context.Users.Find(1);
    user.Name = &quot;新名称&quot;;
    user.Age = 30;
    
    // 获取实体的变更跟踪信息
    var entry = context.Entry(user);
    
    // 检查实体是否被修改
    bool isModified = entry.State == EntityState.Modified;
    
    // 获取所有被修改的属性
    var modifiedProperties = entry.Properties
        .Where(p =&amp;gt; p.IsModified)
        .Select(p =&amp;gt; p.Name)
        .ToList();
    
    // 获取属性的原始值和当前值
    foreach (var property in entry.Properties)
    {
        if (property.IsModified)
        {
            var originalValue = property.OriginalValue;
            var currentValue = property.CurrentValue;
            Console.WriteLine($&quot;{property.Name}: {originalValue} -&amp;gt; {currentValue}&quot;);
        }
    }
    
    // 获取所有被跟踪的实体
    var allEntries = context.ChangeTracker.Entries();
    foreach (var e in allEntries)
    {
        Console.WriteLine($&quot;实体: {e.Entity.GetType().Name}, 状态: {e.State}&quot;);
    }
    
    // 获取特定类型的被跟踪实体
    var userEntries = context.ChangeTracker.Entries&amp;lt;User&amp;gt;();
    foreach (var e in userEntries)
    {
        if (e.State == EntityState.Modified)
        {
            Console.WriteLine($&quot;修改的用户: {e.Entity.Name}&quot;);
        }
    }
    
    context.SaveChanges();
}
&lt;/code&gt;&lt;/pre&gt;
&lt;h4&gt;禁用变更跟踪&lt;/h4&gt;
&lt;pre&gt;&lt;code&gt;using (var context = new MyDbContext())
{
    // AsNoTracking：禁用变更跟踪（提高查询性能）
    var users = context.Users
        .AsNoTracking()
        .ToList();
    
    // 修改后不会自动跟踪
    users[0].Name = &quot;新名称&quot;;
    // 需要手动附加才能更新
    context.Users.Attach(users[0]);
    context.Entry(users[0]).State = EntityState.Modified;
    context.SaveChanges();
    
    // 全局禁用自动检测更改（提高批量操作性能）
    context.Configuration.AutoDetectChangesEnabled = false;
    
    // 手动检测更改
    context.ChangeTracker.DetectChanges();
    
    // 批量操作后重新启用
    context.Configuration.AutoDetectChangesEnabled = true;
}
&lt;/code&gt;&lt;/pre&gt;
&lt;h3&gt;&amp;lt;a id=&quot;concurrency-control&quot;&amp;gt;&amp;lt;/a&amp;gt;并发控制&lt;/h3&gt;
&lt;p&gt;Entity Framework提供了多种并发控制机制来处理多用户同时修改数据的情况。&lt;/p&gt;
&lt;h4&gt;乐观并发控制（Optimistic Concurrency）&lt;/h4&gt;
&lt;pre&gt;&lt;code&gt;// 方式1：使用RowVersion（时间戳）
public class User
{
    public int Id { get; set; }
    public string Name { get; set; }
    
    [Timestamp] // 自动生成时间戳
    public byte[] RowVersion { get; set; }
}

// 更新时会检查RowVersion
using (var context = new MyDbContext())
{
    var user = context.Users.Find(1);
    user.Name = &quot;新名称&quot;;
    
    try
    {
        context.SaveChanges();
    }
    catch (DbUpdateConcurrencyException ex)
    {
        // 处理并发冲突
        var entry = ex.Entries.Single();
        var databaseValues = entry.GetDatabaseValues();
        var clientValues = entry.Entity as User;
        
        // 选择解决策略
        // 策略1：使用数据库值
        entry.OriginalValues.SetValues(databaseValues);
        
        // 策略2：使用客户端值
        entry.CurrentValues.SetValues(clientValues);
        
        // 策略3：合并值
        var databaseUser = databaseValues.ToObject() as User;
        clientValues.Name = databaseUser.Name; // 使用数据库的Name
        entry.CurrentValues.SetValues(clientValues);
        
        context.SaveChanges();
    }
}

// 方式2：使用ConcurrencyCheck特性
public class User
{
    public int Id { get; set; }
    
    [ConcurrencyCheck] // 并发检查
    public int Version { get; set; }
    
    public string Name { get; set; }
}

// 方式3：Fluent API配置并发
protected override void OnModelCreating(DbModelBuilder modelBuilder)
{
    modelBuilder.Entity&amp;lt;User&amp;gt;()
        .Property(u =&amp;gt; u.Version)
        .IsConcurrencyToken(); // 设置为并发令牌
}
&lt;/code&gt;&lt;/pre&gt;
&lt;h4&gt;悲观并发控制（Pessimistic Concurrency）&lt;/h4&gt;
&lt;pre&gt;&lt;code&gt;using (var context = new MyDbContext())
{
    using (var transaction = context.Database.BeginTransaction(IsolationLevel.Serializable))
    {
        try
        {
            // 使用Serializable隔离级别实现悲观锁
            var user = context.Users
                .SqlQuery(&quot;SELECT * FROM Users WITH (UPDLOCK) WHERE Id = @Id&quot;,
                    new SqlParameter(&quot;@Id&quot;, 1))
                .FirstOrDefault();
            
            if (user != null)
            {
                user.Name = &quot;更新的名称&quot;;
                context.SaveChanges();
            }
            
            transaction.Commit();
        }
        catch
        {
            transaction.Rollback();
            throw;
        }
    }
}
&lt;/code&gt;&lt;/pre&gt;
&lt;h3&gt;&amp;lt;a id=&quot;migrations&quot;&amp;gt;&amp;lt;/a&amp;gt;迁移（Migrations）&lt;/h3&gt;
&lt;p&gt;迁移是Entity Framework的数据库版本控制系统，用于管理数据库架构的变更。&lt;/p&gt;
&lt;h4&gt;启用迁移&lt;/h4&gt;
&lt;pre&gt;&lt;code&gt;# 在Package Manager Console中执行
PM&amp;gt; Enable-Migrations

# 指定上下文
PM&amp;gt; Enable-Migrations -ContextTypeName MyDbContext

# 启用自动迁移（不推荐用于生产环境）
PM&amp;gt; Enable-Migrations -EnableAutomaticMigrations
&lt;/code&gt;&lt;/pre&gt;
&lt;h4&gt;创建迁移&lt;/h4&gt;
&lt;pre&gt;&lt;code&gt;# 创建迁移
PM&amp;gt; Add-Migration InitialCreate

# 创建命名迁移
PM&amp;gt; Add-Migration AddUserTable

# 指定迁移名称和配置
PM&amp;gt; Add-Migration AddEmailIndex -ConfigurationTypeName MyProject.Migrations.Configuration
&lt;/code&gt;&lt;/pre&gt;
&lt;h4&gt;迁移文件结构&lt;/h4&gt;
&lt;pre&gt;&lt;code&gt;// 自动生成的迁移文件
public partial class AddUserTable : DbMigration
{
    public override void Up()
    {
        CreateTable(
            &quot;dbo.Users&quot;,
            c =&amp;gt; new
            {
                Id = c.Int(nullable: false, identity: true),
                Name = c.String(nullable: false, maxLength: 100),
                Email = c.String(nullable: false, maxLength: 200),
                Age = c.Int(nullable: false),
                CreateDate = c.DateTime(nullable: false),
            })
            .PrimaryKey(t =&amp;gt; t.Id)
            .Index(t =&amp;gt; t.Email, unique: true, name: &quot;IX_Users_Email&quot;);
    }
    
    public override void Down()
    {
        DropIndex(&quot;dbo.Users&quot;, &quot;IX_Users_Email&quot;);
        DropTable(&quot;dbo.Users&quot;);
    }
}
&lt;/code&gt;&lt;/pre&gt;
&lt;h4&gt;应用迁移&lt;/h4&gt;
&lt;pre&gt;&lt;code&gt;// 方式1：使用Update-Database命令
// PM&amp;gt; Update-Database

// 方式2：在代码中应用迁移
public class MyDbContext : DbContext
{
    public MyDbContext() : base(&quot;DefaultConnection&quot;)
    {
        // 自动应用迁移（仅用于开发环境）
        Database.SetInitializer(new MigrateDatabaseToLatestVersion&amp;lt;MyDbContext, Migrations.Configuration&amp;gt;());
    }
}

// 方式3：手动应用迁移
using (var context = new MyDbContext())
{
    var migrator = new DbMigrator(new Migrations.Configuration());
    migrator.Update(); // 应用所有待处理的迁移
}
&lt;/code&gt;&lt;/pre&gt;
&lt;h4&gt;回滚迁移&lt;/h4&gt;
&lt;pre&gt;&lt;code&gt;# 回滚到指定迁移
PM&amp;gt; Update-Database -TargetMigration PreviousMigrationName

# 回滚所有迁移
PM&amp;gt; Update-Database -TargetMigration 0
&lt;/code&gt;&lt;/pre&gt;
&lt;h3&gt;&amp;lt;a id=&quot;ef6-performance&quot;&amp;gt;&amp;lt;/a&amp;gt;性能优化&lt;/h3&gt;
&lt;p&gt;Entity Framework提供了多种性能优化技巧来提高查询和操作效率。&lt;/p&gt;
&lt;h4&gt;查询性能优化&lt;/h4&gt;
&lt;pre&gt;&lt;code&gt;using (var context = new MyDbContext())
{
    // 1. 使用AsNoTracking禁用变更跟踪（只读查询）
    var users = context.Users
        .AsNoTracking()
        .ToList();
    
    // 2. 使用Select只查询需要的字段
    var userNames = context.Users
        .Select(u =&amp;gt; new { u.Id, u.Name })
        .ToList();
    
    // 3. 使用Include预加载关联数据（避免N+1查询）
    var usersWithOrders = context.Users
        .Include(u =&amp;gt; u.Orders)
        .ToList();
    
    // 4. 禁用延迟加载（避免意外查询）
    context.Configuration.LazyLoadingEnabled = false;
    
    // 5. 使用Find查找（使用主键，性能最好）
    var user = context.Users.Find(1);
    
    // 6. 批量操作时禁用自动检测更改
    context.Configuration.AutoDetectChangesEnabled = false;
    
    for (int i = 0; i &amp;lt; 1000; i++)
    {
        context.Users.Add(new User { Name = $&quot;User{i}&quot;, Email = $&quot;user{i}@example.com&quot;, Age = 20 });
    }
    
    context.SaveChanges(); // 只检测一次更改
    
    context.Configuration.AutoDetectChangesEnabled = true;
    
    // 7. 使用编译查询（重复查询）
    private static readonly Func&amp;lt;MyDbContext, int, User&amp;gt; GetUserById =
        CompiledQuery.Compile((MyDbContext ctx, int id) =&amp;gt;
            ctx.Users.FirstOrDefault(u =&amp;gt; u.Id == id));
    
    var user2 = GetUserById(context, 1);
    
    // 8. 使用原始SQL进行复杂查询
    var users = context.Database.SqlQuery&amp;lt;User&amp;gt;(
        &quot;SELECT * FROM Users WHERE Age &amp;gt; @Age&quot;,
        new SqlParameter(&quot;@Age&quot;, 18))
        .ToList();
}
&lt;/code&gt;&lt;/pre&gt;
&lt;h4&gt;批量操作优化&lt;/h4&gt;
&lt;pre&gt;&lt;code&gt;using (var context = new MyDbContext())
{
    // 1. 批量添加
    var users = new List&amp;lt;User&amp;gt;();
    for (int i = 0; i &amp;lt; 1000; i++)
    {
        users.Add(new User { Name = $&quot;User{i}&quot;, Email = $&quot;user{i}@example.com&quot;, Age = 20 });
    }
    
    context.Configuration.AutoDetectChangesEnabled = false;
    context.Users.AddRange(users);
    context.SaveChanges();
    context.Configuration.AutoDetectChangesEnabled = true;
    
    // 2. 批量更新（使用原始SQL）
    int rowsAffected = context.Database.ExecuteSqlCommand(
        &quot;UPDATE Users SET Age = Age + 1 WHERE Age &amp;lt; @Age&quot;,
        new SqlParameter(&quot;@Age&quot;, 18));
    
    // 3. 批量删除（使用原始SQL）
    int deletedRows = context.Database.ExecuteSqlCommand(
        &quot;DELETE FROM Users WHERE Age &amp;lt; @Age&quot;,
        new SqlParameter(&quot;@Age&quot;, 18));
}
&lt;/code&gt;&lt;/pre&gt;
&lt;h3&gt;&amp;lt;a id=&quot;ef6-advanced&quot;&amp;gt;&amp;lt;/a&amp;gt;高级特性&lt;/h3&gt;
&lt;h4&gt;数据库初始化策略&lt;/h4&gt;
&lt;pre&gt;&lt;code&gt;public class MyDbContext : DbContext
{
    public MyDbContext() : base(&quot;DefaultConnection&quot;)
    {
        // 策略1：如果数据库不存在则创建（开发环境）
        Database.SetInitializer(new CreateDatabaseIfNotExists&amp;lt;MyDbContext&amp;gt;());
        
        // 策略2：总是删除并重新创建（开发环境，危险）
        // Database.SetInitializer(new DropCreateDatabaseAlways&amp;lt;MyDbContext&amp;gt;());
        
        // 策略3：模型改变时删除并重新创建（开发环境）
        // Database.SetInitializer(new DropCreateDatabaseIfModelChanges&amp;lt;MyDbContext&amp;gt;());
        
        // 策略4：使用迁移（生产环境推荐）
        Database.SetInitializer(new MigrateDatabaseToLatestVersion&amp;lt;MyDbContext, Migrations.Configuration&amp;gt;());
        
        // 策略5：禁用初始化（生产环境）
        // Database.SetInitializer&amp;lt;MyDbContext&amp;gt;(null);
    }
}
&lt;/code&gt;&lt;/pre&gt;
&lt;h4&gt;数据库日志记录&lt;/h4&gt;
&lt;pre&gt;&lt;code&gt;public class MyDbContext : DbContext
{
    public MyDbContext() : base(&quot;DefaultConnection&quot;)
    {
        // 记录SQL到控制台
        this.Database.Log = s =&amp;gt; Console.WriteLine(s);
        
        // 记录SQL到文件
        this.Database.Log = s =&amp;gt; 
        {
            File.AppendAllText(&quot;ef.log&quot;, s);
        };
        
        // 记录SQL到调试输出
        #if DEBUG
        this.Database.Log = s =&amp;gt; System.Diagnostics.Debug.WriteLine(s);
        #endif
    }
}
&lt;/code&gt;&lt;/pre&gt;
&lt;h4&gt;存储过程和函数映射&lt;/h4&gt;
&lt;pre&gt;&lt;code&gt;// 定义存储过程结果类
public class UserOrderResult
{
    public int UserId { get; set; }
    public string UserName { get; set; }
    public int OrderCount { get; set; }
}

// 调用存储过程
using (var context = new MyDbContext())
{
    var results = context.Database.SqlQuery&amp;lt;UserOrderResult&amp;gt;(
        &quot;EXEC GetUserOrders @UserId&quot;,
        new SqlParameter(&quot;@UserId&quot;, 1))
        .ToList();
}
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Entity Framework 6提供了强大的ORM功能，从基础的ADO.NET到高级的EF6特性，涵盖了数据访问的各个方面。通过合理使用这些特性，可以大大提高开发效率和代码质量。&lt;/p&gt;
&lt;h2&gt;&amp;lt;a id=&quot;design-patterns&quot;&amp;gt;&amp;lt;/a&amp;gt;C#设计模式详解&lt;/h2&gt;
&lt;p&gt;设计模式是软件工程中经过验证的解决方案，用于解决常见的设计问题。它们提供了一套最佳实践，帮助开发者构建可维护、可扩展和可重用的软件系统。在C#中，设计模式被广泛应用于各种类型的应用程序开发。&lt;/p&gt;
&lt;h3&gt;&amp;lt;a id=&quot;singleton-pattern&quot;&amp;gt;&amp;lt;/a&amp;gt;单例模式&lt;/h3&gt;
&lt;p&gt;单例模式是一种创建型设计模式，它确保一个类只有一个实例，并提供一个全局访问点来获取这个实例。单例模式在需要控制资源访问、协调系统行为或提供全局状态管理时非常有用。&lt;/p&gt;
&lt;h4&gt;单例模式的核心要点&lt;/h4&gt;
&lt;ol&gt;
&lt;li&gt;&lt;strong&gt;单一实例&lt;/strong&gt;：类只能有一个实例&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;全局访问&lt;/strong&gt;：提供一个全局访问点来获取该实例&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;自我实例化&lt;/strong&gt;：类负责创建自己的唯一实例&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;防止外部实例化&lt;/strong&gt;：通过私有构造函数防止外部创建新实例&lt;/li&gt;
&lt;/ol&gt;
&lt;h4&gt;单例模式的实现方式&lt;/h4&gt;
&lt;h5&gt;1. 饿汉式单例&lt;/h5&gt;
&lt;p&gt;饿汉式单例在类加载时就创建实例，线程安全但可能导致资源浪费。&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;// 线程安全的饿汉式单例模式实现
public sealed class Singleton
{
    // 私有静态字段，保存唯一实例
    private static readonly Singleton _instance = new Singleton();
    
    // 静态构造函数，由CLR保证线程安全
    static Singleton() { }
    
    // 私有构造函数，防止外部实例化
    private Singleton() { }
    
    // 公共静态属性，提供全局访问点
    public static Singleton Instance
    {
        get { return _instance; }
    }
    
    // 单例类的成员
    public void DoSomething()
    {
        Console.WriteLine(&quot;Singleton instance is doing something.&quot;);
    }
}

// 使用单例
Singleton.Instance.DoSomething();
&lt;/code&gt;&lt;/pre&gt;
&lt;h5&gt;2. 懒汉式单例（使用Lazy&amp;lt;T&amp;gt;）&lt;/h5&gt;
&lt;p&gt;懒汉式单例在第一次使用时才创建实例，实现了延迟初始化，节省资源。C# 4.0引入的&lt;code&gt;Lazy&amp;lt;T&amp;gt;&lt;/code&gt;类提供了简洁且线程安全的延迟初始化实现。&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;// 使用Lazy&amp;lt;T&amp;gt;实现的懒汉式单例
public sealed class LazySingleton
{
    // 使用Lazy&amp;lt;T&amp;gt;实现延迟初始化
    private static readonly Lazy&amp;lt;LazySingleton&amp;gt; _lazyInstance = 
        new Lazy&amp;lt;LazySingleton&amp;gt;(() =&amp;gt; new LazySingleton(), LazyThreadSafetyMode.ExecutionAndPublication);
    
    private LazySingleton() { }
    
    public static LazySingleton Instance
    {
        get { return _lazyInstance.Value; }
    }
    
    public void DoSomething()
    {
        Console.WriteLine(&quot;Lazy singleton instance is doing something.&quot;);
    }
}
&lt;/code&gt;&lt;/pre&gt;
&lt;h5&gt;3. 双重检查锁定单例&lt;/h5&gt;
&lt;p&gt;双重检查锁定是一种传统的延迟初始化实现方式，通过两次检查实例是否已创建来减少锁的开销。&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;// 双重检查锁定实现的单例
public sealed class DoubleCheckedLockingSingleton
{
    // volatile关键字确保多线程环境下的可见性
    private static volatile DoubleCheckedLockingSingleton _instance;
    private static readonly object _lock = new object();
    
    private DoubleCheckedLockingSingleton() { }
    
    public static DoubleCheckedLockingSingleton Instance
    {
        get
        {
            // 第一次检查（无锁）
            if (_instance == null)
            {
                // 加锁
                lock (_lock)
                {
                    // 第二次检查（有锁）
                    if (_instance == null)
                    {
                        _instance = new DoubleCheckedLockingSingleton();
                    }
                }
            }
            return _instance;
        }
    }
    
    public void DoSomething()
    {
        Console.WriteLine(&quot;Double-checked locking singleton instance is doing something.&quot;);
    }
}
&lt;/code&gt;&lt;/pre&gt;
&lt;h5&gt;4. 静态内部类单例&lt;/h5&gt;
&lt;p&gt;利用C#类加载机制实现的线程安全单例，既实现了延迟初始化，又避免了显式加锁。&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;// 静态内部类实现的单例
public sealed class InnerClassSingleton
{
    private InnerClassSingleton() { }
    
    // 静态内部类
    private static class SingletonHolder
    {
        // 在内部类中创建实例
        internal static readonly InnerClassSingleton Instance = new InnerClassSingleton();
    }
    
    public static InnerClassSingleton Instance
    {
        get { return SingletonHolder.Instance; }
    }
    
    public void DoSomething()
    {
        Console.WriteLine(&quot;Inner class singleton instance is doing something.&quot;);
    }
}
&lt;/code&gt;&lt;/pre&gt;
&lt;h4&gt;单例模式的优缺点&lt;/h4&gt;
&lt;h5&gt;优点&lt;/h5&gt;
&lt;ol&gt;
&lt;li&gt;&lt;strong&gt;唯一实例&lt;/strong&gt;：确保类只有一个实例，避免资源浪费&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;全局访问&lt;/strong&gt;：提供统一的访问点，方便状态管理&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;延迟初始化&lt;/strong&gt;：部分实现支持延迟加载，提高启动性能&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;线程安全&lt;/strong&gt;：正确实现的单例模式是线程安全的&lt;/li&gt;
&lt;/ol&gt;
&lt;h5&gt;缺点&lt;/h5&gt;
&lt;ol&gt;
&lt;li&gt;&lt;strong&gt;隐藏依赖关系&lt;/strong&gt;：使用全局访问点可能导致代码耦合&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;测试困难&lt;/strong&gt;：单例模式可能影响单元测试的隔离性&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;违背单一职责原则&lt;/strong&gt;：单例类既要负责自身的业务逻辑，又要负责管理实例&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;扩展性差&lt;/strong&gt;：通常难以扩展单例类&lt;/li&gt;
&lt;/ol&gt;
&lt;h4&gt;单例模式的应用场景&lt;/h4&gt;
&lt;ol&gt;
&lt;li&gt;&lt;strong&gt;资源管理&lt;/strong&gt;：数据库连接池、日志系统、配置管理器等需要共享资源的场景&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;全局状态&lt;/strong&gt;：需要在应用程序范围内共享状态的场景&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;服务类&lt;/strong&gt;：提供全局服务的类，如缓存服务、消息队列服务等&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;工具类&lt;/strong&gt;：需要统一访问点的工具类，如日期时间管理器、加密服务等&lt;/li&gt;
&lt;/ol&gt;
&lt;h4&gt;单例模式的最佳实践&lt;/h4&gt;
&lt;ol&gt;
&lt;li&gt;&lt;strong&gt;使用sealed关键字&lt;/strong&gt;：防止单例类被继承&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;选择合适的实现方式&lt;/strong&gt;：根据需求选择饿汉式或懒汉式&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;优先使用Lazy&amp;lt;T&amp;gt;&lt;/strong&gt;：在.NET 4.0及以上版本中，推荐使用&lt;code&gt;Lazy&amp;lt;T&amp;gt;&lt;/code&gt;实现延迟初始化&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;避免滥用&lt;/strong&gt;：仅在确实需要单一实例时使用单例模式&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;考虑线程安全&lt;/strong&gt;：确保在多线程环境下的正确性&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;实现IDisposable&lt;/strong&gt;：如果单例持有非托管资源，应实现&lt;code&gt;IDisposable&lt;/code&gt;接口&lt;/li&gt;
&lt;/ol&gt;
&lt;h3&gt;&amp;lt;a id=&quot;factory-pattern&quot;&amp;gt;&amp;lt;/a&amp;gt;工厂模式&lt;/h3&gt;
&lt;p&gt;工厂模式是一种创建型设计模式，它提供了一种创建对象的最佳方式，将对象的创建与使用分离。工厂模式包括简单工厂、工厂方法和抽象工厂三种形式。&lt;/p&gt;
&lt;h4&gt;1. 简单工厂模式&lt;/h4&gt;
&lt;p&gt;简单工厂模式通过一个工厂类来创建所有产品实例，客户端不需要知道具体产品的创建细节。&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;// 产品接口
public interface IProduct
{
    void Display();
}

// 具体产品A
public class ProductA : IProduct
{
    public void Display()
    {
        Console.WriteLine(&quot;这是产品A&quot;);
    }
}

// 具体产品B
public class ProductB : IProduct
{
    public void Display()
    {
        Console.WriteLine(&quot;这是产品B&quot;);
    }
}

// 简单工厂类
public class SimpleFactory
{
    public IProduct CreateProduct(string type)
    {
        switch (type.ToLower())
        {
            case &quot;a&quot;:
                return new ProductA();
            case &quot;b&quot;:
                return new ProductB();
            default:
                throw new ArgumentException(&quot;无效的产品类型&quot;);
        }
    }
}

// 使用示例
public void TestSimpleFactory()
{
    SimpleFactory factory = new SimpleFactory();
    IProduct productA = factory.CreateProduct(&quot;a&quot;);
    productA.Display();
    
    IProduct productB = factory.CreateProduct(&quot;b&quot;);
    productB.Display();
}
&lt;/code&gt;&lt;/pre&gt;
&lt;h4&gt;2. 工厂方法模式&lt;/h4&gt;
&lt;p&gt;工厂方法模式将产品的创建推迟到子类中实现，每个具体产品对应一个具体工厂。&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;// 产品接口
public interface IProduct
{
    void Display();
}

// 具体产品A
public class ProductA : IProduct
{
    public void Display()
    {
        Console.WriteLine(&quot;这是产品A&quot;);
    }
}

// 具体产品B
public class ProductB : IProduct
{
    public void Display()
    {
        Console.WriteLine(&quot;这是产品B&quot;);
    }
}

// 抽象工厂接口
public interface IFactory
{
    IProduct CreateProduct();
}

// 具体工厂A
public class FactoryA : IFactory
{
    public IProduct CreateProduct()
    {
        return new ProductA();
    }
}

// 具体工厂B
public class FactoryB : IFactory
{
    public IProduct CreateProduct()
    {
        return new ProductB();
    }
}

// 使用示例
public void TestFactoryMethod()
{
    IFactory factoryA = new FactoryA();
    IProduct productA = factoryA.CreateProduct();
    productA.Display();
    
    IFactory factoryB = new FactoryB();
    IProduct productB = factoryB.CreateProduct();
    productB.Display();
}
&lt;/code&gt;&lt;/pre&gt;
&lt;h4&gt;3. 抽象工厂模式&lt;/h4&gt;
&lt;p&gt;抽象工厂模式用于创建一系列相关或相互依赖的对象，它提供了一个接口，可以创建多个产品族中的产品对象。&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;// 抽象产品A
public interface IProductA
{
    void Display();
}

// 抽象产品B
public interface IProductB
{
    void Display();
}

// 具体产品A1
public class ProductA1 : IProductA
{
    public void Display()
    {
        Console.WriteLine(&quot;这是产品A1&quot;);
    }
}

// 具体产品A2
public class ProductA2 : IProductA
{
    public void Display()
    {
        Console.WriteLine(&quot;这是产品A2&quot;);
    }
}

// 具体产品B1
public class ProductB1 : IProductB
{
    public void Display()
    {
        Console.WriteLine(&quot;这是产品B1&quot;);
    }
}

// 具体产品B2
public class ProductB2 : IProductB
{
    public void Display()
    {
        Console.WriteLine(&quot;这是产品B2&quot;);
    }
}

// 抽象工厂
public interface IAbstractFactory
{
    IProductA CreateProductA();
    IProductB CreateProductB();
}

// 具体工厂1
public class ConcreteFactory1 : IAbstractFactory
{
    public IProductA CreateProductA()
    {
        return new ProductA1();
    }
    
    public IProductB CreateProductB()
    {
        return new ProductB1();
    }
}

// 具体工厂2
public class ConcreteFactory2 : IAbstractFactory
{
    public IProductA CreateProductA()
    {
        return new ProductA2();
    }
    
    public IProductB CreateProductB()
    {
        return new ProductB2();
    }
}

// 使用示例
public void TestAbstractFactory()
{
    IAbstractFactory factory1 = new ConcreteFactory1();
    IProductA productA1 = factory1.CreateProductA();
    IProductB productB1 = factory1.CreateProductB();
    productA1.Display();
    productB1.Display();
    
    IAbstractFactory factory2 = new ConcreteFactory2();
    IProductA productA2 = factory2.CreateProductA();
    IProductB productB2 = factory2.CreateProductB();
    productA2.Display();
    productB2.Display();
}
&lt;/code&gt;&lt;/pre&gt;
&lt;h3&gt;&amp;lt;a id=&quot;observer-pattern&quot;&amp;gt;&amp;lt;/a&amp;gt;观察者模式&lt;/h3&gt;
&lt;p&gt;观察者模式（发布-订阅模式）定义了对象间的一种一对多依赖关系，当一个对象状态发生变化时，所有依赖它的对象都会得到通知并自动更新。&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;// 被观察者接口
public interface ISubject
{
    void RegisterObserver(IObserver observer);
    void RemoveObserver(IObserver observer);
    void NotifyObservers();
}

// 观察者接口
public interface IObserver
{
    void Update(string message);
}

// 具体被观察者
public class Subject : ISubject
{
    private List&amp;lt;IObserver&amp;gt; _observers = new List&amp;lt;IObserver&amp;gt;();
    private string _message;
    
    public void RegisterObserver(IObserver observer)
    {
        _observers.Add(observer);
    }
    
    public void RemoveObserver(IObserver observer)
    {
        _observers.Remove(observer);
    }
    
    public void NotifyObservers()
    {
        foreach (var observer in _observers)
        {
            observer.Update(_message);
        }
    }
    
    public void SetMessage(string message)
    {
        _message = message;
        NotifyObservers();
    }
}

// 具体观察者A
public class ObserverA : IObserver
{
    public void Update(string message)
    {
        Console.WriteLine($&quot;观察者A收到消息: {message}&quot;);
    }
}

// 具体观察者B
public class ObserverB : IObserver
{
    public void Update(string message)
    {
        Console.WriteLine($&quot;观察者B收到消息: {message}&quot;);
    }
}

// 使用示例
public void TestObserver()
{
    Subject subject = new Subject();
    
    IObserver observerA = new ObserverA();
    IObserver observerB = new ObserverB();
    
    subject.RegisterObserver(observerA);
    subject.RegisterObserver(observerB);
    
    subject.SetMessage(&quot;Hello, Observers!&quot;);
    
    subject.RemoveObserver(observerA);
    
    subject.SetMessage(&quot;Second message!&quot;);
}
&lt;/code&gt;&lt;/pre&gt;
&lt;h3&gt;&amp;lt;a id=&quot;strategy-pattern&quot;&amp;gt;&amp;lt;/a&amp;gt;策略模式&lt;/h3&gt;
&lt;p&gt;策略模式定义了一系列算法，并将每个算法封装起来，使它们可以相互替换。策略模式让算法独立于使用它的客户而变化。&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;// 策略接口
public interface IStrategy
{
    int Calculate(int a, int b);
}

// 具体策略A：加法
public class AddStrategy : IStrategy
{
    public int Calculate(int a, int b)
    {
        return a + b;
    }
}

// 具体策略B：减法
public class SubtractStrategy : IStrategy
{
    public int Calculate(int a, int b)
    {
        return a - b;
    }
}

// 具体策略C：乘法
public class MultiplyStrategy : IStrategy
{
    public int Calculate(int a, int b)
    {
        return a * b;
    }
}

// 上下文类
public class Context
{
    private IStrategy _strategy;
    
    public Context(IStrategy strategy)
    {
        _strategy = strategy;
    }
    
    public void SetStrategy(IStrategy strategy)
    {
        _strategy = strategy;
    }
    
    public int ExecuteStrategy(int a, int b)
    {
        return _strategy.Calculate(a, b);
    }
}

// 使用示例
public void TestStrategy()
{
    Context context = new Context(new AddStrategy());
    Console.WriteLine($&quot;10 + 5 = {context.ExecuteStrategy(10, 5)}&quot;);
    
    context.SetStrategy(new SubtractStrategy());
    Console.WriteLine($&quot;10 - 5 = {context.ExecuteStrategy(10, 5)}&quot;);
    
    context.SetStrategy(new MultiplyStrategy());
    Console.WriteLine($&quot;10 * 5 = {context.ExecuteStrategy(10, 5)}&quot;);
}
&lt;/code&gt;&lt;/pre&gt;
&lt;h3&gt;&amp;lt;a id=&quot;adapter-pattern&quot;&amp;gt;&amp;lt;/a&amp;gt;适配器模式&lt;/h3&gt;
&lt;p&gt;适配器模式将一个类的接口转换成客户希望的另外一个接口，使得原本由于接口不兼容而不能一起工作的那些类可以一起工作。&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;// 目标接口
public interface ITarget
{
    void Request();
}

// 适配者类
public class Adaptee
{
    public void SpecificRequest()
    {
        Console.WriteLine(&quot;适配者的具体请求&quot;);
    }
}

// 类适配器
public class ClassAdapter : Adaptee, ITarget
{
    public void Request()
    {
        SpecificRequest();
    }
}

// 对象适配器
public class ObjectAdapter : ITarget
{
    private Adaptee _adaptee;
    
    public ObjectAdapter(Adaptee adaptee)
    {
        _adaptee = adaptee;
    }
    
    public void Request()
    {
        _adaptee.SpecificRequest();
    }
}

// 使用示例
public void TestAdapter()
{
    // 类适配器
    ITarget classAdapter = new ClassAdapter();
    classAdapter.Request();
    
    // 对象适配器
    Adaptee adaptee = new Adaptee();
    ITarget objectAdapter = new ObjectAdapter(adaptee);
    objectAdapter.Request();
}
&lt;/code&gt;&lt;/pre&gt;
&lt;h3&gt;&amp;lt;a id=&quot;decorator-pattern&quot;&amp;gt;&amp;lt;/a&amp;gt;装饰器模式&lt;/h3&gt;
&lt;p&gt;装饰器模式动态地给一个对象添加一些额外的职责，就增加功能来说，装饰器模式比生成子类更为灵活。&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;// 组件接口
public interface IComponent
{
    string Operation();
}

// 具体组件
public class ConcreteComponent : IComponent
{
    public string Operation()
    {
        return &quot;具体组件的操作&quot;;
    }
}

// 装饰器抽象类
public abstract class Decorator : IComponent
{
    protected IComponent _component;
    
    public Decorator(IComponent component)
    {
        _component = component;
    }
    
    public virtual string Operation()
    {
        return _component.Operation();
    }
}

// 具体装饰器A
public class ConcreteDecoratorA : Decorator
{
    public ConcreteDecoratorA(IComponent component) : base(component) { }
    
    public override string Operation()
    {
        return $&quot;{base.Operation()} + 装饰器A的操作&quot;;
    }
}

// 具体装饰器B
public class ConcreteDecoratorB : Decorator
{
    public ConcreteDecoratorB(IComponent component) : base(component) { }
    
    public override string Operation()
    {
        return $&quot;{base.Operation()} + 装饰器B的操作&quot;;
    }
}

// 使用示例
public void TestDecorator()
{
    IComponent component = new ConcreteComponent();
    Console.WriteLine(component.Operation());
    
    IComponent decoratorA = new ConcreteDecoratorA(component);
    Console.WriteLine(decoratorA.Operation());
    
    IComponent decoratorB = new ConcreteDecoratorB(decoratorA);
    Console.WriteLine(decoratorB.Operation());
}
&lt;/code&gt;&lt;/pre&gt;
&lt;h3&gt;&amp;lt;a id=&quot;dependency-injection-pattern&quot;&amp;gt;&amp;lt;/a&amp;gt;依赖注入模式&lt;/h3&gt;
&lt;p&gt;依赖注入模式是一种实现控制反转（IoC）的设计模式，它允许对象在运行时接收依赖关系，而不是在编译时硬编码。&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;// 服务接口
public interface IService
{
    void Execute();
}

// 具体服务A
public class ServiceA : IService
{
    public void Execute()
    {
        Console.WriteLine(&quot;ServiceA执行&quot;);
    }
}

// 具体服务B
public class ServiceB : IService
{
    public void Execute()
    {
        Console.WriteLine(&quot;ServiceB执行&quot;);
    }
}

// 客户端类
public class Client
{
    private IService _service;
    
    // 构造函数注入
    public Client(IService service)
    {
        _service = service;
    }
    
    // 属性注入
    public IService Service
    {
        get { return _service; }
        set { _service = value; }
    }
    
    // 方法注入
    public void SetService(IService service)
    {
        _service = service;
    }
    
    public void DoSomething()
    {
        _service.Execute();
    }
}

// 使用示例
public void TestDependencyInjection()
{
    // 构造函数注入
    IService serviceA = new ServiceA();
    Client client = new Client(serviceA);
    client.DoSomething();
    
    // 属性注入
    IService serviceB = new ServiceB();
    client.Service = serviceB;
    client.DoSomething();
    
    // 方法注入
    client.SetService(serviceA);
    client.DoSomething();
}
&lt;/code&gt;&lt;/pre&gt;
&lt;h2&gt;&amp;lt;a id=&quot;communication-programming&quot;&amp;gt;&amp;lt;/a&amp;gt;C#通信编程详解&lt;/h2&gt;
&lt;p&gt;通信编程是C#开发中的重要组成部分，它涉及不同系统之间的数据交换和通信。C#提供了多种通信方式，包括TCP/IP、HTTP、WebSocket、命名管道等。本章将详细介绍C#中的各种通信编程技术。&lt;/p&gt;
&lt;h3&gt;&amp;lt;a id=&quot;tcp-ip-basics&quot;&amp;gt;&amp;lt;/a&amp;gt;TCP/IP通信基础&lt;/h3&gt;
&lt;p&gt;TCP/IP是Internet的基础协议，它提供了可靠的、面向连接的通信机制。在C#中，可以通过Socket编程或高级封装类（如TcpClient、TcpListener）来实现TCP/IP通信。&lt;/p&gt;
&lt;h4&gt;TCP/IP协议栈&lt;/h4&gt;
&lt;p&gt;TCP/IP协议栈由四层组成：&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;strong&gt;应用层&lt;/strong&gt;：提供应用程序接口，如HTTP、FTP、SMTP等&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;传输层&lt;/strong&gt;：提供端到端的通信服务，主要协议包括TCP和UDP&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;网络层&lt;/strong&gt;：负责数据包的路由和转发，主要协议是IP&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;链路层&lt;/strong&gt;：负责物理网络的连接和数据帧的传输&lt;/li&gt;
&lt;/ul&gt;
&lt;h4&gt;TCP与UDP的区别&lt;/h4&gt;
&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;特性&lt;/th&gt;
&lt;th&gt;TCP&lt;/th&gt;
&lt;th&gt;UDP&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;连接类型&lt;/td&gt;
&lt;td&gt;面向连接&lt;/td&gt;
&lt;td&gt;无连接&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;可靠性&lt;/td&gt;
&lt;td&gt;可靠&lt;/td&gt;
&lt;td&gt;不可靠&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;传输速度&lt;/td&gt;
&lt;td&gt;相对较慢&lt;/td&gt;
&lt;td&gt;相对较快&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;数据大小&lt;/td&gt;
&lt;td&gt;无限制&lt;/td&gt;
&lt;td&gt;有限制（通常小于65535字节）&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;适用场景&lt;/td&gt;
&lt;td&gt;文件传输、网页浏览等&lt;/td&gt;
&lt;td&gt;实时通信、视频流等&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;
&lt;h3&gt;&amp;lt;a id=&quot;socket-programming&quot;&amp;gt;&amp;lt;/a&amp;gt;Socket编程&lt;/h3&gt;
&lt;p&gt;Socket是TCP/IP通信的基础，它提供了低级别的网络通信API。通过Socket编程，可以实现复杂的网络通信逻辑。&lt;/p&gt;
&lt;h4&gt;Socket基本概念&lt;/h4&gt;
&lt;ul&gt;
&lt;li&gt;
&lt;p&gt;&lt;strong&gt;Socket类型&lt;/strong&gt;：&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;code&gt;Stream&lt;/code&gt;：面向连接的TCP套接字&lt;/li&gt;
&lt;li&gt;&lt;code&gt;Datagram&lt;/code&gt;：无连接的UDP套接字&lt;/li&gt;
&lt;li&gt;&lt;code&gt;Raw&lt;/code&gt;：原始套接字，用于直接访问网络层协议&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;&lt;strong&gt;Socket地址&lt;/strong&gt;：&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;code&gt;IPEndPoint&lt;/code&gt;：包含IP地址和端口号&lt;/li&gt;
&lt;li&gt;&lt;code&gt;Dns&lt;/code&gt;：用于域名解析&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;h4&gt;TCP Socket服务器示例&lt;/h4&gt;
&lt;pre&gt;&lt;code&gt;using System;
using System.Net;
using System.Net.Sockets;
using System.Text;
using System.Threading;

public class TcpSocketServer
{
    public static void Start()
    {
        // 创建TCP监听套接字
        TcpListener server = new TcpListener(IPAddress.Any, 8888);
        server.Start();
        Console.WriteLine(&quot;TCP服务器已启动，监听端口8888...&quot;);

        while (true)
        {
            // 等待客户端连接
            TcpClient client = server.AcceptTcpClient();
            Console.WriteLine($&quot;客户端 {((IPEndPoint)client.Client.RemoteEndPoint).Address} 已连接&quot;);

            // 为每个客户端创建一个新线程处理通信
            Thread clientThread = new Thread(HandleClient);
            clientThread.Start(client);
        }
    }

    private static void HandleClient(object obj)
    {
        TcpClient client = (TcpClient)obj;
        NetworkStream stream = client.GetStream();
        byte[] buffer = new byte[1024];

        try
        {
            while (true)
            {
                // 读取客户端数据
                int bytesRead = stream.Read(buffer, 0, buffer.Length);
                if (bytesRead == 0) break;

                string message = Encoding.UTF8.GetString(buffer, 0, bytesRead);
                Console.WriteLine($&quot;收到客户端消息: {message}&quot;);

                // 回复客户端
                string response = $&quot;服务器已收到消息: {message}&quot;;
                byte[] responseBytes = Encoding.UTF8.GetBytes(response);
                stream.Write(responseBytes, 0, responseBytes.Length);
            }
        }
        catch (Exception ex)
        {
            Console.WriteLine($&quot;通信错误: {ex.Message}&quot;);
        }
        finally
        {
            stream.Close();
            client.Close();
            Console.WriteLine($&quot;客户端 {((IPEndPoint)client.Client.RemoteEndPoint).Address} 已断开连接&quot;);
        }
    }
}

// 使用示例
// TcpSocketServer.Start();
&lt;/code&gt;&lt;/pre&gt;
&lt;h4&gt;TCP Socket客户端示例&lt;/h4&gt;
&lt;pre&gt;&lt;code&gt;using System;
using System.Net.Sockets;
using System.Text;

public class TcpSocketClient
{
    public static void Start()
    {
        TcpClient client = new TcpClient();
        
        try
        {
            // 连接服务器
            client.Connect(&quot;127.0.0.1&quot;, 8888);
            Console.WriteLine(&quot;已连接到服务器&quot;);

            NetworkStream stream = client.GetStream();
            
            // 发送消息
            string message = &quot;Hello, Server!&quot;;
            byte[] messageBytes = Encoding.UTF8.GetBytes(message);
            stream.Write(messageBytes, 0, messageBytes.Length);
            Console.WriteLine($&quot;已发送消息: {message}&quot;);

            // 接收回复
            byte[] buffer = new byte[1024];
            int bytesRead = stream.Read(buffer, 0, buffer.Length);
            string response = Encoding.UTF8.GetString(buffer, 0, bytesRead);
            Console.WriteLine($&quot;收到服务器回复: {response}&quot;);
        }
        catch (Exception ex)
        {
            Console.WriteLine($&quot;连接错误: {ex.Message}&quot;);
        }
        finally
        {
            client.Close();
        }
    }
}

// 使用示例
// TcpSocketClient.Start();
&lt;/code&gt;&lt;/pre&gt;
&lt;h3&gt;&amp;lt;a id=&quot;tcpclient-tcplistener&quot;&amp;gt;&amp;lt;/a&amp;gt;TcpClient与TcpListener&lt;/h3&gt;
&lt;p&gt;TcpClient和TcpListener是Socket的高级封装，它们简化了TCP通信的开发过程。&lt;/p&gt;
&lt;h4&gt;TcpListener（TCP服务器）示例&lt;/h4&gt;
&lt;pre&gt;&lt;code&gt;using System;
using System.Net;
using System.Net.Sockets;
using System.Text;
using System.Threading.Tasks;

public class TcpListenerExample
{
    public static async Task StartAsync()
    {
        // 创建TcpListener实例
        TcpListener listener = new TcpListener(IPAddress.Loopback, 9000);
        listener.Start();
        Console.WriteLine(&quot;TcpListener服务器已启动，监听端口9000...&quot;);

        while (true)
        {
            // 异步接受客户端连接
            TcpClient client = await listener.AcceptTcpClientAsync();
            Console.WriteLine($&quot;客户端已连接: {client.Client.RemoteEndPoint}&quot;);
            
            // 使用异步方法处理客户端通信
            _ = HandleClientAsync(client);
        }
    }

    private static async Task HandleClientAsync(TcpClient client)
    {
        using (client)
        {
            NetworkStream stream = client.GetStream();
            byte[] buffer = new byte[1024];

            try
            {
                while (true)
                {
                    // 异步读取数据
                    int bytesRead = await stream.ReadAsync(buffer, 0, buffer.Length);
                    if (bytesRead == 0) break;

                    string message = Encoding.UTF8.GetString(buffer, 0, bytesRead);
                    Console.WriteLine($&quot;收到消息: {message}&quot;);

                    // 异步回复
                    string response = $&quot;已处理消息: {message}&quot;;
                    byte[] responseBytes = Encoding.UTF8.GetBytes(response);
                    await stream.WriteAsync(responseBytes, 0, responseBytes.Length);
                }
            }
            catch (Exception ex)
            {
                Console.WriteLine($&quot;通信错误: {ex.Message}&quot;);
            }
            finally
            {
                Console.WriteLine($&quot;客户端已断开连接: {client.Client.RemoteEndPoint}&quot;);
            }
        }
    }
}

// 使用示例
// await TcpListenerExample.StartAsync();
&lt;/code&gt;&lt;/pre&gt;
&lt;h4&gt;TcpClient（TCP客户端）示例&lt;/h4&gt;
&lt;pre&gt;&lt;code&gt;using System;
using System.Net.Sockets;
using System.Text;
using System.Threading.Tasks;

public class TcpClientExample
{
    public static async Task StartAsync()
    {
        using (TcpClient client = new TcpClient())
        {
            // 异步连接服务器
            await client.ConnectAsync(IPAddress.Loopback, 9000);
            Console.WriteLine(&quot;已连接到服务器&quot;);

            NetworkStream stream = client.GetStream();

            // 发送多条消息
            for (int i = 1; i &amp;lt;= 3; i++)
            {
                string message = $&quot;消息 #{i}&quot;;
                byte[] messageBytes = Encoding.UTF8.GetBytes(message);
                await stream.WriteAsync(messageBytes, 0, messageBytes.Length);
                Console.WriteLine($&quot;已发送: {message}&quot;);

                // 接收回复
                byte[] buffer = new byte[1024];
                int bytesRead = await stream.ReadAsync(buffer, 0, buffer.Length);
                string response = Encoding.UTF8.GetString(buffer, 0, bytesRead);
                Console.WriteLine($&quot;收到回复: {response}&quot;);

                // 等待1秒
                await Task.Delay(1000);
            }
        }
    }
}

// 使用示例
// await TcpClientExample.StartAsync();
&lt;/code&gt;&lt;/pre&gt;
&lt;h3&gt;&amp;lt;a id=&quot;udpclient-programming&quot;&amp;gt;&amp;lt;/a&amp;gt;UdpClient编程&lt;/h3&gt;
&lt;p&gt;UdpClient是UDP通信的高级封装，它简化了UDP套接字的使用。&lt;/p&gt;
&lt;h4&gt;UDP服务器示例&lt;/h4&gt;
&lt;pre&gt;&lt;code&gt;using System;
using System.Net;
using System.Net.Sockets;
using System.Text;
using System.Threading.Tasks;

public class UdpServerExample
{
    public static async Task StartAsync()
    {
        UdpClient server = new UdpClient(9001);
        Console.WriteLine(&quot;UDP服务器已启动，监听端口9001...&quot;);

        IPEndPoint remoteEndPoint = new IPEndPoint(IPAddress.Any, 0);

        try
        {
            while (true)
            {
                // 异步接收数据
                byte[] receivedBytes = await server.ReceiveAsync();
                string message = Encoding.UTF8.GetString(receivedBytes);
                Console.WriteLine($&quot;收到来自 {remoteEndPoint.Address}:{remoteEndPoint.Port} 的消息: {message}&quot;);

                // 回复客户端
                string response = $&quot;服务器已收到UDP消息: {message}&quot;;
                byte[] responseBytes = Encoding.UTF8.GetBytes(response);
                await server.SendAsync(responseBytes, responseBytes.Length, remoteEndPoint);
            }
        }
        catch (Exception ex)
        {
            Console.WriteLine($&quot;UDP服务器错误: {ex.Message}&quot;);
        }
        finally
        {
            server.Close();
        }
    }
}

// 使用示例
// await UdpServerExample.StartAsync();
&lt;/code&gt;&lt;/pre&gt;
&lt;h4&gt;UDP客户端示例&lt;/h4&gt;
&lt;pre&gt;&lt;code&gt;using System;
using System.Net;
using System.Net.Sockets;
using System.Text;
using System.Threading.Tasks;

public class UdpClientExample
{
    public static async Task StartAsync()
    {
        using (UdpClient client = new UdpClient())
        {
            IPEndPoint serverEndPoint = new IPEndPoint(IPAddress.Loopback, 9001);

            // 发送UDP消息
            string message = &quot;Hello UDP Server!&quot;;
            byte[] messageBytes = Encoding.UTF8.GetBytes(message);
            await client.SendAsync(messageBytes, messageBytes.Length, serverEndPoint);
            Console.WriteLine($&quot;已发送UDP消息: {message}&quot;);

            // 接收回复
            client.Client.ReceiveTimeout = 5000;
            IPEndPoint remoteEndPoint = new IPEndPoint(IPAddress.Any, 0);
            byte[] receivedBytes = await client.ReceiveAsync();
            string response = Encoding.UTF8.GetString(receivedBytes);
            Console.WriteLine($&quot;收到UDP回复: {response}&quot;);
        }
    }
}

// 使用示例
// await UdpClientExample.StartAsync();
&lt;/code&gt;&lt;/pre&gt;
&lt;h3&gt;&amp;lt;a id=&quot;http-communication&quot;&amp;gt;&amp;lt;/a&amp;gt;HTTP通信&lt;/h3&gt;
&lt;p&gt;HTTP是Web通信的基础协议，C#提供了多种方式来实现HTTP通信，包括HttpClient、HttpWebRequest等。&lt;/p&gt;
&lt;h4&gt;HttpClient示例&lt;/h4&gt;
&lt;p&gt;HttpClient是.NET 4.5引入的现代化HTTP客户端，它支持异步操作，是推荐的HTTP通信方式。&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;using System;
using System.Net.Http;
using System.Threading.Tasks;

public class HttpClientExample
{
    private static readonly HttpClient _client = new HttpClient();

    public static async Task GetAsync()
    {
        try
        {
            // 发送GET请求
            HttpResponseMessage response = await _client.GetAsync(&quot;https://jsonplaceholder.typicode.com/posts/1&quot;);
            
            // 确保请求成功
            response.EnsureSuccessStatusCode();
            
            // 读取响应内容
            string responseBody = await response.Content.ReadAsStringAsync();
            Console.WriteLine($&quot;GET请求成功，响应内容: {responseBody}&quot;);
        }
        catch (HttpRequestException ex)
        {
            Console.WriteLine($&quot;GET请求失败: {ex.Message}&quot;);
        }
    }

    public static async Task PostAsync()
    {
        try
        {
            // 创建请求内容
            var postData = new {
                title = &quot;测试标题&quot;,
                body = &quot;测试内容&quot;,
                userId = 1
            };
            
            // 序列化对象为JSON
            string json = Newtonsoft.Json.JsonConvert.SerializeObject(postData);
            var content = new StringContent(json, System.Text.Encoding.UTF8, &quot;application/json&quot;);
            
            // 发送POST请求
            HttpResponseMessage response = await _client.PostAsync(&quot;https://jsonplaceholder.typicode.com/posts&quot;, content);
            
            response.EnsureSuccessStatusCode();
            
            string responseBody = await response.Content.ReadAsStringAsync();
            Console.WriteLine($&quot;POST请求成功，响应内容: {responseBody}&quot;);
        }
        catch (HttpRequestException ex)
        {
            Console.WriteLine($&quot;POST请求失败: {ex.Message}&quot;);
        }
    }

    public static async Task PutAsync()
    {
        try
        {
            var putData = new {
                id = 1,
                title = &quot;更新后的标题&quot;,
                body = &quot;更新后的内容&quot;,
                userId = 1
            };
            
            string json = Newtonsoft.Json.JsonConvert.SerializeObject(putData);
            var content = new StringContent(json, System.Text.Encoding.UTF8, &quot;application/json&quot;);
            
            HttpResponseMessage response = await _client.PutAsync(&quot;https://jsonplaceholder.typicode.com/posts/1&quot;, content);
            
            response.EnsureSuccessStatusCode();
            
            string responseBody = await response.Content.ReadAsStringAsync();
            Console.WriteLine($&quot;PUT请求成功，响应内容: {responseBody}&quot;);
        }
        catch (HttpRequestException ex)
        {
            Console.WriteLine($&quot;PUT请求失败: {ex.Message}&quot;);
        }
    }

    public static async Task DeleteAsync()
    {
        try
        {
            HttpResponseMessage response = await _client.DeleteAsync(&quot;https://jsonplaceholder.typicode.com/posts/1&quot;);
            
            response.EnsureSuccessStatusCode();
            Console.WriteLine($&quot;DELETE请求成功，状态码: {response.StatusCode}&quot;);
        }
        catch (HttpRequestException ex)
        {
            Console.WriteLine($&quot;DELETE请求失败: {ex.Message}&quot;);
        }
    }
}

// 使用示例
// await HttpClientExample.GetAsync();
// await HttpClientExample.PostAsync();
// await HttpClientExample.PutAsync();
// await HttpClientExample.DeleteAsync();
&lt;/code&gt;&lt;/pre&gt;
&lt;h3&gt;&amp;lt;a id=&quot;websocket-communication&quot;&amp;gt;&amp;lt;/a&amp;gt;WebSocket通信&lt;/h3&gt;
&lt;p&gt;WebSocket提供了全双工的通信方式，允许服务器主动向客户端推送数据。C#中可以使用ClientWebSocket和WebSocket类来实现WebSocket通信。&lt;/p&gt;
&lt;h4&gt;WebSocket服务器示例（使用ASP.NET Core）&lt;/h4&gt;
&lt;pre&gt;&lt;code&gt;// 在ASP.NET Core控制器中
using Microsoft.AspNetCore.Mvc;
using Microsoft.AspNetCore.SignalR;

public class ChatHub : Hub
{
    public async Task SendMessage(string user, string message)
    {
        // 广播消息给所有客户端
        await Clients.All.SendAsync(&quot;ReceiveMessage&quot;, user, message);
    }

    public async Task SendPrivateMessage(string user, string receiver, string message)
    {
        // 发送私聊消息
        await Clients.User(receiver).SendAsync(&quot;ReceivePrivateMessage&quot;, user, message);
    }

    public override async Task OnConnectedAsync()
    {
        // 客户端连接时的处理
        await Clients.All.SendAsync(&quot;UserConnected&quot;, Context.ConnectionId);
        await base.OnConnectedAsync();
    }

    public override async Task OnDisconnectedAsync(Exception exception)
    {
        // 客户端断开连接时的处理
        await Clients.All.SendAsync(&quot;UserDisconnected&quot;, Context.ConnectionId);
        await base.OnDisconnectedAsync(exception);
    }
}

// Startup.cs中配置SignalR
public void ConfigureServices(IServiceCollection services)
{
    services.AddSignalR();
    // 其他配置...
}

public void Configure(IApplicationBuilder app, IWebHostEnvironment env)
{
    // 其他配置...
    app.UseEndpoints(endpoints =&amp;gt;
    {
        endpoints.MapHub&amp;lt;ChatHub&amp;gt;(&quot;/chathub&quot;);
        // 其他端点...
    });
}
&lt;/code&gt;&lt;/pre&gt;
&lt;h4&gt;WebSocket客户端示例&lt;/h4&gt;
&lt;pre&gt;&lt;code&gt;using System;
using System.Net.WebSockets;
using System.Text;
using System.Threading;
using System.Threading.Tasks;

public class WebSocketClientExample
{
    public static async Task StartAsync()
    {
        using (ClientWebSocket client = new ClientWebSocket())
        {
            // 连接到WebSocket服务器
            Uri serverUri = new Uri(&quot;wss://echo.websocket.org&quot;);
            await client.ConnectAsync(serverUri, CancellationToken.None);
            Console.WriteLine(&quot;WebSocket客户端已连接&quot;);

            // 发送消息
            string message = &quot;Hello WebSocket!&quot;;
            byte[] buffer = Encoding.UTF8.GetBytes(message);
            await client.SendAsync(new ArraySegment&amp;lt;byte&amp;gt;(buffer), WebSocketMessageType.Text, true, CancellationToken.None);
            Console.WriteLine($&quot;已发送WebSocket消息: {message}&quot;);

            // 接收消息
            buffer = new byte[1024];
            WebSocketReceiveResult result = await client.ReceiveAsync(new ArraySegment&amp;lt;byte&amp;gt;(buffer), CancellationToken.None);
            string response = Encoding.UTF8.GetString(buffer, 0, result.Count);
            Console.WriteLine($&quot;收到WebSocket回复: {response}&quot;);

            // 关闭连接
            await client.CloseAsync(WebSocketCloseStatus.NormalClosure, &quot;正常关闭&quot;, CancellationToken.None);
            Console.WriteLine(&quot;WebSocket连接已关闭&quot;);
        }
    }
}

// 使用示例
// await WebSocketClientExample.StartAsync();
&lt;/code&gt;&lt;/pre&gt;
&lt;h3&gt;&amp;lt;a id=&quot;named-pipes&quot;&amp;gt;&amp;lt;/a&amp;gt;命名管道通信&lt;/h3&gt;
&lt;p&gt;命名管道是Windows操作系统提供的一种进程间通信机制，它允许同一台计算机上的不同进程或不同计算机上的进程进行通信。&lt;/p&gt;
&lt;h4&gt;命名管道服务器示例&lt;/h4&gt;
&lt;pre&gt;&lt;code&gt;using System;
using System.IO.Pipes;
using System.Text;
using System.Threading.Tasks;

public class NamedPipeServerExample
{
    public static async Task StartAsync()
    {
        while (true)
        {
            // 创建命名管道服务器
            using (NamedPipeServerStream pipeServer = new NamedPipeServerStream(
                &quot;TestPipe&quot;,
                PipeDirection.InOut,
                NamedPipeServerStream.MaxAllowedServerInstances,
                PipeTransmissionMode.Message))
            {
                Console.WriteLine(&quot;命名管道服务器已启动，等待客户端连接...&quot;);
                
                // 等待客户端连接
                await pipeServer.WaitForConnectionAsync();
                Console.WriteLine(&quot;客户端已连接&quot;);

                try
                {
                    // 读取客户端消息
                    byte[] buffer = new byte[1024];
                    int bytesRead = await pipeServer.ReadAsync(buffer, 0, buffer.Length);
                    string message = Encoding.UTF8.GetString(buffer, 0, bytesRead);
                    Console.WriteLine($&quot;收到客户端消息: {message}&quot;);

                    // 回复客户端
                    string response = $&quot;服务器已收到消息: {message}&quot;;
                    byte[] responseBytes = Encoding.UTF8.GetBytes(response);
                    await pipeServer.WriteAsync(responseBytes, 0, responseBytes.Length);
                    pipeServer.Flush();
                }
                catch (Exception ex)
                {
                    Console.WriteLine($&quot;通信错误: {ex.Message}&quot;);
                }
            }
        }
    }
}

// 使用示例
// await NamedPipeServerExample.StartAsync();
&lt;/code&gt;&lt;/pre&gt;
&lt;h4&gt;命名管道客户端示例&lt;/h4&gt;
&lt;pre&gt;&lt;code&gt;using System;
using System.IO.Pipes;
using System.Text;
using System.Threading.Tasks;

public class NamedPipeClientExample
{
    public static async Task StartAsync()
    {
        // 创建命名管道客户端
        using (NamedPipeClientStream pipeClient = new NamedPipeClientStream(
            &quot;.&quot;,
            &quot;TestPipe&quot;,
            PipeDirection.InOut,
            PipeOptions.Asynchronous))
        {
            Console.WriteLine(&quot;正在连接到命名管道服务器...&quot;);
            
            // 连接到服务器
            await pipeClient.ConnectAsync();
            Console.WriteLine(&quot;已连接到命名管道服务器&quot;);

            // 发送消息
            string message = &quot;Hello Named Pipe Server!&quot;;
            byte[] buffer = Encoding.UTF8.GetBytes(message);
            await pipeClient.WriteAsync(buffer, 0, buffer.Length);
            pipeClient.Flush();
            Console.WriteLine($&quot;已发送消息: {message}&quot;);

            // 接收回复
            buffer = new byte[1024];
            int bytesRead = await pipeClient.ReadAsync(buffer, 0, buffer.Length);
            string response = Encoding.UTF8.GetString(buffer, 0, bytesRead);
            Console.WriteLine($&quot;收到回复: {response}&quot;);
        }
    }
}

// 使用示例
// await NamedPipeClientExample.StartAsync();
&lt;/code&gt;&lt;/pre&gt;
&lt;h3&gt;&amp;lt;a id=&quot;wcf-communication&quot;&amp;gt;&amp;lt;/a&amp;gt;WCF通信&lt;/h3&gt;
&lt;p&gt;WCF（Windows Communication Foundation）是.NET框架中的一种通信技术，它提供了统一的编程模型，用于构建面向服务的应用程序。&lt;/p&gt;
&lt;h4&gt;WCF服务契约示例&lt;/h4&gt;
&lt;pre&gt;&lt;code&gt;using System.ServiceModel;

// 定义服务契约
[ServiceContract]
public interface ICalculatorService
{
    [OperationContract]
    int Add(int a, int b);
    
    [OperationContract]
    int Subtract(int a, int b);
    
    [OperationContract]
    int Multiply(int a, int b);
    
    [OperationContract]
    double Divide(int a, int b);
}

// 实现服务契约
public class CalculatorService : ICalculatorService
{
    public int Add(int a, int b)
    {
        return a + b;
    }
    
    public int Subtract(int a, int b)
    {
        return a - b;
    }
    
    public int Multiply(int a, int b)
    {
        return a * b;
    }
    
    public double Divide(int a, int b)
    {
        if (b == 0)
            throw new FaultException(&quot;除数不能为零&quot;);
        
        return (double)a / b;
    }
}
&lt;/code&gt;&lt;/pre&gt;
&lt;h4&gt;WCF服务配置（app.config）&lt;/h4&gt;
&lt;pre&gt;&lt;code&gt;&amp;lt;configuration&amp;gt;
  &amp;lt;system.serviceModel&amp;gt;
    &amp;lt;services&amp;gt;
      &amp;lt;service name=&quot;CalculatorService&quot; behaviorConfiguration=&quot;CalculatorServiceBehavior&quot;&amp;gt;
        &amp;lt;endpoint address=&quot;&quot; binding=&quot;wsHttpBinding&quot; contract=&quot;ICalculatorService&quot;&amp;gt;
          &amp;lt;identity&amp;gt;
            &amp;lt;dns value=&quot;localhost&quot; /&amp;gt;
          &amp;lt;/identity&amp;gt;
        &amp;lt;/endpoint&amp;gt;
        &amp;lt;endpoint address=&quot;mex&quot; binding=&quot;mexHttpBinding&quot; contract=&quot;IMetadataExchange&quot; /&amp;gt;
        &amp;lt;host&amp;gt;
          &amp;lt;baseAddresses&amp;gt;
            &amp;lt;add baseAddress=&quot;http://localhost:8000/CalculatorService&quot; /&amp;gt;
          &amp;lt;/baseAddresses&amp;gt;
        &amp;lt;/host&amp;gt;
      &amp;lt;/service&amp;gt;
    &amp;lt;/services&amp;gt;
    &amp;lt;behaviors&amp;gt;
      &amp;lt;serviceBehaviors&amp;gt;
        &amp;lt;behavior name=&quot;CalculatorServiceBehavior&quot;&amp;gt;
          &amp;lt;serviceMetadata httpGetEnabled=&quot;True&quot; /&amp;gt;
          &amp;lt;serviceDebug includeExceptionDetailInFaults=&quot;False&quot; /&amp;gt;
        &amp;lt;/behavior&amp;gt;
      &amp;lt;/serviceBehaviors&amp;gt;
    &amp;lt;/behaviors&amp;gt;
  &amp;lt;/system.serviceModel&amp;gt;
&amp;lt;/configuration&amp;gt;
&lt;/code&gt;&lt;/pre&gt;
&lt;h4&gt;WCF客户端调用示例&lt;/h4&gt;
&lt;pre&gt;&lt;code&gt;using System;
using System.ServiceModel;

// 使用svcutil.exe生成客户端代理
// svcutil.exe http://localhost:8000/CalculatorService?wsdl

public class WcfClientExample
{
    public static void Start()
    {
        // 创建WCF客户端
        CalculatorServiceClient client = new CalculatorServiceClient();
        
        try
        {
            // 调用WCF服务方法
            int addResult = client.Add(10, 5);
            Console.WriteLine($&quot;10 + 5 = {addResult}&quot;);
            
            int subtractResult = client.Subtract(10, 5);
            Console.WriteLine($&quot;10 - 5 = {subtractResult}&quot;);
            
            int multiplyResult = client.Multiply(10, 5);
            Console.WriteLine($&quot;10 * 5 = {multiplyResult}&quot;);
            
            double divideResult = client.Divide(10, 5);
            Console.WriteLine($&quot;10 / 5 = {divideResult}&quot;);
        }
        catch (FaultException ex)
        {
            Console.WriteLine($&quot;WCF服务调用失败: {ex.Message}&quot;);
        }
        finally
        {
            client.Close();
        }
    }
}
&lt;/code&gt;&lt;/pre&gt;
&lt;h3&gt;&amp;lt;a id=&quot;grpc-communication&quot;&amp;gt;&amp;lt;/a&amp;gt;gRPC通信&lt;/h3&gt;
&lt;p&gt;gRPC是一种高性能、开源的远程过程调用（RPC）框架，它基于HTTP/2协议，使用Protocol Buffers作为序列化机制。&lt;/p&gt;
&lt;h4&gt;gRPC服务定义（.proto文件）&lt;/h4&gt;
&lt;pre&gt;&lt;code&gt;// calculator.proto
syntax = &quot;proto3&quot;;

option csharp_namespace = &quot;GrpcCalculator&quot;;

package calculator;

// 定义计算器服务
service Calculator {
  // 定义Add方法
  rpc Add (AddRequest) returns (AddResponse);
  
  // 定义Subtract方法
  rpc Subtract (SubtractRequest) returns (SubtractResponse);
  
  // 定义Multiply方法
  rpc Multiply (MultiplyRequest) returns (MultiplyResponse);
  
  // 定义Divide方法
  rpc Divide (DivideRequest) returns (DivideResponse);
}

// Add请求消息
message AddRequest {
  int32 a = 1;
  int32 b = 2;
}

// Add响应消息
message AddResponse {
  int32 result = 1;
}

// Subtract请求消息
message SubtractRequest {
  int32 a = 1;
  int32 b = 2;
}

// Subtract响应消息
message SubtractResponse {
  int32 result = 1;
}

// Multiply请求消息
message MultiplyRequest {
  int32 a = 1;
  int32 b = 2;
}

// Multiply响应消息
message MultiplyResponse {
  int32 result = 1;
}

// Divide请求消息
message DivideRequest {
  int32 a = 1;
  int32 b = 2;
}

// Divide响应消息
message DivideResponse {
  double result = 1;
}
&lt;/code&gt;&lt;/pre&gt;
&lt;h4&gt;gRPC服务实现&lt;/h4&gt;
&lt;pre&gt;&lt;code&gt;using Grpc.Core;
using GrpcCalculator;

public class CalculatorServiceImpl : Calculator.CalculatorBase
{
    public override Task&amp;lt;AddResponse&amp;gt; Add(AddRequest request, ServerCallContext context)
    {
        int result = request.A + request.B;
        return Task.FromResult(new AddResponse { Result = result });
    }

    public override Task&amp;lt;SubtractResponse&amp;gt; Subtract(SubtractRequest request, ServerCallContext context)
    {
        int result = request.A - request.B;
        return Task.FromResult(new SubtractResponse { Result = result });
    }

    public override Task&amp;lt;MultiplyResponse&amp;gt; Multiply(MultiplyRequest request, ServerCallContext context)
    {
        int result = request.A * request.B;
        return Task.FromResult(new MultiplyResponse { Result = result });
    }

    public override Task&amp;lt;DivideResponse&amp;gt; Divide(DivideRequest request, ServerCallContext context)
    {
        if (request.B == 0)
        {
            throw new RpcException(new Status(StatusCode.InvalidArgument, &quot;除数不能为零&quot;));
        }
        double result = (double)request.A / request.B;
        return Task.FromResult(new DivideResponse { Result = result });
    }
}
&lt;/code&gt;&lt;/pre&gt;
&lt;h4&gt;gRPC服务器启动&lt;/h4&gt;
&lt;pre&gt;&lt;code&gt;using Grpc.Core;

public class GrpcServerExample
{
    public static void Start()
    {
        const int Port = 50051;

        Server server = new Server
        {
            Services = { Calculator.BindService(new CalculatorServiceImpl()) },
            Ports = { new ServerPort(&quot;localhost&quot;, Port, ServerCredentials.Insecure) }
        };

        server.Start();
        Console.WriteLine($&quot;gRPC服务器已启动，监听端口 {Port}...&quot;);
        Console.WriteLine(&quot;按任意键停止服务器...&quot;);
        Console.ReadKey();

        server.ShutdownAsync().Wait();
    }
}

// 使用示例
// GrpcServerExample.Start();
&lt;/code&gt;&lt;/pre&gt;
&lt;h4&gt;gRPC客户端调用&lt;/h4&gt;
&lt;pre&gt;&lt;code&gt;using Grpc.Core;
using GrpcCalculator;

public class GrpcClientExample
{
    public static void Start()
    {
        Channel channel = new Channel(&quot;localhost:50051&quot;, ChannelCredentials.Insecure);
        Calculator.CalculatorClient client = new Calculator.CalculatorClient(channel);

        try
        {
            // 调用Add方法
            AddRequest addRequest = new AddRequest { A = 10, B = 5 };
            AddResponse addResponse = client.Add(addRequest);
            Console.WriteLine($&quot;10 + 5 = {addResponse.Result}&quot;);

            // 调用Subtract方法
            SubtractRequest subtractRequest = new SubtractRequest { A = 10, B = 5 };
            SubtractResponse subtractResponse = client.Subtract(subtractRequest);
            Console.WriteLine($&quot;10 - 5 = {subtractResponse.Result}&quot;);

            // 调用Multiply方法
            MultiplyRequest multiplyRequest = new MultiplyRequest { A = 10, B = 5 };
            MultiplyResponse multiplyResponse = client.Multiply(multiplyRequest);
            Console.WriteLine($&quot;10 * 5 = {multiplyResponse.Result}&quot;);

            // 调用Divide方法
            DivideRequest divideRequest = new DivideRequest { A = 10, B = 5 };
            DivideResponse divideResponse = client.Divide(divideRequest);
            Console.WriteLine($&quot;10 / 5 = {divideResponse.Result}&quot;);
        }
        catch (RpcException ex)
        {
            Console.WriteLine($&quot;gRPC调用失败: {ex.Status.Detail}&quot;);
        }
        finally
        {
            channel.ShutdownAsync().Wait();
        }
    }
}

// 使用示例
// GrpcClientExample.Start();
&lt;/code&gt;&lt;/pre&gt;
&lt;h3&gt;&amp;lt;a id=&quot;message-queues&quot;&amp;gt;&amp;lt;/a&amp;gt;消息队列&lt;/h3&gt;
&lt;p&gt;消息队列是一种异步通信机制，它允许应用程序之间通过消息进行通信，而不需要直接调用彼此。常见的消息队列系统包括RabbitMQ、Azure Service Bus、Apache Kafka等。&lt;/p&gt;
&lt;h4&gt;RabbitMQ示例&lt;/h4&gt;
&lt;pre&gt;&lt;code&gt;using RabbitMQ.Client;
using RabbitMQ.Client.Events;
using System;
using System.Text;

public class RabbitMqExample
{
    private const string QueueName = &quot;test_queue&quot;;
    private const string ExchangeName = &quot;test_exchange&quot;;
    private const string RoutingKey = &quot;test_routing_key&quot;;

    // 发送消息
    public static void SendMessage()
    {
        var factory = new ConnectionFactory() { HostName = &quot;localhost&quot; };
        using (var connection = factory.CreateConnection())
        using (var channel = connection.CreateModel())
        {
            // 声明交换机
            channel.ExchangeDeclare(exchange: ExchangeName, type: ExchangeType.Direct);
            
            // 声明队列
            channel.QueueDeclare(queue: QueueName, durable: false, exclusive: false, autoDelete: false, arguments: null);
            
            // 绑定队列到交换机
            channel.QueueBind(queue: QueueName, exchange: ExchangeName, routingKey: RoutingKey);
            
            // 发送消息
            string message = &quot;Hello RabbitMQ!&quot;;
            var body = Encoding.UTF8.GetBytes(message);
            channel.BasicPublish(exchange: ExchangeName, routingKey: RoutingKey, basicProperties: null, body: body);
            Console.WriteLine($&quot;已发送消息: {message}&quot;);
        }
    }

    // 接收消息
    public static void ReceiveMessages()
    {
        var factory = new ConnectionFactory() { HostName = &quot;localhost&quot; };
        using (var connection = factory.CreateConnection())
        using (var channel = connection.CreateModel())
        {
            channel.QueueDeclare(queue: QueueName, durable: false, exclusive: false, autoDelete: false, arguments: null);
            
            var consumer = new EventingBasicConsumer(channel);
            consumer.Received += (model, ea) =&amp;gt;
            {
                var body = ea.Body.ToArray();
                var message = Encoding.UTF8.GetString(body);
                Console.WriteLine($&quot;收到消息: {message}&quot;);
            };
            
            channel.BasicConsume(queue: QueueName, autoAck: true, consumer: consumer);
            
            Console.WriteLine(&quot;正在等待消息...&quot;);
            Console.WriteLine(&quot;按任意键退出&quot;);
            Console.ReadKey();
        }
    }
}

// 使用示例
// RabbitMqExample.SendMessage();
// RabbitMqExample.ReceiveMessages();
&lt;/code&gt;&lt;/pre&gt;
&lt;h2&gt;通信编程总结&lt;/h2&gt;
&lt;p&gt;C#提供了丰富的通信编程API，从低级别的Socket编程到高级别的框架如WCF和gRPC，开发者可以根据具体需求选择合适的通信方式：&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;strong&gt;Socket编程&lt;/strong&gt;：适用于需要精细控制网络通信的场景&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;TcpClient/TcpListener&lt;/strong&gt;：适用于简单的TCP通信场景&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;HttpClient&lt;/strong&gt;：适用于HTTP/HTTPS通信，特别是REST API调用&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;WebSocket&lt;/strong&gt;：适用于需要双向实时通信的场景，如聊天应用、实时数据推送&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;命名管道&lt;/strong&gt;：适用于同一台计算机上的进程间通信&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;WCF&lt;/strong&gt;：适用于构建面向服务的应用程序，支持多种通信协议&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;gRPC&lt;/strong&gt;：适用于高性能、跨语言的分布式系统&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;消息队列&lt;/strong&gt;：适用于异步通信、解耦系统组件的场景&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;选择合适的通信方式需要考虑多种因素，包括性能要求、可靠性要求、跨平台需求、开发复杂度等。在实际开发中，应根据具体项目需求选择最适合的通信技术。&lt;/p&gt;
&lt;h3&gt;&amp;lt;a id=&quot;serial-communication&quot;&amp;gt;&amp;lt;/a&amp;gt;上位机串口通信&lt;/h3&gt;
&lt;p&gt;串口通信是上位机与下位机（如单片机、PLC等）之间常用的通信方式。C#通过&lt;code&gt;System.IO.Ports.SerialPort&lt;/code&gt;类提供了完整的串口通信支持。&lt;/p&gt;
&lt;h4&gt;串口通信基本概念&lt;/h4&gt;
&lt;ul&gt;
&lt;li&gt;&lt;strong&gt;串口&lt;/strong&gt;：计算机上的物理接口，用于串行数据传输&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;波特率&lt;/strong&gt;：每秒传输的位数，常见值有9600、19200、38400、115200等&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;数据位&lt;/strong&gt;：每个字符包含的数据位数，通常为8位&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;停止位&lt;/strong&gt;：表示一个字符传输结束的位数，通常为1位&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;校验位&lt;/strong&gt;：用于检测传输错误，可选值有None、Odd、Even、Mark、Space&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;流控制&lt;/strong&gt;：用于控制数据传输速率，可选值有None、XonXoff、RequestToSend、RequestToSendXonXoff&lt;/li&gt;
&lt;/ul&gt;
&lt;h4&gt;SerialPort类简介&lt;/h4&gt;
&lt;p&gt;&lt;code&gt;SerialPort&lt;/code&gt;类提供了串口通信的完整功能，包括：&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;串口配置（波特率、数据位、停止位、校验位等）&lt;/li&gt;
&lt;li&gt;串口打开和关闭&lt;/li&gt;
&lt;li&gt;数据读写操作&lt;/li&gt;
&lt;li&gt;串口事件（数据接收、错误等）&lt;/li&gt;
&lt;/ul&gt;
&lt;h4&gt;串口通信基本流程&lt;/h4&gt;
&lt;ol&gt;
&lt;li&gt;创建SerialPort实例&lt;/li&gt;
&lt;li&gt;配置串口参数&lt;/li&gt;
&lt;li&gt;打开串口&lt;/li&gt;
&lt;li&gt;读写数据&lt;/li&gt;
&lt;li&gt;关闭串口&lt;/li&gt;
&lt;/ol&gt;
&lt;h4&gt;串口通信示例&lt;/h4&gt;
&lt;pre&gt;&lt;code&gt;using System;
using System.IO.Ports;
using System.Text;

class SerialCommunicationExample
{
    private static SerialPort _serialPort;
    
    static void Main()
    {
        // 初始化串口
        InitializeSerialPort();
        
        // 打开串口
        try
        {
            _serialPort.Open();
            Console.WriteLine(&quot;串口已打开&quot;);
            
            // 发送测试数据
            _serialPort.WriteLine(&quot;Hello from PC!&quot;);
            Console.WriteLine(&quot;已发送: Hello from PC!&quot;);
            
            // 接收数据（阻塞方式）
            Console.WriteLine(&quot;等待接收数据...&quot;);
            string response = _serialPort.ReadLine();
            Console.WriteLine($&quot;已接收: {response}&quot;);
            
            // 关闭串口
            _serialPort.Close();
            Console.WriteLine(&quot;串口已关闭&quot;);
        }
        catch (Exception ex)
        {
            Console.WriteLine($&quot;串口操作异常: {ex.Message}&quot;);
        }
        
        Console.WriteLine(&quot;按任意键退出...&quot;);
        Console.ReadKey();
    }
    
    private static void InitializeSerialPort()
    {
        // 创建SerialPort实例
        _serialPort = new SerialPort
        {
            // 串口名称，根据实际情况修改
            PortName = &quot;COM3&quot;,
            // 波特率
            BaudRate = 9600,
            // 数据位
            DataBits = 8,
            // 停止位
            StopBits = StopBits.One,
            // 校验位
            Parity = Parity.None,
            // 流控制
            Handshake = Handshake.None,
            // 读取超时时间（毫秒）
            ReadTimeout = 5000,
            // 写入超时时间（毫秒）
            WriteTimeout = 5000,
            // NewLine字符，用于ReadLine和WriteLine方法
            NewLine = &quot;\r\n&quot;,
            // 编码
            Encoding = Encoding.ASCII
        };
        
        // 注册数据接收事件
        _serialPort.DataReceived += SerialPort_DataReceived;
        // 注册错误事件
        _serialPort.ErrorReceived += SerialPort_ErrorReceived;
    }
    
    // 数据接收事件处理
    private static void SerialPort_DataReceived(object sender, SerialDataReceivedEventArgs e)
    {
        SerialPort sp = (SerialPort)sender;
        
        // 读取数据的多种方式
        
        // 方式1：读取所有可用字节
        int bytesToRead = sp.BytesToRead;
        byte[] buffer = new byte[bytesToRead];
        sp.Read(buffer, 0, bytesToRead);
        string data = Encoding.ASCII.GetString(buffer);
        Console.WriteLine($&quot;事件接收: {data}&quot;);
        
        // 方式2：读取一行数据（需要结束符）
        // string line = sp.ReadLine();
        
        // 方式3：读取指定长度的数据
        // byte[] buffer = new byte[1024];
        // int bytesRead = sp.Read(buffer, 0, buffer.Length);
        // string data = Encoding.ASCII.GetString(buffer, 0, bytesRead);
    }
    
    // 错误事件处理
    private static void SerialPort_ErrorReceived(object sender, SerialErrorReceivedEventArgs e)
    {
        Console.WriteLine($&quot;串口错误: {e.EventType}&quot;);
    }
}
&lt;/code&gt;&lt;/pre&gt;
&lt;h4&gt;上位机串口通信最佳实践&lt;/h4&gt;
&lt;ol&gt;
&lt;li&gt;
&lt;p&gt;&lt;strong&gt;异常处理&lt;/strong&gt;：串口操作容易出现异常，如端口不存在、被占用等，务必添加异常处理&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;&lt;strong&gt;资源管理&lt;/strong&gt;：使用&lt;code&gt;using&lt;/code&gt;语句或在&lt;code&gt;finally&lt;/code&gt;块中关闭串口，确保资源正确释放&lt;/p&gt;
&lt;/li&gt;
&lt;/ol&gt;
&lt;pre&gt;&lt;code&gt;using (SerialPort serialPort = new SerialPort(&quot;COM3&quot;))
{
    serialPort.BaudRate = 9600;
    serialPort.Open();
    // 串口操作
}
// 串口自动关闭
&lt;/code&gt;&lt;/pre&gt;
&lt;ol&gt;
&lt;li&gt;&lt;strong&gt;异步通信&lt;/strong&gt;：对于GUI应用程序，应使用异步通信方式，避免阻塞UI线程&lt;/li&gt;
&lt;/ol&gt;
&lt;pre&gt;&lt;code&gt;public async Task SerialCommunicationAsync()
{
    using (SerialPort serialPort = new SerialPort(&quot;COM3&quot;, 9600))
    {
        serialPort.Open();
        
        // 异步写入
        byte[] dataToSend = Encoding.ASCII.GetBytes(&quot;Hello\r\n&quot;);
        await serialPort.BaseStream.WriteAsync(dataToSend, 0, dataToSend.Length);
        
        // 异步读取
        byte[] buffer = new byte[1024];
        int bytesRead = await serialPort.BaseStream.ReadAsync(buffer, 0, buffer.Length);
        string response = Encoding.ASCII.GetString(buffer, 0, bytesRead);
        Console.WriteLine($&quot;已接收: {response}&quot;);
    }
}
&lt;/code&gt;&lt;/pre&gt;
&lt;ol&gt;
&lt;li&gt;
&lt;p&gt;&lt;strong&gt;线程安全&lt;/strong&gt;：在多线程环境中，访问SerialPort时需要考虑线程安全&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;&lt;strong&gt;数据验证&lt;/strong&gt;：接收到的数据可能包含噪声或错误，应进行适当的验证&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;&lt;strong&gt;波特率匹配&lt;/strong&gt;：确保上位机和下位机的波特率、数据位、停止位、校验位等参数一致&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;&lt;strong&gt;流控制设置&lt;/strong&gt;：根据下位机的支持情况设置适当的流控制&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;&lt;strong&gt;缓冲区管理&lt;/strong&gt;：对于大数据量传输，应合理管理缓冲区，避免数据丢失&lt;/p&gt;
&lt;/li&gt;
&lt;/ol&gt;
&lt;h4&gt;WinForm上位机串口通信示例&lt;/h4&gt;
&lt;pre&gt;&lt;code&gt;using System;
using System.IO.Ports;
using System.Text;
using System.Windows.Forms;

public partial class SerialMonitorForm : Form
{
    private SerialPort _serialPort;
    
    public SerialMonitorForm()
    {
        InitializeComponent();
        InitializeSerialPort();
        LoadAvailablePorts();
    }
    
    private void InitializeSerialPort()
    {
        _serialPort = new SerialPort
        {
            BaudRate = 9600,
            DataBits = 8,
            StopBits = StopBits.One,
            Parity = Parity.None,
            Handshake = Handshake.None,
            Encoding = Encoding.ASCII
        };
        
        _serialPort.DataReceived += SerialPort_DataReceived;
    }
    
    private void LoadAvailablePorts()
    {
        // 加载可用串口列表
        string[] ports = SerialPort.GetPortNames();
        comboBoxPorts.Items.AddRange(ports);
        if (ports.Length &amp;gt; 0)
        {
            comboBoxPorts.SelectedIndex = 0;
        }
    }
    
    private void btnOpenClose_Click(object sender, EventArgs e)
    {
        try
        {
            if (!_serialPort.IsOpen)
            {
                // 打开串口
                _serialPort.PortName = comboBoxPorts.SelectedItem?.ToString();
                _serialPort.Open();
                btnOpenClose.Text = &quot;关闭串口&quot;;
                UpdateUIStatus(&quot;串口已打开&quot;);
            }
            else
            {
                // 关闭串口
                _serialPort.Close();
                btnOpenClose.Text = &quot;打开串口&quot;;
                UpdateUIStatus(&quot;串口已关闭&quot;);
            }
        }
        catch (Exception ex)
        {
            UpdateUIStatus($&quot;错误: {ex.Message}&quot;);
            MessageBox.Show($&quot;串口操作失败: {ex.Message}&quot;, &quot;错误&quot;, MessageBoxButtons.OK, MessageBoxIcon.Error);
        }
    }
    
    private void btnSend_Click(object sender, EventArgs e)
    {
        if (!_serialPort.IsOpen)
        {
            UpdateUIStatus(&quot;请先打开串口&quot;);
            return;
        }
        
        try
        {
            string data = textBoxSend.Text;
            _serialPort.WriteLine(data);
            UpdateUILog($&quot;发送: {data}&quot;);
            textBoxSend.Clear();
        }
        catch (Exception ex)
        {
            UpdateUIStatus($&quot;发送失败: {ex.Message}&quot;);
        }
    }
    
    private void SerialPort_DataReceived(object sender, SerialDataReceivedEventArgs e)
    {
        try
        {
            string data = _serialPort.ReadLine();
            // 跨线程更新UI
            this.Invoke(new Action(() =&amp;gt;
            {
                UpdateUILog($&quot;接收: {data}&quot;);
            }));
        }
        catch (Exception ex)
        {
            this.Invoke(new Action(() =&amp;gt;
            {
                UpdateUIStatus($&quot;接收失败: {ex.Message}&quot;);
            }));
        }
    }
    
    private void UpdateUILog(string message)
    {
        textBoxLog.AppendText($&quot;[{DateTime.Now:HH:mm:ss}] {message}\r\n&quot;);
        textBoxLog.ScrollToCaret();
    }
    
    private void UpdateUIStatus(string message)
    {
        labelStatus.Text = message;
    }
    
    private void SerialMonitorForm_FormClosing(object sender, FormClosingEventArgs e)
    {
        if (_serialPort.IsOpen)
        {
            _serialPort.Close();
        }
    }
}
&lt;/code&gt;&lt;/pre&gt;
&lt;h4&gt;串口通信调试工具&lt;/h4&gt;
&lt;p&gt;在开发串口通信程序时，常用的调试工具有：&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;strong&gt;PuTTY&lt;/strong&gt;：开源的串口、SSH、Telnet客户端&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Serial Port Monitor&lt;/strong&gt;：专业的串口监控工具&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Tera Term&lt;/strong&gt;：免费的串口终端仿真程序&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;RealTerm&lt;/strong&gt;：功能强大的串口终端&lt;/li&gt;
&lt;/ul&gt;
&lt;h4&gt;常见串口通信问题及解决方案&lt;/h4&gt;
&lt;ol&gt;
&lt;li&gt;
&lt;p&gt;&lt;strong&gt;串口无法打开&lt;/strong&gt;&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;检查串口名称是否正确&lt;/li&gt;
&lt;li&gt;检查串口是否被其他程序占用&lt;/li&gt;
&lt;li&gt;检查串口驱动是否安装正确&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;&lt;strong&gt;数据接收不完整或乱码&lt;/strong&gt;&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;检查波特率、数据位、停止位、校验位等参数是否匹配&lt;/li&gt;
&lt;li&gt;检查编码方式是否正确&lt;/li&gt;
&lt;li&gt;检查下位机发送的数据格式是否符合预期&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;&lt;strong&gt;数据丢失&lt;/strong&gt;&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;增加串口缓冲区大小&lt;/li&gt;
&lt;li&gt;优化通信协议，减少数据量&lt;/li&gt;
&lt;li&gt;检查流控制设置&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;&lt;strong&gt;串口通信不稳定&lt;/strong&gt;&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;检查串口线是否接触良好&lt;/li&gt;
&lt;li&gt;考虑使用屏蔽线&lt;/li&gt;
&lt;li&gt;减少通信距离或增加中继器&lt;/li&gt;
&lt;li&gt;优化波特率设置&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;/ol&gt;
&lt;p&gt;串口通信是上位机与下位机通信的重要方式，掌握C#中的串口通信编程，对于开发工业控制、嵌入式系统等应用具有重要意义。&lt;/p&gt;
&lt;h2&gt;&amp;lt;a id=&quot;attributes-detail&quot;&amp;gt;&amp;lt;/a&amp;gt;C#特性（Attributes）详解&lt;/h2&gt;
&lt;p&gt;特性（Attributes）是C#中的元数据机制，它允许为程序元素（类、方法、属性等）添加声明性信息。特性可以在运行时通过反射访问，广泛应用于序列化、ORM、依赖注入、单元测试等场景。&lt;/p&gt;
&lt;h3&gt;&amp;lt;a id=&quot;attributes-concept&quot;&amp;gt;&amp;lt;/a&amp;gt;特性的基本概念&lt;/h3&gt;
&lt;h4&gt;什么是特性？&lt;/h4&gt;
&lt;p&gt;特性是一种声明性标签，用于向程序元素添加元数据信息。特性本身不直接影响程序的执行逻辑，但可以通过反射在运行时获取和利用这些信息。&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;// 特性使用方括号 [] 语法
[Serializable]
public class Person
{
    [Obsolete(&quot;使用新版本的方法&quot;)]
    public void OldMethod() { }
}
&lt;/code&gt;&lt;/pre&gt;
&lt;h4&gt;特性的作用&lt;/h4&gt;
&lt;ol&gt;
&lt;li&gt;&lt;strong&gt;添加元数据&lt;/strong&gt;：为代码元素添加描述性信息&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;编译时检查&lt;/strong&gt;：某些特性会被编译器识别并产生警告或错误&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;运行时访问&lt;/strong&gt;：通过反射可以获取特性信息&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;框架支持&lt;/strong&gt;：许多框架（如ASP.NET、Entity Framework）依赖特性来配置行为&lt;/li&gt;
&lt;/ol&gt;
&lt;h3&gt;&amp;lt;a id=&quot;attributes-usage&quot;&amp;gt;&amp;lt;/a&amp;gt;特性的定义与使用&lt;/h3&gt;
&lt;h4&gt;特性的语法&lt;/h4&gt;
&lt;pre&gt;&lt;code&gt;// 基本语法：[特性名]
[Serializable]

// 带参数的特性：[特性名(参数)]
[Obsolete(&quot;此方法已过时&quot;)]

// 多个参数：[特性名(参数1, 参数2, 命名参数=值)]
[Author(&quot;张三&quot;, &quot;2024-01-01&quot;, Version = 2.0)]

// 多个特性：可以堆叠或分开
[Serializable]
[Obsolete]
public class MyClass { }

// 或者
[Serializable, Obsolete]
public class MyClass { }
&lt;/code&gt;&lt;/pre&gt;
&lt;h4&gt;特性的应用目标&lt;/h4&gt;
&lt;p&gt;特性可以应用于各种程序元素：&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;// 应用于程序集
[assembly: AssemblyTitle(&quot;MyApplication&quot;)]

// 应用于模块
[module: CLSCompliant(true)]

// 应用于类
[Serializable]
public class MyClass { }

// 应用于方法
[Obsolete(&quot;此方法已过时&quot;)]
public void MyMethod() { }

// 应用于属性
[Required]
public string Name { get; set; }

// 应用于字段
[NonSerialized]
private int _value;

// 应用于参数
public void Method([In] int parameter) { }

// 应用于返回值
[return: MarshalAs(UnmanagedType.Bool)]
public bool GetValue() { return true; }
&lt;/code&gt;&lt;/pre&gt;
&lt;h3&gt;&amp;lt;a id=&quot;common-attributes&quot;&amp;gt;&amp;lt;/a&amp;gt;常见内置特性&lt;/h3&gt;
&lt;h4&gt;序列化相关特性&lt;/h4&gt;
&lt;pre&gt;&lt;code&gt;using System;
using System.Runtime.Serialization;

// Serializable：标记类可序列化
[Serializable]
public class Person
{
    public string Name { get; set; }
    public int Age { get; set; }
    
    // NonSerialized：标记字段不序列化
    [NonSerialized]
    private string _tempData;
    
    // OptionalField：标记字段为可选（用于版本兼容）
    [OptionalField]
    public string Email { get; set; }
}

// DataContract：用于WCF数据契约
[DataContract]
public class Customer
{
    [DataMember(Name = &quot;CustomerName&quot;)]
    public string Name { get; set; }
    
    [DataMember(IsRequired = true)]
    public int Id { get; set; }
    
    // 不标记DataMember的成员不会被序列化
    public string TempData { get; set; }
}
&lt;/code&gt;&lt;/pre&gt;
&lt;h4&gt;过时标记特性&lt;/h4&gt;
&lt;pre&gt;&lt;code&gt;// Obsolete：标记代码元素已过时
[Obsolete(&quot;此方法已过时，请使用NewMethod代替&quot;)]
public void OldMethod() { }

// 第二个参数为true时，使用该元素会产生编译错误
[Obsolete(&quot;此方法已移除&quot;, true)]
public void RemovedMethod() { }

public void NewMethod() { }
&lt;/code&gt;&lt;/pre&gt;
&lt;h4&gt;条件编译特性&lt;/h4&gt;
&lt;pre&gt;&lt;code&gt;using System.Diagnostics;

// Conditional：条件编译特性
[Conditional(&quot;DEBUG&quot;)]
public void DebugMethod()
{
    Console.WriteLine(&quot;仅在DEBUG模式下执行&quot;);
}

// 使用示例
public void Test()
{
    DebugMethod(); // 在DEBUG模式下才会编译和执行
}
&lt;/code&gt;&lt;/pre&gt;
&lt;h4&gt;调用者信息特性&lt;/h4&gt;
&lt;pre&gt;&lt;code&gt;using System.Runtime.CompilerServices;

public class Logger
{
    // CallerMemberName：自动获取调用者成员名
    public void Log(
        string message,
        [CallerMemberName] string memberName = &quot;&quot;,
        [CallerFilePath] string filePath = &quot;&quot;,
        [CallerLineNumber] int lineNumber = 0)
    {
        Console.WriteLine($&quot;[{memberName}] {filePath}:{lineNumber} - {message}&quot;);
    }
}

// 使用示例
public void Test()
{
    Logger logger = new Logger();
    logger.Log(&quot;测试消息&quot;);
    // 输出: [Test] C:\...\Program.cs:123 - 测试消息
}
&lt;/code&gt;&lt;/pre&gt;
&lt;h4&gt;参数验证特性&lt;/h4&gt;
&lt;pre&gt;&lt;code&gt;// 在.NET Core/.NET 5+中的参数验证
public void ProcessData(
    [NotNull] string data,
    [Range(0, 100)] int value,
    [EmailAddress] string email)
{
    // data不为null
    // value在0-100范围内
    // email是有效的邮箱地址
}
&lt;/code&gt;&lt;/pre&gt;
&lt;h4&gt;结构布局特性&lt;/h4&gt;
&lt;pre&gt;&lt;code&gt;using System.Runtime.InteropServices;

// StructLayout：控制结构体在内存中的布局
[StructLayout(LayoutKind.Sequential)]
public struct Point
{
    public int X;
    public int Y;
}

// 显式布局（用于与C/C++互操作）
[StructLayout(LayoutKind.Explicit)]
public struct Union
{
    [FieldOffset(0)]
    public int Integer;
    
    [FieldOffset(0)]
    public float Float;
}

// MarshalAs：指定如何封送数据
[DllImport(&quot;user32.dll&quot;)]
public static extern int MessageBox(
    IntPtr hWnd,
    [MarshalAs(UnmanagedType.LPStr)] string text,
    [MarshalAs(UnmanagedType.LPStr)] string caption,
    uint type);
&lt;/code&gt;&lt;/pre&gt;
&lt;h3&gt;&amp;lt;a id=&quot;custom-attributes&quot;&amp;gt;&amp;lt;/a&amp;gt;自定义特性&lt;/h3&gt;
&lt;h4&gt;创建自定义特性&lt;/h4&gt;
&lt;p&gt;自定义特性类必须继承自&lt;code&gt;Attribute&lt;/code&gt;类：&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;using System;

// 定义自定义特性
[AttributeUsage(AttributeTargets.Class | AttributeTargets.Method, AllowMultiple = false)]
public class AuthorAttribute : Attribute
{
    public string Name { get; }
    public string Date { get; set; }
    public double Version { get; set; }
    
    // 位置参数（必需参数）
    public AuthorAttribute(string name)
    {
        Name = name;
    }
}

// 使用自定义特性
[Author(&quot;张三&quot;, Date = &quot;2024-01-01&quot;, Version = 1.0)]
public class MyClass
{
    [Author(&quot;李四&quot;)]
    public void MyMethod() { }
}
&lt;/code&gt;&lt;/pre&gt;
&lt;h4&gt;AttributeUsage特性&lt;/h4&gt;
&lt;p&gt;&lt;code&gt;AttributeUsage&lt;/code&gt;用于定义特性的使用规则：&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;[AttributeUsage(
    AttributeTargets.Class | AttributeTargets.Method,  // 应用目标
    AllowMultiple = false,                              // 是否允许多次应用
    Inherited = true)]                                  // 是否可继承
public class MyAttribute : Attribute { }
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;&lt;strong&gt;AttributeTargets枚举值：&lt;/strong&gt;&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;code&gt;All&lt;/code&gt;：所有目标&lt;/li&gt;
&lt;li&gt;&lt;code&gt;Class&lt;/code&gt;：类&lt;/li&gt;
&lt;li&gt;&lt;code&gt;Method&lt;/code&gt;：方法&lt;/li&gt;
&lt;li&gt;&lt;code&gt;Property&lt;/code&gt;：属性&lt;/li&gt;
&lt;li&gt;&lt;code&gt;Field&lt;/code&gt;：字段&lt;/li&gt;
&lt;li&gt;&lt;code&gt;Parameter&lt;/code&gt;：参数&lt;/li&gt;
&lt;li&gt;&lt;code&gt;Constructor&lt;/code&gt;：构造函数&lt;/li&gt;
&lt;li&gt;&lt;code&gt;Event&lt;/code&gt;：事件&lt;/li&gt;
&lt;li&gt;&lt;code&gt;Interface&lt;/code&gt;：接口&lt;/li&gt;
&lt;li&gt;&lt;code&gt;Struct&lt;/code&gt;：结构体&lt;/li&gt;
&lt;li&gt;&lt;code&gt;Enum&lt;/code&gt;：枚举&lt;/li&gt;
&lt;li&gt;&lt;code&gt;Assembly&lt;/code&gt;：程序集&lt;/li&gt;
&lt;li&gt;&lt;code&gt;Module&lt;/code&gt;：模块&lt;/li&gt;
&lt;li&gt;&lt;code&gt;ReturnValue&lt;/code&gt;：返回值&lt;/li&gt;
&lt;/ul&gt;
&lt;h4&gt;自定义特性示例&lt;/h4&gt;
&lt;pre&gt;&lt;code&gt;// 示例1：验证特性
[AttributeUsage(AttributeTargets.Property)]
public class RequiredAttribute : Attribute
{
    public string ErrorMessage { get; set; }
    
    public RequiredAttribute(string errorMessage = &quot;此字段是必需的&quot;)
    {
        ErrorMessage = errorMessage;
    }
}

// 示例2：权限特性
[AttributeUsage(AttributeTargets.Method | AttributeTargets.Class)]
public class AuthorizeAttribute : Attribute
{
    public string[] Roles { get; set; }
    
    public AuthorizeAttribute(params string[] roles)
    {
        Roles = roles;
    }
}

// 示例3：性能监控特性
[AttributeUsage(AttributeTargets.Method)]
public class PerformanceMonitorAttribute : Attribute
{
    public int Threshold { get; set; } // 阈值（毫秒）
    
    public PerformanceMonitorAttribute(int threshold = 1000)
    {
        Threshold = threshold;
    }
}

// 使用示例
public class UserService
{
    [Authorize(&quot;Admin&quot;, &quot;Manager&quot;)]
    [PerformanceMonitor(500)]
    public void DeleteUser(int userId)
    {
        // 方法实现
    }
    
    [Required(&quot;用户名不能为空&quot;)]
    public string UserName { get; set; }
}
&lt;/code&gt;&lt;/pre&gt;
&lt;h3&gt;&amp;lt;a id=&quot;attributes-reflection&quot;&amp;gt;&amp;lt;/a&amp;gt;特性的反射访问&lt;/h3&gt;
&lt;h4&gt;获取特性&lt;/h4&gt;
&lt;p&gt;通过反射可以获取和应用在代码元素上的特性：&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;using System;
using System.Reflection;

// 获取类上的特性
[Author(&quot;张三&quot;, Date = &quot;2024-01-01&quot;)]
public class MyClass { }

// 获取特性
Type type = typeof(MyClass);
AuthorAttribute authorAttr = type.GetCustomAttribute&amp;lt;AuthorAttribute&amp;gt;();

if (authorAttr != null)
{
    Console.WriteLine($&quot;作者: {authorAttr.Name}&quot;);
    Console.WriteLine($&quot;日期: {authorAttr.Date}&quot;);
    Console.WriteLine($&quot;版本: {authorAttr.Version}&quot;);
}

// 获取所有特性
object[] attributes = type.GetCustomAttributes(true);
foreach (Attribute attr in attributes)
{
    Console.WriteLine($&quot;特性: {attr.GetType().Name}&quot;);
}

// 获取方法上的特性
[Author(&quot;李四&quot;)]
public void MyMethod() { }

MethodInfo method = typeof(MyClass).GetMethod(&quot;MyMethod&quot;);
AuthorAttribute methodAttr = method.GetCustomAttribute&amp;lt;AuthorAttribute&amp;gt;();

// 获取属性上的特性
public class Person
{
    [Required(ErrorMessage = &quot;姓名不能为空&quot;)]
    [StringLength(50, ErrorMessage = &quot;姓名不能超过50个字符&quot;)]
    public string Name { get; set; }
    
    [Range(18, 120, ErrorMessage = &quot;年龄必须在18-120之间&quot;)]
    public int Age { get; set; }
    
    [EmailAddress(ErrorMessage = &quot;请输入有效的邮箱地址&quot;)]
    public string Email { get; set; }
}

// 获取所有属性
PropertyInfo[] properties = typeof(Person).GetProperties();
foreach (PropertyInfo property in properties)
{
    Console.WriteLine($&quot;\n属性名: {property.Name}&quot;);
    
    // 获取属性上的所有特性
    object[] propAttributes = property.GetCustomAttributes(true);
    foreach (Attribute attr in propAttributes)
    {
        Console.WriteLine($&quot;  特性类型: {attr.GetType().Name}&quot;);
        
        // 处理Required特性
        if (attr is RequiredAttribute requiredAttr)
        {
            Console.WriteLine($&quot;  错误信息: {requiredAttr.ErrorMessage}&quot;);
        }
        
        // 处理StringLength特性
        if (attr is StringLengthAttribute stringLengthAttr)
        {
            Console.WriteLine($&quot;  最大长度: {stringLengthAttr.MaximumLength}&quot;);
            Console.WriteLine($&quot;  错误信息: {stringLengthAttr.ErrorMessage}&quot;);
        }
        
        // 处理Range特性
        if (attr is RangeAttribute rangeAttr)
        {
            Console.WriteLine($&quot;  最小值: {rangeAttr.Minimum}&quot;);
            Console.WriteLine($&quot;  最大值: {rangeAttr.Maximum}&quot;);
            Console.WriteLine($&quot;  错误信息: {rangeAttr.ErrorMessage}&quot;);
        }
        
        // 处理EmailAddress特性
        if (attr is EmailAddressAttribute emailAttr)
        {
            Console.WriteLine($&quot;  错误信息: {emailAttr.ErrorMessage}&quot;);
        }
    }
}

// 获取特定属性的特定特性
PropertyInfo nameProperty = typeof(Person).GetProperty(&quot;Name&quot;);
RequiredAttribute nameRequiredAttr = nameProperty.GetCustomAttribute&amp;lt;RequiredAttribute&amp;gt;();
if (nameRequiredAttr != null)
{
    Console.WriteLine($&quot;\nName属性的Required特性: {nameRequiredAttr.ErrorMessage}&quot;);
}

// 获取属性的多个特性
PropertyInfo emailProperty = typeof(Person).GetProperty(&quot;Email&quot;);
Attribute[] emailAttributes = emailProperty.GetCustomAttributes(typeof(ValidationAttribute), true) as Attribute[];
foreach (Attribute attr in emailAttributes)
{
    Console.WriteLine($&quot;\nEmail属性的验证特性: {attr.GetType().Name}&quot;);
}
&lt;/code&gt;&lt;/pre&gt;
&lt;h4&gt;实现特性驱动的验证&lt;/h4&gt;
&lt;pre&gt;&lt;code&gt;using System;
using System.Collections.Generic;
using System.Reflection;

// 验证特性基类
public abstract class ValidationAttribute : Attribute
{
    public abstract bool IsValid(object value);
    public abstract string ErrorMessage { get; }
}

// 必需字段特性
[AttributeUsage(AttributeTargets.Property)]
public class RequiredAttribute : ValidationAttribute
{
    public override bool IsValid(object value)
    {
        if (value == null) return false;
        if (value is string str) return !string.IsNullOrWhiteSpace(str);
        return true;
    }
    
    public override string ErrorMessage =&amp;gt; &quot;此字段是必需的&quot;;
}

// 范围验证特性
[AttributeUsage(AttributeTargets.Property)]
public class RangeAttribute : ValidationAttribute
{
    public int Min { get; }
    public int Max { get; }
    
    public RangeAttribute(int min, int max)
    {
        Min = min;
        Max = max;
    }
    
    public override bool IsValid(object value)
    {
        if (value is int intValue)
        {
            return intValue &amp;gt;= Min &amp;amp;&amp;amp; intValue &amp;lt;= Max;
        }
        return false;
    }
    
    public override string ErrorMessage =&amp;gt; $&quot;值必须在 {Min} 到 {Max} 之间&quot;;
}

// 验证器类
public static class Validator
{
    public static List&amp;lt;string&amp;gt; Validate(object obj)
    {
        List&amp;lt;string&amp;gt; errors = new List&amp;lt;string&amp;gt;();
        Type type = obj.GetType();
        
        foreach (PropertyInfo property in type.GetProperties())
        {
            // 获取所有验证特性
            var validationAttributes = property.GetCustomAttributes&amp;lt;ValidationAttribute&amp;gt;();
            
            foreach (var attribute in validationAttributes)
            {
                object value = property.GetValue(obj);
                
                if (!attribute.IsValid(value))
                {
                    errors.Add($&quot;{property.Name}: {attribute.ErrorMessage}&quot;);
                }
            }
        }
        
        return errors;
    }
}

// 使用示例
[Required]
public class Person
{
    [Required]
    public string Name { get; set; }
    
    [Range(0, 150)]
    public int Age { get; set; }
    
    [Required]
    public string Email { get; set; }
}

// 验证
Person person = new Person { Name = &quot;&quot;, Age = 200, Email = null };
List&amp;lt;string&amp;gt; errors = Validator.Validate(person);
foreach (string error in errors)
{
    Console.WriteLine(error);
}
&lt;/code&gt;&lt;/pre&gt;
&lt;h4&gt;特性驱动的AOP（面向切面编程）&lt;/h4&gt;
&lt;pre&gt;&lt;code&gt;using System;
using System.Diagnostics;
using System.Reflection;

// 日志特性
[AttributeUsage(AttributeTargets.Method)]
public class LogAttribute : Attribute
{
    public LogLevel Level { get; set; } = LogLevel.Info;
}

public enum LogLevel
{
    Debug,
    Info,
    Warning,
    Error
}

// 缓存特性
[AttributeUsage(AttributeTargets.Method)]
public class CacheAttribute : Attribute
{
    public int Duration { get; set; } // 缓存时长（秒）
    
    public CacheAttribute(int duration = 60)
    {
        Duration = duration;
    }
}

// 特性处理器（简化示例）
public static class AttributeProcessor
{
    public static void ProcessMethod(MethodInfo method, object instance, object[] parameters)
    {
        // 检查日志特性
        var logAttr = method.GetCustomAttribute&amp;lt;LogAttribute&amp;gt;();
        if (logAttr != null)
        {
            Console.WriteLine($&quot;[{logAttr.Level}] 调用方法: {method.Name}&quot;);
        }
        
        // 检查缓存特性
        var cacheAttr = method.GetCustomAttribute&amp;lt;CacheAttribute&amp;gt;();
        if (cacheAttr != null)
        {
            Console.WriteLine($&quot;检查缓存，缓存时长: {cacheAttr.Duration}秒&quot;);
            // 实现缓存逻辑
        }
        
        // 执行方法
        method.Invoke(instance, parameters);
    }
}
&lt;/code&gt;&lt;/pre&gt;
&lt;h3&gt;&amp;lt;a id=&quot;attributes-scenarios&quot;&amp;gt;&amp;lt;/a&amp;gt;特性的应用场景&lt;/h3&gt;
&lt;h4&gt;1. 数据序列化&lt;/h4&gt;
&lt;pre&gt;&lt;code&gt;using System;
using System.Text.Json;
using System.Text.Json.Serialization;

public class Product
{
    [JsonPropertyName(&quot;product_id&quot;)]
    public int Id { get; set; }
    
    [JsonPropertyName(&quot;product_name&quot;)]
    public string Name { get; set; }
    
    [JsonIgnore]
    public decimal InternalPrice { get; set; }
    
    [JsonPropertyName(&quot;price&quot;)]
    public decimal PublicPrice { get; set; }
}

// 序列化时会使用特性指定的名称
Product product = new Product { Id = 1, Name = &quot;商品&quot;, PublicPrice = 99.99m };
string json = JsonSerializer.Serialize(product);
// 输出: {&quot;product_id&quot;:1,&quot;product_name&quot;:&quot;商品&quot;,&quot;price&quot;:99.99}
&lt;/code&gt;&lt;/pre&gt;
&lt;h4&gt;2. ORM映射&lt;/h4&gt;
&lt;pre&gt;&lt;code&gt;// Entity Framework使用特性进行映射
[Table(&quot;Users&quot;)]
public class User
{
    [Key]
    [DatabaseGenerated(DatabaseGeneratedOption.Identity)]
    public int Id { get; set; }
    
    [Required]
    [MaxLength(100)]
    [Column(&quot;UserName&quot;)]
    public string Name { get; set; }
    
    [NotMapped]
    public string TempData { get; set; }
}
&lt;/code&gt;&lt;/pre&gt;
&lt;h4&gt;3. API路由和验证&lt;/h4&gt;
&lt;pre&gt;&lt;code&gt;// ASP.NET Core中使用特性
[ApiController]
[Route(&quot;api/[controller]&quot;)]
public class UsersController : ControllerBase
{
    [HttpGet(&quot;{id}&quot;)]
    [Authorize(Roles = &quot;Admin&quot;)]
    public IActionResult GetUser(int id)
    {
        // 实现
        return Ok();
    }
    
    [HttpPost]
    [ValidateAntiForgeryToken]
    public IActionResult CreateUser([FromBody] CreateUserRequest request)
    {
        // 实现
        return Ok();
    }
}

public class CreateUserRequest
{
    [Required(ErrorMessage = &quot;用户名是必需的&quot;)]
    [StringLength(50, MinimumLength = 3)]
    public string UserName { get; set; }
    
    [Required]
    [EmailAddress]
    public string Email { get; set; }
}
&lt;/code&gt;&lt;/pre&gt;
&lt;h4&gt;4. 单元测试&lt;/h4&gt;
&lt;pre&gt;&lt;code&gt;using Microsoft.VisualStudio.TestTools.UnitTesting;

[TestClass]
public class CalculatorTests
{
    [TestMethod]
    [TestCategory(&quot;Math&quot;)]
    [Priority(1)]
    public void Add_TwoNumbers_ReturnsSum()
    {
        // 测试代码
    }
    
    [TestMethod]
    [ExpectedException(typeof(ArgumentException))]
    public void Divide_ByZero_ThrowsException()
    {
        // 测试代码
    }
    
    [TestInitialize]
    public void Setup()
    {
        // 测试初始化
    }
    
    [TestCleanup]
    public void Cleanup()
    {
        // 测试清理
    }
}
&lt;/code&gt;&lt;/pre&gt;
&lt;h4&gt;5. 依赖注入&lt;/h4&gt;
&lt;pre&gt;&lt;code&gt;// 使用特性标记需要注入的依赖
public class UserService
{
    [Inject]
    private IUserRepository _repository;
    
    [Inject]
    private ILogger _logger;
}

// 或者方法注入
public class OrderService
{
    private IOrderRepository _repository;
    
    [Inject]
    public void SetRepository(IOrderRepository repository)
    {
        _repository = repository;
    }
}
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;特性是C#中强大的元数据机制，它使得代码更加声明式、可配置，并且为框架提供了丰富的扩展点。通过合理使用特性，可以构建更加灵活和可维护的应用程序。&lt;/p&gt;
&lt;h2&gt;&amp;lt;a id=&quot;reflection-detail&quot;&amp;gt;&amp;lt;/a&amp;gt;C#反射（Reflection）详解&lt;/h2&gt;
&lt;p&gt;反射（Reflection）是C#中强大的运行时类型检查和操作机制。它允许在运行时获取类型信息、创建对象实例、调用方法和访问属性，而无需在编译时知道这些类型。反射广泛应用于序列化、ORM框架、依赖注入、代码生成等场景。&lt;/p&gt;
&lt;h3&gt;&amp;lt;a id=&quot;reflection-concept&quot;&amp;gt;&amp;lt;/a&amp;gt;反射的基本概念&lt;/h3&gt;
&lt;h4&gt;什么是反射？&lt;/h4&gt;
&lt;p&gt;反射是程序在运行时检查、访问和修改自身结构的能力。通过反射，可以：&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;获取类型的元数据信息&lt;/li&gt;
&lt;li&gt;动态创建类型实例&lt;/li&gt;
&lt;li&gt;动态调用方法和访问属性&lt;/li&gt;
&lt;li&gt;在运行时构建和执行代码&lt;/li&gt;
&lt;/ul&gt;
&lt;h4&gt;为什么需要反射？&lt;/h4&gt;
&lt;ol&gt;
&lt;li&gt;&lt;strong&gt;框架开发&lt;/strong&gt;：框架需要处理未知类型（如ORM、序列化器）&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;插件系统&lt;/strong&gt;：动态加载和调用插件&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;代码生成&lt;/strong&gt;：动态生成和执行代码&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;调试和诊断&lt;/strong&gt;：运行时类型检查和分析&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;配置驱动&lt;/strong&gt;：基于配置动态创建和配置对象&lt;/li&gt;
&lt;/ol&gt;
&lt;h4&gt;反射的性能考虑&lt;/h4&gt;
&lt;p&gt;反射操作比直接调用慢很多，因为需要：&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;运行时类型查找&lt;/li&gt;
&lt;li&gt;动态方法调用&lt;/li&gt;
&lt;li&gt;安全性检查&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;&lt;strong&gt;性能优化建议：&lt;/strong&gt;&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;缓存反射结果&lt;/li&gt;
&lt;li&gt;使用&lt;code&gt;Delegate.CreateDelegate&lt;/code&gt;创建委托&lt;/li&gt;
&lt;li&gt;使用表达式树编译&lt;/li&gt;
&lt;li&gt;避免在频繁调用的代码中使用反射&lt;/li&gt;
&lt;/ul&gt;
&lt;h3&gt;&amp;lt;a id=&quot;type-class&quot;&amp;gt;&amp;lt;/a&amp;gt;Type类型详解&lt;/h3&gt;
&lt;p&gt;&lt;code&gt;Type&lt;/code&gt;类是反射的核心，它表示类型声明（类、接口、数组等）。&lt;/p&gt;
&lt;h4&gt;获取Type对象&lt;/h4&gt;
&lt;pre&gt;&lt;code&gt;using System;
using System.Reflection;

// 方式1：使用typeof运算符（编译时已知类型）
Type type1 = typeof(string);
Type type2 = typeof(int);
Type type3 = typeof(Person);

// 方式2：使用GetType()方法（运行时获取）
string str = &quot;Hello&quot;;
Type type4 = str.GetType();

Person person = new Person();
Type type5 = person.GetType();

// 方式3：使用Type.GetType()（通过类型名）
Type type6 = Type.GetType(&quot;System.String&quot;);
Type type7 = Type.GetType(&quot;MyNamespace.Person&quot;, true); // true表示找不到时抛出异常

// 方式4：从程序集获取
Assembly assembly = Assembly.GetExecutingAssembly();
Type[] types = assembly.GetTypes(); // 获取所有类型
Type type8 = assembly.GetType(&quot;MyNamespace.Person&quot;);
&lt;/code&gt;&lt;/pre&gt;
&lt;h4&gt;Type的常用属性&lt;/h4&gt;
&lt;pre&gt;&lt;code&gt;Type type = typeof(Person);

// 类型基本信息
Console.WriteLine($&quot;名称: {type.Name}&quot;);                    // Person
Console.WriteLine($&quot;完整名称: {type.FullName}&quot;);            // MyNamespace.Person
Console.WriteLine($&quot;命名空间: {type.Namespace}&quot;);           // MyNamespace
Console.WriteLine($&quot;程序集: {type.Assembly.FullName}&quot;);     // 程序集名称

// 类型特性
Console.WriteLine($&quot;是类: {type.IsClass}&quot;);                 // True
Console.WriteLine($&quot;是接口: {type.IsInterface}&quot;);           // False
Console.WriteLine($&quot;是值类型: {type.IsValueType}&quot;);         // False
Console.WriteLine($&quot;是抽象类: {type.IsAbstract}&quot;);          // False
Console.WriteLine($&quot;是密封类: {type.IsSealed}&quot;);            // False
Console.WriteLine($&quot;是泛型: {type.IsGenericType}&quot;);         // False

// 可见性
Console.WriteLine($&quot;是公开的: {type.IsPublic}&quot;);            // True
Console.WriteLine($&quot;不是公开的: {type.IsNotPublic}&quot;);       // False

// 继承关系
Console.WriteLine($&quot;基类: {type.BaseType}&quot;);                // System.Object
Console.WriteLine($&quot;是否实现接口: {type.IsAssignableFrom(typeof(IDisposable))}&quot;);
&lt;/code&gt;&lt;/pre&gt;
&lt;h4&gt;获取类型的成员&lt;/h4&gt;
&lt;p&gt;通过反射获取类型成员是反射机制的核心功能之一，它允许我们在运行时动态检查类型的结构。&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;// 获取Person类型的Type对象
Type type = typeof(Person);

// 获取类型的所有公共成员（包括继承的成员）
// MemberInfo是所有成员信息类型的基类，包含成员的基本信息
MemberInfo[] allMembers = type.GetMembers();

// 获取特定类型的公共成员
// PropertyInfo：表示属性信息，用于获取和设置属性值
PropertyInfo[] properties = type.GetProperties();
// MethodInfo：表示方法信息，用于调用方法
MethodInfo[] methods = type.GetMethods();
// FieldInfo：表示字段信息，用于获取和设置字段值
FieldInfo[] fields = type.GetFields();
// ConstructorInfo：表示构造函数信息，用于创建实例
ConstructorInfo[] constructors = type.GetConstructors();
// EventInfo：表示事件信息，用于添加或移除事件处理程序
EventInfo[] events = type.GetEvents();

// 使用BindingFlags控制搜索范围和条件
// BindingFlags枚举用于指定反射如何搜索成员
// - Public：搜索公共成员
// - NonPublic：搜索非公共成员（私有、保护、内部）
// - Instance：搜索实例成员
// - Static：搜索静态成员
BindingFlags flags = BindingFlags.Public | BindingFlags.NonPublic | BindingFlags.Instance | BindingFlags.Static;

// 获取所有属性（包括公共和非公共、实例和静态）
PropertyInfo[] allProperties = type.GetProperties(flags);
// 获取所有方法（包括公共和非公共、实例和静态）
MethodInfo[] allMethods = type.GetMethods(flags);
// 获取所有字段（包括公共和非公共、实例和静态）
FieldInfo[] allFields = type.GetFields(flags);

// 获取特定名称的成员
// 获取名为&quot;Name&quot;的公共属性
PropertyInfo nameProperty = type.GetProperty(&quot;Name&quot;);
// 获取ToString方法（继承自Object）
MethodInfo toStringMethod = type.GetMethod(&quot;ToString&quot;);
// 获取名为&quot;_id&quot;的非公共实例字段
FieldInfo idField = type.GetField(&quot;_id&quot;, BindingFlags.NonPublic | BindingFlags.Instance);
// 获取接收string和int参数的构造函数
ConstructorInfo constructor = type.GetConstructor(new Type[] { typeof(string), typeof(int) });
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;&lt;strong&gt;成员类型说明：&lt;/strong&gt;&lt;/p&gt;
&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;成员类型&lt;/th&gt;
&lt;th&gt;描述&lt;/th&gt;
&lt;th&gt;主要用途&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;PropertyInfo&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;属性信息&lt;/td&gt;
&lt;td&gt;获取/设置属性值、获取属性元数据&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;MethodInfo&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;方法信息&lt;/td&gt;
&lt;td&gt;调用方法、获取方法参数和返回值信息&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;FieldInfo&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;字段信息&lt;/td&gt;
&lt;td&gt;获取/设置字段值、获取字段元数据&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;ConstructorInfo&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;构造函数信息&lt;/td&gt;
&lt;td&gt;创建类的实例&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;EventInfo&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;事件信息&lt;/td&gt;
&lt;td&gt;添加/移除事件处理程序&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;MemberInfo&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;成员基类&lt;/td&gt;
&lt;td&gt;提供成员的通用信息&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;
&lt;p&gt;&lt;strong&gt;BindingFlags枚举完整值：&lt;/strong&gt;&lt;/p&gt;
&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;标志&lt;/th&gt;
&lt;th&gt;描述&lt;/th&gt;
&lt;th&gt;用途&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;Default&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;表示默认绑定标志&lt;/td&gt;
&lt;td&gt;不常用，通常使用其他特定标志组合&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;IgnoreCase&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;搜索时忽略名称的大小写&lt;/td&gt;
&lt;td&gt;允许以不区分大小写的方式查找成员&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;DeclaredOnly&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;只搜索类型本身声明的成员，不包括继承的成员&lt;/td&gt;
&lt;td&gt;限制搜索范围到当前类型&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;Instance&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;搜索实例成员&lt;/td&gt;
&lt;td&gt;用于获取实例字段、属性、方法等&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;Static&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;搜索静态成员&lt;/td&gt;
&lt;td&gt;用于获取静态字段、属性、方法等&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;Public&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;搜索公共成员&lt;/td&gt;
&lt;td&gt;用于获取公共访问级别的成员&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;NonPublic&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;搜索非公共成员（私有、保护、内部）&lt;/td&gt;
&lt;td&gt;用于获取私有、保护或内部访问级别的成员&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;FlattenHierarchy&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;搜索基类中的公共和保护静态成员&lt;/td&gt;
&lt;td&gt;用于在继承层次结构中搜索静态成员&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;InvokeMethod&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;用于InvokeMember，指示要调用方法&lt;/td&gt;
&lt;td&gt;仅用于Type.InvokeMember方法&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;CreateInstance&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;用于InvokeMember，指示要创建实例&lt;/td&gt;
&lt;td&gt;仅用于Type.InvokeMember方法&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;GetField&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;用于InvokeMember，指示要获取字段&lt;/td&gt;
&lt;td&gt;仅用于Type.InvokeMember方法&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;SetField&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;用于InvokeMember，指示要设置字段&lt;/td&gt;
&lt;td&gt;仅用于Type.InvokeMember方法&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;GetProperty&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;用于InvokeMember，指示要获取属性&lt;/td&gt;
&lt;td&gt;仅用于Type.InvokeMember方法&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;SetProperty&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;用于InvokeMember，指示要设置属性&lt;/td&gt;
&lt;td&gt;仅用于Type.InvokeMember方法&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;PutDispProperty&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;用于InvokeMember，指示要调用IDispatch的PutProperty&lt;/td&gt;
&lt;td&gt;仅用于COM互操作场景&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;PutRefDispProperty&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;用于InvokeMember，指示要调用IDispatch的PutRefProperty&lt;/td&gt;
&lt;td&gt;仅用于COM互操作场景&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;ExactBinding&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;用于InvokeMember，指示参数类型必须完全匹配&lt;/td&gt;
&lt;td&gt;要求精确的参数类型匹配&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;SuppressChangeType&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;用于InvokeMember，指示不要将参数类型转换为匹配参数类型&lt;/td&gt;
&lt;td&gt;禁用参数类型自动转换&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;OptionalParamBinding&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;用于InvokeMember，指示方法可以有可选参数&lt;/td&gt;
&lt;td&gt;用于处理带有可选参数的方法&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;IgnoreReturn&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;用于InvokeMember，指示忽略方法的返回值&lt;/td&gt;
&lt;td&gt;调用方法但不关心返回结果&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;DoNotWrapExceptions&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;用于InvokeMember，指示不要将异常包装在TargetInvocationException中&lt;/td&gt;
&lt;td&gt;直接抛出原始异常&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;
&lt;h3&gt;&amp;lt;a id=&quot;assembly-operations&quot;&amp;gt;&amp;lt;/a&amp;gt;程序集（Assembly）操作&lt;/h3&gt;
&lt;p&gt;程序集是.NET中代码部署和版本控制的基本单位。&lt;/p&gt;
&lt;h4&gt;加载程序集&lt;/h4&gt;
&lt;pre&gt;&lt;code&gt;using System.Reflection;

// 方式1：加载已加载的程序集
Assembly assembly1 = Assembly.GetExecutingAssembly(); // 当前程序集
Assembly assembly2 = Assembly.GetCallingAssembly();   // 调用者程序集
Assembly assembly3 = Assembly.GetEntryAssembly();     // 入口程序集

// 方式2：通过程序集名称加载
Assembly assembly4 = Assembly.Load(&quot;MyAssembly&quot;);
Assembly assembly5 = Assembly.LoadFrom(&quot;C:\\Path\\To\\MyAssembly.dll&quot;);
Assembly assembly6 = Assembly.LoadFile(&quot;C:\\Path\\To\\MyAssembly.dll&quot;);

// 方式3：通过类型获取程序集
Assembly assembly7 = typeof(Person).Assembly;

// 方式4：反射加载（推荐用于插件系统）
string assemblyPath = &quot;MyPlugin.dll&quot;;
Assembly pluginAssembly = Assembly.LoadFrom(assemblyPath);
&lt;/code&gt;&lt;/pre&gt;
&lt;h4&gt;程序集信息&lt;/h4&gt;
&lt;pre&gt;&lt;code&gt;Assembly assembly = Assembly.GetExecutingAssembly();

// 程序集基本信息
Console.WriteLine($&quot;程序集名称: {assembly.GetName().Name}&quot;);
Console.WriteLine($&quot;完整名称: {assembly.FullName}&quot;);
Console.WriteLine($&quot;位置: {assembly.Location}&quot;);
Console.WriteLine($&quot;是否在GAC: {assembly.GlobalAssemblyCache}&quot;);

// 获取程序集中的所有类型
Type[] types = assembly.GetTypes();
foreach (Type type in types)
{
    Console.WriteLine($&quot;类型: {type.FullName}&quot;);
}

// 获取导出的类型（公开类型）
Type[] exportedTypes = assembly.GetExportedTypes();

// 获取类型（通过名称）
Type type = assembly.GetType(&quot;MyNamespace.Person&quot;);

// 获取程序集的清单资源
string[] resources = assembly.GetManifestResourceNames();
foreach (string resource in resources)
{
    Console.WriteLine($&quot;资源: {resource}&quot;);
}

// 加载嵌入资源
using (Stream stream = assembly.GetManifestResourceStream(&quot;MyNamespace.resource.txt&quot;))
{
    // 读取资源
}
&lt;/code&gt;&lt;/pre&gt;
&lt;h3&gt;&amp;lt;a id=&quot;type-members&quot;&amp;gt;&amp;lt;/a&amp;gt;类型成员访问&lt;/h3&gt;
&lt;h4&gt;属性（Property）访问&lt;/h4&gt;
&lt;pre&gt;&lt;code&gt;using System.Reflection;

public class Person
{
    public string Name { get; set; }
    public int Age { get; private set; }
    private string _email;
    
    public Person(string name, int age)
    {
        Name = name;
        Age = age;
    }
}

// 获取属性信息
Type type = typeof(Person);
PropertyInfo nameProperty = type.GetProperty(&quot;Name&quot;);
PropertyInfo ageProperty = type.GetProperty(&quot;Age&quot;);

// 属性信息
Console.WriteLine($&quot;属性名: {nameProperty.Name}&quot;);
Console.WriteLine($&quot;属性类型: {nameProperty.PropertyType}&quot;);
Console.WriteLine($&quot;可读: {nameProperty.CanRead}&quot;);
Console.WriteLine($&quot;可写: {nameProperty.CanWrite}&quot;);
Console.WriteLine($&quot;是静态: {nameProperty.GetMethod.IsStatic}&quot;);

// 获取和设置属性值
Person person = new Person(&quot;张三&quot;, 25);

// 获取值
object nameValue = nameProperty.GetValue(person);
Console.WriteLine($&quot;Name: {nameValue}&quot;); // 输出: Name: 张三

// 设置值
nameProperty.SetValue(person, &quot;李四&quot;);
Console.WriteLine($&quot;Name: {person.Name}&quot;); // 输出: Name: 李四

// 获取所有属性
PropertyInfo[] properties = type.GetProperties();
foreach (PropertyInfo prop in properties)
{
    Console.WriteLine($&quot;{prop.Name} ({prop.PropertyType.Name})&quot;);
}

// 获取特性
var attributes = nameProperty.GetCustomAttributes();
foreach (Attribute attr in attributes)
{
    Console.WriteLine($&quot;特性: {attr.GetType().Name}&quot;);
}
&lt;/code&gt;&lt;/pre&gt;
&lt;h4&gt;方法（Method）访问&lt;/h4&gt;
&lt;pre&gt;&lt;code&gt;public class Calculator
{
    public int Add(int a, int b)
    {
        return a + b;
    }
    
    private int Multiply(int a, int b)
    {
        return a * b;
    }
    
    public static int Subtract(int a, int b)
    {
        return a - b;
    }
}

// 获取方法信息
Type type = typeof(Calculator);
MethodInfo addMethod = type.GetMethod(&quot;Add&quot;);
MethodInfo multiplyMethod = type.GetMethod(&quot;Multiply&quot;, BindingFlags.NonPublic | BindingFlags.Instance);

// 方法信息
Console.WriteLine($&quot;方法名: {addMethod.Name}&quot;);
Console.WriteLine($&quot;返回类型: {addMethod.ReturnType}&quot;);
Console.WriteLine($&quot;参数数量: {addMethod.GetParameters().Length}&quot;);
Console.WriteLine($&quot;是静态: {addMethod.IsStatic}&quot;);

// 获取参数信息
ParameterInfo[] parameters = addMethod.GetParameters();
foreach (ParameterInfo param in parameters)
{
    Console.WriteLine($&quot;参数: {param.Name}, 类型: {param.ParameterType}&quot;);
}

// 调用实例方法
Calculator calc = new Calculator();
object result = addMethod.Invoke(calc, new object[] { 10, 20 });
Console.WriteLine($&quot;结果: {result}&quot;); // 输出: 结果: 30

// 调用私有方法
object multiplyResult = multiplyMethod.Invoke(calc, new object[] { 5, 6 });
Console.WriteLine($&quot;结果: {multiplyResult}&quot;); // 输出: 结果: 30

// 调用静态方法
MethodInfo subtractMethod = type.GetMethod(&quot;Subtract&quot;);
object subtractResult = subtractMethod.Invoke(null, new object[] { 20, 10 });
Console.WriteLine($&quot;结果: {subtractResult}&quot;); // 输出: 结果: 10
&lt;/code&gt;&lt;/pre&gt;
&lt;h4&gt;字段（Field）访问&lt;/h4&gt;
&lt;pre&gt;&lt;code&gt;public class Person
{
    public string Name;
    private int _age;
    public static int Count;
}

// 获取字段信息
Type type = typeof(Person);
FieldInfo nameField = type.GetField(&quot;Name&quot;);
FieldInfo ageField = type.GetField(&quot;_age&quot;, BindingFlags.NonPublic | BindingFlags.Instance);
FieldInfo countField = type.GetField(&quot;Count&quot;);

// 字段信息
Console.WriteLine($&quot;字段名: {nameField.Name}&quot;);
Console.WriteLine($&quot;字段类型: {nameField.FieldType}&quot;);
Console.WriteLine($&quot;是静态: {nameField.IsStatic}&quot;);

// 获取和设置字段值
Person person = new Person();

// 设置公共字段
nameField.SetValue(person, &quot;张三&quot;);
Console.WriteLine($&quot;Name: {person.Name}&quot;); // 输出: Name: 张三

// 获取公共字段
object nameValue = nameField.GetValue(person);
Console.WriteLine($&quot;Name: {nameValue}&quot;); // 输出: Name: 张三

// 访问私有字段
ageField.SetValue(person, 25);
int age = (int)ageField.GetValue(person);
Console.WriteLine($&quot;Age: {age}&quot;); // 输出: Age: 25

// 访问静态字段
countField.SetValue(null, 10);
int count = (int)countField.GetValue(null);
Console.WriteLine($&quot;Count: {count}&quot;); // 输出: Count: 10
&lt;/code&gt;&lt;/pre&gt;
&lt;h4&gt;构造函数访问&lt;/h4&gt;
&lt;pre&gt;&lt;code&gt;public class Person
{
    public string Name { get; set; }
    public int Age { get; set; }
    
    public Person() { }
    
    public Person(string name)
    {
        Name = name;
    }
    
    public Person(string name, int age)
    {
        Name = name;
        Age = age;
    }
    
    private Person(int age)
    {
        Age = age;
    }
}

// 获取构造函数
Type type = typeof(Person);

// 获取无参构造函数
ConstructorInfo defaultConstructor = type.GetConstructor(Type.EmptyTypes);
Person person1 = (Person)defaultConstructor.Invoke(null);

// 获取带参数的构造函数
ConstructorInfo constructor1 = type.GetConstructor(new Type[] { typeof(string) });
Person person2 = (Person)constructor1.Invoke(new object[] { &quot;张三&quot; });

ConstructorInfo constructor2 = type.GetConstructor(new Type[] { typeof(string), typeof(int) });
Person person3 = (Person)constructor2.Invoke(new object[] { &quot;李四&quot;, 25 });

// 获取所有构造函数
ConstructorInfo[] constructors = type.GetConstructors();
foreach (ConstructorInfo ctor in constructors)
{
    Console.WriteLine($&quot;构造函数参数: {ctor.GetParameters().Length}&quot;);
}

// 获取私有构造函数
ConstructorInfo privateConstructor = type.GetConstructor(
    BindingFlags.NonPublic | BindingFlags.Instance,
    null,
    new Type[] { typeof(int) },
    null);
Person person4 = (Person)privateConstructor.Invoke(new object[] { 30 });
&lt;/code&gt;&lt;/pre&gt;
&lt;h3&gt;&amp;lt;a id=&quot;dynamic-object-creation&quot;&amp;gt;&amp;lt;/a&amp;gt;动态创建对象&lt;/h3&gt;
&lt;h4&gt;使用Activator创建对象&lt;/h4&gt;
&lt;pre&gt;&lt;code&gt;using System;

// 方式1：使用Activator.CreateInstance（无参构造函数）
Type type = typeof(Person);
Person person1 = (Person)Activator.CreateInstance(type);

// 方式2：带参数
Person person2 = (Person)Activator.CreateInstance(type, &quot;张三&quot;, 25);

// 方式3：通过类型名创建
object person3 = Activator.CreateInstance(&quot;MyAssembly&quot;, &quot;MyNamespace.Person&quot;);
object person4 = Activator.CreateInstance(&quot;MyAssembly&quot;, &quot;MyNamespace.Person&quot;, false, 
    BindingFlags.Default, null, new object[] { &quot;李四&quot;, 30 }, null, null);

// 方式4：使用泛型方法（编译时已知类型）
Person person5 = Activator.CreateInstance&amp;lt;Person&amp;gt;();
&lt;/code&gt;&lt;/pre&gt;
&lt;h4&gt;使用构造函数创建对象&lt;/h4&gt;
&lt;pre&gt;&lt;code&gt;// 性能更好的方式：使用ConstructorInfo
Type type = typeof(Person);
ConstructorInfo constructor = type.GetConstructor(new Type[] { typeof(string), typeof(int) });

// 创建委托以提高性能（只创建一次）
Func&amp;lt;string, int, Person&amp;gt; createPerson = (name, age) =&amp;gt;
    (Person)constructor.Invoke(new object[] { name, age });

// 使用委托创建对象（比直接Invoke快）
Person person = createPerson(&quot;张三&quot;, 25);
&lt;/code&gt;&lt;/pre&gt;
&lt;h4&gt;使用表达式树创建对象（高性能）&lt;/h4&gt;
&lt;pre&gt;&lt;code&gt;using System.Linq.Expressions;

// 创建对象工厂（编译后性能接近直接new）
public static class ObjectFactory
{
    public static Func&amp;lt;object&amp;gt; CreateFactory(Type type)
    {
        NewExpression newExpr = Expression.New(type);
        Expression&amp;lt;Func&amp;lt;object&amp;gt;&amp;gt; lambda = Expression.Lambda&amp;lt;Func&amp;lt;object&amp;gt;&amp;gt;(newExpr);
        return lambda.Compile();
    }
    
    public static Func&amp;lt;T&amp;gt; CreateFactory&amp;lt;T&amp;gt;()
    {
        return Expression.Lambda&amp;lt;Func&amp;lt;T&amp;gt;&amp;gt;(Expression.New(typeof(T))).Compile();
    }
}

// 使用
Func&amp;lt;Person&amp;gt; factory = ObjectFactory.CreateFactory&amp;lt;Person&amp;gt;();
Person person = factory(); // 性能接近 new Person()
&lt;/code&gt;&lt;/pre&gt;
&lt;h3&gt;&amp;lt;a id=&quot;method-property-access&quot;&amp;gt;&amp;lt;/a&amp;gt;方法调用与属性访问&lt;/h3&gt;
&lt;h4&gt;性能优化的方法调用&lt;/h4&gt;
&lt;pre&gt;&lt;code&gt;using System.Reflection;

// 方式1：直接Invoke（最慢）
MethodInfo method = typeof(Calculator).GetMethod(&quot;Add&quot;);
Calculator calc = new Calculator();
int result = (int)method.Invoke(calc, new object[] { 10, 20 });

// 方式2：使用Delegate.CreateDelegate（较快）
MethodInfo addMethod = typeof(Calculator).GetMethod(&quot;Add&quot;);
Func&amp;lt;Calculator, int, int, int&amp;gt; addDelegate = 
    (Func&amp;lt;Calculator, int, int, int&amp;gt;)Delegate.CreateDelegate(
        typeof(Func&amp;lt;Calculator, int, int, int&amp;gt;), addMethod);
int result2 = addDelegate(calc, 10, 20);

// 方式3：使用表达式树编译（最快，接近直接调用）
using System.Linq.Expressions;

MethodInfo methodInfo = typeof(Calculator).GetMethod(&quot;Add&quot;);
ParameterExpression instance = Expression.Parameter(typeof(Calculator), &quot;calc&quot;);
ParameterExpression param1 = Expression.Parameter(typeof(int), &quot;a&quot;);
ParameterExpression param2 = Expression.Parameter(typeof(int), &quot;b&quot;);
MethodCallExpression call = Expression.Call(instance, methodInfo, param1, param2);
Expression&amp;lt;Func&amp;lt;Calculator, int, int, int&amp;gt;&amp;gt; lambda = 
    Expression.Lambda&amp;lt;Func&amp;lt;Calculator, int, int, int&amp;gt;&amp;gt;(call, instance, param1, param2);
Func&amp;lt;Calculator, int, int, int&amp;gt; compiled = lambda.Compile();
int result3 = compiled(calc, 10, 20);
&lt;/code&gt;&lt;/pre&gt;
&lt;h4&gt;属性访问优化&lt;/h4&gt;
&lt;pre&gt;&lt;code&gt;// 方式1：直接GetValue/SetValue（较慢）
PropertyInfo property = typeof(Person).GetProperty(&quot;Name&quot;);
Person person = new Person();
property.SetValue(person, &quot;张三&quot;);
string name = (string)property.GetValue(person);

// 方式2：使用委托（较快）
PropertyInfo nameProperty = typeof(Person).GetProperty(&quot;Name&quot;);

// 创建getter和setter委托
Func&amp;lt;Person, string&amp;gt; getter = (Func&amp;lt;Person, string&amp;gt;)Delegate.CreateDelegate(
    typeof(Func&amp;lt;Person, string&amp;gt;), nameProperty.GetMethod);
Action&amp;lt;Person, string&amp;gt; setter = (Action&amp;lt;Person, string&amp;gt;)Delegate.CreateDelegate(
    typeof(Action&amp;lt;Person, string&amp;gt;), nameProperty.SetMethod);

// 使用委托
setter(person, &quot;李四&quot;);
string value = getter(person);
&lt;/code&gt;&lt;/pre&gt;
&lt;h3&gt;&amp;lt;a id=&quot;generic-reflection&quot;&amp;gt;&amp;lt;/a&amp;gt;泛型类型的反射&lt;/h3&gt;
&lt;h4&gt;处理泛型类型&lt;/h4&gt;
&lt;pre&gt;&lt;code&gt;// 获取泛型类型定义
Type listType = typeof(List&amp;lt;&amp;gt;);
Type dictionaryType = typeof(Dictionary&amp;lt;,&amp;gt;);

// 创建封闭的泛型类型
Type stringListType = listType.MakeGenericType(typeof(string));
Type intStringDictType = dictionaryType.MakeGenericType(typeof(int), typeof(string));

// 创建泛型类型实例
object stringList = Activator.CreateInstance(stringListType);
object intStringDict = Activator.CreateInstance(intStringDictType);

// 调用泛型方法
public class GenericHelper
{
    public static T Create&amp;lt;T&amp;gt;() where T : new()
    {
        return new T();
    }
    
    public static void Process&amp;lt;T&amp;gt;(T item)
    {
        Console.WriteLine($&quot;处理: {item}&quot;);
    }
}

// 使用反射调用泛型方法
MethodInfo createMethod = typeof(GenericHelper).GetMethod(&quot;Create&quot;);
MethodInfo genericCreateMethod = createMethod.MakeGenericMethod(typeof(Person));
Person person = (Person)genericCreateMethod.Invoke(null, null);

MethodInfo processMethod = typeof(GenericHelper).GetMethod(&quot;Process&quot;);
MethodInfo genericProcessMethod = processMethod.MakeGenericMethod(typeof(string));
genericProcessMethod.Invoke(null, new object[] { &quot;测试&quot; });

// 检查泛型类型
Type type = typeof(List&amp;lt;string&amp;gt;);
Console.WriteLine($&quot;是泛型: {type.IsGenericType}&quot;);              // True
Console.WriteLine($&quot;是泛型定义: {type.IsGenericTypeDefinition}&quot;); // False
Console.WriteLine($&quot;泛型参数: {string.Join(&quot;, &quot;, type.GetGenericArguments().Select(t =&amp;gt; t.Name))}&quot;);
&lt;/code&gt;&lt;/pre&gt;
&lt;h3&gt;&amp;lt;a id=&quot;reflection-performance&quot;&amp;gt;&amp;lt;/a&amp;gt;反射的性能优化&lt;/h3&gt;
&lt;h4&gt;缓存反射结果&lt;/h4&gt;
&lt;pre&gt;&lt;code&gt;using System.Collections.Generic;

// 缓存Type对象
private static Dictionary&amp;lt;string, Type&amp;gt; _typeCache = new Dictionary&amp;lt;string, Type&amp;gt;();

public static Type GetCachedType(string typeName)
{
    if (!_typeCache.TryGetValue(typeName, out Type type))
    {
        type = Type.GetType(typeName);
        _typeCache[typeName] = type;
    }
    return type;
}

// 缓存MethodInfo
private static Dictionary&amp;lt;string, MethodInfo&amp;gt; _methodCache = new Dictionary&amp;lt;string, MethodInfo&amp;gt;();

public static MethodInfo GetCachedMethod(Type type, string methodName, Type[] parameterTypes)
{
    string key = $&quot;{type.FullName}.{methodName}({string.Join(&quot;,&quot;, parameterTypes.Select(t =&amp;gt; t.Name))})&quot;;
    
    if (!_methodCache.TryGetValue(key, out MethodInfo method))
    {
        method = type.GetMethod(methodName, parameterTypes);
        _methodCache[key] = method;
    }
    return method;
}

// 缓存编译后的委托
private static Dictionary&amp;lt;string, Delegate&amp;gt; _delegateCache = new Dictionary&amp;lt;string, Delegate&amp;gt;();

public static Func&amp;lt;T, TResult&amp;gt; GetCachedGetter&amp;lt;T, TResult&amp;gt;(PropertyInfo property)
{
    string key = $&quot;{typeof(T).FullName}.{property.Name}.Getter&quot;;
    
    if (!_delegateCache.TryGetValue(key, out Delegate del))
    {
        MethodInfo getter = property.GetMethod;
        del = Delegate.CreateDelegate(typeof(Func&amp;lt;T, TResult&amp;gt;), getter);
        _delegateCache[key] = del;
    }
    
    return (Func&amp;lt;T, TResult&amp;gt;)del;
}
&lt;/code&gt;&lt;/pre&gt;
&lt;h3&gt;&amp;lt;a id=&quot;reflection-scenarios&quot;&amp;gt;&amp;lt;/a&amp;gt;反射的应用场景&lt;/h3&gt;
&lt;h4&gt;1. 对象映射（Object Mapper）&lt;/h4&gt;
&lt;pre&gt;&lt;code&gt;public static class ObjectMapper
{
    public static TTarget Map&amp;lt;TSource, TTarget&amp;gt;(TSource source) where TTarget : new()
    {
        TTarget target = new TTarget();
        Type sourceType = typeof(TSource);
        Type targetType = typeof(TTarget);
        
        foreach (PropertyInfo targetProperty in targetType.GetProperties())
        {
            PropertyInfo sourceProperty = sourceType.GetProperty(targetProperty.Name);
            
            if (sourceProperty != null &amp;amp;&amp;amp; sourceProperty.PropertyType == targetProperty.PropertyType)
            {
                object value = sourceProperty.GetValue(source);
                targetProperty.SetValue(target, value);
            }
        }
        
        return target;
    }
}

// 使用
public class Source
{
    public string Name { get; set; }
    public int Age { get; set; }
}

public class Target
{
    public string Name { get; set; }
    public int Age { get; set; }
}

Source source = new Source { Name = &quot;张三&quot;, Age = 25 };
Target target = ObjectMapper.Map&amp;lt;Source, Target&amp;gt;(source);
&lt;/code&gt;&lt;/pre&gt;
&lt;h4&gt;2. 依赖注入容器&lt;/h4&gt;
&lt;pre&gt;&lt;code&gt;public class SimpleContainer
{
    private Dictionary&amp;lt;Type, Type&amp;gt; _mappings = new Dictionary&amp;lt;Type, Type&amp;gt;();
    private Dictionary&amp;lt;Type, object&amp;gt; _singletons = new Dictionary&amp;lt;Type, object&amp;gt;();
    
    public void Register&amp;lt;TInterface, TImplementation&amp;gt;() where TImplementation : TInterface
    {
        _mappings[typeof(TInterface)] = typeof(TImplementation);
    }
    
    public void RegisterSingleton&amp;lt;TInterface, TImplementation&amp;gt;() where TImplementation : TInterface
    {
        _mappings[typeof(TInterface)] = typeof(TImplementation);
    }
    
    public T Resolve&amp;lt;T&amp;gt;()
    {
        return (T)Resolve(typeof(T));
    }
    
    private object Resolve(Type type)
    {
        // 检查单例
        if (_singletons.ContainsKey(type))
        {
            return _singletons[type];
        }
        
        // 获取实现类型
        Type implementationType = _mappings.ContainsKey(type) ? _mappings[type] : type;
        
        // 获取构造函数
        ConstructorInfo constructor = implementationType.GetConstructors()[0];
        ParameterInfo[] parameters = constructor.GetParameters();
        
        // 解析参数
        object[] resolvedParameters = parameters.Select(p =&amp;gt; Resolve(p.ParameterType)).ToArray();
        
        // 创建实例
        object instance = constructor.Invoke(resolvedParameters);
        
        // 如果是单例，缓存
        if (_singletons.ContainsKey(type))
        {
            _singletons[type] = instance;
        }
        
        return instance;
    }
}
&lt;/code&gt;&lt;/pre&gt;
&lt;h4&gt;3. 序列化器&lt;/h4&gt;
&lt;pre&gt;&lt;code&gt;public static class SimpleSerializer
{
    public static string Serialize(object obj)
    {
        Type type = obj.GetType();
        var properties = type.GetProperties();
        
        var keyValues = properties.Select(p =&amp;gt; 
            $&quot;\&quot;{p.Name}\&quot;:\&quot;{p.GetValue(obj)}\&quot;&quot;);
        
        return &quot;{&quot; + string.Join(&quot;,&quot;, keyValues) + &quot;}&quot;;
    }
    
    public static T Deserialize&amp;lt;T&amp;gt;(string json) where T : new()
    {
        T obj = new T();
        Type type = typeof(T);
        
        // 简化的JSON解析（实际应使用JSON库）
        // 这里仅演示反射的使用
        
        return obj;
    }
}
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;反射是C#中强大的运行时类型操作机制，它使得程序能够动态地处理类型、创建对象和调用方法。虽然反射有一定的性能开销，但在框架开发、插件系统、序列化等场景中，反射是不可或缺的工具。通过合理使用缓存和委托，可以在保持灵活性的同时提高性能。&lt;/p&gt;
&lt;h2&gt;&amp;lt;a id=&quot;async-programming&quot;&amp;gt;&amp;lt;/a&amp;gt;C#异步编程详解&lt;/h2&gt;
&lt;p&gt;异步编程是C#中处理耗时操作（如I/O操作、网络请求等）的重要技术。通过异步编程，可以在等待耗时操作完成的同时，不阻塞主线程，从而提高应用程序的响应性和性能。在现代C#开发中，&lt;code&gt;async/await&lt;/code&gt;关键字使得异步编程变得简单而优雅。&lt;/p&gt;
&lt;h3&gt;&amp;lt;a id=&quot;async-basics&quot;&amp;gt;&amp;lt;/a&amp;gt;异步编程基础&lt;/h3&gt;
&lt;h4&gt;为什么需要异步编程？&lt;/h4&gt;
&lt;p&gt;在传统的同步编程中，当执行耗时操作（如读取大文件、网络请求）时，程序会阻塞等待操作完成，导致用户界面冻结、应用程序无响应等问题。异步编程通过非阻塞的方式处理这些操作，让程序在等待期间可以继续执行其他任务。&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;// 同步方式：会阻塞线程
private void LoadFileSync()
{
    string content = File.ReadAllText(&quot;largefile.txt&quot;); // 阻塞，UI冻结
    textBox1.Text = content;
}

// 异步方式：不阻塞线程
private async void LoadFileAsync()
{
    string content = await File.ReadAllTextAsync(&quot;largefile.txt&quot;); // 不阻塞，UI保持响应
    textBox1.Text = content;
}
&lt;/code&gt;&lt;/pre&gt;
&lt;h4&gt;异步编程的核心概念&lt;/h4&gt;
&lt;ol&gt;
&lt;li&gt;&lt;strong&gt;异步方法&lt;/strong&gt;：使用&lt;code&gt;async&lt;/code&gt;关键字标记的方法&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;await表达式&lt;/strong&gt;：等待异步操作完成，但不阻塞当前线程&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Task和Task&amp;lt;T&amp;gt;&lt;/strong&gt;：表示异步操作的返回类型&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;异步状态机&lt;/strong&gt;：编译器将异步方法转换为状态机&lt;/li&gt;
&lt;/ol&gt;
&lt;h3&gt;&amp;lt;a id=&quot;async-await&quot;&amp;gt;&amp;lt;/a&amp;gt;async/await关键字&lt;/h3&gt;
&lt;h4&gt;async关键字&lt;/h4&gt;
&lt;p&gt;&lt;code&gt;async&lt;/code&gt;关键字用于标记一个方法是异步方法。异步方法必须返回&lt;code&gt;void&lt;/code&gt;、&lt;code&gt;Task&lt;/code&gt;或&lt;code&gt;Task&amp;lt;T&amp;gt;&lt;/code&gt;类型。&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;// 返回Task的异步方法
public async Task DoWorkAsync()
{
    await Task.Delay(1000); // 模拟异步操作
    Console.WriteLine(&quot;工作完成&quot;);
}

// 返回Task&amp;lt;T&amp;gt;的异步方法
public async Task&amp;lt;string&amp;gt; GetDataAsync()
{
    await Task.Delay(1000);
    return &quot;数据&quot;;
}

// 异步事件处理程序（返回void）
private async void button1_Click(object sender, EventArgs e)
{
    await DoWorkAsync();
}
&lt;/code&gt;&lt;/pre&gt;
&lt;h4&gt;await关键字&lt;/h4&gt;
&lt;p&gt;&lt;code&gt;await&lt;/code&gt;关键字用于等待异步操作完成。它只能在&lt;code&gt;async&lt;/code&gt;方法中使用。&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;public async Task&amp;lt;string&amp;gt; DownloadFileAsync(string url)
{
    using (HttpClient client = new HttpClient())
    {
        // await等待
        异步操作完成
        string content = await client.GetStringAsync(url);
        return content;
    }
}
&lt;/code&gt;&lt;/pre&gt;
&lt;h4&gt;async/await的工作原理&lt;/h4&gt;
&lt;p&gt;当遇到&lt;code&gt;await&lt;/code&gt;表达式时：&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;如果异步操作已完成，方法继续同步执行&lt;/li&gt;
&lt;li&gt;如果异步操作未完成，方法返回调用者，不阻塞线程&lt;/li&gt;
&lt;li&gt;当异步操作完成时，方法从&lt;code&gt;await&lt;/code&gt;处继续执行&lt;/li&gt;
&lt;/ol&gt;
&lt;pre&gt;&lt;code&gt;public async Task ProcessDataAsync()
{
    Console.WriteLine(&quot;开始处理&quot;);
    
    // 第一个异步操作
    string data1 = await FetchDataAsync(&quot;url1&quot;);
    Console.WriteLine($&quot;数据1: {data1}&quot;);
    
    // 第二个异步操作
    string data2 = await FetchDataAsync(&quot;url2&quot;);
    Console.WriteLine($&quot;数据2: {data2}&quot;);
    
    // 处理数据
    ProcessData(data1, data2);
    Console.WriteLine(&quot;处理完成&quot;);
}
&lt;/code&gt;&lt;/pre&gt;
&lt;h3&gt;&amp;lt;a id=&quot;task-types&quot;&amp;gt;&amp;lt;/a&amp;gt;Task与Task&amp;lt;T&amp;gt;&lt;/h3&gt;
&lt;h4&gt;Task类型&lt;/h4&gt;
&lt;p&gt;&lt;code&gt;Task&lt;/code&gt;表示一个没有返回值的异步操作。&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;// 创建并启动Task
Task task = Task.Run(() =&amp;gt;
{
    Console.WriteLine(&quot;在后台线程执行&quot;);
    Thread.Sleep(1000);
});

// 等待Task完成
await task;
Console.WriteLine(&quot;Task完成&quot;);

// 使用Task.Delay延迟
await Task.Delay(2000); // 延迟2秒
&lt;/code&gt;&lt;/pre&gt;
&lt;h4&gt;Task&amp;lt;T&amp;gt;类型&lt;/h4&gt;
&lt;p&gt;&lt;code&gt;Task&amp;lt;T&amp;gt;&lt;/code&gt;表示一个返回类型为&lt;code&gt;T&lt;/code&gt;的异步操作。&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;// 创建返回值的Task
Task&amp;lt;int&amp;gt; task = Task.Run(() =&amp;gt;
{
    Thread.Sleep(1000);
    return 42;
});

// 获取结果
int result = await task;
Console.WriteLine($&quot;结果: {result}&quot;);

// 异步方法返回Task&amp;lt;T&amp;gt;
public async Task&amp;lt;int&amp;gt; CalculateAsync()
{
    await Task.Delay(1000);
    return 100;
}
&lt;/code&gt;&lt;/pre&gt;
&lt;h4&gt;Task的常用方法&lt;/h4&gt;
&lt;pre&gt;&lt;code&gt;// Task.Run：在线程池中执行代码
Task task1 = Task.Run(() =&amp;gt; DoWork());

// Task.Delay：异步延迟
await Task.Delay(1000);

// Task.WhenAll：等待所有任务完成
Task task1 = DoWork1Async();
Task task2 = DoWork2Async();
Task task3 = DoWork3Async();
await Task.WhenAll(task1, task2, task3);

// Task.WhenAny：等待任意一个任务完成
Task&amp;lt;int&amp;gt; task1 = GetData1Async();
Task&amp;lt;int&amp;gt; task2 = GetData2Async();
Task&amp;lt;int&amp;gt; completedTask = await Task.WhenAny(task1, task2);
int result = await completedTask;

// Task.FromResult：创建已完成的任务
Task&amp;lt;string&amp;gt; completedTask = Task.FromResult(&quot;已完成&quot;);
string result = await completedTask;
&lt;/code&gt;&lt;/pre&gt;
&lt;h3&gt;&amp;lt;a id=&quot;async-best-practices&quot;&amp;gt;&amp;lt;/a&amp;gt;异步方法最佳实践&lt;/h3&gt;
&lt;h4&gt;1. 避免async void&lt;/h4&gt;
&lt;p&gt;除了事件处理程序外，应避免使用&lt;code&gt;async void&lt;/code&gt;。使用&lt;code&gt;async Task&lt;/code&gt;代替。&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;// ❌ 错误：async void（除了事件处理程序）
public async void BadMethod()
{
    await DoWorkAsync();
}

// ✅ 正确：async Task
public async Task GoodMethod()
{
    await DoWorkAsync();
}
&lt;/code&gt;&lt;/pre&gt;
&lt;h4&gt;2. 使用ConfigureAwait(false)&lt;/h4&gt;
&lt;p&gt;在库代码中，如果不需要在原始上下文中继续执行，使用&lt;code&gt;ConfigureAwait(false)&lt;/code&gt;可以提高性能。&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;// 在库代码中
public async Task&amp;lt;string&amp;gt; GetDataAsync()
{
    using (var client = new HttpClient())
    {
        // 不需要返回到UI线程，使用ConfigureAwait(false)
        string result = await client.GetStringAsync(url).ConfigureAwait(false);
        return result;
    }
}
&lt;/code&gt;&lt;/pre&gt;
&lt;h4&gt;3. 异常处理&lt;/h4&gt;
&lt;p&gt;异步方法中的异常会被包装在&lt;code&gt;Task&lt;/code&gt;中，需要使用&lt;code&gt;try-catch&lt;/code&gt;捕获。&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;public async Task ProcessDataAsync()
{
    try
    {
        string data = await FetchDataAsync(&quot;url&quot;);
        ProcessData(data);
    }
    catch (HttpRequestException ex)
    {
        Console.WriteLine($&quot;网络错误: {ex.Message}&quot;);
    }
    catch (Exception ex)
    {
        Console.WriteLine($&quot;错误: {ex.Message}&quot;);
    }
}

// 多个异步操作的异常处理
public async Task ProcessMultipleAsync()
{
    try
    {
        await Task.WhenAll(
            Process1Async(),
            Process2Async(),
            Process3Async()
        );
    }
    catch (AggregateException ex)
    {
        foreach (var innerEx in ex.InnerExceptions)
        {
            Console.WriteLine($&quot;错误: {innerEx.Message}&quot;);
        }
    }
}
&lt;/code&gt;&lt;/pre&gt;
&lt;h4&gt;4. 取消操作&lt;/h4&gt;
&lt;p&gt;&lt;code&gt;CancellationTokenSource&lt;/code&gt;（简称cts）和&lt;code&gt;CancellationToken&lt;/code&gt;是.NET中用于支持异步操作取消的核心机制。它们提供了一种优雅的方式来取消长时间运行的操作，避免资源浪费。&lt;/p&gt;
&lt;h5&gt;CancellationTokenSource基础&lt;/h5&gt;
&lt;p&gt;&lt;code&gt;CancellationTokenSource&lt;/code&gt;用于创建和管理&lt;code&gt;CancellationToken&lt;/code&gt;，后者被传递给异步方法以支持取消。&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;// 创建CancellationTokenSource
CancellationTokenSource cts = new CancellationTokenSource();

// 获取CancellationToken
CancellationToken token = cts.Token;

// 启动异步操作
Task task = LongRunningOperationAsync(token);

// 取消操作
cts.Cancel();

try
{
    await task;
}
catch (OperationCanceledException ex)
{
    Console.WriteLine($&quot;操作已取消: {ex.Message}&quot;);
}

async Task LongRunningOperationAsync(CancellationToken cancellationToken)
{
    for (int i = 0; i &amp;lt; 100; i++)
    {
        // 方式1：抛出OperationCanceledException
        cancellationToken.ThrowIfCancellationRequested();
        
        // 方式2：手动检查并处理
        if (cancellationToken.IsCancellationRequested)
        {
            Console.WriteLine(&quot;操作被取消，执行清理逻辑&quot;);
            break;
        }
        
        await Task.Delay(100); // 模拟耗时操作
        Console.WriteLine($&quot;进度: {i + 1}%&quot;);
    }
}
&lt;/code&gt;&lt;/pre&gt;
&lt;h5&gt;CancellationTokenSource高级用法&lt;/h5&gt;
&lt;h6&gt;1. 超时自动取消&lt;/h6&gt;
&lt;pre&gt;&lt;code&gt;// 创建3秒后自动取消的CancellationTokenSource
CancellationTokenSource timeoutCts = new CancellationTokenSource(TimeSpan.FromSeconds(3));

// 或者后续设置超时
timeoutCts = new CancellationTokenSource();
timeoutCts.CancelAfter(TimeSpan.FromSeconds(3));

try
{
    await LongRunningOperationAsync(timeoutCts.Token);
    Console.WriteLine(&quot;操作成功完成&quot;);
}
catch (OperationCanceledException)
{
    Console.WriteLine(&quot;操作超时被取消&quot;);
}
&lt;/code&gt;&lt;/pre&gt;
&lt;h6&gt;2. 链接多个CancellationTokenSource&lt;/h6&gt;
&lt;pre&gt;&lt;code&gt;// 主取消源
CancellationTokenSource mainCts = new CancellationTokenSource();

// 超时取消源
CancellationTokenSource timeoutCts = new CancellationTokenSource(TimeSpan.FromSeconds(5));

// 链接两个取消源，任何一个取消都会触发
CancellationTokenSource linkedCts = CancellationTokenSource.CreateLinkedTokenSource(mainCts.Token, timeoutCts.Token);

try
{
    await LongRunningOperationAsync(linkedCts.Token);
}
catch (OperationCanceledException)
{
    if (mainCts.IsCancellationRequested)
        Console.WriteLine(&quot;操作被手动取消&quot;);
    else if (timeoutCts.IsCancellationRequested)
        Console.WriteLine(&quot;操作超时被取消&quot;);
}

// 手动取消
// mainCts.Cancel();
&lt;/code&gt;&lt;/pre&gt;
&lt;h6&gt;3. 注册取消回调&lt;/h6&gt;
&lt;pre&gt;&lt;code&gt;CancellationTokenSource cts = new CancellationTokenSource();

// 注册取消回调
CancellationTokenRegistration registration = cts.Token.Register(() =&amp;gt;
{
    Console.WriteLine(&quot;取消回调被触发，执行清理逻辑&quot;);
    // 可以在这里执行资源清理、日志记录等操作
});

try
{
    await LongRunningOperationAsync(cts.Token);
}
catch (OperationCanceledException)
{
    Console.WriteLine(&quot;操作已取消&quot;);
}
finally
{
    // 取消注册（可选，尤其是在using块外使用时）
    registration.Dispose();
    cts.Dispose();
}
&lt;/code&gt;&lt;/pre&gt;
&lt;h6&gt;4. 异步方法中的取消协作&lt;/h6&gt;
&lt;pre&gt;&lt;code&gt;async Task DownloadFileAsync(string url, string destination, CancellationToken cancellationToken)
{
    using (HttpClient client = new HttpClient())
    {
        // 使用支持取消的异步方法
        using (HttpResponseMessage response = await client.GetAsync(url, HttpCompletionOption.ResponseHeadersRead, cancellationToken))
        {
            response.EnsureSuccessStatusCode();
            
            using (Stream contentStream = await response.Content.ReadAsStreamAsync(cancellationToken))
            using (FileStream fileStream = new FileStream(destination, FileMode.Create, FileAccess.Write, FileShare.None, 8192, true))
            {
                // 使用CopyToAsync，它支持取消
                await contentStream.CopyToAsync(fileStream, 8192, cancellationToken);
            }
        }
    }
}
&lt;/code&gt;&lt;/pre&gt;
&lt;h5&gt;CancellationToken最佳实践&lt;/h5&gt;
&lt;ol&gt;
&lt;li&gt;&lt;strong&gt;始终接受CancellationToken参数&lt;/strong&gt;：为所有长时间运行的异步方法添加可选的&lt;code&gt;CancellationToken&lt;/code&gt;参数&lt;/li&gt;
&lt;/ol&gt;
&lt;pre&gt;&lt;code&gt;// 推荐写法
async Task DoWorkAsync(CancellationToken cancellationToken = default)
{
    // 方法实现
}
&lt;/code&gt;&lt;/pre&gt;
&lt;ol&gt;
&lt;li&gt;
&lt;p&gt;&lt;strong&gt;适当检查取消状态&lt;/strong&gt;：在耗时操作开始前、循环迭代中、I/O操作前后检查取消状态&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;&lt;strong&gt;使用支持取消的异步方法&lt;/strong&gt;：优先使用内置支持&lt;code&gt;CancellationToken&lt;/code&gt;的.NET方法，如&lt;code&gt;Task.Delay&lt;/code&gt;、&lt;code&gt;HttpClient.GetAsync&lt;/code&gt;等&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;&lt;strong&gt;正确处理OperationCanceledException&lt;/strong&gt;：取消操作不是错误，应适当处理&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;&lt;strong&gt;释放资源&lt;/strong&gt;：确保在取消时正确释放资源&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;&lt;strong&gt;避免过度检查&lt;/strong&gt;：在非常频繁的循环中，适当减少检查频率&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;&lt;strong&gt;使用using语句&lt;/strong&gt;：正确释放&lt;code&gt;CancellationTokenSource&lt;/code&gt;&lt;/p&gt;
&lt;/li&gt;
&lt;/ol&gt;
&lt;pre&gt;&lt;code&gt;using (CancellationTokenSource cts = new CancellationTokenSource())
{
    // 使用cts
}
&lt;/code&gt;&lt;/pre&gt;
&lt;h5&gt;完整示例：用户取消的文件下载&lt;/h5&gt;
&lt;pre&gt;&lt;code&gt;class FileDownloader
{
    public async Task DownloadAsync(string url, string destination, IProgress&amp;lt;int&amp;gt; progress = null, CancellationToken cancellationToken = default)
    {
        using (HttpClient client = new HttpClient())
        {
            using (HttpResponseMessage response = await client.GetAsync(url, HttpCompletionOption.ResponseHeadersRead, cancellationToken))
            {
                response.EnsureSuccessStatusCode();
                
                long? totalBytes = response.Content.Headers.ContentLength;
                long downloadedBytes = 0;
                
                using (Stream contentStream = await response.Content.ReadAsStreamAsync(cancellationToken))
                using (FileStream fileStream = new FileStream(destination, FileMode.Create, FileAccess.Write, FileShare.None, 8192, true))
                {
                    byte[] buffer = new byte[8192];
                    int bytesRead;
                    
                    while ((bytesRead = await contentStream.ReadAsync(buffer, 0, buffer.Length, cancellationToken)) &amp;gt; 0)
                    {
                        await fileStream.WriteAsync(buffer, 0, bytesRead, cancellationToken);
                        
                        downloadedBytes += bytesRead;
                        
                        // 报告进度
                        if (totalBytes.HasValue &amp;amp;&amp;amp; progress != null)
                        {
                            int percent = (int)((downloadedBytes * 100) / totalBytes.Value);
                            progress.Report(percent);
                        }
                    }
                }
            }
        }
    }
}

// 使用示例
async Task Main()
{
    var downloader = new FileDownloader();
    var progress = new Progress&amp;lt;int&amp;gt;(percent =&amp;gt;
    {
        Console.WriteLine($&quot;下载进度: {percent}%&quot;);
    });
    
    using (CancellationTokenSource cts = new CancellationTokenSource())
    {
        // 模拟用户在2秒后取消
        _ = Task.Delay(2000).ContinueWith(_ =&amp;gt;
        {
            Console.WriteLine(&quot;用户取消了下载&quot;);
            cts.Cancel();
        });
        
        try
        {
            await downloader.DownloadAsync(
                &quot;https://example.com/large-file.zip&quot;,
                &quot;large-file.zip&quot;,
                progress,
                cts.Token);
            Console.WriteLine(&quot;下载完成&quot;);
        }
        catch (OperationCanceledException)
        {
            Console.WriteLine(&quot;下载已取消&quot;);
        }
        catch (Exception ex)
        {
            Console.WriteLine($&quot;下载出错: {ex.Message}&quot;);
        }
    }
}
&lt;/code&gt;&lt;/pre&gt;
&lt;h5&gt;CancellationToken在UI中的应用&lt;/h5&gt;
&lt;p&gt;在WinForm、WPF等UI应用中，&lt;code&gt;CancellationTokenSource&lt;/code&gt;常用于处理用户取消操作，如取消按钮点击：&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;private CancellationTokenSource _cts;

private async void btnStart_Click(object sender, EventArgs e)
{
    btnStart.Enabled = false;
    btnCancel.Enabled = true;
    
    // 创建新的CancellationTokenSource
    _cts = new CancellationTokenSource();
    
    try
    {
        var progress = new Progress&amp;lt;int&amp;gt;(value =&amp;gt;
        {
            progressBar.Value = value;
        });
        
        await LongRunningOperationAsync(progress, _cts.Token);
        MessageBox.Show(&quot;操作完成&quot;);
    }
    catch (OperationCanceledException)
    {
        MessageBox.Show(&quot;操作已取消&quot;);
    }
    catch (Exception ex)
    {
        MessageBox.Show($&quot;错误: {ex.Message}&quot;);
    }
    finally
    {
        _cts.Dispose();
        _cts = null;
        btnStart.Enabled = true;
        btnCancel.Enabled = false;
    }
}

private void btnCancel_Click(object sender, EventArgs e)
{
    // 取消操作
    _cts?.Cancel();
}

async Task LongRunningOperationAsync(IProgress&amp;lt;int&amp;gt; progress, CancellationToken cancellationToken)
{
    for (int i = 0; i &amp;lt;= 100; i += 5)
    {
        cancellationToken.ThrowIfCancellationRequested();
        await Task.Delay(200);
        progress.Report(i);
    }
}
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;通过合理使用&lt;code&gt;CancellationTokenSource&lt;/code&gt;和&lt;code&gt;CancellationToken&lt;/code&gt;，可以使异步操作更加健壮、高效，并提供更好的用户体验。&lt;/p&gt;
&lt;h4&gt;5. 异步异常捕获&lt;/h4&gt;
&lt;p&gt;异步操作中的异常处理是一个重要话题，特别是当涉及到父task和子task之间的异常传播时。&lt;/p&gt;
&lt;h5&gt;基本的异步异常捕获&lt;/h5&gt;
&lt;p&gt;在异步方法中，可以使用标准的&lt;code&gt;try-catch&lt;/code&gt;块来捕获异常：&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;async Task DoWorkAsync()
{
    try
    {
        await Task.Delay(100);
        throw new InvalidOperationException(&quot;异步操作中的异常&quot;);
    }
    catch (InvalidOperationException ex)
    {
        Console.WriteLine($&quot;捕获到异常: {ex.Message}&quot;);
    }
}

// 调用异步方法
try
{
    await DoWorkAsync();
}
catch (Exception ex)
{
    Console.WriteLine($&quot;调用者捕获到异常: {ex.Message}&quot;);
}
&lt;/code&gt;&lt;/pre&gt;
&lt;h5&gt;父Task与子Task的异常关系&lt;/h5&gt;
&lt;p&gt;当一个Task内部创建并等待其他Task时，会形成父Task与子Task的关系。异常在这种关系中的传播方式取决于Task的创建方式。&lt;/p&gt;
&lt;h6&gt;1. 同步等待子Task（使用await）&lt;/h6&gt;
&lt;p&gt;当使用&lt;code&gt;await&lt;/code&gt;关键字等待子Task时，子Task的异常会直接冒泡到父Task：&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;async Task ParentTaskAsync()
{
    Console.WriteLine(&quot;父Task开始&quot;);
    
    try
    {
        await ChildTaskAsync(); // 同步等待子Task
        Console.WriteLine(&quot;父Task完成&quot;);
    }
    catch (Exception ex)
    {
        Console.WriteLine($&quot;父Task捕获到子Task的异常: {ex.Message}&quot;);
    }
}

async Task ChildTaskAsync()
{
    Console.WriteLine(&quot;子Task开始&quot;);
    await Task.Delay(100);
    throw new Exception(&quot;子Task抛出异常&quot;);
}

// 输出:
// 父Task开始
// 子Task开始
// 父Task捕获到子Task的异常: 子Task抛出异常
&lt;/code&gt;&lt;/pre&gt;
&lt;h6&gt;2. 异步创建子Task（使用Task.Run但不await）&lt;/h6&gt;
&lt;p&gt;当使用&lt;code&gt;Task.Run&lt;/code&gt;创建子Task但不立即等待时，子Task的异常不会自动传播到父Task，除非显式等待：&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;async Task ParentTaskAsync()
{
    Console.WriteLine(&quot;父Task开始&quot;);
    
    // 创建子Task但不等待
    Task childTask = Task.Run(async () =&amp;gt;
    {
        Console.WriteLine(&quot;子Task开始&quot;);
        await Task.Delay(100);
        throw new Exception(&quot;子Task抛出异常&quot;);
    });
    
    // 做一些其他工作
    await Task.Delay(50);
    Console.WriteLine(&quot;父Task继续执行&quot;);
    
    try
    {
        await childTask; // 显式等待子Task，此时异常会传播
        Console.WriteLine(&quot;父Task完成&quot;);
    }
    catch (Exception ex)
    {
        Console.WriteLine($&quot;父Task捕获到子Task的异常: {ex.Message}&quot;);
    }
}

// 输出:
// 父Task开始
// 子Task开始
// 父Task继续执行
// 父Task捕获到子Task的异常: 子Task抛出异常
&lt;/code&gt;&lt;/pre&gt;
&lt;h5&gt;异常冒泡机制&lt;/h5&gt;
&lt;p&gt;在异步编程中，异常冒泡遵循以下规则：&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;&lt;strong&gt;使用await时&lt;/strong&gt;：子Task的异常会直接冒泡到父Task，父Task的&lt;code&gt;try-catch&lt;/code&gt;可以捕获到&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;使用Task.Wait()或Task.Result时&lt;/strong&gt;：异常会被包装在&lt;code&gt;AggregateException&lt;/code&gt;中抛出&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;未等待的Task&lt;/strong&gt;：异常会被存储在Task对象中，直到Task被等待或其异常被观察到&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;子Task异常&lt;/strong&gt;：当父Task包含多个子Task时，所有异常会被包装在&lt;code&gt;AggregateException&lt;/code&gt;中&lt;/li&gt;
&lt;/ol&gt;
&lt;h6&gt;AggregateException处理&lt;/h6&gt;
&lt;p&gt;当使用&lt;code&gt;Task.Wait()&lt;/code&gt;、&lt;code&gt;Task.Result&lt;/code&gt;或&lt;code&gt;Task.WaitAll()&lt;/code&gt;时，多个异常会被包装在&lt;code&gt;AggregateException&lt;/code&gt;中：&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;async Task HandleMultipleExceptionsAsync()
{
    Task task1 = Task.Run(() =&amp;gt;
    {
        throw new InvalidOperationException(&quot;异常1&quot;);
    });
    
    Task task2 = Task.Run(() =&amp;gt;
    {
        throw new ArgumentException(&quot;异常2&quot;);
    });
    
    // 使用Task.WaitAll()等待多个Task
    try
    {
        Task.WaitAll(task1, task2);
    }
    catch (AggregateException ex)
    {
        Console.WriteLine($&quot;捕获到AggregateException，包含{ex.InnerExceptions.Count}个异常:&quot;);
        
        // 遍历所有内部异常
        foreach (Exception innerEx in ex.InnerExceptions)
        {
            Console.WriteLine($&quot;- {innerEx.GetType().Name}: {innerEx.Message}&quot;);
        }
        
        // 仅处理特定类型的异常
        ex.Handle(innerEx =&amp;gt;
        {
            if (innerEx is InvalidOperationException)
            {
                Console.WriteLine($&quot;已处理InvalidOperationException: {innerEx.Message}&quot;);
                return true; // 返回true表示已处理
            }
            return false; // 返回false表示未处理，会重新抛出
        });
    }
}
&lt;/code&gt;&lt;/pre&gt;
&lt;h6&gt;Task.WhenAll()与异常&lt;/h6&gt;
&lt;p&gt;&lt;code&gt;Task.WhenAll()&lt;/code&gt;与&lt;code&gt;Task.WaitAll()&lt;/code&gt;的异常处理不同：&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;code&gt;Task.WaitAll()&lt;/code&gt;：阻塞调用线程，异常包装在&lt;code&gt;AggregateException&lt;/code&gt;中&lt;/li&gt;
&lt;li&gt;&lt;code&gt;Task.WhenAll()&lt;/code&gt;：异步等待，只抛出第一个异常，其他异常会被忽略&lt;/li&gt;
&lt;/ul&gt;
&lt;pre&gt;&lt;code&gt;async Task CompareWaitAllAndWhenAll()
{
    Task task1 = Task.Run(() =&amp;gt; { throw new Exception(&quot;异常1&quot;); });
    Task task2 = Task.Run(() =&amp;gt; { throw new Exception(&quot;异常2&quot;); });
    
    // 使用Task.WhenAll()
    try
    {
        await Task.WhenAll(task1, task2);
    }
    catch (Exception ex)
    {
        Console.WriteLine($&quot;Task.WhenAll()抛出: {ex.Message}&quot;); // 只抛出第一个异常
    }
    
    // 使用Task.WaitAll()
    try
    {
        Task.WaitAll(task1, task2);
    }
    catch (AggregateException ex)
    {
        Console.WriteLine($&quot;Task.WaitAll()抛出AggregateException，包含{ex.InnerExceptions.Count}个异常&quot;);
    }
}
&lt;/code&gt;&lt;/pre&gt;
&lt;h5&gt;未观察到的异常&lt;/h5&gt;
&lt;p&gt;在.NET 4.0中，未观察到的Task异常会导致应用程序崩溃。从.NET 4.5开始，未观察到的异常会被&lt;code&gt;TaskScheduler.UnobservedTaskException&lt;/code&gt;事件捕获，默认不会导致应用程序崩溃，但会被记录到事件日志。&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;// 注册未观察到的异常事件
TaskScheduler.UnobservedTaskException += (sender, e) =&amp;gt;
{
    Console.WriteLine($&quot;捕获到未观察到的Task异常: {e.Exception.Message}&quot;);
    e.SetObserved(); // 标记异常为已观察
};

// 创建并启动Task，但不等待（会产生未观察到的异常）
Task.Run(() =&amp;gt;
{
    throw new Exception(&quot;未观察到的异常&quot;);
});

// 强制垃圾回收，触发未观察到的异常事件
GC.Collect();
GC.WaitForPendingFinalizers();
&lt;/code&gt;&lt;/pre&gt;
&lt;h5&gt;异步异常处理最佳实践&lt;/h5&gt;
&lt;ol&gt;
&lt;li&gt;&lt;strong&gt;始终等待Task&lt;/strong&gt;：避免创建未观察到的异常&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;使用await而非Task.Wait()或Task.Result&lt;/strong&gt;：避免死锁和AggregateException包装&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;使用Task.WhenAll()处理多个异步操作&lt;/strong&gt;：异步等待多个Task，提高性能&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;正确处理AggregateException&lt;/strong&gt;：当使用同步等待时，记得遍历内部异常&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;注册UnobservedTaskException事件&lt;/strong&gt;：捕获未观察到的异常，便于调试&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;在异步方法中使用try-catch&lt;/strong&gt;：在适当的层级处理异常，提高代码可读性&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;避免在finally块中使用await&lt;/strong&gt;：finally块中的await可能导致异常丢失&lt;/li&gt;
&lt;/ol&gt;
&lt;h5&gt;完整示例：父Task与子Task异常处理&lt;/h5&gt;
&lt;pre&gt;&lt;code&gt;async Task ParentChildExceptionDemo()
{
    Console.WriteLine(&quot;=== 父Task与子Task异常处理演示 ===&quot;);
    
    // 创建三个子Task，其中两个会抛出异常
    Task task1 = ChildTaskAsync(1, false); // 成功
    Task task2 = ChildTaskAsync(2, true);  // 抛出异常
    Task task3 = ChildTaskAsync(3, true);  // 抛出异常
    
    // 方式1：使用await Task.WhenAll()
    Console.WriteLine(&quot;\n--- 方式1：使用await Task.WhenAll() ---&quot;);
    try
    {
        await Task.WhenAll(task1, task2, task3);
        Console.WriteLine(&quot;所有Task成功完成&quot;);
    }
    catch (Exception ex)
    {
        Console.WriteLine($&quot;Task.WhenAll()捕获到: {ex.Message}&quot;);
        // 检查其他Task的状态
        Console.WriteLine($&quot;Task1状态: {task1.Status}&quot;);
        Console.WriteLine($&quot;Task2状态: {task2.Status}&quot;);
        Console.WriteLine($&quot;Task3状态: {task3.Status}&quot;);
    }
    
    // 方式2：分别等待每个Task
    Console.WriteLine(&quot;\n--- 方式2：分别等待每个Task ---&quot;);
    List&amp;lt;Task&amp;gt; tasks = new List&amp;lt;Task&amp;gt;
    {
        ChildTaskAsync(4, false),
        ChildTaskAsync(5, true),
        ChildTaskAsync(6, true)
    };
    
    foreach (Task task in tasks)
    {
        try
        {
            await task;
            Console.WriteLine($&quot;Task {GetTaskId(task)} 成功&quot;);
        }
        catch (Exception ex)
        {
            Console.WriteLine($&quot;Task {GetTaskId(task)} 失败: {ex.Message}&quot;);
        }
    }
}

async Task ChildTaskAsync(int id, bool throwException)
{
    Console.WriteLine($&quot;ChildTask {id} 开始&quot;);
    await Task.Delay(100);
    
    if (throwException)
    {
        throw new Exception($&quot;ChildTask {id} 抛出异常&quot;);
    }
    
    Console.WriteLine($&quot;ChildTask {id} 完成&quot;);
}

// 辅助方法：获取Task的ID（简化示例）
int GetTaskId(Task task)
{
    // 实际应用中，应该通过其他方式跟踪Task ID
    return task.GetHashCode() % 1000;
}
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;通过理解异步异常的传播机制和正确的处理方式，可以编写更加健壮、可靠的异步代码，避免隐藏的bug和难以调试的问题。&lt;/p&gt;
&lt;h3&gt;&amp;lt;a id=&quot;async-winform&quot;&amp;gt;&amp;lt;/a&amp;gt;异步编程与WinForm集成&lt;/h3&gt;
&lt;p&gt;在WinForm应用程序中，异步编程特别重要，可以保持UI响应性。&lt;/p&gt;
&lt;h4&gt;异步加载数据&lt;/h4&gt;
&lt;pre&gt;&lt;code&gt;// WinForm窗体中的异步方法
public partial class MainForm : Form
{
    private async void btnLoadData_Click(object sender, EventArgs e)
    {
        btnLoadData.Enabled = false;
        progressBar1.Visible = true;
        
        try
        {
            // 异步加载数据，不阻塞UI线程
            string data = await LoadDataFromFileAsync(&quot;data.txt&quot;);
            
            // 更新UI（自动返回到UI线程）
            textBox1.Text = data;
            labelStatus.Text = &quot;加载成功&quot;;
        }
        catch (Exception ex)
        {
            MessageBox.Show($&quot;加载失败: {ex.Message}&quot;, &quot;错误&quot;, 
                MessageBoxButtons.OK, MessageBoxIcon.Error);
        }
        finally
        {
            btnLoadData.Enabled = true;
            progressBar1.Visible = false;
        }
    }
    
    private async Task&amp;lt;string&amp;gt; LoadDataFromFileAsync(string filePath)
    {
        // 使用异步文件读取
        using (StreamReader reader = new StreamReader(filePath))
        {
            return await reader.ReadToEndAsync();
        }
    }
}
&lt;/code&gt;&lt;/pre&gt;
&lt;h4&gt;异步网络请求&lt;/h4&gt;
&lt;pre&gt;&lt;code&gt;public partial class MainForm : Form
{
    private async void btnDownload_Click(object sender, EventArgs e)
    {
        btnDownload.Enabled = false;
        progressBar1.Value = 0;
        
        try
        {
            using (HttpClient client = new HttpClient())
            {
                // 异步下载文件
                byte[] data = await client.GetByteArrayAsync(&quot;https://example.com/file.zip&quot;);
                
                // 异步保存文件
                await File.WriteAllBytesAsync(&quot;downloaded.zip&quot;, data);
                
                MessageBox.Show(&quot;下载完成！&quot;, &quot;成功&quot;, 
                    MessageBoxButtons.OK, MessageBoxIcon.Information);
            }
        }
        catch (HttpRequestException ex)
        {
            MessageBox.Show($&quot;网络错误: {ex.Message}&quot;, &quot;错误&quot;, 
                MessageBoxButtons.OK, MessageBoxIcon.Error);
        }
        catch (Exception ex)
        {
            MessageBox.Show($&quot;错误: {ex.Message}&quot;, &quot;错误&quot;, 
                MessageBoxButtons.OK, MessageBoxIcon.Error);
        }
        finally
        {
            btnDownload.Enabled = true;
        }
    }
}
&lt;/code&gt;&lt;/pre&gt;
&lt;h4&gt;异步进度报告&lt;/h4&gt;
&lt;pre&gt;&lt;code&gt;public partial class MainForm : Form
{
    private async void btnProcess_Click(object sender, EventArgs e)
    {
        btnProcess.Enabled = false;
        progressBar1.Value = 0;
        
        // 使用Progress&amp;lt;T&amp;gt;报告进度
        var progress = new Progress&amp;lt;int&amp;gt;(percent =&amp;gt;
        {
            progressBar1.Value = percent;
            labelProgress.Text = $&quot;{percent}%&quot;;
        });
        
        try
        {
            await ProcessDataAsync(progress);
            MessageBox.Show(&quot;处理完成！&quot;, &quot;成功&quot;, 
                MessageBoxButtons.OK, MessageBoxIcon.Information);
        }
        catch (Exception ex)
        {
            MessageBox.Show($&quot;处理失败: {ex.Message}&quot;, &quot;错误&quot;, 
                MessageBoxButtons.OK, MessageBoxIcon.Error);
        }
        finally
        {
            btnProcess.Enabled = true;
        }
    }
    
    private async Task ProcessDataAsync(IProgress&amp;lt;int&amp;gt; progress)
    {
        for (int i = 0; i &amp;lt;= 100; i++)
        {
            // 模拟处理
            await Task.Delay(50);
            
            // 报告进度
            progress?.Report(i);
        }
    }
}
&lt;/code&gt;&lt;/pre&gt;
&lt;h4&gt;异步数据库操作&lt;/h4&gt;
&lt;pre&gt;&lt;code&gt;public partial class MainForm : Form
{
    private async void btnLoadUsers_Click(object sender, EventArgs e)
    {
        dataGridView1.DataSource = null;
        labelStatus.Text = &quot;加载中...&quot;;
        
        try
        {
            // 异步加载数据
            List&amp;lt;User&amp;gt; users = await LoadUsersFromDatabaseAsync();
            
            // 更新UI
            dataGridView1.DataSource = users;
            labelStatus.Text = $&quot;已加载 {users.Count} 条记录&quot;;
        }
        catch (Exception ex)
        {
            MessageBox.Show($&quot;加载失败: {ex.Message}&quot;, &quot;错误&quot;, 
                MessageBoxButtons.OK, MessageBoxIcon.Error);
        }
    }
    
    private async Task&amp;lt;List&amp;lt;User&amp;gt;&amp;gt; LoadUsersFromDatabaseAsync()
    {
        // 模拟异步数据库查询
        await Task.Delay(1000);
        
        return new List&amp;lt;User&amp;gt;
        {
            new User { Id = 1, Name = &quot;张三&quot;, Email = &quot;zhangsan@example.com&quot; },
            new User { Id = 2, Name = &quot;李四&quot;, Email = &quot;lisi@example.com&quot; },
            new User { Id = 3, Name = &quot;王五&quot;, Email = &quot;wangwu@example.com&quot; }
        };
    }
}

public class User
{
    public int Id { get; set; }
    public string Name { get; set; }
    public string Email { get; set; }
}
&lt;/code&gt;&lt;/pre&gt;
&lt;h2&gt;&amp;lt;a id=&quot;file-stream&quot;&amp;gt;&amp;lt;/a&amp;gt;C#文件操作与流处理&lt;/h2&gt;
&lt;p&gt;文件操作和流处理是C#中处理数据输入输出的核心功能。通过流（Stream），可以高效地读写文件、处理网络数据、操作内存数据等。&lt;/p&gt;
&lt;h3&gt;&amp;lt;a id=&quot;file-basics&quot;&amp;gt;&amp;lt;/a&amp;gt;文件操作基础&lt;/h3&gt;
&lt;h4&gt;File类&lt;/h4&gt;
&lt;p&gt;&lt;code&gt;File&lt;/code&gt;类提供了静态方法用于文件操作，适合简单的文件读写。&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;// 读取文件内容
string content = File.ReadAllText(&quot;file.txt&quot;, Encoding.UTF8);

// 写入文件内容
File.WriteAllText(&quot;file.txt&quot;, &quot;Hello World&quot;, Encoding.UTF8);

// 追加内容
File.AppendAllText(&quot;file.txt&quot;, &quot;\n追加内容&quot;, Encoding.UTF8);

// 读取所有行
string[] lines = File.ReadAllLines(&quot;file.txt&quot;, Encoding.UTF8);

// 写入所有行
File.WriteAllLines(&quot;file.txt&quot;, lines, Encoding.UTF8);

// 读取字节数组
byte[] bytes = File.ReadAllBytes(&quot;file.bin&quot;);

// 写入字节数组
File.WriteAllBytes(&quot;file.bin&quot;, bytes);

// 检查文件是否存在
if (File.Exists(&quot;file.txt&quot;))
{
    // 文件存在
}

// 删除文件
File.Delete(&quot;file.txt&quot;);

// 复制文件
File.Copy(&quot;source.txt&quot;, &quot;dest.txt&quot;, overwrite: true);

// 移动文件
File.Move(&quot;old.txt&quot;, &quot;new.txt&quot;);
&lt;/code&gt;&lt;/pre&gt;
&lt;h4&gt;FileInfo类&lt;/h4&gt;
&lt;p&gt;&lt;code&gt;FileInfo&lt;/code&gt;类提供了实例方法用于文件操作，适合需要多次操作同一文件。&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;FileInfo fileInfo = new FileInfo(&quot;file.txt&quot;);

// 检查文件是否存在
if (fileInfo.Exists)
{
    // 获取文件信息
    long size = fileInfo.Length;
    DateTime created = fileInfo.CreationTime;
    DateTime modified = fileInfo.LastWriteTime;
    
    // 读取文件
    string content = File.ReadAllText(fileInfo.FullName);
    
    // 删除文件
    fileInfo.Delete();
}
&lt;/code&gt;&lt;/pre&gt;
&lt;h4&gt;Directory类&lt;/h4&gt;
&lt;p&gt;&lt;code&gt;Directory&lt;/code&gt;类用于目录操作。&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;// 创建目录
Directory.CreateDirectory(&quot;NewFolder&quot;);

// 检查目录是否存在
if (Directory.Exists(&quot;Folder&quot;))
{
    // 目录存在
}

// 获取目录中的所有文件
string[] files = Directory.GetFiles(&quot;Folder&quot;);

// 获取目录中的所有子目录
string[] directories = Directory.GetDirectories(&quot;Folder&quot;);

// 删除目录
Directory.Delete(&quot;Folder&quot;, recursive: true);
&lt;/code&gt;&lt;/pre&gt;
&lt;h3&gt;&amp;lt;a id=&quot;stream-overview&quot;&amp;gt;&amp;lt;/a&amp;gt;流（Stream）概述&lt;/h3&gt;
&lt;p&gt;流是C#中处理数据输入输出的抽象基类，提供了统一的接口来处理不同类型的数据源（文件、内存、网络等）。&lt;/p&gt;
&lt;h4&gt;Stream类的层次结构&lt;/h4&gt;
&lt;pre&gt;&lt;code&gt;Stream (抽象基类)
├── FileStream (文件流)
├── MemoryStream (内存流)
├── NetworkStream (网络流)
├── BufferedStream (缓冲流)
└── 其他流类型
&lt;/code&gt;&lt;/pre&gt;
&lt;h4&gt;Stream的常用属性和方法&lt;/h4&gt;
&lt;pre&gt;&lt;code&gt;// 属性
long Length { get; }           // 流的长度
long Position { get; set; }    // 流的当前位置
bool CanRead { get; }          // 是否可读
bool CanWrite { get; }         // 是否可写
bool CanSeek { get; }          // 是否可定位

// 方法
int Read(byte[] buffer, int offset, int count);      // 读取数据
void Write(byte[] buffer, int offset, int count);   // 写入数据
long Seek(long offset, SeekOrigin origin);           // 定位
void Flush();                                        // 刷新缓冲区
void Close();                                        // 关闭流
&lt;/code&gt;&lt;/pre&gt;
&lt;h3&gt;&amp;lt;a id=&quot;filestream&quot;&amp;gt;&amp;lt;/a&amp;gt;FileStream文件流&lt;/h3&gt;
&lt;p&gt;&lt;code&gt;FileStream&lt;/code&gt;用于读写文件，提供了对文件的底层访问。&lt;/p&gt;
&lt;h4&gt;FileStream的基本使用&lt;/h4&gt;
&lt;pre&gt;&lt;code&gt;// 创建FileStream（写入模式）
using (FileStream fs = new FileStream(&quot;file.txt&quot;, FileMode.Create, FileAccess.Write))
{
    string text = &quot;Hello World&quot;;
    byte[] bytes = Encoding.UTF8.GetBytes(text);
    fs.Write(bytes, 0, bytes.Length);
    fs.Flush(); // 确保数据写入磁盘
}

// 创建FileStream（读取模式）
using (FileStream fs = new FileStream(&quot;file.txt&quot;, FileMode.Open, FileAccess.Read))
{
    byte[] buffer = new byte[1024];
    int bytesRead = fs.Read(buffer, 0, buffer.Length);
    string text = Encoding.UTF8.GetString(buffer, 0, bytesRead);
    Console.WriteLine(text);
}

// FileMode枚举
// Create: 创建新文件，如果存在则覆盖
// Open: 打开现有文件
// OpenOrCreate: 打开文件，如果不存在则创建
// Append: 追加模式
// Truncate: 截断文件

// FileAccess枚举
// Read: 只读
// Write: 只写
// ReadWrite: 读写
&lt;/code&gt;&lt;/pre&gt;
&lt;h4&gt;FileStream的异步操作&lt;/h4&gt;
&lt;pre&gt;&lt;code&gt;// 异步写入
public async Task WriteFileAsync(string filePath, string content)
{
    using (FileStream fs = new FileStream(filePath, FileMode.Create, FileAccess.Write, FileShare.None, 4096, true))
    {
        byte[] bytes = Encoding.UTF8.GetBytes(content);
        await fs.WriteAsync(bytes, 0, bytes.Length);
        await fs.FlushAsync();
    }
}

// 异步读取
public async Task&amp;lt;string&amp;gt; ReadFileAsync(string filePath)
{
    using (FileStream fs = new FileStream(filePath, FileMode.Open, FileAccess.Read, FileShare.Read, 4096, true))
    {
        byte[] buffer = new byte[fs.Length];
        await fs.ReadAsync(buffer, 0, (int)fs.Length);
        return Encoding.UTF8.GetString(buffer);
    }
}
&lt;/code&gt;&lt;/pre&gt;
&lt;h3&gt;&amp;lt;a id=&quot;stream-reader-writer&quot;&amp;gt;&amp;lt;/a&amp;gt;StreamReader和StreamWriter&lt;/h3&gt;
&lt;p&gt;&lt;code&gt;StreamReader&lt;/code&gt;和&lt;code&gt;StreamWriter&lt;/code&gt;提供了对文本文件的便捷读写操作。&lt;/p&gt;
&lt;h4&gt;StreamReader的使用&lt;/h4&gt;
&lt;pre&gt;&lt;code&gt;// 读取整个文件
using (StreamReader reader = new StreamReader(&quot;file.txt&quot;, Encoding.UTF8))
{
    string content = reader.ReadToEnd();
    Console.WriteLine(content);
}

// 逐行读取
using (StreamReader reader = new StreamReader(&quot;file.txt&quot;, Encoding.UTF8))
{
    string line;
    while ((line = reader.ReadLine()) != null)
    {
        Console.WriteLine(line);
    }
}

// 读取指定字符数
using (StreamReader reader = new StreamReader(&quot;file.txt&quot;, Encoding.UTF8))
{
    char[] buffer = new char[100];
    int charsRead = reader.Read(buffer, 0, buffer.Length);
    string text = new string(buffer, 0, charsRead);
    Console.WriteLine(text);
}

// 异步读取
public async Task&amp;lt;string&amp;gt; ReadFileAsync(string filePath)
{
    using (StreamReader reader = new StreamReader(filePath, Encoding.UTF8))
    {
        return await reader.ReadToEndAsync();
    }
}

// 异步逐行读取
public async Task&amp;lt;List&amp;lt;string&amp;gt;&amp;gt; ReadLinesAsync(string filePath)
{
    List&amp;lt;string&amp;gt; lines = new List&amp;lt;string&amp;gt;();
    using (StreamReader reader = new StreamReader(filePath, Encoding.UTF8))
    {
        string line;
        while ((line = await reader.ReadLineAsync()) != null)
        {
            lines.Add(line);
        }
    }
    return lines;
}
&lt;/code&gt;&lt;/pre&gt;
&lt;h4&gt;StreamWriter的使用&lt;/h4&gt;
&lt;pre&gt;&lt;code&gt;// 写入文本
using (StreamWriter writer = new StreamWriter(&quot;file.txt&quot;, append: false, Encoding.UTF8))
{
    writer.Write(&quot;Hello&quot;);
    writer.WriteLine(&quot; World&quot;);
    writer.WriteLine(&quot;New Line&quot;);
}

// 追加文本
using (StreamWriter writer = new StreamWriter(&quot;file.txt&quot;, append: true, Encoding.UTF8))
{
    writer.WriteLine(&quot;追加的内容&quot;);
}

// 异步写入
public async Task WriteFileAsync(string filePath, string content)
{
    using (StreamWriter writer = new StreamWriter(filePath, append: false, Encoding.UTF8))
    {
        await writer.WriteAsync(content);
        await writer.FlushAsync();
    }
}

// 异步写入多行
public async Task WriteLinesAsync(string filePath, IEnumerable&amp;lt;string&amp;gt; lines)
{
    using (StreamWriter writer = new StreamWriter(filePath, append: false, Encoding.UTF8))
    {
        foreach (string line in lines)
        {
            await writer.WriteLineAsync(line);
        }
        await writer.FlushAsync();
    }
}
&lt;/code&gt;&lt;/pre&gt;
&lt;h3&gt;&amp;lt;a id=&quot;memorystream&quot;&amp;gt;&amp;lt;/a&amp;gt;MemoryStream内存流&lt;/h3&gt;
&lt;p&gt;&lt;code&gt;MemoryStream&lt;/code&gt;用于在内存中操作数据，不需要实际文件。&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;// 创建MemoryStream并写入数据
using (MemoryStream ms = new MemoryStream())
{
    string text = &quot;Hello MemoryStream&quot;;
    byte[] bytes = Encoding.UTF8.GetBytes(text);
    ms.Write(bytes, 0, bytes.Length);
    
    // 读取数据
    ms.Position = 0; // 重置位置
    byte[] buffer = new byte[ms.Length];
    ms.Read(buffer, 0, (int)ms.Length);
    string result = Encoding.UTF8.GetString(buffer);
    Console.WriteLine(result);
}

// 从字节数组创建MemoryStream
byte[] data = { 1, 2, 3, 4, 5 };
using (MemoryStream ms = new MemoryStream(data))
{
    // 读取数据
    byte[] buffer = new byte[ms.Length];
    ms.Read(buffer, 0, (int)ms.Length);
}

// 获取MemoryStream的字节数组
using (MemoryStream ms = new MemoryStream())
{
    ms.WriteByte(1);
    ms.WriteByte(2);
    byte[] array = ms.ToArray(); // 获取字节数组
}
&lt;/code&gt;&lt;/pre&gt;
&lt;h3&gt;&amp;lt;a id=&quot;file-winform&quot;&amp;gt;&amp;lt;/a&amp;gt;文件操作与WinForm集成&lt;/h3&gt;
&lt;p&gt;在WinForm应用程序中，文件操作通常与用户界面交互，需要异步处理以保持UI响应性。&lt;/p&gt;
&lt;h4&gt;异步文件读取示例&lt;/h4&gt;
&lt;pre&gt;&lt;code&gt;public partial class FileReaderForm : Form
{
    private TextBox textBoxContent;
    private Button btnLoad;
    private ProgressBar progressBar;
    private Label labelStatus;
    
    public FileReaderForm()
    {
        InitializeComponent();
    }
    
    private async void btnLoad_Click(object sender, EventArgs e)
    {
        // 打开文件对话框
        OpenFileDialog dialog = new OpenFileDialog
        {
            Filter = &quot;文本文件 (*.txt)|*.txt|所有文件 (*.*)|*.*&quot;,
            Title = &quot;选择要读取的文件&quot;
        };
        
        if (dialog.ShowDialog() == DialogResult.OK)
        {
            btnLoad.Enabled = false;
            progressBar.Visible = true;
            labelStatus.Text = &quot;加载中...&quot;;
            
            try
            {
                // 异步读取文件
                string content = await ReadFileAsync(dialog.FileName);
                
                // 更新UI
                textBoxContent.Text = content;
                labelStatus.Text = &quot;加载成功&quot;;
            }
            catch (FileNotFoundException)
            {
                MessageBox.Show(&quot;文件不存在&quot;, &quot;错误&quot;, 
                    MessageBoxButtons.OK, MessageBoxIcon.Error);
            }
            catch (UnauthorizedAccessException)
            {
                MessageBox.Show(&quot;没有访问权限&quot;, &quot;错误&quot;, 
                    MessageBoxButtons.OK, MessageBoxIcon.Error);
            }
            catch (Exception ex)
            {
                MessageBox.Show($&quot;读取文件失败: {ex.Message}&quot;, &quot;错误&quot;, 
                    MessageBoxButtons.OK, MessageBoxIcon.Error);
            }
            finally
            {
                btnLoad.Enabled = true;
                progressBar.Visible = false;
            }
        }
    }
    
    private async Task&amp;lt;string&amp;gt; ReadFileAsync(string filePath)
    {
        // 使用StreamReader异步读取
        using (StreamReader reader = new StreamReader(filePath, Encoding.UTF8))
        {
            return await reader.ReadToEndAsync();
        }
    }
}
&lt;/code&gt;&lt;/pre&gt;
&lt;h4&gt;异步文件写入示例&lt;/h4&gt;
&lt;pre&gt;&lt;code&gt;public partial class FileWriterForm : Form
{
    private TextBox textBoxContent;
    private Button btnSave;
    private Label labelStatus;
    
    private async void btnSave_Click(object sender, EventArgs e)
    {
        // 保存文件对话框
        SaveFileDialog dialog = new SaveFileDialog
        {
            Filter = &quot;文本文件 (*.txt)|*.txt|所有文件 (*.*)|*.*&quot;,
            Title = &quot;保存文件&quot;,
            DefaultExt = &quot;txt&quot;
        };
        
        if (dialog.ShowDialog() == DialogResult.OK)
        {
            btnSave.Enabled = false;
            labelStatus.Text = &quot;保存中...&quot;;
            
            try
            {
                // 异步写入文件
                await WriteFileAsync(dialog.FileName, textBoxContent.Text);
                
                labelStatus.Text = &quot;保存成功&quot;;
                MessageBox.Show(&quot;文件保存成功！&quot;, &quot;成功&quot;, 
                    MessageBoxButtons.OK, MessageBoxIcon.Information);
            }
            catch (UnauthorizedAccessException)
            {
                MessageBox.Show(&quot;没有写入权限&quot;, &quot;错误&quot;, 
                    MessageBoxButtons.OK, MessageBoxIcon.Error);
            }
            catch (Exception ex)
            {
                MessageBox.Show($&quot;保存文件失败: {ex.Message}&quot;, &quot;错误&quot;, 
                    MessageBoxButtons.OK, MessageBoxIcon.Error);
            }
            finally
            {
                btnSave.Enabled = true;
            }
        }
    }
    
    private async Task WriteFileAsync(string filePath, string content)
    {
        // 使用StreamWriter异步写入
        using (StreamWriter writer = new StreamWriter(filePath, append: false, Encoding.UTF8))
        {
            await writer.WriteAsync(content);
            await writer.FlushAsync();
        }
    }
}
&lt;/code&gt;&lt;/pre&gt;
&lt;h4&gt;大文件处理示例&lt;/h4&gt;
&lt;pre&gt;&lt;code&gt;public partial class LargeFileForm : Form
{
    private ProgressBar progressBar;
    private Label labelProgress;
    private Button btnProcess;
    
    private async void btnProcess_Click(object sender, EventArgs e)
    {
        OpenFileDialog dialog = new OpenFileDialog
        {
            Filter = &quot;所有文件 (*.*)|*.*&quot;,
            Title = &quot;选择要处理的文件&quot;
        };
        
        if (dialog.ShowDialog() == DialogResult.OK)
        {
            btnProcess.Enabled = false;
            progressBar.Value = 0;
            
            try
            {
                // 处理大文件，显示进度
                await ProcessLargeFileAsync(dialog.FileName, new Progress&amp;lt;long&amp;gt;(bytesProcessed =&amp;gt;
                {
                    FileInfo fileInfo = new FileInfo(dialog.FileName);
                    int percent = (int)(bytesProcessed * 100 / fileInfo.Length);
                    progressBar.Value = percent;
                    labelProgress.Text = $&quot;{percent}% ({bytesProcessed}/{fileInfo.Length} 字节)&quot;;
                }));
                
                MessageBox.Show(&quot;处理完成！&quot;, &quot;成功&quot;, 
                    MessageBoxButtons.OK, MessageBoxIcon.Information);
            }
            catch (Exception ex)
            {
                MessageBox.Show($&quot;处理失败: {ex.Message}&quot;, &quot;错误&quot;, 
                    MessageBoxButtons.OK, MessageBoxIcon.Error);
            }
            finally
            {
                btnProcess.Enabled = true;
            }
        }
    }
    
    private async Task ProcessLargeFileAsync(string filePath, IProgress&amp;lt;long&amp;gt; progress)
    {
        const int bufferSize = 8192; // 8KB缓冲区
        
        using (FileStream fs = new FileStream(filePath, FileMode.Open, FileAccess.Read, FileShare.Read, bufferSize, true))
        {
            byte[] buffer = new byte[bufferSize];
            long totalBytesRead = 0;
            int bytesRead;
            
            while ((bytesRead = await fs.ReadAsync(buffer, 0, bufferSize)) &amp;gt; 0)
            {
                // 处理数据
                ProcessBuffer(buffer, bytesRead);
                
                totalBytesRead += bytesRead;
                progress?.Report(totalBytesRead);
            }
        }
    }
    
    private void ProcessBuffer(byte[] buffer, int count)
    {
        // 处理缓冲区数据
        // 例如：数据转换、加密、压缩等
    }
}
&lt;/code&gt;&lt;/pre&gt;
&lt;h4&gt;文件复制示例&lt;/h4&gt;
&lt;pre&gt;&lt;code&gt;public partial class FileCopyForm : Form
{
    private ProgressBar progressBar;
    private Label labelStatus;
    private Button btnCopy;
    
    private async void btnCopy_Click(object sender, EventArgs e)
    {
        OpenFileDialog openDialog = new OpenFileDialog
        {
            Title = &quot;选择源文件&quot;
        };
        
        if (openDialog.ShowDialog() == DialogResult.OK)
        {
            SaveFileDialog saveDialog = new SaveFileDialog
            {
                Title = &quot;选择目标位置&quot;,
                FileName = Path.GetFileName(openDialog.FileName)
            };
            
            if (saveDialog.ShowDialog() == DialogResult.OK)
            {
                btnCopy.Enabled = false;
                progressBar.Value = 0;
                
                try
                {
                    await CopyFileAsync(openDialog.FileName, saveDialog.FileName, new Progress&amp;lt;int&amp;gt;(percent =&amp;gt;
                    {
                        progressBar.Value = percent;
                        labelStatus.Text = $&quot;{percent}%&quot;;
                    }));
                    
                    MessageBox.Show(&quot;复制完成！&quot;, &quot;成功&quot;, 
                        MessageBoxButtons.OK, MessageBoxIcon.Information);
                }
                catch (Exception ex)
                {
                    MessageBox.Show($&quot;复制失败: {ex.Message}&quot;, &quot;错误&quot;, 
                        MessageBoxButtons.OK, MessageBoxIcon.Error);
                }
                finally
                {
                    btnCopy.Enabled = true;
                }
            }
        }
    }
    
    private async Task CopyFileAsync(string sourcePath, string destPath, IProgress&amp;lt;int&amp;gt; progress)
    {
        const int bufferSize = 8192;
        
        using (FileStream sourceStream = new FileStream(sourcePath, FileMode.Open, FileAccess.Read, FileShare.Read, bufferSize, true))
        using (FileStream destStream = new FileStream(destPath, FileMode.Create, FileAccess.Write, FileShare.None, bufferSize, true))
        {
            long fileLength = sourceStream.Length;
            byte[] buffer = new byte[bufferSize];
            long totalBytesRead = 0;
            int bytesRead;
            
            while ((bytesRead = await sourceStream.ReadAsync(buffer, 0, bufferSize)) &amp;gt; 0)
            {
                await destStream.WriteAsync(buffer, 0, bytesRead);
                
                totalBytesRead += bytesRead;
                int percent = (int)(totalBytesRead * 100 / fileLength);
                progress?.Report(percent);
            }
            
            await destStream.FlushAsync();
        }
    }
}
&lt;/code&gt;&lt;/pre&gt;
&lt;h2&gt;&amp;lt;a id=&quot;multithreading&quot;&amp;gt;&amp;lt;/a&amp;gt;C#多线程编程详解&lt;/h2&gt;
&lt;p&gt;多线程编程是C#中实现并发执行的核心技术。通过多线程，可以让程序同时执行多个任务，充分利用多核CPU资源，提高程序性能。本章将深入讲解C#中的多线程编程，包括线程创建、同步、通信等各个方面。&lt;/p&gt;
&lt;h3&gt;&amp;lt;a id=&quot;multithreading-basics&quot;&amp;gt;&amp;lt;/a&amp;gt;多线程基础概念&lt;/h3&gt;
&lt;h4&gt;什么是线程？&lt;/h4&gt;
&lt;p&gt;**线程（Thread）**是操作系统能够进行运算调度的最小单位。一个进程可以包含多个线程，每个线程可以独立执行不同的任务。&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;进程 vs 线程：&lt;/strong&gt;&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;strong&gt;进程&lt;/strong&gt;：程序的执行实例，拥有独立的内存空间&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;线程&lt;/strong&gt;：进程内的执行单元，共享进程的内存空间&lt;/li&gt;
&lt;/ul&gt;
&lt;h4&gt;为什么需要多线程？&lt;/h4&gt;
&lt;ol&gt;
&lt;li&gt;&lt;strong&gt;提高性能&lt;/strong&gt;：充分利用多核CPU，并行处理任务&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;响应性&lt;/strong&gt;：在后台执行耗时操作，保持UI响应&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;资源利用&lt;/strong&gt;：在等待I/O操作时，可以执行其他任务&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;并发处理&lt;/strong&gt;：同时处理多个请求或任务&lt;/li&gt;
&lt;/ol&gt;
&lt;h4&gt;多线程的挑战&lt;/h4&gt;
&lt;ol&gt;
&lt;li&gt;&lt;strong&gt;线程安全&lt;/strong&gt;：多个线程访问共享资源时的数据竞争&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;死锁&lt;/strong&gt;：线程相互等待导致的程序卡死&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;竞态条件&lt;/strong&gt;：执行顺序不确定导致的结果不一致&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;性能开销&lt;/strong&gt;：线程创建、切换、同步的开销&lt;/li&gt;
&lt;/ol&gt;
&lt;h3&gt;&amp;lt;a id=&quot;thread-class&quot;&amp;gt;&amp;lt;/a&amp;gt;Thread类详解&lt;/h3&gt;
&lt;p&gt;&lt;code&gt;Thread&lt;/code&gt;类是C#中创建和管理线程的基础类。&lt;/p&gt;
&lt;h4&gt;创建线程&lt;/h4&gt;
&lt;pre&gt;&lt;code&gt;using System.Threading;

// 方式1：使用ThreadStart委托
Thread thread1 = new Thread(new ThreadStart(DoWork));
thread1.Start();

// 方式2：使用ParameterizedThreadStart（带参数）
Thread thread2 = new Thread(new ParameterizedThreadStart(DoWorkWithParameter));
thread2.Start(&quot;参数值&quot;);

// 方式3：使用Lambda表达式
Thread thread3 = new Thread(() =&amp;gt; 
{
    Console.WriteLine(&quot;线程执行中...&quot;);
    Thread.Sleep(1000);
    Console.WriteLine(&quot;线程完成&quot;);
});
thread3.Start();

// 方式4：使用匿名方法
Thread thread4 = new Thread(delegate()
{
    Console.WriteLine(&quot;匿名方法线程&quot;);
});
thread4.Start();

// 线程方法
static void DoWork()
{
    Console.WriteLine($&quot;线程ID: {Thread.CurrentThread.ManagedThreadId}&quot;);
    Console.WriteLine($&quot;线程名称: {Thread.CurrentThread.Name}&quot;);
    Console.WriteLine($&quot;是否后台线程: {Thread.CurrentThread.IsBackground}&quot;);
    Console.WriteLine($&quot;线程状态: {Thread.CurrentThread.ThreadState}&quot;);
    
    for (int i = 0; i &amp;lt; 10; i++)
    {
        Console.WriteLine($&quot;工作线程: {i}&quot;);
        Thread.Sleep(100);
    }
}

static void DoWorkWithParameter(object parameter)
{
    Console.WriteLine($&quot;接收参数: {parameter}&quot;);
    // 处理参数...
}
&lt;/code&gt;&lt;/pre&gt;
&lt;h4&gt;Thread的常用属性&lt;/h4&gt;
&lt;pre&gt;&lt;code&gt;Thread thread = new Thread(DoWork);

// 线程属性
thread.Name = &quot;工作线程1&quot;;              // 线程名称
thread.IsBackground = true;            // 是否为后台线程
thread.Priority = ThreadPriority.Normal; // 线程优先级
thread.CurrentCulture = CultureInfo.CurrentCulture; // 当前文化
thread.CurrentUICulture = CultureInfo.CurrentUICulture; // UI文化

// 线程状态
ThreadState state = thread.ThreadState; // 获取线程状态

// 线程ID
int threadId = thread.ManagedThreadId;  // 托管线程ID

thread.Start();
&lt;/code&gt;&lt;/pre&gt;
&lt;h4&gt;Thread的常用方法&lt;/h4&gt;
&lt;pre&gt;&lt;code&gt;Thread thread = new Thread(DoWork);

// 启动线程
thread.Start();

// 等待线程完成（阻塞当前线程）
thread.Join();

// 等待指定时间
bool completed = thread.Join(1000); // 等待1秒，返回是否完成

// 中断线程（抛出ThreadInterruptedException）
thread.Interrupt();

// 中止线程（已过时，不推荐使用）
// thread.Abort(); // 已废弃

// 挂起线程（已过时）
// thread.Suspend(); // 已废弃

// 恢复线程（已过时）
// thread.Resume(); // 已废弃
&lt;/code&gt;&lt;/pre&gt;
&lt;h4&gt;线程优先级&lt;/h4&gt;
&lt;pre&gt;&lt;code&gt;Thread thread = new Thread(DoWork);

// 设置线程优先级
thread.Priority = ThreadPriority.Lowest;    // 最低
thread.Priority = ThreadPriority.BelowNormal; // 低于正常
thread.Priority = ThreadPriority.Normal;      // 正常（默认）
thread.Priority = ThreadPriority.AboveNormal; // 高于正常
thread.Priority = ThreadPriority.Highest;     // 最高

thread.Start();
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;&lt;strong&gt;注意&lt;/strong&gt;：线程优先级只是建议，操作系统可能不遵循。过度使用高优先级可能导致其他线程饥饿。&lt;/p&gt;
&lt;h4&gt;前台线程 vs 后台线程&lt;/h4&gt;
&lt;pre&gt;&lt;code&gt;// 前台线程（默认）
Thread foregroundThread = new Thread(DoWork);
foregroundThread.IsBackground = false; // 前台线程
foregroundThread.Start();
// 进程会等待所有前台线程结束才退出

// 后台线程
Thread backgroundThread = new Thread(DoWork);
backgroundThread.IsBackground = true; // 后台线程
backgroundThread.Start();
// 进程退出时，后台线程会被强制终止
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;&lt;strong&gt;区别：&lt;/strong&gt;&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;strong&gt;前台线程&lt;/strong&gt;：进程会等待所有前台线程结束才退出&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;后台线程&lt;/strong&gt;：进程退出时，后台线程会被强制终止&lt;/li&gt;
&lt;/ul&gt;
&lt;h4&gt;线程状态&lt;/h4&gt;
&lt;pre&gt;&lt;code&gt;Thread thread = new Thread(DoWork);

// 线程状态枚举
// Unstarted: 未启动
// Running: 运行中
// WaitSleepJoin: 等待、睡眠或加入
// Suspended: 已挂起（已废弃）
// AbortRequested: 中止请求（已废弃）
// Stopped: 已停止
// Aborted: 已中止（已废弃）

ThreadState state = thread.ThreadState;
Console.WriteLine($&quot;线程状态: {state}&quot;);

thread.Start();
state = thread.ThreadState;
Console.WriteLine($&quot;启动后状态: {state}&quot;);
&lt;/code&gt;&lt;/pre&gt;
&lt;h4&gt;线程本地存储（ThreadLocal）&lt;/h4&gt;
&lt;pre&gt;&lt;code&gt;// ThreadLocal：每个线程有独立的值
ThreadLocal&amp;lt;int&amp;gt; threadLocal = new ThreadLocal&amp;lt;int&amp;gt;(() =&amp;gt; 
{
    return Thread.CurrentThread.ManagedThreadId; // 初始化为线程ID
});

// 在不同线程中使用
Thread thread1 = new Thread(() =&amp;gt;
{
    threadLocal.Value = 100;
    Console.WriteLine($&quot;线程1的值: {threadLocal.Value}&quot;);
});

Thread thread2 = new Thread(() =&amp;gt;
{
    threadLocal.Value = 200;
    Console.WriteLine($&quot;线程2的值: {threadLocal.Value}&quot;);
});

thread1.Start();
thread2.Start();
thread1.Join();
thread2.Join();

// 每个线程的值是独立的
Console.WriteLine($&quot;主线程的值: {threadLocal.Value}&quot;);

// 清理资源
threadLocal.Dispose();
&lt;/code&gt;&lt;/pre&gt;
&lt;h4&gt;ThreadStatic特性&lt;/h4&gt;
&lt;pre&gt;&lt;code&gt;// 使用[ThreadStatic]特性标记静态字段
[ThreadStatic]
private static int threadStaticValue = 0;

// 每个线程都有独立的副本
Thread thread1 = new Thread(() =&amp;gt;
{
    threadStaticValue = 100;
    Console.WriteLine($&quot;线程1的值: {threadStaticValue}&quot;);
});

Thread thread2 = new Thread(() =&amp;gt;
{
    threadStaticValue = 200;
    Console.WriteLine($&quot;线程2的值: {threadStaticValue}&quot;);
});

thread1.Start();
thread2.Start();
thread1.Join();
thread2.Join();

// 注意：主线程的值仍然是0（初始值）
Console.WriteLine($&quot;主线程的值: {threadStaticValue}&quot;);
&lt;/code&gt;&lt;/pre&gt;
&lt;h3&gt;&amp;lt;a id=&quot;threadpool&quot;&amp;gt;&amp;lt;/a&amp;gt;ThreadPool线程池&lt;/h3&gt;
&lt;p&gt;线程池是.NET提供的线程管理机制，可以重用线程，减少线程创建和销毁的开销。&lt;/p&gt;
&lt;h4&gt;为什么使用线程池？&lt;/h4&gt;
&lt;ol&gt;
&lt;li&gt;&lt;strong&gt;性能优化&lt;/strong&gt;：重用线程，减少创建/销毁开销&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;资源管理&lt;/strong&gt;：自动管理线程数量，避免创建过多线程&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;简单易用&lt;/strong&gt;：无需手动管理线程生命周期&lt;/li&gt;
&lt;/ol&gt;
&lt;h4&gt;ThreadPool的基本使用&lt;/h4&gt;
&lt;pre&gt;&lt;code&gt;using System.Threading;

// 方式1：使用QueueUserWorkItem（无返回值）
ThreadPool.QueueUserWorkItem(DoWork);
ThreadPool.QueueUserWorkItem(DoWork, &quot;参数值&quot;);

// 方式2：使用Lambda表达式
ThreadPool.QueueUserWorkItem(state =&amp;gt;
{
    Console.WriteLine($&quot;线程池线程执行: {state}&quot;);
    Console.WriteLine($&quot;线程ID: {Thread.CurrentThread.ManagedThreadId}&quot;);
    Console.WriteLine($&quot;是否线程池线程: {Thread.CurrentThread.IsThreadPoolThread}&quot;);
});

// 方式3：使用WaitCallback委托
WaitCallback callback = new WaitCallback(DoWork);
ThreadPool.QueueUserWorkItem(callback, &quot;数据&quot;);

static void DoWork(object state)
{
    Console.WriteLine($&quot;执行工作: {state}&quot;);
    Thread.Sleep(1000);
    Console.WriteLine(&quot;工作完成&quot;);
}
&lt;/code&gt;&lt;/pre&gt;
&lt;h4&gt;ThreadPool的配置&lt;/h4&gt;
&lt;pre&gt;&lt;code&gt;// 获取线程池信息
int workerThreads, completionPortThreads;
ThreadPool.GetAvailableThreads(out workerThreads, out completionPortThreads);
Console.WriteLine($&quot;可用工作线程: {workerThreads}&quot;);
Console.WriteLine($&quot;可用I/O线程: {completionPortThreads}&quot;);

// 获取最大线程数
ThreadPool.GetMaxThreads(out workerThreads, out completionPortThreads);
Console.WriteLine($&quot;最大工作线程: {workerThreads}&quot;);
Console.WriteLine($&quot;最大I/O线程: {completionPortThreads}&quot;);

// 获取最小线程数
ThreadPool.GetMinThreads(out workerThreads, out completionPortThreads);
Console.WriteLine($&quot;最小工作线程: {workerThreads}&quot;);
Console.WriteLine($&quot;最小I/O线程: {completionPortThreads}&quot;);

// 设置最小线程数
ThreadPool.SetMinThreads(10, 10);

// 设置最大线程数
ThreadPool.SetMaxThreads(100, 100);
&lt;/code&gt;&lt;/pre&gt;
&lt;h4&gt;ThreadPool vs Thread&lt;/h4&gt;
&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;特性&lt;/th&gt;
&lt;th&gt;Thread&lt;/th&gt;
&lt;th&gt;ThreadPool&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;线程创建&lt;/td&gt;
&lt;td&gt;手动创建&lt;/td&gt;
&lt;td&gt;自动管理&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;线程重用&lt;/td&gt;
&lt;td&gt;不重用&lt;/td&gt;
&lt;td&gt;重用线程&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;适用场景&lt;/td&gt;
&lt;td&gt;长时间运行的任务&lt;/td&gt;
&lt;td&gt;短时间任务&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;控制能力&lt;/td&gt;
&lt;td&gt;完全控制&lt;/td&gt;
&lt;td&gt;有限控制&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;性能开销&lt;/td&gt;
&lt;td&gt;较大&lt;/td&gt;
&lt;td&gt;较小&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;
&lt;h3&gt;&amp;lt;a id=&quot;thread-synchronization&quot;&amp;gt;&amp;lt;/a&amp;gt;线程同步机制&lt;/h3&gt;
&lt;p&gt;当多个线程访问共享资源时，需要使用同步机制确保线程安全。&lt;/p&gt;
&lt;h4&gt;lock关键字&lt;/h4&gt;
&lt;p&gt;&lt;code&gt;lock&lt;/code&gt;是最常用的同步机制，基于&lt;code&gt;Monitor&lt;/code&gt;类实现。&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;// 共享资源
private int _counter = 0;
private readonly object _lockObject = new object();

// 使用lock保护共享资源
public void Increment()
{
    lock (_lockObject)
    {
        _counter++;
        Console.WriteLine($&quot;计数器: {_counter}, 线程ID: {Thread.CurrentThread.ManagedThreadId}&quot;);
    }
}

// 多线程测试
Thread thread1 = new Thread(() =&amp;gt;
{
    for (int i = 0; i &amp;lt; 1000; i++)
    {
        Increment();
    }
});

Thread thread2 = new Thread(() =&amp;gt;
{
    for (int i = 0; i &amp;lt; 1000; i++)
    {
        Increment();
    }
});

thread1.Start();
thread2.Start();
thread1.Join();
thread2.Join();

Console.WriteLine($&quot;最终计数: {_counter}&quot;); // 应该是2000
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;&lt;strong&gt;lock的注意事项：&lt;/strong&gt;&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;锁定对象应该是&lt;code&gt;private readonly&lt;/code&gt;，避免外部锁定&lt;/li&gt;
&lt;li&gt;不要锁定&lt;code&gt;this&lt;/code&gt;或&lt;code&gt;Type&lt;/code&gt;对象&lt;/li&gt;
&lt;li&gt;避免嵌套锁定，防止死锁&lt;/li&gt;
&lt;li&gt;锁定范围要尽可能小&lt;/li&gt;
&lt;/ol&gt;
&lt;h4&gt;Monitor类&lt;/h4&gt;
&lt;p&gt;&lt;code&gt;Monitor&lt;/code&gt;提供了比&lt;code&gt;lock&lt;/code&gt;更灵活的控制。&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;private readonly object _lockObject = new object();

// 使用Monitor
public void DoWork()
{
    Monitor.Enter(_lockObject);
    try
    {
        // 临界区代码
        Console.WriteLine(&quot;执行工作&quot;);
    }
    finally
    {
        Monitor.Exit(_lockObject);
    }
}

// Monitor.TryEnter（非阻塞）
public bool TryDoWork()
{
    if (Monitor.TryEnter(_lockObject, 1000)) // 等待1秒
    {
        try
        {
            // 临界区代码
            return true;
        }
        finally
        {
            Monitor.Exit(_lockObject);
        }
    }
    return false;
}

// Monitor.Wait和Monitor.Pulse（线程间通信）
private bool _condition = false;

public void WaitForCondition()
{
    lock (_lockObject)
    {
        while (!_condition)
        {
            Monitor.Wait(_lockObject); // 释放锁并等待
        }
        // 条件满足，继续执行
    }
}

public void SetCondition()
{
    lock (_lockObject)
    {
        _condition = true;
        Monitor.Pulse(_lockObject); // 通知等待的线程
    }
}
&lt;/code&gt;&lt;/pre&gt;
&lt;h4&gt;Mutex互斥锁&lt;/h4&gt;
&lt;p&gt;&lt;code&gt;Mutex&lt;/code&gt;是跨进程的同步机制。&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;// 创建Mutex
Mutex mutex = new Mutex(false, &quot;MyMutex&quot;);

// 等待获取锁
mutex.WaitOne();
try
{
    // 临界区代码
    Console.WriteLine(&quot;执行工作&quot;);
}
finally
{
    mutex.ReleaseMutex();
}

// 使用using自动释放
using (Mutex mutex = new Mutex(false, &quot;MyMutex&quot;))
{
    if (mutex.WaitOne(1000)) // 等待1秒
    {
        try
        {
            // 临界区代码
        }
        finally
        {
            mutex.ReleaseMutex();
        }
    }
}
&lt;/code&gt;&lt;/pre&gt;
&lt;h4&gt;Semaphore信号量&lt;/h4&gt;
&lt;p&gt;&lt;code&gt;Semaphore&lt;/code&gt;控制同时访问资源的线程数量。&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;// 创建信号量：最多允许3个线程同时访问
Semaphore semaphore = new Semaphore(3, 3, &quot;MySemaphore&quot;);

// 多个线程竞争访问
for (int i = 0; i &amp;lt; 10; i++)
{
    int threadId = i;
    Thread thread = new Thread(() =&amp;gt;
    {
        semaphore.WaitOne(); // 等待获取信号量
        try
        {
            Console.WriteLine($&quot;线程 {threadId} 开始工作&quot;);
            Thread.Sleep(2000);
            Console.WriteLine($&quot;线程 {threadId} 完成工作&quot;);
        }
        finally
        {
            semaphore.Release(); // 释放信号量
        }
    });
    thread.Start();
}
&lt;/code&gt;&lt;/pre&gt;
&lt;h4&gt;SemaphoreSlim（轻量级信号量）&lt;/h4&gt;
&lt;p&gt;&lt;code&gt;SemaphoreSlim&lt;/code&gt;是&lt;code&gt;Semaphore&lt;/code&gt;的轻量级、高性能版本，专为.NET Framework 4.0及以上设计，提供异步支持。它控制同时访问资源的线程数量，适用于高并发场景。&lt;/p&gt;
&lt;h5&gt;SemaphoreSlim与Semaphore的区别&lt;/h5&gt;
&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;特性&lt;/th&gt;
&lt;th&gt;SemaphoreSlim&lt;/th&gt;
&lt;th&gt;Semaphore&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;性能&lt;/td&gt;
&lt;td&gt;更高，轻量级实现&lt;/td&gt;
&lt;td&gt;较低，基于内核对象&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;异步支持&lt;/td&gt;
&lt;td&gt;原生支持异步等待&lt;/td&gt;
&lt;td&gt;不支持异步等待&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;等待超时&lt;/td&gt;
&lt;td&gt;支持&lt;/td&gt;
&lt;td&gt;支持&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;取消支持&lt;/td&gt;
&lt;td&gt;支持CancellationToken&lt;/td&gt;
&lt;td&gt;不支持&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;命名信号量&lt;/td&gt;
&lt;td&gt;不支持&lt;/td&gt;
&lt;td&gt;支持（跨进程）&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;
&lt;h5&gt;基本用法&lt;/h5&gt;
&lt;pre&gt;&lt;code&gt;// 创建SemaphoreSlim，初始计数3，最大计数3
SemaphoreSlim semaphore = new SemaphoreSlim(3, 3);

// 异步等待获取信号量
async Task DoWorkAsync()
{
    Console.WriteLine($&quot;线程 {Thread.CurrentThread.ManagedThreadId} 等待获取信号量&quot;);
    await semaphore.WaitAsync(); // 异步等待，不阻塞调用线程
    try
    {
        Console.WriteLine($&quot;线程 {Thread.CurrentThread.ManagedThreadId} 获取到信号量，开始工作&quot;);
        await Task.Delay(1000); // 模拟耗时操作
        Console.WriteLine($&quot;线程 {Thread.CurrentThread.ManagedThreadId} 完成工作&quot;);
    }
    finally
    {
        semaphore.Release(); // 释放信号量，允许其他线程获取
        Console.WriteLine($&quot;线程 {Thread.CurrentThread.ManagedThreadId} 释放信号量&quot;);
    }
}

// 同步等待获取信号量
void DoWork()
{
    semaphore.Wait(); // 同步等待，会阻塞调用线程
    try
    {
        // 临界区代码
    }
    finally
    {
        semaphore.Release();
    }
}
&lt;/code&gt;&lt;/pre&gt;
&lt;h5&gt;高级用法&lt;/h5&gt;
&lt;h6&gt;1. 带超时的等待&lt;/h6&gt;
&lt;pre&gt;&lt;code&gt;// 异步等待1秒，超时返回false
bool acquired = await semaphore.WaitAsync(TimeSpan.FromSeconds(1));
if (acquired)
{
    try
    {
        // 临界区代码
    }
    finally
    {
        semaphore.Release();
    }
}
else
{
    Console.WriteLine(&quot;超时未获取到信号量&quot;);
}

// 同步等待1秒
acquired = semaphore.Wait(TimeSpan.FromSeconds(1));
&lt;/code&gt;&lt;/pre&gt;
&lt;h6&gt;2. 支持取消操作&lt;/h6&gt;
&lt;pre&gt;&lt;code&gt;CancellationTokenSource cts = new CancellationTokenSource();

// 异步等待并支持取消
try
{
    await semaphore.WaitAsync(cts.Token);
    try
    {
        // 临界区代码
    }
    finally
    {
        semaphore.Release();
    }
}
catch (OperationCanceledException)
{
    Console.WriteLine(&quot;等待已取消&quot;);
}

// 取消等待
cts.Cancel();
&lt;/code&gt;&lt;/pre&gt;
&lt;h6&gt;3. 释放多个计数&lt;/h6&gt;
&lt;pre&gt;&lt;code&gt;// 释放2个计数，允许2个线程同时获取
int previousCount = semaphore.Release(2);
Console.WriteLine($&quot;释放前计数: {previousCount}, 释放后计数: {previousCount + 2}&quot;);
&lt;/code&gt;&lt;/pre&gt;
&lt;h6&gt;4. 获取当前计数&lt;/h6&gt;
&lt;pre&gt;&lt;code&gt;int currentCount = semaphore.CurrentCount;
Console.WriteLine($&quot;当前信号量计数: {currentCount}&quot;);
&lt;/code&gt;&lt;/pre&gt;
&lt;h5&gt;应用场景&lt;/h5&gt;
&lt;ol&gt;
&lt;li&gt;&lt;strong&gt;限制并发请求数&lt;/strong&gt;：例如限制同时处理的HTTP请求数&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;资源池管理&lt;/strong&gt;：例如数据库连接池、线程池&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;限流&lt;/strong&gt;：防止系统过载，保护关键资源&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;并行任务控制&lt;/strong&gt;：控制同时执行的并行任务数量&lt;/li&gt;
&lt;/ol&gt;
&lt;h5&gt;最佳实践&lt;/h5&gt;
&lt;ol&gt;
&lt;li&gt;始终在&lt;code&gt;finally&lt;/code&gt;块中释放信号量，确保即使发生异常也能正确释放&lt;/li&gt;
&lt;li&gt;避免长时间持有信号量，将临界区代码保持尽可能小&lt;/li&gt;
&lt;li&gt;适当设置初始计数和最大计数，根据系统资源和负载调整&lt;/li&gt;
&lt;li&gt;对于跨进程场景，使用&lt;code&gt;Semaphore&lt;/code&gt;而非&lt;code&gt;SemaphoreSlim&lt;/code&gt;&lt;/li&gt;
&lt;li&gt;结合&lt;code&gt;CancellationToken&lt;/code&gt;使用，支持优雅取消&lt;/li&gt;
&lt;/ol&gt;
&lt;h5&gt;完整示例：并发请求限流&lt;/h5&gt;
&lt;pre&gt;&lt;code&gt;class RequestThrottler
{
    // 限制最多3个并发请求
    private readonly SemaphoreSlim _semaphore = new SemaphoreSlim(3, 3);
    
    public async Task ProcessRequestAsync(int requestId, CancellationToken cancellationToken = default)
    {
        Console.WriteLine($&quot;请求 {requestId} 等待处理&quot;);
        
        // 异步等待获取信号量，支持取消
        await _semaphore.WaitAsync(cancellationToken);
        
        try
        {
            Console.WriteLine($&quot;请求 {requestId} 开始处理&quot;);
            // 模拟请求处理耗时
            await Task.Delay(1000, cancellationToken);
            Console.WriteLine($&quot;请求 {requestId} 处理完成&quot;);
        }
        finally
        {
            _semaphore.Release();
            Console.WriteLine($&quot;请求 {requestId} 释放资源&quot;);
        }
    }
}

// 使用示例
async Task Main()
{
    var throttler = new RequestThrottler();
    var tasks = new List&amp;lt;Task&amp;gt;();
    
    // 模拟10个并发请求
    for (int i = 1; i &amp;lt;= 10; i++)
    {
        int requestId = i;
        tasks.Add(throttler.ProcessRequestAsync(requestId));
    }
    
    await Task.WhenAll(tasks);
    Console.WriteLine(&quot;所有请求处理完成&quot;);
}
&lt;/code&gt;&lt;/pre&gt;
&lt;h4&gt;ReaderWriterLockSlim读写锁&lt;/h4&gt;
&lt;p&gt;&lt;code&gt;ReaderWriterLockSlim&lt;/code&gt;允许多个读操作或单个写操作。&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;private ReaderWriterLockSlim _rwLock = new ReaderWriterLockSlim();
private int _data = 0;

// 读操作（允许多个线程同时读）
public int ReadData()
{
    _rwLock.EnterReadLock();
    try
    {
        return _data;
    }
    finally
    {
        _rwLock.ExitReadLock();
    }
}

// 写操作（独占）
public void WriteData(int value)
{
    _rwLock.EnterWriteLock();
    try
    {
        _data = value;
    }
    finally
    {
        _rwLock.ExitWriteLock();
    }
}

// 可升级锁（先读后写）
public void UpgradeReadToWrite()
{
    _rwLock.EnterUpgradeableReadLock();
    try
    {
        // 读取数据
        int current = _data;
        
        // 需要时升级为写锁
        _rwLock.EnterWriteLock();
        try
        {
            _data = current + 1;
        }
        finally
        {
            _rwLock.ExitWriteLock();
        }
    }
    finally
    {
        _rwLock.ExitUpgradeableReadLock();
    }
}
&lt;/code&gt;&lt;/pre&gt;
&lt;h4&gt;Interlocked原子操作&lt;/h4&gt;
&lt;p&gt;&lt;code&gt;Interlocked&lt;/code&gt;提供原子操作，无需锁定。&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;private int _counter = 0;

// 原子递增
int newValue = Interlocked.Increment(ref _counter);

// 原子递减
int newValue = Interlocked.Decrement(ref _counter);

// 原子加法
int newValue = Interlocked.Add(ref _counter, 10);

// 原子交换
int oldValue = Interlocked.Exchange(ref _counter, 100);

// 原子比较并交换（CAS）
int expected = 50;
int desired = 100;
bool success = Interlocked.CompareExchange(ref _counter, desired, expected) == expected;

// 原子读取（64位）
long longValue = 0;
long result = Interlocked.Read(ref longValue);
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;&lt;strong&gt;Interlocked的优势：&lt;/strong&gt;&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;性能高：无需锁定，CPU级别原子操作&lt;/li&gt;
&lt;li&gt;无死锁风险：不涉及锁&lt;/li&gt;
&lt;li&gt;适合简单操作：递增、递减、交换等&lt;/li&gt;
&lt;/ul&gt;
&lt;h3&gt;&amp;lt;a id=&quot;thread-safe-collections&quot;&amp;gt;&amp;lt;/a&amp;gt;线程安全集合&lt;/h3&gt;
&lt;p&gt;.NET提供了线程安全的集合类，可以在多线程环境中安全使用。&lt;/p&gt;
&lt;h4&gt;ConcurrentQueue&amp;lt;T&amp;gt;线程安全队列&lt;/h4&gt;
&lt;pre&gt;&lt;code&gt;using System.Collections.Concurrent;

ConcurrentQueue&amp;lt;int&amp;gt; queue = new ConcurrentQueue&amp;lt;int&amp;gt;();

// 多线程入队
Parallel.For(0, 100, i =&amp;gt;
{
    queue.Enqueue(i);
});

// 出队
int value;
while (queue.TryDequeue(out value))
{
    Console.WriteLine($&quot;出队: {value}&quot;);
}

// 查看但不移除
if (queue.TryPeek(out value))
{
    Console.WriteLine($&quot;队首元素: {value}&quot;);
}
&lt;/code&gt;&lt;/pre&gt;
&lt;h4&gt;ConcurrentStack&amp;lt;T&amp;gt;线程安全栈&lt;/h4&gt;
&lt;pre&gt;&lt;code&gt;ConcurrentStack&amp;lt;int&amp;gt; stack = new ConcurrentStack&amp;lt;int&amp;gt;();

// 入栈
stack.Push(1);
stack.Push(2);
stack.Push(3);

// 出栈
int value;
if (stack.TryPop(out value))
{
    Console.WriteLine($&quot;出栈: {value}&quot;);
}

// 查看栈顶
if (stack.TryPeek(out value))
{
    Console.WriteLine($&quot;栈顶: {value}&quot;);
}

// 批量出栈
int[] values = new int[10];
int count = stack.TryPopRange(values);
&lt;/code&gt;&lt;/pre&gt;
&lt;h4&gt;ConcurrentDictionary&amp;lt;TKey, TValue&amp;gt;线程安全字典&lt;/h4&gt;
&lt;pre&gt;&lt;code&gt;ConcurrentDictionary&amp;lt;string, int&amp;gt; dictionary = new ConcurrentDictionary&amp;lt;string, int&amp;gt;();

// 添加或更新
dictionary.TryAdd(&quot;key1&quot;, 1);
dictionary.TryAdd(&quot;key2&quot;, 2);

// 获取或添加
int value = dictionary.GetOrAdd(&quot;key3&quot;, 3);

// 添加或更新
dictionary.AddOrUpdate(&quot;key1&quot;, 1, (key, oldValue) =&amp;gt; oldValue + 1);

// 尝试更新
if (dictionary.TryUpdate(&quot;key1&quot;, 2, 1))
{
    Console.WriteLine(&quot;更新成功&quot;);
}

// 尝试移除
if (dictionary.TryRemove(&quot;key1&quot;, out value))
{
    Console.WriteLine($&quot;移除的值: {value}&quot;);
}

// 遍历（快照）
foreach (var kvp in dictionary)
{
    Console.WriteLine($&quot;{kvp.Key}: {kvp.Value}&quot;);
}
&lt;/code&gt;&lt;/pre&gt;
&lt;h4&gt;ConcurrentBag&amp;lt;T&amp;gt;线程安全包&lt;/h4&gt;
&lt;pre&gt;&lt;code&gt;ConcurrentBag&amp;lt;int&amp;gt; bag = new ConcurrentBag&amp;lt;int&amp;gt;();

// 添加
bag.Add(1);
bag.Add(2);
bag.Add(3);

// 取出（不保证顺序）
if (bag.TryTake(out int value))
{
    Console.WriteLine($&quot;取出: {value}&quot;);
}

// 查看但不移除
if (bag.TryPeek(out value))
{
    Console.WriteLine($&quot;查看: {value}&quot;);
}
&lt;/code&gt;&lt;/pre&gt;
&lt;h4&gt;BlockingCollection&amp;lt;T&amp;gt;阻塞集合&lt;/h4&gt;
&lt;pre&gt;&lt;code&gt;// 创建阻塞集合（基于ConcurrentQueue）
BlockingCollection&amp;lt;int&amp;gt; collection = new BlockingCollection&amp;lt;int&amp;gt;();

// 生产者线程
Thread producer = new Thread(() =&amp;gt;
{
    for (int i = 0; i &amp;lt; 10; i++)
    {
        collection.Add(i);
        Console.WriteLine($&quot;生产: {i}&quot;);
        Thread.Sleep(100);
    }
    collection.CompleteAdding(); // 标记完成
});

// 消费者线程
Thread consumer = new Thread(() =&amp;gt;
{
    foreach (int item in collection.GetConsumingEnumerable())
    {
        Console.WriteLine($&quot;消费: {item}&quot;);
    }
});

producer.Start();
consumer.Start();
producer.Join();
consumer.Join();
&lt;/code&gt;&lt;/pre&gt;
&lt;h3&gt;&amp;lt;a id=&quot;parallel-programming&quot;&amp;gt;&amp;lt;/a&amp;gt;并行编程（Parallel类）&lt;/h3&gt;
&lt;p&gt;&lt;code&gt;Parallel&lt;/code&gt;类提供了简单的并行循环和并行执行方法。&lt;/p&gt;
&lt;h4&gt;Parallel.For并行循环&lt;/h4&gt;
&lt;pre&gt;&lt;code&gt;using System.Threading.Tasks;

// 基本用法
Parallel.For(0, 100, i =&amp;gt;
{
    Console.WriteLine($&quot;并行执行: {i}, 线程ID: {Thread.CurrentThread.ManagedThreadId}&quot;);
});

// 带选项的并行循环
ParallelOptions options = new ParallelOptions
{
    MaxDegreeOfParallelism = 4, // 最大并行度
    CancellationToken = CancellationToken.None
};

Parallel.For(0, 100, options, i =&amp;gt;
{
    // 执行工作
    Console.WriteLine($&quot;执行: {i}&quot;);
});

// 带状态的并行循环
long sum = 0;
Parallel.For&amp;lt;long&amp;gt;(0, 100,
    () =&amp;gt; 0, // 初始化局部状态
    (i, loop, localSum) =&amp;gt; // 循环体
    {
        localSum += i;
        return localSum;
    },
    localSum =&amp;gt; // 合并局部状态
    {
        Interlocked.Add(ref sum, localSum);
    }
);
Console.WriteLine($&quot;总和: {sum}&quot;);
&lt;/code&gt;&lt;/pre&gt;
&lt;h4&gt;Parallel.ForEach并行遍历&lt;/h4&gt;
&lt;pre&gt;&lt;code&gt;List&amp;lt;int&amp;gt; numbers = Enumerable.Range(0, 100).ToList();

// 基本用法
Parallel.ForEach(numbers, number =&amp;gt;
{
    Console.WriteLine($&quot;处理: {number}&quot;);
});

// 带选项
ParallelOptions options = new ParallelOptions
{
    MaxDegreeOfParallelism = 4
};

Parallel.ForEach(numbers, options, number =&amp;gt;
{
    // 处理每个元素
    ProcessNumber(number);
});

// 带局部状态
int total = 0;
Parallel.ForEach(numbers,
    () =&amp;gt; 0, // 初始化
    (number, loopState, localTotal) =&amp;gt; // 循环体
    {
        return localTotal + number;
    },
    localTotal =&amp;gt; // 合并
    {
        Interlocked.Add(ref total, localTotal);
    }
);
&lt;/code&gt;&lt;/pre&gt;
&lt;h4&gt;Parallel.Invoke并行执行&lt;/h4&gt;
&lt;pre&gt;&lt;code&gt;// 并行执行多个方法
Parallel.Invoke(
    () =&amp;gt; DoWork1(),
    () =&amp;gt; DoWork2(),
    () =&amp;gt; DoWork3(),
    () =&amp;gt; DoWork4()
);

// 带选项
ParallelOptions options = new ParallelOptions
{
    MaxDegreeOfParallelism = 2
};

Parallel.Invoke(options,
    () =&amp;gt; DoWork1(),
    () =&amp;gt; DoWork2(),
    () =&amp;gt; DoWork3()
);
&lt;/code&gt;&lt;/pre&gt;
&lt;h4&gt;中断和取消并行操作&lt;/h4&gt;
&lt;pre&gt;&lt;code&gt;// 使用ParallelLoopState中断
Parallel.For(0, 100, (i, loopState) =&amp;gt;
{
    if (i == 50)
    {
        loopState.Break(); // 中断循环
        // 或
        loopState.Stop(); // 立即停止
    }
    
    Console.WriteLine($&quot;执行: {i}&quot;);
    
    // 检查是否应该停止
    if (loopState.ShouldExitCurrentIteration)
    {
        return;
    }
});

// 使用CancellationToken取消
CancellationTokenSource cts = new CancellationTokenSource();
ParallelOptions options = new ParallelOptions
{
    CancellationToken = cts.Token
};

Task.Run(() =&amp;gt;
{
    Thread.Sleep(2000);
    cts.Cancel(); // 2秒后取消
});

try
{
    Parallel.For(0, 100, options, i =&amp;gt;
    {
        // 检查取消
        options.CancellationToken.ThrowIfCancellationRequested();
        Console.WriteLine($&quot;执行: {i}&quot;);
        Thread.Sleep(100);
    });
}
catch (OperationCanceledException)
{
    Console.WriteLine(&quot;操作已取消&quot;);
}
&lt;/code&gt;&lt;/pre&gt;
&lt;h3&gt;&amp;lt;a id=&quot;task-parallel-library&quot;&amp;gt;&amp;lt;/a&amp;gt;任务并行库（TPL）&lt;/h3&gt;
&lt;p&gt;TPL（Task Parallel Library）是.NET推荐的并行编程方式，比直接使用Thread更高级。&lt;/p&gt;
&lt;h4&gt;Task基础&lt;/h4&gt;
&lt;pre&gt;&lt;code&gt;using System.Threading.Tasks;

// 创建并启动Task
Task task = Task.Run(() =&amp;gt;
{
    Console.WriteLine(&quot;Task执行中...&quot;);
    Thread.Sleep(1000);
    Console.WriteLine(&quot;Task完成&quot;);
});

// 等待Task完成
task.Wait();

// 创建带返回值的Task
Task&amp;lt;int&amp;gt; taskWithResult = Task.Run(() =&amp;gt;
{
    Thread.Sleep(1000);
    return 42;
});

int result = taskWithResult.Result; // 阻塞等待结果

// 使用Task.Factory创建Task
Task task2 = Task.Factory.StartNew(() =&amp;gt;
{
    Console.WriteLine(&quot;使用Factory创建Task&quot;);
});

// 创建未启动的Task
Task task3 = new Task(() =&amp;gt;
{
    Console.WriteLine(&quot;手动启动的Task&quot;);
});
task3.Start();
task3.Wait();
&lt;/code&gt;&lt;/pre&gt;
&lt;h4&gt;Task的延续&lt;/h4&gt;
&lt;pre&gt;&lt;code&gt;// ContinueWith：任务完成后执行
Task task1 = Task.Run(() =&amp;gt;
{
    Console.WriteLine(&quot;任务1&quot;);
    return 10;
});

Task task2 = task1.ContinueWith(previousTask =&amp;gt;
{
    int result = previousTask.Result;
    Console.WriteLine($&quot;任务2，接收结果: {result}&quot;);
    return result * 2;
});

Task task3 = task2.ContinueWith(previousTask =&amp;gt;
{
    int result = previousTask.Result;
    Console.WriteLine($&quot;任务3，接收结果: {result}&quot;);
});

task3.Wait();

// 多个延续选项
Task task = Task.Run(() =&amp;gt; DoWork());

task.ContinueWith(t =&amp;gt; Console.WriteLine(&quot;成功&quot;), TaskContinuationOptions.OnlyOnRanToCompletion);
task.ContinueWith(t =&amp;gt; Console.WriteLine(&quot;失败&quot;), TaskContinuationOptions.OnlyOnFaulted);
task.ContinueWith(t =&amp;gt; Console.WriteLine(&quot;取消&quot;), TaskContinuationOptions.OnlyOnCanceled);
&lt;/code&gt;&lt;/pre&gt;
&lt;h4&gt;Task异常处理&lt;/h4&gt;
&lt;pre&gt;&lt;code&gt;// Task中的异常会被包装在AggregateException中
Task task = Task.Run(() =&amp;gt;
{
    throw new InvalidOperationException(&quot;任务异常&quot;);
});

try
{
    task.Wait();
}
catch (AggregateException ex)
{
    foreach (var innerEx in ex.InnerExceptions)
    {
        Console.WriteLine($&quot;异常: {innerEx.Message}&quot;);
    }
    ex.Handle(e =&amp;gt; e is InvalidOperationException);
}

// 检查Task状态
if (task.IsFaulted)
{
    Console.WriteLine(&quot;Task失败&quot;);
    Console.WriteLine(task.Exception?.Message);
}

if (task.IsCanceled)
{
    Console.WriteLine(&quot;Task已取消&quot;);
}

if (task.IsCompleted)
{
    Console.WriteLine(&quot;Task已完成&quot;);
}
&lt;/code&gt;&lt;/pre&gt;
&lt;h4&gt;Task组合&lt;/h4&gt;
&lt;pre&gt;&lt;code&gt;// Task.WhenAll：等待所有任务完成
Task task1 = Task.Run(() =&amp;gt; { Thread.Sleep(1000); return 1; });
Task task2 = Task.Run(() =&amp;gt; { Thread.Sleep(2000); return 2; });
Task task3 = Task.Run(() =&amp;gt; { Thread.Sleep(1500); return 3; });

Task&amp;lt;int[]&amp;gt; allTasks = Task.WhenAll(task1, task2, task3);
int[] results = await allTasks;
Console.WriteLine($&quot;结果: {string.Join(&quot;, &quot;, results)}&quot;);

// Task.WhenAny：等待任意一个任务完成
Task&amp;lt;int&amp;gt;[] tasks = new[]
{
    Task.Run(() =&amp;gt; { Thread.Sleep(1000); return 1; }),
    Task.Run(() =&amp;gt; { Thread.Sleep(2000); return 2; }),
    Task.Run(() =&amp;gt; { Thread.Sleep(1500); return 3; })
};

Task&amp;lt;int&amp;gt; completedTask = await Task.WhenAny(tasks);
int result = await completedTask;
Console.WriteLine($&quot;第一个完成的任务结果: {result}&quot;);
&lt;/code&gt;&lt;/pre&gt;
&lt;h3&gt;&amp;lt;a id=&quot;thread-communication&quot;&amp;gt;&amp;lt;/a&amp;gt;线程间通信&lt;/h3&gt;
&lt;p&gt;线程间需要协调和通信时，可以使用多种机制。&lt;/p&gt;
&lt;h4&gt;事件等待句柄（EventWaitHandle）&lt;/h4&gt;
&lt;pre&gt;&lt;code&gt;// AutoResetEvent：自动重置事件
AutoResetEvent autoEvent = new AutoResetEvent(false);

Thread thread1 = new Thread(() =&amp;gt;
{
    Console.WriteLine(&quot;线程1等待事件&quot;);
    autoEvent.WaitOne(); // 等待事件
    Console.WriteLine(&quot;线程1收到事件&quot;);
});

Thread thread2 = new Thread(() =&amp;gt;
{
    Thread.Sleep(2000);
    Console.WriteLine(&quot;线程2设置事件&quot;);
    autoEvent.Set(); // 设置事件
});

thread1.Start();
thread2.Start();
thread1.Join();
thread2.Join();

// ManualResetEvent：手动重置事件
ManualResetEvent manualEvent = new ManualResetEvent(false);

Thread thread3 = new Thread(() =&amp;gt;
{
    Console.WriteLine(&quot;线程3等待事件&quot;);
    manualEvent.WaitOne();
    Console.WriteLine(&quot;线程3收到事件&quot;);
});

Thread thread4 = new Thread(() =&amp;gt;
{
    Thread.Sleep(2000);
    Console.WriteLine(&quot;线程4设置事件&quot;);
    manualEvent.Set(); // 设置事件，所有等待的线程都会继续
    Thread.Sleep(1000);
    manualEvent.Reset(); // 重置事件
});

thread3.Start();
thread4.Start();
thread3.Join();
thread4.Join();
&lt;/code&gt;&lt;/pre&gt;
&lt;h4&gt;CountdownEvent倒计时事件&lt;/h4&gt;
&lt;pre&gt;&lt;code&gt;// 等待多个操作完成
CountdownEvent countdown = new CountdownEvent(3);

for (int i = 0; i &amp;lt; 3; i++)
{
    int taskId = i;
    Task.Run(() =&amp;gt;
    {
        Console.WriteLine($&quot;任务 {taskId} 开始&quot;);
        Thread.Sleep(1000);
        Console.WriteLine($&quot;任务 {taskId} 完成&quot;);
        countdown.Signal(); // 信号计数减1
    });
}

countdown.Wait(); // 等待计数为0
Console.WriteLine(&quot;所有任务完成&quot;);
countdown.Dispose();
&lt;/code&gt;&lt;/pre&gt;
&lt;h4&gt;Barrier屏障&lt;/h4&gt;
&lt;pre&gt;&lt;code&gt;// 多个线程在屏障处同步
Barrier barrier = new Barrier(3, b =&amp;gt;
{
    Console.WriteLine($&quot;阶段 {b.CurrentPhaseNumber} 完成&quot;);
});

for (int i = 0; i &amp;lt; 3; i++)
{
    int threadId = i;
    Task.Run(() =&amp;gt;
    {
        Console.WriteLine($&quot;线程 {threadId} 阶段1&quot;);
        barrier.SignalAndWait(); // 到达屏障并等待
        
        Console.WriteLine($&quot;线程 {threadId} 阶段2&quot;);
        barrier.SignalAndWait();
        
        Console.WriteLine($&quot;线程 {threadId} 完成&quot;);
    });
}

Thread.Sleep(5000);
barrier.Dispose();
&lt;/code&gt;&lt;/pre&gt;
&lt;h3&gt;&amp;lt;a id=&quot;deadlock-race-condition&quot;&amp;gt;&amp;lt;/a&amp;gt;死锁与竞态条件&lt;/h3&gt;
&lt;h4&gt;死锁（Deadlock）&lt;/h4&gt;
&lt;p&gt;死锁是指两个或多个线程相互等待对方释放资源，导致程序无法继续执行。&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;// 死锁示例
object lock1 = new object();
object lock2 = new object();

Thread thread1 = new Thread(() =&amp;gt;
{
    lock (lock1)
    {
        Thread.Sleep(100);
        lock (lock2) // 等待lock2
        {
            Console.WriteLine(&quot;线程1完成&quot;);
        }
    }
});

Thread thread2 = new Thread(() =&amp;gt;
{
    lock (lock2)
    {
        Thread.Sleep(100);
        lock (lock1) // 等待lock1
        {
            Console.WriteLine(&quot;线程2完成&quot;);
        }
    }
});

thread1.Start();
thread2.Start();
// 两个线程相互等待，导致死锁
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;&lt;strong&gt;避免死锁的方法：&lt;/strong&gt;&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;按相同顺序获取锁&lt;/li&gt;
&lt;li&gt;使用超时机制（Monitor.TryEnter）&lt;/li&gt;
&lt;li&gt;避免嵌套锁定&lt;/li&gt;
&lt;li&gt;使用更高级的同步机制&lt;/li&gt;
&lt;/ol&gt;
&lt;pre&gt;&lt;code&gt;// 避免死锁：按相同顺序获取锁
object lock1 = new object();
object lock2 = new object();

void SafeMethod1()
{
    lock (lock1)
    {
        lock (lock2) // 总是先lock1后lock2
        {
            // 安全代码
        }
    }
}

void SafeMethod2()
{
    lock (lock1) // 同样先lock1
    {
        lock (lock2) // 后lock2
        {
            // 安全代码
        }
    }
}
&lt;/code&gt;&lt;/pre&gt;
&lt;h4&gt;竞态条件（Race Condition）&lt;/h4&gt;
&lt;p&gt;竞态条件是指多个线程访问共享资源时，执行顺序不确定导致的结果不一致。&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;// 竞态条件示例
private int _counter = 0;

void Increment()
{
    _counter++; // 不是原子操作
    // 实际包含：读取、加1、写入三个步骤
}

// 多线程调用会导致结果不正确
Parallel.For(0, 10000, i =&amp;gt; Increment());
Console.WriteLine($&quot;计数器: {_counter}&quot;); // 可能不是10000
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;&lt;strong&gt;解决方法：&lt;/strong&gt;&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;// 方法1：使用lock
private int _counter = 0;
private readonly object _lock = new object();

void SafeIncrement()
{
    lock (_lock)
    {
        _counter++;
    }
}

// 方法2：使用Interlocked
private int _counter = 0;

void AtomicIncrement()
{
    Interlocked.Increment(ref _counter);
}

// 方法3：使用线程安全集合
private ConcurrentDictionary&amp;lt;string, int&amp;gt; _counters = new ConcurrentDictionary&amp;lt;string, int&amp;gt;();

void SafeIncrement(string key)
{
    _counters.AddOrUpdate(key, 1, (k, v) =&amp;gt; v + 1);
}
&lt;/code&gt;&lt;/pre&gt;
&lt;h3&gt;&amp;lt;a id=&quot;multithreading-best-practices&quot;&amp;gt;&amp;lt;/a&amp;gt;多线程最佳实践&lt;/h3&gt;
&lt;h4&gt;1. 优先使用Task而不是Thread&lt;/h4&gt;
&lt;pre&gt;&lt;code&gt;// ❌ 不推荐：直接使用Thread
Thread thread = new Thread(DoWork);
thread.Start();

// ✅ 推荐：使用Task
Task task = Task.Run(DoWork);
await task;
&lt;/code&gt;&lt;/pre&gt;
&lt;h4&gt;2. 使用async/await进行异步编程&lt;/h4&gt;
&lt;pre&gt;&lt;code&gt;// ❌ 不推荐：阻塞等待
Task task = Task.Run(DoWork);
task.Wait();

// ✅ 推荐：异步等待
await Task.Run(DoWork);
&lt;/code&gt;&lt;/pre&gt;
&lt;h4&gt;3. 避免共享可变状态&lt;/h4&gt;
&lt;pre&gt;&lt;code&gt;// ❌ 不推荐：共享可变状态
private int _sharedCounter = 0;

// ✅ 推荐：使用不可变对象或线程安全集合
private readonly ConcurrentDictionary&amp;lt;string, int&amp;gt; _counters = new ConcurrentDictionary&amp;lt;string, int&amp;gt;();
&lt;/code&gt;&lt;/pre&gt;
&lt;h4&gt;4. 使用线程安全集合&lt;/h4&gt;
&lt;pre&gt;&lt;code&gt;// ❌ 不推荐：普通集合+锁
private List&amp;lt;int&amp;gt; _list = new List&amp;lt;int&amp;gt;();
private readonly object _lock = new object();

// ✅ 推荐：线程安全集合
private ConcurrentBag&amp;lt;int&amp;gt; _bag = new ConcurrentBag&amp;lt;int&amp;gt;();
&lt;/code&gt;&lt;/pre&gt;
&lt;h4&gt;5. 合理使用锁的范围&lt;/h4&gt;
&lt;pre&gt;&lt;code&gt;// ❌ 不推荐：锁范围太大
lock (_lock)
{
    DoWork1(); // 不需要锁的操作
    DoWork2(); // 不需要锁的操作
    UpdateSharedResource(); // 需要锁的操作
}

// ✅ 推荐：锁范围最小化
DoWork1();
DoWork2();
lock (_lock)
{
    UpdateSharedResource();
}
&lt;/code&gt;&lt;/pre&gt;
&lt;h4&gt;6. 避免在锁内调用外部方法&lt;/h4&gt;
&lt;pre&gt;&lt;code&gt;// ❌ 不推荐：锁内调用外部方法可能导致死锁
lock (_lock)
{
    ExternalMethod(); // 可能获取其他锁
}

// ✅ 推荐：先调用外部方法，再锁定
var result = ExternalMethod();
lock (_lock)
{
    UpdateWithResult(result);
}
&lt;/code&gt;&lt;/pre&gt;
&lt;h4&gt;7. 使用CancellationToken支持取消&lt;/h4&gt;
&lt;pre&gt;&lt;code&gt;// ✅ 推荐：支持取消
async Task DoWorkAsync(CancellationToken cancellationToken)
{
    for (int i = 0; i &amp;lt; 100; i++)
    {
        cancellationToken.ThrowIfCancellationRequested();
        await ProcessItemAsync(i);
    }
}
&lt;/code&gt;&lt;/pre&gt;
&lt;h4&gt;8. 合理设置并行度&lt;/h4&gt;
&lt;pre&gt;&lt;code&gt;// ✅ 推荐：根据CPU核心数设置并行度
int maxParallelism = Environment.ProcessorCount;
ParallelOptions options = new ParallelOptions
{
    MaxDegreeOfParallelism = maxParallelism
};

Parallel.For(0, 1000, options, i =&amp;gt;
{
    // 处理工作
});
&lt;/code&gt;&lt;/pre&gt;
&lt;h4&gt;9. 及时释放资源&lt;/h4&gt;
&lt;pre&gt;&lt;code&gt;// ✅ 推荐：使用using确保资源释放
using (SemaphoreSlim semaphore = new SemaphoreSlim(1))
{
    await semaphore.WaitAsync();
    try
    {
        // 使用资源
    }
    finally
    {
        semaphore.Release();
    }
}
&lt;/code&gt;&lt;/pre&gt;
&lt;h4&gt;10. 监控和诊断&lt;/h4&gt;
&lt;pre&gt;&lt;code&gt;// 使用性能计数器监控线程
PerformanceCounter threadCounter = new PerformanceCounter(
    &quot;Process&quot;, &quot;Thread Count&quot;, Process.GetCurrentProcess().ProcessName);

Console.WriteLine($&quot;线程数: {threadCounter.NextValue()}&quot;);

// 使用诊断工具
System.Diagnostics.Trace.WriteLine($&quot;线程ID: {Thread.CurrentThread.ManagedThreadId}&quot;);
&lt;/code&gt;&lt;/pre&gt;
&lt;h2&gt;总结&lt;/h2&gt;
&lt;p&gt;C#进阶特性包括泛型、委托与Lambda表达式、LINQ、ORM框架、设计模式、特性（Attributes）、反射（Reflection）、异步编程、文件操作流处理和多线程编程等。这些特性在现代C#开发中发挥着重要作用：&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;&lt;strong&gt;泛型&lt;/strong&gt;：提供类型安全、代码重用和性能优化&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;委托与Lambda表达式&lt;/strong&gt;：支持函数式编程，简化代码&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;LINQ&lt;/strong&gt;：统一的查询语法，简化数据查询和处理&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;ORM框架&lt;/strong&gt;：从ADO.NET到Entity Framework 6，提供了完整的数据访问解决方案&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;设计模式&lt;/strong&gt;：提供经过验证的解决方案&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;特性（Attributes）&lt;/strong&gt;：为代码添加元数据，支持声明式编程&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;反射（Reflection）&lt;/strong&gt;：运行时类型检查和操作，支持框架和插件系统&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;异步编程&lt;/strong&gt;：提高应用程序响应性和性能&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;文件操作与流处理&lt;/strong&gt;：高效处理数据输入输出&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;多线程编程&lt;/strong&gt;：充分利用多核CPU，实现并发执行&lt;/li&gt;
&lt;/ol&gt;
&lt;p&gt;在WinForm开发中，合理运用这些特性可以构建出高性能、可维护的桌面应用程序：&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;
&lt;p&gt;&lt;strong&gt;ADO.NET&lt;/strong&gt;是数据访问的基础，提供了直接访问数据库的能力，包括连接管理、命令执行、参数化查询、事务处理和存储过程等核心功能。掌握ADO.NET有助于理解数据访问的底层机制。&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;&lt;strong&gt;Entity Framework 6&lt;/strong&gt;是强大的ORM框架，提供了Code First、Database First等多种开发模式。通过DbContext和DbSet，可以以面向对象的方式操作数据库。EF6支持LINQ查询、变更跟踪、并发控制、迁移等高级特性，大大提高了开发效率。&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;&lt;strong&gt;LINQ&lt;/strong&gt;统一了数据查询语法，无论是查询内存集合、数据库还是XML，都能使用一致的语法，大大简化了数据处理代码。LINQ与EF6结合使用，可以实现类型安全、高效的数据库查询。&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;&lt;strong&gt;异步编程和多线程编程&lt;/strong&gt;特别重要，可以保持UI响应性并提高程序性能；但需要注意线程安全和同步机制，避免死锁和竞态条件。在数据访问中，使用异步方法可以避免阻塞UI线程。&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;**特性（Attributes）**广泛用于数据验证、序列化、ORM映射等场景，使得代码更加声明式和可配置。在EF6中，Data Annotations提供了简洁的实体配置方式。&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;**反射（Reflection）**在框架开发、插件系统、对象映射等场景中不可或缺，但需要注意性能优化，通过缓存和委托提高执行效率。EF6内部大量使用反射来实现ORM功能。&lt;/p&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;掌握这些进阶特性，可以让您构建更加灵活、高效和可维护的C#应用程序。从基础的ADO.NET到高级的Entity Framework 6，从简单的CRUD操作到复杂的查询和性能优化，这些技术构成了现代C#数据访问的完整体系。通过合理使用ORM框架，可以大大提高开发效率，同时保持代码的可维护性和性能。&lt;/p&gt;
</content:encoded></item><item><title>C#基础语法详解</title><link>https://meteor-comet.github.io/posts/csharp-fundamentals/</link><guid isPermaLink="true">https://meteor-comet.github.io/posts/csharp-fundamentals/</guid><description>类/对象/方法/枚举/结构体</description><pubDate>Sat, 10 Feb 2024 00:00:00 GMT</pubDate><content:encoded>&lt;h1&gt;C#核心基础语法详解&lt;/h1&gt;
&lt;h2&gt;目录&lt;/h2&gt;
&lt;ol&gt;
&lt;li&gt;&lt;a href=&quot;#intro&quot;&gt;C#简介：能做什么，解决什么问题？&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;#data-types&quot;&gt;C#数据类型详解&lt;/a&gt;
&lt;ul&gt;
&lt;li&gt;&lt;a href=&quot;#struct-detail&quot;&gt;结构体（struct）详解&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;#enum-detail&quot;&gt;枚举（enum）详解&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;#type-conversion&quot;&gt;C#类型转换详解&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;#operators&quot;&gt;C#运算符详解&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;#branch-statements&quot;&gt;C#分支语句详解&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;#functions&quot;&gt;C#函数详解&lt;/a&gt;
&lt;ul&gt;
&lt;li&gt;&lt;a href=&quot;#anonymous-function&quot;&gt;匿名函数与Lambda表达式&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;#func-delegate&quot;&gt;Func委托&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;#action-delegate&quot;&gt;Action委托&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;#predicate-delegate&quot;&gt;Predicate委托&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;#func-action-application&quot;&gt;Func与Action的应用场景&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;#class-detail&quot;&gt;C#类详解&lt;/a&gt;
&lt;ul&gt;
&lt;li&gt;&lt;a href=&quot;#class-definition&quot;&gt;类的定义与结构&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;#properties-getset&quot;&gt;属性与访问器（get/set）&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;#class-inheritance&quot;&gt;类的继承与多态&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;#abstract-interface&quot;&gt;抽象类与接口&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;#static-class&quot;&gt;静态类&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;#math-class&quot;&gt;C# Math类详解&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;#random-class&quot;&gt;C# Random类详解&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;#datetime-class&quot;&gt;C# DateTime类详解&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;#array-class&quot;&gt;C# Array类详解&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;#string-class&quot;&gt;C# String类详解&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;#object-class&quot;&gt;C# Object类详解&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;#arraylist-class&quot;&gt;C# ArrayList类详解&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;#list-class&quot;&gt;C# List类详解&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;#hashset-class&quot;&gt;C# HashSet类详解&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;#dictionary-class&quot;&gt;C# Dictionary类详解&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;#iterator-detail&quot;&gt;C#迭代器详解&lt;/a&gt;&lt;/li&gt;
&lt;/ol&gt;
&lt;h2&gt;&amp;lt;a id=&quot;intro&quot;&amp;gt;&amp;lt;/a&amp;gt;C#简介：能做什么，解决什么问题？&lt;/h2&gt;
&lt;p&gt;C#（读作&quot;C Sharp&quot;）是微软公司于2000年推出的一门现代、面向对象的编程语言。它由 Anders Hejlsberg 领导设计，是.NET生态系统的核心语言之一。C#结合了C++的强大功能和Visual Basic的易用性，成为当今最受欢迎的编程语言之一。&lt;/p&gt;
&lt;h3&gt;C#能做什么？&lt;/h3&gt;
&lt;h4&gt;1. 桌面应用程序开发&lt;/h4&gt;
&lt;p&gt;使用Windows Forms、WPF或UWP等技术，C#可以创建功能丰富的桌面应用程序。从简单的工具软件到复杂的企业级应用，C#都能胜任。&lt;/p&gt;
&lt;h4&gt;2. Web应用程序开发&lt;/h4&gt;
&lt;p&gt;通过ASP.NET Core框架，C#可以构建高性能的Web应用程序和API服务。无论是传统的MVC网站还是现代化的RESTful API，C#都是优秀的选择。&lt;/p&gt;
&lt;h4&gt;3. 移动应用开发&lt;/h4&gt;
&lt;p&gt;借助Xamarin和MAUI（.NET Multi-platform App UI）技术，开发者可以用C#编写跨平台的移动应用，一套代码可以同时运行在iOS和Android平台上。&lt;/p&gt;
&lt;h4&gt;4. 游戏开发&lt;/h4&gt;
&lt;p&gt;Unity游戏引擎广泛使用C#作为脚本语言，使得C#成为游戏开发的热门选择。从手机小游戏到3A级大作，都有C#的身影。&lt;/p&gt;
&lt;h4&gt;5. 云和微服务&lt;/h4&gt;
&lt;p&gt;Azure云平台原生支持C#，配合ASP.NET Core和Docker容器技术，C#非常适合构建云原生应用和微服务架构。&lt;/p&gt;
&lt;h4&gt;6. 数据科学和机器学习&lt;/h4&gt;
&lt;p&gt;通过ML.NET框架，开发者可以使用C#构建机器学习模型。同时，C#也可以与Python生态系统集成，进行数据分析和科学计算。&lt;/p&gt;
&lt;h3&gt;C#解决了什么问题？&lt;/h3&gt;
&lt;h4&gt;1. 简化复杂性&lt;/h4&gt;
&lt;p&gt;相比C++，C#通过自动内存管理和垃圾回收机制，大大简化了内存管理的复杂性，减少了内存泄漏和指针错误等问题。&lt;/p&gt;
&lt;h4&gt;2. 提高开发效率&lt;/h4&gt;
&lt;p&gt;C#提供了丰富的标准库和现代化的语法特性，使得开发者能够更快速地构建应用程序，减少样板代码的编写。&lt;/p&gt;
&lt;h4&gt;3. 跨平台兼容性&lt;/h4&gt;
&lt;p&gt;随着.NET Core（现为.NET 5+）的推出，C#实现了真正的跨平台支持，可以在Windows、Linux和macOS上运行相同的代码。&lt;/p&gt;
&lt;h4&gt;4. 类型安全&lt;/h4&gt;
&lt;p&gt;C#是强类型语言，编译器会在编译时捕获许多类型相关的错误，提高了程序的稳定性和可靠性。&lt;/p&gt;
&lt;h4&gt;5. 面向对象编程支持&lt;/h4&gt;
&lt;p&gt;C#提供了完整的面向对象编程支持，包括封装、继承、多态等特性，帮助开发者构建可维护、可扩展的软件系统。&lt;/p&gt;
&lt;h4&gt;6. 异步编程模型&lt;/h4&gt;
&lt;p&gt;C#内置了强大的异步编程支持（async/await），简化了并发和异步操作的实现，提高了应用程序的响应性。&lt;/p&gt;
&lt;p&gt;通过这些特性和功能，C#成为了构建各种类型应用程序的强大工具，无论是企业级应用、Web服务、移动应用还是游戏开发，都能提供优秀的解决方案。&lt;/p&gt;
&lt;h2&gt;&amp;lt;a id=&quot;data-types&quot;&amp;gt;&amp;lt;/a&amp;gt;C#数据类型详解&lt;/h2&gt;
&lt;p&gt;在编程中，数据类型用于定义变量可以存储的数据种类。C#是一种强类型语言，这意味着每个变量都必须有一个明确的类型。数据类型决定了：&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;变量可以存储的数据种类&lt;/li&gt;
&lt;li&gt;变量占用的内存大小&lt;/li&gt;
&lt;li&gt;可以对变量执行的操作&lt;/li&gt;
&lt;/ol&gt;
&lt;h3&gt;为什么需要对数据类型进行分类管理？&lt;/h3&gt;
&lt;h4&gt;1. 内存管理优化&lt;/h4&gt;
&lt;p&gt;不同类型的数据占用不同的内存空间。例如，一个bool类型只需要1位，而一个long类型需要64位。通过分类管理，编译器可以为变量分配最合适的内存空间，避免浪费。&lt;/p&gt;
&lt;h4&gt;2. 类型安全&lt;/h4&gt;
&lt;p&gt;类型分类有助于编译器在编译时检查代码的正确性。例如，不能将一个字符串直接赋值给一个整数变量，这种错误会在编译时被发现。&lt;/p&gt;
&lt;h4&gt;3. 性能优化&lt;/h4&gt;
&lt;p&gt;不同类型支持不同的操作和运算。例如，数值类型支持数学运算，而字符类型支持字符操作。通过类型分类，编译器可以优化代码执行效率。&lt;/p&gt;
&lt;h4&gt;4. 代码可读性和维护性&lt;/h4&gt;
&lt;p&gt;明确的数据类型使代码更易理解和维护。开发者可以清楚地知道每个变量的用途和可以执行的操作。&lt;/p&gt;
&lt;h3&gt;C#数据类型分类&lt;/h3&gt;
&lt;p&gt;C#中的数据类型可以分为两大类：&lt;/p&gt;
&lt;h4&gt;1. 值类型（Value Types）&lt;/h4&gt;
&lt;p&gt;值类型直接包含数据，存储在栈（Stack）内存中。当您创建一个值类型变量时，系统会在栈上直接存储该变量的值。&lt;/p&gt;
&lt;h5&gt;基本数值类型：&lt;/h5&gt;
&lt;ul&gt;
&lt;li&gt;&lt;strong&gt;整数类型&lt;/strong&gt;：sbyte、byte、short、ushort、int、uint、long、ulong&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;浮点类型&lt;/strong&gt;：float、double、decimal&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;字符类型&lt;/strong&gt;：char&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;布尔类型&lt;/strong&gt;：bool&lt;/li&gt;
&lt;/ul&gt;
&lt;h5&gt;示例代码：&lt;/h5&gt;
&lt;pre&gt;&lt;code&gt;// 整数类型
int age = 25;
long population = 7800000000L;
short temperature = -10;

// 浮点类型
float price = 99.9f;
double pi = 3.14159265359;
decimal accountBalance = 1234.56m;

// 字符类型
char grade = &apos;A&apos;;
char symbol = &apos;@&apos;;

// 布尔类型
bool isCompleted = true;
bool isRunning = false;
&lt;/code&gt;&lt;/pre&gt;
&lt;h4&gt;2. 引用类型（Reference Types）&lt;/h4&gt;
&lt;p&gt;引用类型存储对数据的引用（内存地址），实际数据存储在堆（Heap）内存中。引用类型包括：&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;strong&gt;字符串类型&lt;/strong&gt;：string&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;对象类型&lt;/strong&gt;：object&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;类&lt;/strong&gt;：class&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;接口&lt;/strong&gt;：interface&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;数组&lt;/strong&gt;：array&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;委托&lt;/strong&gt;：delegate&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;除了这些基本的引用类型，C#还提供了丰富的集合类型，用于存储和操作多个数据项：&lt;/p&gt;
&lt;h5&gt;集合类型（Collections）&lt;/h5&gt;
&lt;h6&gt;1. List&amp;lt;T&amp;gt;（列表）&lt;/h6&gt;
&lt;p&gt;List&amp;lt;T&amp;gt;是动态数组，可以根据需要自动调整大小。&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;// 创建整数列表
List&amp;lt;int&amp;gt; numbers = new List&amp;lt;int&amp;gt;();
numbers.Add(1);
numbers.Add(2);
numbers.Add(3);

// 创建并初始化字符串列表
List&amp;lt;string&amp;gt; names = new List&amp;lt;string&amp;gt; { &quot;张三&quot;, &quot;李四&quot;, &quot;王五&quot; };

// 访问列表元素
int firstNumber = numbers[0];
string firstName = names[0];

// 遍历列表
foreach (string name in names)
{
    Console.WriteLine(name);
}
&lt;/code&gt;&lt;/pre&gt;
&lt;h6&gt;2. Dictionary&amp;lt;TKey, TValue&amp;gt;（字典/哈希表）&lt;/h6&gt;
&lt;p&gt;Dictionary&amp;lt;TKey, TValue&amp;gt;存储键值对，提供快速的键值查找。&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;// 创建字典
Dictionary&amp;lt;string, int&amp;gt; ages = new Dictionary&amp;lt;string, int&amp;gt;();
ages[&quot;张三&quot;] = 25;
ages[&quot;李四&quot;] = 30;

// 创建并初始化字典
Dictionary&amp;lt;string, string&amp;gt; capitals = new Dictionary&amp;lt;string, string&amp;gt;
{
    { &quot;中国&quot;, &quot;北京&quot; },
    { &quot;美国&quot;, &quot;华盛顿&quot; },
    { &quot;日本&quot;, &quot;东京&quot; }
};

// 访问字典元素
int zhangSanAge = ages[&quot;张三&quot;];
string chinaCapital = capitals[&quot;中国&quot;];

// 检查键是否存在
if (ages.ContainsKey(&quot;王五&quot;))
{
    Console.WriteLine($&quot;王五的年龄是: {ages[&quot;王五&quot;]}&quot;);
}
else
{
    Console.WriteLine(&quot;未找到王五的信息&quot;);
}

// 遍历字典
foreach (var kvp in capitals)
{
    Console.WriteLine($&quot;{kvp.Key}: {kvp.Value}&quot;);
}
&lt;/code&gt;&lt;/pre&gt;
&lt;h6&gt;3. 其他常用集合类型&lt;/h6&gt;
&lt;ul&gt;
&lt;li&gt;&lt;strong&gt;ArrayList&lt;/strong&gt;：非泛型动态数组（不推荐在新代码中使用）&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Hashtable&lt;/strong&gt;：非泛型哈希表（不推荐在新代码中使用）&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Queue&amp;lt;T&amp;gt;&lt;/strong&gt;：先进先出（FIFO）队列&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Stack&amp;lt;T&amp;gt;&lt;/strong&gt;：后进先出（LIFO）栈&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;HashSet&amp;lt;T&amp;gt;&lt;/strong&gt;：不包含重复元素的集合&lt;/li&gt;
&lt;/ul&gt;
&lt;pre&gt;&lt;code&gt;// Queue&amp;lt;T&amp;gt; 示例
Queue&amp;lt;string&amp;gt; taskQueue = new Queue&amp;lt;string&amp;gt;();
taskQueue.Enqueue(&quot;任务1&quot;);
taskQueue.Enqueue(&quot;任务2&quot;);
string nextTask = taskQueue.Dequeue(); // 返回&quot;任务1&quot;

// Stack&amp;lt;T&amp;gt; 示例
Stack&amp;lt;int&amp;gt; numberStack = new Stack&amp;lt;int&amp;gt;();
numberStack.Push(1);
numberStack.Push(2);
int topNumber = numberStack.Pop(); // 返回2

// HashSet&amp;lt;T&amp;gt; 示例
HashSet&amp;lt;string&amp;gt; uniqueNames = new HashSet&amp;lt;string&amp;gt;();
uniqueNames.Add(&quot;张三&quot;);
uniqueNames.Add(&quot;李四&quot;);
uniqueNames.Add(&quot;张三&quot;); // 不会添加重复项
&lt;/code&gt;&lt;/pre&gt;
&lt;h5&gt;示例代码：&lt;/h5&gt;
&lt;pre&gt;&lt;code&gt;// 字符串类型
string name = &quot;张三&quot;;
string message = &quot;Hello, World!&quot;;

// 对象类型（可以存储任何类型的值）
object obj1 = 100;
object obj2 = &quot;文本&quot;;
object obj3 = true;

// 数组类型
int[] numbers = {1, 2, 3, 4, 5};
string[] names = {&quot;张三&quot;, &quot;李四&quot;, &quot;王五&quot;};
&lt;/code&gt;&lt;/pre&gt;
&lt;h3&gt;值类型与引用类型的区别&lt;/h3&gt;
&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;特性&lt;/th&gt;
&lt;th&gt;值类型&lt;/th&gt;
&lt;th&gt;引用类型&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;存储位置&lt;/td&gt;
&lt;td&gt;栈内存&lt;/td&gt;
&lt;td&gt;堆内存&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;内存分配&lt;/td&gt;
&lt;td&gt;直接存储值&lt;/td&gt;
&lt;td&gt;存储引用（地址）&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;赋值操作&lt;/td&gt;
&lt;td&gt;复制值&lt;/td&gt;
&lt;td&gt;复制引用&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;默认值&lt;/td&gt;
&lt;td&gt;0、false、&apos;\0&apos;等&lt;/td&gt;
&lt;td&gt;null&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;性能&lt;/td&gt;
&lt;td&gt;较快&lt;/td&gt;
&lt;td&gt;相对较慢&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;内存回收&lt;/td&gt;
&lt;td&gt;自动释放&lt;/td&gt;
&lt;td&gt;由垃圾回收器管理&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;
&lt;p&gt;通过合理使用这些数据类型，我们可以编写出高效、安全且易于维护的C#程序。&lt;/p&gt;
&lt;h3&gt;&amp;lt;a id=&quot;struct-detail&quot;&amp;gt;&amp;lt;/a&amp;gt;结构体（struct）详解&lt;/h3&gt;
&lt;p&gt;结构体是C#中的值类型，用于表示具有多个相关字段的数据结构。结构体适用于表示轻量级的、经常使用的小数据结构。&lt;/p&gt;
&lt;h4&gt;结构体的定义&lt;/h4&gt;
&lt;p&gt;结构体使用&lt;code&gt;struct&lt;/code&gt;关键字定义，包含字段、属性、方法和构造函数等成员。&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;// 定义一个表示点的结构体
struct Point
{
    // 字段
    public int X;
    public int Y;
    
    // 构造函数
    public Point(int x, int y)
    {
        X = x;
        Y = y;
    }
    
    // 方法
    public double DistanceFromOrigin()
    {
        return Math.Sqrt(X * X + Y * Y);
    }
    
    // 重写ToString方法
    public override string ToString()
    {
        return $&quot;({X}, {Y})&quot;;
    }
}
&lt;/code&gt;&lt;/pre&gt;
&lt;h4&gt;结构体的特点&lt;/h4&gt;
&lt;ol&gt;
&lt;li&gt;&lt;strong&gt;值类型&lt;/strong&gt;：结构体实例存储在栈上，具有值类型的特点&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;不可继承&lt;/strong&gt;：结构体不能作为基类，但可以实现接口&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;默认构造函数&lt;/strong&gt;：结构体总是有一个默认的无参数构造函数，不能删除&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;性能优势&lt;/strong&gt;：对于小数据结构，结构体比类具有更好的性能&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;不支持null&lt;/strong&gt;：除非使用可空类型（如&lt;code&gt;Point?&lt;/code&gt;）&lt;/li&gt;
&lt;/ol&gt;
&lt;h4&gt;结构体的使用示例&lt;/h4&gt;
&lt;pre&gt;&lt;code&gt;// 创建结构体实例
Point p1 = new Point(3, 4);
Point p2 = new Point(5, 12);

// 访问字段
Console.WriteLine($&quot;p1的坐标: ({p1.X}, {p1.Y})&quot;);

// 调用方法
double distance = p1.DistanceFromOrigin();
Console.WriteLine($&quot;p1到原点的距离: {distance}&quot;);

// 结构体赋值（值复制）
Point p3 = p1;
p3.X = 10;
Console.WriteLine($&quot;p1: {p1}&quot;); // 输出: (3, 4)
Console.WriteLine($&quot;p3: {p3}&quot;); // 输出: (10, 4)
&lt;/code&gt;&lt;/pre&gt;
&lt;h3&gt;&amp;lt;a id=&quot;enum-detail&quot;&amp;gt;&amp;lt;/a&amp;gt;枚举（enum）详解&lt;/h3&gt;
&lt;p&gt;枚举是C#中的值类型，用于定义命名的整数常量集合。枚举可以提高代码的可读性和可维护性。&lt;/p&gt;
&lt;h4&gt;枚举的定义&lt;/h4&gt;
&lt;p&gt;枚举使用&lt;code&gt;enum&lt;/code&gt;关键字定义，可以指定基础整数类型（默认为int）。&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;// 定义一个表示星期的枚举
enum DayOfWeek
{
    Monday,
    Tuesday,
    Wednesday,
    Thursday,
    Friday,
    Saturday,
    Sunday
}

// 定义一个指定基础类型的枚举
enum Month : byte
{
    January = 1,
    February,
    March,
    April,
    May,
    June,
    July,
    August,
    September,
    October,
    November,
    December
}
&lt;/code&gt;&lt;/pre&gt;
&lt;h4&gt;枚举的特点&lt;/h4&gt;
&lt;ol&gt;
&lt;li&gt;&lt;strong&gt;值类型&lt;/strong&gt;：枚举实例存储在栈上，具有值类型的特点&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;整数常量&lt;/strong&gt;：枚举的每个成员都是整数常量，默认从0开始递增&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;基础类型&lt;/strong&gt;：可以使用byte、sbyte、short、ushort、int、uint、long或ulong作为基础类型&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;命名常量&lt;/strong&gt;：枚举成员必须是唯一的命名常量&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;可转换性&lt;/strong&gt;：可以与基础整数类型相互转换&lt;/li&gt;
&lt;/ol&gt;
&lt;h4&gt;枚举的使用示例&lt;/h4&gt;
&lt;pre&gt;&lt;code&gt;// 使用枚举
DayOfWeek today = DayOfWeek.Monday;
Console.WriteLine($&quot;今天是: {today}&quot;);

// 枚举比较
if (today == DayOfWeek.Monday)
{
    Console.WriteLine(&quot;今天是工作日的第一天&quot;);
}

// 枚举转换为整数
int dayValue = (int)today;
Console.WriteLine($&quot;Monday的值: {dayValue}&quot;); // 输出: 0

// 整数转换为枚举
DayOfWeek anotherDay = (DayOfWeek)2;
Console.WriteLine($&quot;值为2的枚举成员: {anotherDay}&quot;); // 输出: Wednesday

// 使用指定值的枚举
Month currentMonth = Month.December;
Console.WriteLine($&quot;当前月份: {currentMonth}&quot;); // 输出: December
int monthValue = (int)currentMonth;
Console.WriteLine($&quot;December的值: {monthValue}&quot;); // 输出: 12
&lt;/code&gt;&lt;/pre&gt;
&lt;h2&gt;&amp;lt;a id=&quot;type-conversion&quot;&amp;gt;&amp;lt;/a&amp;gt;C#类型转换详解&lt;/h2&gt;
&lt;p&gt;在编程过程中，我们经常需要在不同的数据类型之间进行转换。C#提供了多种类型转换机制，理解这些机制对编写高效、安全的代码至关重要。&lt;/p&gt;
&lt;h3&gt;隐式类型转换（Implicit Conversion）&lt;/h3&gt;
&lt;p&gt;隐式类型转换是由编译器自动执行的转换，不需要程序员显式指定。这种转换是安全的，不会导致数据丢失。&lt;/p&gt;
&lt;h4&gt;基本原理&lt;/h4&gt;
&lt;p&gt;隐式转换发生在从较小范围的类型向较大范围的类型转换时。例如，从int到long的转换是安全的，因为long类型的范围比int类型更大，可以容纳int类型的所有可能值。&lt;/p&gt;
&lt;h4&gt;数值类型间的隐式转换&lt;/h4&gt;
&lt;p&gt;C#中数值类型的隐式转换遵循一定的规则：&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;// 整数类型间的隐式转换
byte b = 100;
short s = b;     // byte -&amp;gt; short (安全)
int i = s;       // short -&amp;gt; int (安全)
long l = i;      // int -&amp;gt; long (安全)

// 有符号到无符号类型的隐式转换（仅在特定情况下）
sbyte sb = 100;
short ss = sb;   // sbyte -&amp;gt; short (安全)

// 整数到浮点数的隐式转换
int intValue = 1000;
float floatValue = intValue;    // int -&amp;gt; float (可能精度丢失，但仍为隐式)
double doubleValue = intValue;  // int -&amp;gt; double (安全)

// float到double的隐式转换
float f = 3.14f;
double d = f;    // float -&amp;gt; double (安全)
&lt;/code&gt;&lt;/pre&gt;
&lt;h4&gt;隐式转换的特点&lt;/h4&gt;
&lt;ol&gt;
&lt;li&gt;&lt;strong&gt;安全性&lt;/strong&gt;：隐式转换不会导致数据丢失或溢出&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;自动性&lt;/strong&gt;：编译器自动处理，无需程序员干预&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;方向性&lt;/strong&gt;：通常是从&quot;小&quot;类型向&quot;大&quot;类型转换&lt;/li&gt;
&lt;/ol&gt;
&lt;h4&gt;隐式引用类型转换&lt;/h4&gt;
&lt;p&gt;除了值类型，引用类型之间也存在隐式转换：&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;// 对象到object的隐式转换
string str = &quot;Hello&quot;;
object obj = str;  // string -&amp;gt; object (安全)

// 派生类到基类的隐式转换
List&amp;lt;int&amp;gt; list = new List&amp;lt;int&amp;gt;();
IEnumerable&amp;lt;int&amp;gt; enumerable = list;  // List&amp;lt;int&amp;gt; -&amp;gt; IEnumerable&amp;lt;int&amp;gt; (安全)
&lt;/code&gt;&lt;/pre&gt;
&lt;h4&gt;用户定义的隐式转换&lt;/h4&gt;
&lt;p&gt;C#允许开发者定义自定义的隐式转换操作符：&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;public class Celsius
{
    public double Temperature { get; set; }
    
    public Celsius(double temperature)
    {
        Temperature = temperature;
    }
    
    // 定义从Celsius到Fahrenheit的隐式转换
    public static implicit operator Fahrenheit(Celsius c)
    {
        return new Fahrenheit(c.Temperature * 9 / 5 + 32);
    }
}

public class Fahrenheit
{
    public double Temperature { get; set; }
    
    public Fahrenheit(double temperature)
    {
        Temperature = temperature;
    }
}

// 使用示例
Celsius celsius = new Celsius(25);
Fahrenheit fahrenheit = celsius;  // 隐式转换
Console.WriteLine($&quot;摄氏度: {celsius.Temperature}, 华氏度: {fahrenheit.Temperature}&quot;);
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;隐式转换使代码更加简洁和易读，但需要注意的是，并非所有类型之间都可以进行隐式转换，只有在保证安全的前提下编译器才会自动执行这种转换。&lt;/p&gt;
&lt;h3&gt;显式类型转换（Explicit Conversion）和强制类型转换（Casting）&lt;/h3&gt;
&lt;p&gt;与隐式转换相对，显式转换需要程序员明确指定转换操作。这类转换可能存在数据丢失的风险，因此必须由程序员显式声明。&lt;/p&gt;
&lt;h4&gt;基本概念&lt;/h4&gt;
&lt;p&gt;显式转换（也称为强制类型转换或类型转换）使用强制转换运算符`()来执行。当从较大范围的类型转换为较小范围的类型，或者在不同类型之间转换时，通常需要显式转换。&lt;/p&gt;
&lt;h4&gt;数值类型间的显式转换&lt;/h4&gt;
&lt;pre&gt;&lt;code&gt;// 从大范围整数类型到小范围整数类型的显式转换
long longValue = 1000000L;
int intValue = (int)longValue;     // long -&amp;gt; int (需要显式转换)

// 从浮点数到整数的显式转换
double doubleValue = 123.456;
int intFromDouble = (int)doubleValue;  // 会丢失小数部分，结果为123

// 从double到float的显式转换
double d = 123.456789;
float f = (float)d;  // 可能精度丢失

// 从decimal到其他数值类型的显式转换
decimal decimalValue = 123.45m;
double doubleFromDecimal = (double)decimalValue;
int intFromDecimal = (int)decimalValue;
&lt;/code&gt;&lt;/pre&gt;
&lt;h4&gt;显式转换的特点&lt;/h4&gt;
&lt;ol&gt;
&lt;li&gt;&lt;strong&gt;风险性&lt;/strong&gt;：可能导致数据丢失或精度降低&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;必要性&lt;/strong&gt;：编译器要求必须显式声明&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;可控性&lt;/strong&gt;：程序员明确知道转换可能发生的问题&lt;/li&gt;
&lt;/ol&gt;
&lt;h4&gt;溢出检查和unchecked上下文&lt;/h4&gt;
&lt;p&gt;在进行显式转换时，可能会发生溢出。C#提供了&lt;code&gt;checked&lt;/code&gt;和&lt;code&gt;unchecked&lt;/code&gt;关键字来控制溢出检查：&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;// checked上下文 - 检查溢出并抛出异常
try
{
    int largeNumber = int.MaxValue;
    long longNumber = largeNumber + 1000000L;
    int result = checked((int)longNumber);  // 会抛出OverflowException
}
catch (OverflowException ex)
{
    Console.WriteLine(&quot;发生溢出: &quot; + ex.Message);
}

// unchecked上下文 - 不检查溢出
int largeNumber = int.MaxValue;
long longNumber = largeNumber + 1000000L;
int result = unchecked((int)longNumber);  // 不会抛出异常，但结果可能不正确
Console.WriteLine($&quot;unchecked转换结果: {result}&quot;);
&lt;/code&gt;&lt;/pre&gt;
&lt;h4&gt;引用类型间的显式转换&lt;/h4&gt;
&lt;p&gt;引用类型之间也经常需要显式转换，特别是基类到派生类的转换：&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;// 基类到派生类的显式转换
object obj = &quot;Hello World&quot;;
string str = (string)obj;  // object -&amp;gt; string (需要显式转换)

// 使用as操作符进行安全转换
object obj2 = 123;
string str2 = obj2 as string;  // 不会抛出异常，转换失败时返回null
if (str2 != null)
{
    Console.WriteLine(&quot;转换成功: &quot; + str2);
}
else
{
    Console.WriteLine(&quot;转换失败&quot;);
}

// 使用is操作符检查类型兼容性
if (obj is string)
{
    string str3 = (string)obj;  // 安全转换
    Console.WriteLine(&quot;转换后的字符串: &quot; + str3);
}
&lt;/code&gt;&lt;/pre&gt;
&lt;h4&gt;用户定义的显式转换&lt;/h4&gt;
&lt;p&gt;与隐式转换类似，C#允许定义自定义的显式转换操作符：&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;public class Fahrenheit
{
    public double Temperature { get; set; }
    
    public Fahrenheit(double temperature)
    {
        Temperature = temperature;
    }
    
    // 定义从Fahrenheit到Celsius的显式转换
    public static explicit operator Celsius(Fahrenheit f)
    {
        return new Celsius((f.Temperature - 32) * 5 / 9);
    }
}

public class Celsius
{
    public double Temperature { get; set; }
    
    public Celsius(double temperature)
    {
        Temperature = temperature;
    }
}

// 使用示例
Fahrenheit fahrenheit = new Fahrenheit(100);
Celsius celsius = (Celsius)fahrenheit;  // 必须显式转换
Console.WriteLine($&quot;华氏度: {fahrenheit.Temperature}, 摄氏度: {celsius.Temperature}&quot;);
&lt;/code&gt;&lt;/pre&gt;
&lt;h4&gt;转换方法和辅助类&lt;/h4&gt;
&lt;p&gt;除了强制转换运算符，C#还提供了其他转换方法，这些方法在处理字符串到数值类型的转换时特别有用：&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;// Convert类的使用
string numberString = &quot;123&quot;;
int convertedInt = Convert.ToInt32(numberString);

// Parse方法的使用
int parsedInt = int.Parse(numberString);

// TryParse方法的使用（推荐）
if (int.TryParse(numberString, out int result))
{
    Console.WriteLine($&quot;转换成功: {result}&quot;);
}
else
{
    Console.WriteLine(&quot;转换失败&quot;);
}

// 处理可能失败的转换
string invalidString = &quot;abc&quot;;
if (int.TryParse(invalidString, out int invalidResult))
{
    Console.WriteLine($&quot;转换成功: {invalidResult}&quot;);
}
else
{
    Console.WriteLine($&quot;&apos;{invalidString}&apos; 无法转换为整数&quot;);
}
&lt;/code&gt;&lt;/pre&gt;
&lt;h5&gt;Parse方法详解&lt;/h5&gt;
&lt;p&gt;Parse方法用于将字符串转换为指定的数据类型。如果转换成功，返回转换后的值；如果转换失败，则抛出异常。&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;try
{
    string validNumber = &quot;123&quot;;
    int number = int.Parse(validNumber);  // 成功转换为123
    Console.WriteLine($&quot;Parse成功: {number}&quot;);
    
    string invalidNumber = &quot;abc&quot;;
    int invalid = int.Parse(invalidNumber);  // 抛出FormatException异常
}
catch (FormatException)
{
    Console.WriteLine(&quot;输入的字符串格式不正确&quot;);
}
catch (OverflowException)
{
    Console.WriteLine(&quot;数值超出范围&quot;);
}
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Parse方法适用于当你确定字符串可以被正确转换的情况，但在实际应用中，由于用户输入的不确定性，使用Parse方法时必须处理可能的异常。&lt;/p&gt;
&lt;h5&gt;TryParse方法详解&lt;/h5&gt;
&lt;p&gt;TryParse方法是更安全的转换方法，它不会抛出异常。如果转换成功，返回true并将结果存储在out参数中；如果转换失败，返回false。&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;// 基本用法
string input1 = &quot;123&quot;;
string input2 = &quot;abc&quot;;

if (int.TryParse(input1, out int result1))
{
    Console.WriteLine($&quot;{input1} 转换成功: {result1}&quot;);
}
else
{
    Console.WriteLine($&quot;{input1} 转换失败&quot;);
}

if (int.TryParse(input2, out int result2))
{
    Console.WriteLine($&quot;{input2} 转换成功: {result2}&quot;);
}
else
{
    Console.WriteLine($&quot;{input2} 转换失败&quot;);
}

// 处理不同数值类型
string floatString = &quot;123.45&quot;;
string doubleString = &quot;678.901&quot;;

if (float.TryParse(floatString, out float floatResult))
{
    Console.WriteLine($&quot;float转换成功: {floatResult}&quot;);
}

if (double.TryParse(doubleString, out double doubleResult))
{
    Console.WriteLine($&quot;double转换成功: {doubleResult}&quot;);
}
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;TryParse方法是处理用户输入的推荐方式，因为它避免了异常处理的开销，并且代码更加清晰易读。&lt;/p&gt;
&lt;h5&gt;Convert类详解&lt;/h5&gt;
&lt;p&gt;Convert类提供了更广泛的转换功能，可以处理不同类型之间的转换，包括DBNull值的处理：&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;// 基本转换
string stringNumber = &quot;123&quot;;
int convertedInt = Convert.ToInt32(stringNumber);

// 处理null值
string nullString = null;
try
{
    int nullResult = int.Parse(nullString);  // 抛出ArgumentNullException
}
catch (ArgumentNullException)
{
    Console.WriteLine(&quot;Parse无法处理null值&quot;);
}

int convertResult = Convert.ToInt32(nullString);  // 返回0
Console.WriteLine($&quot;Convert处理null值结果: {convertResult}&quot;);

// 处理各种数据类型
object objInt = 123;
object objString = &quot;456&quot;;
object objDouble = 789.12;

int intFromObject = Convert.ToInt32(objInt);
int stringFromObject = Convert.ToInt32(objString);
int doubleFromObject = Convert.ToInt32(objDouble);

Console.WriteLine($&quot;从int转换: {intFromObject}&quot;);
Console.WriteLine($&quot;从string转换: {stringFromObject}&quot;);
Console.WriteLine($&quot;从double转换: {doubleFromObject}&quot;);
&lt;/code&gt;&lt;/pre&gt;
&lt;h5&gt;三种方法的比较&lt;/h5&gt;
&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;方法&lt;/th&gt;
&lt;th&gt;异常处理&lt;/th&gt;
&lt;th&gt;性能&lt;/th&gt;
&lt;th&gt;适用场景&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;Parse&lt;/td&gt;
&lt;td&gt;抛出异常&lt;/td&gt;
&lt;td&gt;较快&lt;/td&gt;
&lt;td&gt;确定转换会成功&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;TryParse&lt;/td&gt;
&lt;td&gt;返回bool值&lt;/td&gt;
&lt;td&gt;中等&lt;/td&gt;
&lt;td&gt;不确定输入是否有效&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Convert&lt;/td&gt;
&lt;td&gt;处理null值&lt;/td&gt;
&lt;td&gt;较慢&lt;/td&gt;
&lt;td&gt;需要处理多种类型或null值&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;
&lt;p&gt;在实际开发中，推荐使用TryParse方法处理用户输入，因为它提供了最佳的安全性和性能平衡。&lt;/p&gt;
&lt;p&gt;显式转换和强制类型转换是C#中处理类型转换的重要机制，它们允许我们在不同类型之间进行转换，但需要程序员明确意识到可能存在的风险，如数据丢失或溢出。&lt;/p&gt;
&lt;h2&gt;&amp;lt;a id=&quot;operators&quot;&amp;gt;&amp;lt;/a&amp;gt;C#运算符详解&lt;/h2&gt;
&lt;p&gt;运算符是用于执行程序代码运算的符号，它们可以对一个或多个操作数进行数学或逻辑运算。C#提供了丰富的运算符来支持各种操作。&lt;/p&gt;
&lt;h3&gt;算术运算符&lt;/h3&gt;
&lt;p&gt;算术运算符用于执行基本的数学运算：&lt;/p&gt;
&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;运算符&lt;/th&gt;
&lt;th&gt;名称&lt;/th&gt;
&lt;th&gt;示例&lt;/th&gt;
&lt;th&gt;说明&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;+&lt;/td&gt;
&lt;td&gt;加法&lt;/td&gt;
&lt;td&gt;a + b&lt;/td&gt;
&lt;td&gt;两个数相加&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;-&lt;/td&gt;
&lt;td&gt;减法&lt;/td&gt;
&lt;td&gt;a - b&lt;/td&gt;
&lt;td&gt;两个数相减&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;*&lt;/td&gt;
&lt;td&gt;乘法&lt;/td&gt;
&lt;td&gt;a * b&lt;/td&gt;
&lt;td&gt;两个数相乘&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;/&lt;/td&gt;
&lt;td&gt;除法&lt;/td&gt;
&lt;td&gt;a / b&lt;/td&gt;
&lt;td&gt;两个数相除&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;%&lt;/td&gt;
&lt;td&gt;取模&lt;/td&gt;
&lt;td&gt;a % b&lt;/td&gt;
&lt;td&gt;返回除法的余数&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;++&lt;/td&gt;
&lt;td&gt;自增&lt;/td&gt;
&lt;td&gt;a++&lt;/td&gt;
&lt;td&gt;将变量值加1&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;--&lt;/td&gt;
&lt;td&gt;自减&lt;/td&gt;
&lt;td&gt;a--&lt;/td&gt;
&lt;td&gt;将变量值减1&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;
&lt;pre&gt;&lt;code&gt;int a = 10;
int b = 3;

// 基本算术运算
int sum = a + b;        // 13
int difference = a - b; // 7
int product = a * b;    // 30
int quotient = a / b;   // 3 (整数除法)
int remainder = a % b;  // 1 (取余数)

// 自增和自减
int x = 5;
x++; // x现在是6
x--; // x现在是5

// 前置和后置自增
int y = 5;
int result1 = ++y; // 先自增，再赋值，result1 = 6, y = 6
int z = 5;
int result2 = z++; // 先赋值，再自增，result2 = 5, z = 6
&lt;/code&gt;&lt;/pre&gt;
&lt;h3&gt;比较运算符&lt;/h3&gt;
&lt;p&gt;比较运算符用于比较两个值，结果为布尔类型（true或false）：&lt;/p&gt;
&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;运算符&lt;/th&gt;
&lt;th&gt;名称&lt;/th&gt;
&lt;th&gt;示例&lt;/th&gt;
&lt;th&gt;说明&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;==&lt;/td&gt;
&lt;td&gt;等于&lt;/td&gt;
&lt;td&gt;a == b&lt;/td&gt;
&lt;td&gt;检查两个值是否相等&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;!=&lt;/td&gt;
&lt;td&gt;不等于&lt;/td&gt;
&lt;td&gt;a != b&lt;/td&gt;
&lt;td&gt;检查两个值是否不相等&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&amp;gt;&lt;/td&gt;
&lt;td&gt;大于&lt;/td&gt;
&lt;td&gt;a &amp;gt; b&lt;/td&gt;
&lt;td&gt;检查左边值是否大于右边值&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&amp;lt;&lt;/td&gt;
&lt;td&gt;小于&lt;/td&gt;
&lt;td&gt;a &amp;lt; b&lt;/td&gt;
&lt;td&gt;检查左边值是否小于右边值&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&amp;gt;=&lt;/td&gt;
&lt;td&gt;大于等于&lt;/td&gt;
&lt;td&gt;a &amp;gt;= b&lt;/td&gt;
&lt;td&gt;检查左边值是否大于等于右边值&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&amp;lt;=&lt;/td&gt;
&lt;td&gt;小于等于&lt;/td&gt;
&lt;td&gt;a &amp;lt;= b&lt;/td&gt;
&lt;td&gt;检查左边值是否小于等于右边值&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;
&lt;pre&gt;&lt;code&gt;int x = 10;
int y = 20;

bool isEqual = (x == y);        // false
bool isNotEqual = (x != y);     // true
bool isGreater = (x &amp;gt; y);       // false
bool isLess = (x &amp;lt; y);          // true
bool isGreaterOrEqual = (x &amp;gt;= y); // false
bool isLessOrEqual = (x &amp;lt;= y);   // true
&lt;/code&gt;&lt;/pre&gt;
&lt;h3&gt;逻辑运算符&lt;/h3&gt;
&lt;p&gt;逻辑运算符用于组合多个布尔表达式：&lt;/p&gt;
&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;运算符&lt;/th&gt;
&lt;th&gt;名称&lt;/th&gt;
&lt;th&gt;示例&lt;/th&gt;
&lt;th&gt;说明&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;&amp;amp;&amp;amp;&lt;/td&gt;
&lt;td&gt;逻辑与&lt;/td&gt;
&lt;td&gt;a &amp;amp;&amp;amp; b&lt;/td&gt;
&lt;td&gt;当两个条件都为true时结果为true&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;||&lt;/td&gt;
&lt;td&gt;逻辑或&lt;/td&gt;
&lt;td&gt;a || b&lt;/td&gt;
&lt;td&gt;当至少一个条件为true时结果为true&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;!&lt;/td&gt;
&lt;td&gt;逻辑非&lt;/td&gt;
&lt;td&gt;!a&lt;/td&gt;
&lt;td&gt;取反布尔值&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;
&lt;pre&gt;&lt;code&gt;bool isSunny = true;
bool isWarm = false;

// 逻辑与
bool isGoodWeather = isSunny &amp;amp;&amp;amp; isWarm; // false

// 逻辑或
bool picnicDay = isSunny || isWarm; // true

// 逻辑非
bool isNotSunny = !isSunny; // false

// 复合逻辑表达式
int age = 25;
bool canDrive = (age &amp;gt;= 18) &amp;amp;&amp;amp; (age &amp;lt;= 80); // true
&lt;/code&gt;&lt;/pre&gt;
&lt;h3&gt;赋值运算符&lt;/h3&gt;
&lt;p&gt;赋值运算符用于给变量赋值：&lt;/p&gt;
&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;运算符&lt;/th&gt;
&lt;th&gt;示例&lt;/th&gt;
&lt;th&gt;等价于&lt;/th&gt;
&lt;th&gt;说明&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;=&lt;/td&gt;
&lt;td&gt;a = b&lt;/td&gt;
&lt;td&gt;&lt;/td&gt;
&lt;td&gt;简单赋值&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;+=&lt;/td&gt;
&lt;td&gt;a += b&lt;/td&gt;
&lt;td&gt;a = a + b&lt;/td&gt;
&lt;td&gt;加法赋值&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;-=&lt;/td&gt;
&lt;td&gt;a -= b&lt;/td&gt;
&lt;td&gt;a = a - b&lt;/td&gt;
&lt;td&gt;减法赋值&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;*=&lt;/td&gt;
&lt;td&gt;a *= b&lt;/td&gt;
&lt;td&gt;a = a * b&lt;/td&gt;
&lt;td&gt;乘法赋值&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;/=&lt;/td&gt;
&lt;td&gt;a /= b&lt;/td&gt;
&lt;td&gt;a = a / b&lt;/td&gt;
&lt;td&gt;除法赋值&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;%=&lt;/td&gt;
&lt;td&gt;a %= b&lt;/td&gt;
&lt;td&gt;a = a % b&lt;/td&gt;
&lt;td&gt;取模赋值&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;
&lt;pre&gt;&lt;code&gt;int x = 10;

x += 5;  // 等价于 x = x + 5; x现在是15
x -= 3;  // 等价于 x = x - 3; x现在是12
x *= 2;  // 等价于 x = x * 2; x现在是24
x /= 4;  // 等价于 x = x / 4; x现在是6
x %= 4;  // 等价于 x = x % 4; x现在是2
&lt;/code&gt;&lt;/pre&gt;
&lt;h3&gt;位运算符&lt;/h3&gt;
&lt;p&gt;位运算符用于对整数类型的二进制位进行操作：&lt;/p&gt;
&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;运算符&lt;/th&gt;
&lt;th&gt;名称&lt;/th&gt;
&lt;th&gt;示例&lt;/th&gt;
&lt;th&gt;说明&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;&amp;amp;&lt;/td&gt;
&lt;td&gt;按位与&lt;/td&gt;
&lt;td&gt;a &amp;amp; b&lt;/td&gt;
&lt;td&gt;对两个数的每一位执行与操作&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;|&lt;/td&gt;
&lt;td&gt;按位或&lt;/td&gt;
&lt;td&gt;a | b&lt;/td&gt;
&lt;td&gt;对两个数的每一位执行或操作&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;^&lt;/td&gt;
&lt;td&gt;按位异或&lt;/td&gt;
&lt;td&gt;a ^ b&lt;/td&gt;
&lt;td&gt;对两个数的每一位执行异或操作&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;~&lt;/td&gt;
&lt;td&gt;按位取反&lt;/td&gt;
&lt;td&gt;~a&lt;/td&gt;
&lt;td&gt;对数的每一位执行取反操作&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&amp;lt;&amp;lt;&lt;/td&gt;
&lt;td&gt;左移&lt;/td&gt;
&lt;td&gt;a &amp;lt;&amp;lt; b&lt;/td&gt;
&lt;td&gt;将数的二进制位向左移动指定位数&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&amp;gt;&amp;gt;&lt;/td&gt;
&lt;td&gt;右移&lt;/td&gt;
&lt;td&gt;a &amp;gt;&amp;gt; b&lt;/td&gt;
&lt;td&gt;将数的二进制位向右移动指定位数&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;
&lt;pre&gt;&lt;code&gt;int a = 5;  // 二进制: 0101
int b = 3;  // 二进制: 0011

int andResult = a &amp;amp; b;   // 0001 = 1
int orResult = a | b;    // 0111 = 7
int xorResult = a ^ b;   // 0110 = 6
int notResult = ~a;      // 1010 = -6 (补码表示)
int leftShift = a &amp;lt;&amp;lt; 1;  // 1010 = 10
int rightShift = a &amp;gt;&amp;gt; 1; // 0010 = 2
&lt;/code&gt;&lt;/pre&gt;
&lt;h3&gt;条件运算符（三元运算符）&lt;/h3&gt;
&lt;p&gt;条件运算符是C#中唯一的三元运算符，它根据条件的真假返回两个值中的一个：&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;// 语法: 条件 ? 值1 : 值2
int age = 18;
string status = (age &amp;gt;= 18) ? &quot;成年人&quot; : &quot;未成年人&quot;;

// 等价于以下if-else语句
string status2;
if (age &amp;gt;= 18)
{
    status2 = &quot;成年人&quot;;
}
else
{
    status2 = &quot;未成年人&quot;;
}
&lt;/code&gt;&lt;/pre&gt;
&lt;h3&gt;运算符优先级&lt;/h3&gt;
&lt;p&gt;在复杂的表达式中，运算符按照优先级顺序执行。以下是一些常见运算符的优先级（从高到低）：&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;括号：()&lt;/li&gt;
&lt;li&gt;自增/自减：++、--&lt;/li&gt;
&lt;li&gt;乘法、除法、取模：*、/、%&lt;/li&gt;
&lt;li&gt;加法、减法：+、-&lt;/li&gt;
&lt;li&gt;比较运算符：&amp;lt;、&amp;lt;=、&amp;gt;、&amp;gt;=&lt;/li&gt;
&lt;li&gt;相等运算符：==、!=&lt;/li&gt;
&lt;li&gt;逻辑与：&amp;amp;&amp;amp;&lt;/li&gt;
&lt;li&gt;逻辑或：&lt;code&gt;||&lt;/code&gt;&lt;/li&gt;
&lt;li&gt;条件运算符：? :&lt;/li&gt;
&lt;li&gt;赋值运算符：=、+=、-=、*=、/=、%=&lt;/li&gt;
&lt;/ol&gt;
&lt;pre&gt;&lt;code&gt;int result = 5 + 3 * 2;        // 11 (先乘法后加法)
int result2 = (5 + 3) * 2;     // 16 (括号优先)
bool condition = 5 &amp;gt; 3 &amp;amp;&amp;amp; 2 &amp;lt; 4; // true (比较运算符优先于逻辑运算符)
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;理解并正确使用这些运算符是编写C#程序的基础。在实际编程中，适当使用括号可以提高代码的可读性并确保运算顺序符合预期。&lt;/p&gt;
&lt;h2&gt;&amp;lt;a id=&quot;branch-statements&quot;&amp;gt;&amp;lt;/a&amp;gt;C#分支语句详解&lt;/h2&gt;
&lt;p&gt;在程序设计中，分支语句用于根据不同的条件执行不同的代码路径。C#提供了多种分支语句来实现程序的条件执行逻辑。&lt;/p&gt;
&lt;h3&gt;if语句&lt;/h3&gt;
&lt;p&gt;if语句是最基本的条件语句，它根据条件的真假来决定是否执行某段代码。&lt;/p&gt;
&lt;h4&gt;基本if语句&lt;/h4&gt;
&lt;pre&gt;&lt;code&gt;int age = 18;

// 基本if语句
if (age &amp;gt;= 18)
{
    Console.WriteLine(&quot;您已成年&quot;);
}
&lt;/code&gt;&lt;/pre&gt;
&lt;h4&gt;if-else语句&lt;/h4&gt;
&lt;p&gt;当条件为真时执行一段代码，为假时执行另一段代码。&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;int age = 16;

// if-else语句
if (age &amp;gt;= 18)
{
    Console.WriteLine(&quot;您已成年&quot;);
}
else
{
    Console.WriteLine(&quot;您未成年&quot;);
}
&lt;/code&gt;&lt;/pre&gt;
&lt;h4&gt;if-else if-else语句&lt;/h4&gt;
&lt;p&gt;可以检查多个条件，按顺序执行第一个为真的条件对应的代码块。&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;int score = 85;

// 多重if-else语句
if (score &amp;gt;= 90)
{
    Console.WriteLine(&quot;优秀&quot;);
}
else if (score &amp;gt;= 80)
{
    Console.WriteLine(&quot;良好&quot;);
}
else if (score &amp;gt;= 70)
{
    Console.WriteLine(&quot;中等&quot;);
}
else if (score &amp;gt;= 60)
{
    Console.WriteLine(&quot;及格&quot;);
}
else
{
    Console.WriteLine(&quot;不及格&quot;);
}
&lt;/code&gt;&lt;/pre&gt;
&lt;h4&gt;嵌套if语句&lt;/h4&gt;
&lt;p&gt;if语句可以嵌套使用，以实现更复杂的条件判断。&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;int age = 25;
bool hasLicense = true;

// 嵌套if语句
if (age &amp;gt;= 18)
{
    if (hasLicense)
    {
        Console.WriteLine(&quot;您可以合法驾驶&quot;);
    }
    else
    {
        Console.WriteLine(&quot;您已成年但没有驾照&quot;);
    }
}
else
{
    Console.WriteLine(&quot;您未成年&quot;);
}
&lt;/code&gt;&lt;/pre&gt;
&lt;h3&gt;switch语句&lt;/h3&gt;
&lt;p&gt;switch语句用于根据变量的值执行不同的代码块，是多重条件判断的另一种实现方式。&lt;/p&gt;
&lt;h4&gt;基本switch语句&lt;/h4&gt;
&lt;pre&gt;&lt;code&gt;int day = 3;
string dayName;

// 基本switch语句
switch (day)
{
    case 1:
        dayName = &quot;星期一&quot;;
        break;
    case 2:
        dayName = &quot;星期二&quot;;
        break;
    case 3:
        dayName = &quot;星期三&quot;;
        break;
    case 4:
        dayName = &quot;星期四&quot;;
        break;
    case 5:
        dayName = &quot;星期五&quot;;
        break;
    case 6:
        dayName = &quot;星期六&quot;;
        break;
    case 7:
        dayName = &quot;星期日&quot;;
        break;
    default:
        dayName = &quot;无效的日期&quot;;
        break;
}

Console.WriteLine(dayName);
&lt;/code&gt;&lt;/pre&gt;
&lt;h4&gt;switch语句中的fall through（贯穿）&lt;/h4&gt;
&lt;p&gt;在某些情况下，可以故意省略break语句来实现贯穿效果。&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;int score = 85;

// switch语句中的贯穿
switch (score / 10)
{
    case 10:
    case 9:
        Console.WriteLine(&quot;优秀&quot;);
        break;
    case 8:
        Console.WriteLine(&quot;良好&quot;);
        break;
    case 7:
        Console.WriteLine(&quot;中等&quot;);
        break;
    case 6:
        Console.WriteLine(&quot;及格&quot;);
        break;
    default:
        Console.WriteLine(&quot;不及格&quot;);
        break;
}
&lt;/code&gt;&lt;/pre&gt;
&lt;h4&gt;C# 8.0及以后版本的switch表达式&lt;/h4&gt;
&lt;p&gt;C# 8.0引入了switch表达式，提供了更简洁的语法。&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;int day = 3;

// switch表达式
string dayName = day switch
{
    1 =&amp;gt; &quot;星期一&quot;,
    2 =&amp;gt; &quot;星期二&quot;,
    3 =&amp;gt; &quot;星期三&quot;,
    4 =&amp;gt; &quot;星期四&quot;,
    5 =&amp;gt; &quot;星期五&quot;,
    6 =&amp;gt; &quot;星期六&quot;,
    7 =&amp;gt; &quot;星期日&quot;,
    _ =&amp;gt; &quot;无效的日期&quot;  // 相当于default
};

Console.WriteLine(dayName);

// 带条件的switch表达式
int score = 85;
string grade = score switch
{
    &amp;gt;= 90 =&amp;gt; &quot;优秀&quot;,
    &amp;gt;= 80 =&amp;gt; &quot;良好&quot;,
    &amp;gt;= 70 =&amp;gt; &quot;中等&quot;,
    &amp;gt;= 60 =&amp;gt; &quot;及格&quot;,
    _ =&amp;gt; &quot;不及格&quot;
};

Console.WriteLine(grade);
&lt;/code&gt;&lt;/pre&gt;
&lt;h3&gt;条件运算符（三元运算符）&lt;/h3&gt;
&lt;p&gt;条件运算符(?:)是if-else语句的简化形式，适用于简单的条件判断。&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;int age = 18;

// 三元运算符
string status = (age &amp;gt;= 18) ? &quot;成年人&quot; : &quot;未成年人&quot;;
Console.WriteLine(status);

// 三元运算符嵌套
int score = 85;
string grade = score &amp;gt;= 60 ? 
    (score &amp;gt;= 80 ? 
        (score &amp;gt;= 90 ? &quot;优秀&quot; : &quot;良好&quot;) : 
        &quot;及格&quot;) : 
    &quot;不及格&quot;;
Console.WriteLine(grade);
&lt;/code&gt;&lt;/pre&gt;
&lt;h3&gt;分支语句的最佳实践&lt;/h3&gt;
&lt;h4&gt;1. 避免深层嵌套&lt;/h4&gt;
&lt;p&gt;深层嵌套的if语句会降低代码的可读性，应尽量避免。&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;// 不推荐的深层嵌套
if (condition1)
{
    if (condition2)
    {
        if (condition3)
        {
            // 执行操作
        }
    }
}

// 推荐的扁平化写法
if (condition1 &amp;amp;&amp;amp; condition2 &amp;amp;&amp;amp; condition3)
{
    // 执行操作
}

// 或者提前返回
if (!condition1)
    return;

if (!condition2)
    return;

if (!condition3)
    return;

// 执行操作
&lt;/code&gt;&lt;/pre&gt;
&lt;h4&gt;2. 使用卫语句（Guard Clauses）&lt;/h4&gt;
&lt;p&gt;卫语句是一种编程模式，通过提前返回来减少嵌套层级。&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;// 不使用卫语句
void ProcessUser(User user)
{
    if (user != null)
    {
        if (user.IsActive)
        {
            if (user.Age &amp;gt;= 18)
            {
                // 处理用户
            }
            else
            {
                Console.WriteLine(&quot;用户未成年&quot;);
            }
        }
        else
        {
            Console.WriteLine(&quot;用户未激活&quot;);
        }
    }
    else
    {
        Console.WriteLine(&quot;用户为空&quot;);
    }
}

// 使用卫语句
void ProcessUser(User user)
{
    if (user == null)
    {
        Console.WriteLine(&quot;用户为空&quot;);
        return;
    }

    if (!user.IsActive)
    {
        Console.WriteLine(&quot;用户未激活&quot;);
        return;
    }

    if (user.Age &amp;lt; 18)
    {
        Console.WriteLine(&quot;用户未成年&quot;);
        return;
    }

    // 处理用户
}
&lt;/code&gt;&lt;/pre&gt;
&lt;h4&gt;3. 合理使用switch语句&lt;/h4&gt;
&lt;p&gt;当有多个离散值需要判断时，switch语句比多重if-else更清晰。&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;// 适合使用switch的情况
string GetDayType(DayOfWeek day)
{
    return day switch
    {
        DayOfWeek.Saturday or DayOfWeek.Sunday =&amp;gt; &quot;周末&quot;,
        DayOfWeek.Monday or DayOfWeek.Tuesday or DayOfWeek.Wednesday or 
        DayOfWeek.Thursday or DayOfWeek.Friday =&amp;gt; &quot;工作日&quot;,
        _ =&amp;gt; &quot;未知&quot;
    };
}
&lt;/code&gt;&lt;/pre&gt;
&lt;h4&gt;4. 注意浮点数比较&lt;/h4&gt;
&lt;p&gt;在进行浮点数比较时，应该使用误差范围而不是直接相等比较。&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;double a = 0.1 + 0.2;
double b = 0.3;

// 错误的做法
if (a == b)
{
    Console.WriteLine(&quot;相等&quot;);
}

// 正确的做法
if (Math.Abs(a - b) &amp;lt; 0.0001)
{
    Console.WriteLine(&quot;近似相等&quot;);
}
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;分支语句是程序控制流程的基础，合理使用这些语句可以让程序根据不同的条件执行相应的逻辑，实现复杂的业务需求。&lt;/p&gt;
&lt;h2&gt;&amp;lt;a id=&quot;functions&quot;&amp;gt;&amp;lt;/a&amp;gt;C#函数详解&lt;/h2&gt;
&lt;p&gt;函数（在C#中也称为方法）是执行特定任务的代码块。函数有助于将复杂的程序分解为更小、更易管理的部分，提高代码的可重用性和可维护性。&lt;/p&gt;
&lt;h3&gt;函数的基本结构&lt;/h3&gt;
&lt;p&gt;C#中的函数由以下几个部分组成：&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;访问修饰符（如public、private等）&lt;/li&gt;
&lt;li&gt;返回类型（函数返回值的类型）&lt;/li&gt;
&lt;li&gt;函数名&lt;/li&gt;
&lt;li&gt;参数列表（括号中的参数）&lt;/li&gt;
&lt;li&gt;函数体（花括号中的代码）&lt;/li&gt;
&lt;/ol&gt;
&lt;pre&gt;&lt;code&gt;// 函数的基本结构示例
public int Add(int a, int b)
{
    return a + b;
}
&lt;/code&gt;&lt;/pre&gt;
&lt;h3&gt;函数的定义和调用&lt;/h3&gt;
&lt;h4&gt;无返回值的函数（void类型）&lt;/h4&gt;
&lt;pre&gt;&lt;code&gt;// 定义一个无返回值的函数
public void SayHello()
{
    Console.WriteLine(&quot;Hello, World!&quot;);
}

// 调用函数
SayHello();
&lt;/code&gt;&lt;/pre&gt;
&lt;h4&gt;有返回值的函数&lt;/h4&gt;
&lt;pre&gt;&lt;code&gt;// 定义一个有返回值的函数
public int Add(int x, int y)
{
    return x + y;
}

// 调用函数并使用返回值
int result = Add(5, 3);
Console.WriteLine($&quot;5 + 3 = {result}&quot;);
&lt;/code&gt;&lt;/pre&gt;
&lt;h3&gt;return语句详解&lt;/h3&gt;
&lt;p&gt;return语句用于从函数中返回，并可以返回一个值（对于有返回类型的函数）。理解return语句的各种用法对于编写清晰的代码至关重要。&lt;/p&gt;
&lt;h4&gt;1. 基本return语句&lt;/h4&gt;
&lt;pre&gt;&lt;code&gt;// 返回值的return语句
public int GetMax(int a, int b)
{
    if (a &amp;gt; b)
        return a;  // 提前返回
    return b;      // 正常返回
}

// void函数的return（可选）
public void ProcessData()
{
    if (data == null)
        return;  // 提前退出，不返回值
    // 继续处理
}

// 返回表达式的值
public int Square(int x)
{
    return x * x;  // 返回表达式的结果
}

// 返回字面量
public string GetGreeting()
{
    return &quot;Hello, World!&quot;;
}
&lt;/code&gt;&lt;/pre&gt;
&lt;h4&gt;2. 提前返回（Early Return）&lt;/h4&gt;
&lt;p&gt;提前返回是一种编程模式，通过提前退出函数来减少嵌套层级，提高代码可读性。&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;// 不使用提前返回（深层嵌套）
public bool IsValidUser(User user)
{
    if (user != null)
    {
        if (!string.IsNullOrEmpty(user.Name))
        {
            if (user.Age &amp;gt;= 18)
            {
                if (user.Email != null &amp;amp;&amp;amp; user.Email.Contains(&quot;@&quot;))
                {
                    return true;
                }
            }
        }
    }
    return false;
}

// 使用提前返回（推荐）
public bool IsValidUser(User user)
{
    // 提前返回无效情况
    if (user == null)
        return false;
    
    if (string.IsNullOrEmpty(user.Name))
        return false;
    
    if (user.Age &amp;lt; 18)
        return false;
    
    if (user.Email == null || !user.Email.Contains(&quot;@&quot;))
        return false;
    
    // 所有验证通过
    return true;
}

// 提前返回在void函数中的应用
public void ProcessOrder(Order order)
{
    if (order == null)
        return;  // 提前退出
    
    if (order.Items.Count == 0)
        return;  // 提前退出
    
    // 处理订单
    Console.WriteLine($&quot;处理订单: {order.Id}&quot;);
}
&lt;/code&gt;&lt;/pre&gt;
&lt;h4&gt;3. 返回多个值&lt;/h4&gt;
&lt;p&gt;C#提供了多种方式返回多个值：&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;// 方式1：使用out参数（已介绍）

// 方式2：使用元组（Tuple）- C# 7.0+
public (int sum, int product) Calculate(int a, int b)
{
    return (a + b, a * b);
}

// 调用示例
var result = Calculate(5, 3);
Console.WriteLine($&quot;和: {result.sum}, 积: {result.product}&quot;);

// 解构元组
(int sum, int product) = Calculate(5, 3);
Console.WriteLine($&quot;和: {sum}, 积: {product}&quot;);

// 方式3：使用命名元组
public (int Sum, int Product) CalculateNamed(int a, int b)
{
    return (Sum: a + b, Product: a * b);
}

// 调用示例
var result2 = CalculateNamed(5, 3);
Console.WriteLine($&quot;和: {result2.Sum}, 积: {result2.Product}&quot;);

// 方式4：返回自定义类或结构
public class CalculationResult
{
    public int Sum { get; set; }
    public int Product { get; set; }
}

public CalculationResult CalculateWithClass(int a, int b)
{
    return new CalculationResult
    {
        Sum = a + b,
        Product = a * b
    };
}

// 方式5：返回数组或集合
public int[] GetMinMax(int[] numbers)
{
    if (numbers == null || numbers.Length == 0)
        return new int[0];
    
    return new int[] { numbers.Min(), numbers.Max() };
}
&lt;/code&gt;&lt;/pre&gt;
&lt;h4&gt;4. 条件返回&lt;/h4&gt;
&lt;pre&gt;&lt;code&gt;// 三元运算符返回
public string GetStatus(int score)
{
    return score &amp;gt;= 60 ? &quot;及格&quot; : &quot;不及格&quot;;
}

// 多个条件返回
public string GetGrade(int score)
{
    if (score &amp;gt;= 90) return &quot;优秀&quot;;
    if (score &amp;gt;= 80) return &quot;良好&quot;;
    if (score &amp;gt;= 70) return &quot;中等&quot;;
    if (score &amp;gt;= 60) return &quot;及格&quot;;
    return &quot;不及格&quot;;
}

// switch表达式返回（C# 8.0+）
public string GetGradeSwitch(int score) =&amp;gt; score switch
{
    &amp;gt;= 90 =&amp;gt; &quot;优秀&quot;,
    &amp;gt;= 80 =&amp;gt; &quot;良好&quot;,
    &amp;gt;= 70 =&amp;gt; &quot;中等&quot;,
    &amp;gt;= 60 =&amp;gt; &quot;及格&quot;,
    _ =&amp;gt; &quot;不及格&quot;
};
&lt;/code&gt;&lt;/pre&gt;
&lt;h4&gt;5. 返回null和默认值&lt;/h4&gt;
&lt;pre&gt;&lt;code&gt;// 返回null（引用类型）
public string FindUser(int id)
{
    // 查找用户
    if (userExists)
        return user;
    return null;  // 未找到返回null
}

// 返回默认值
public int GetValueOrDefault(int? value)
{
    return value ?? 0;  // 如果为null返回0
}

// 使用default关键字
public T GetDefault&amp;lt;T&amp;gt;()
{
    return default(T);  // 返回类型的默认值
}

// 调用示例
int defaultInt = GetDefault&amp;lt;int&amp;gt;();        // 0
string defaultString = GetDefault&amp;lt;string&amp;gt;(); // null
bool defaultBool = GetDefault&amp;lt;bool&amp;gt;();     // false
&lt;/code&gt;&lt;/pre&gt;
&lt;h4&gt;6. 返回集合和数组&lt;/h4&gt;
&lt;pre&gt;&lt;code&gt;// 返回数组
public int[] GetEvenNumbers(int[] numbers)
{
    List&amp;lt;int&amp;gt; evens = new List&amp;lt;int&amp;gt;();
    foreach (int num in numbers)
    {
        if (num % 2 == 0)
            evens.Add(num);
    }
    return evens.ToArray();
}

// 返回List
public List&amp;lt;string&amp;gt; GetNames()
{
    return new List&amp;lt;string&amp;gt; { &quot;张三&quot;, &quot;李四&quot;, &quot;王五&quot; };
}

// 返回IEnumerable（延迟执行）
public IEnumerable&amp;lt;int&amp;gt; GetNumbers()
{
    for (int i = 0; i &amp;lt; 10; i++)
    {
        yield return i;  // 使用yield return
    }
}

// 调用示例
foreach (int num in GetNumbers())
{
    Console.WriteLine(num);
}
&lt;/code&gt;&lt;/pre&gt;
&lt;h4&gt;7. 返回委托和Lambda&lt;/h4&gt;
&lt;pre&gt;&lt;code&gt;// 返回委托
public Func&amp;lt;int, int&amp;gt; GetMultiplier(int factor)
{
    return x =&amp;gt; x * factor;
}

// 调用示例
var multiplyBy5 = GetMultiplier(5);
int result = multiplyBy5(10); // 50

// 返回Action
public Action&amp;lt;string&amp;gt; GetLogger(bool isError)
{
    if (isError)
        return message =&amp;gt; Console.WriteLine($&quot;[错误] {message}&quot;);
    else
        return message =&amp;gt; Console.WriteLine($&quot;[信息] {message}&quot;);
}
&lt;/code&gt;&lt;/pre&gt;
&lt;h4&gt;8. return与异常处理&lt;/h4&gt;
&lt;pre&gt;&lt;code&gt;// return在try-catch中的使用
public int SafeDivide(int a, int b)
{
    try
    {
        if (b == 0)
            throw new DivideByZeroException(&quot;除数不能为零&quot;);
        return a / b;
    }
    catch (DivideByZeroException)
    {
        return 0;  // 发生异常时返回默认值
    }
}

// return在finally中的注意事项
public int ProcessWithFinally()
{
    try
    {
        return 100;
    }
    finally
    {
        // finally中的代码会在return之前执行
        Console.WriteLine(&quot;清理资源&quot;);
    }
    // 注意：finally之后不能有return语句
}
&lt;/code&gt;&lt;/pre&gt;
&lt;h4&gt;9. return语句的最佳实践&lt;/h4&gt;
&lt;pre&gt;&lt;code&gt;// 1. 单一返回点 vs 多个返回点
// 多个返回点（推荐用于简单验证）
public bool IsValid(string input)
{
    if (string.IsNullOrEmpty(input))
        return false;
    
    if (input.Length &amp;lt; 5)
        return false;
    
    return true;
}

// 单一返回点（适用于复杂逻辑）
public string ProcessData(string input)
{
    string result = null;
    
    if (!string.IsNullOrEmpty(input))
    {
        result = input.Trim().ToUpper();
        // 更多处理...
    }
    
    return result;
}

// 2. 明确返回值类型
public int? GetNullableInt()
{
    // 明确返回可空类型
    return null;
}

// 3. 避免在finally中使用return（不推荐）
public int BadExample()
{
    try
    {
        return 1;
    }
    finally
    {
        return 2;  // 这会覆盖try中的返回值，不推荐
    }
}

// 4. 使用表达式体成员（C# 6.0+）
public int Add(int a, int b) =&amp;gt; a + b;
public bool IsEven(int n) =&amp;gt; n % 2 == 0;
public string GetName() =&amp;gt; &quot;张三&quot;;
&lt;/code&gt;&lt;/pre&gt;
&lt;h4&gt;10. return语句总结&lt;/h4&gt;
&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;场景&lt;/th&gt;
&lt;th&gt;语法&lt;/th&gt;
&lt;th&gt;说明&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;返回值&lt;/td&gt;
&lt;td&gt;&lt;code&gt;return value;&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;返回指定值&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;提前退出&lt;/td&gt;
&lt;td&gt;&lt;code&gt;return;&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;void函数中提前退出&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;返回null&lt;/td&gt;
&lt;td&gt;&lt;code&gt;return null;&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;返回null值&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;返回默认值&lt;/td&gt;
&lt;td&gt;&lt;code&gt;return default(T);&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;返回类型默认值&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;返回表达式&lt;/td&gt;
&lt;td&gt;&lt;code&gt;return a + b;&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;返回表达式结果&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;条件返回&lt;/td&gt;
&lt;td&gt;&lt;code&gt;return condition ? value1 : value2;&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;三元运算符&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;返回元组&lt;/td&gt;
&lt;td&gt;&lt;code&gt;return (a, b);&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;返回多个值&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;返回集合&lt;/td&gt;
&lt;td&gt;&lt;code&gt;return list;&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;返回集合或数组&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;
&lt;p&gt;return语句是函数控制流程的关键，合理使用return可以让代码更加清晰、易读和高效。&lt;/p&gt;
&lt;h3&gt;函数参数详解&lt;/h3&gt;
&lt;p&gt;参数是函数与外部世界交互的桥梁，C#提供了多种参数传递方式，每种方式都有其特定的用途和适用场景。&lt;/p&gt;
&lt;h4&gt;1. 值参数（Value Parameters）&lt;/h4&gt;
&lt;p&gt;值参数是默认的参数传递方式，函数接收的是实际参数值的副本。对于值类型，传递的是值的副本；对于引用类型，传递的是引用的副本。&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;// 值类型参数 - 传递值的副本
public void ModifyValue(int number)
{
    number = 100; // 这个修改不会影响调用方的变量
    Console.WriteLine($&quot;函数内部: number = {number}&quot;);
}

// 调用示例
int value = 50;
ModifyValue(value);
Console.WriteLine($&quot;函数外部: value = {value}&quot;); // 仍然是50

// 引用类型参数 - 传递引用的副本
public void ModifyArray(int[] arr)
{
    arr[0] = 999; // 修改数组元素会影响原数组（因为引用指向同一对象）
    arr = new int[] { 1, 2, 3 }; // 但重新赋值不会影响原引用
    Console.WriteLine($&quot;函数内部数组: [{string.Join(&quot;, &quot;, arr)}]&quot;);
}

// 调用示例
int[] numbers = { 10, 20, 30 };
ModifyArray(numbers);
Console.WriteLine($&quot;函数外部数组: [{string.Join(&quot;, &quot;, numbers)}]&quot;); // [999, 20, 30]

// 值参数的特点
// - 调用方不需要特殊语法
// - 函数内部对参数的修改不会影响调用方的变量（引用类型除外，但重新赋值引用不会影响）
// - 适用于大多数场景
&lt;/code&gt;&lt;/pre&gt;
&lt;h4&gt;2. 引用参数（ref）&lt;/h4&gt;
&lt;p&gt;使用ref关键字可以按引用传递参数，函数可以直接修改调用方的变量。调用时必须使用ref关键字，且变量必须先初始化。&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;// ref参数示例
public void ModifyRefValue(ref int number)
{
    number = 100; // 这个修改会影响调用方的变量
    Console.WriteLine($&quot;函数内部: number = {number}&quot;);
}

// 调用示例
int value = 50;
ModifyRefValue(ref value); // 必须使用ref关键字
Console.WriteLine($&quot;函数外部: value = {value}&quot;); // 现在是100

// ref参数用于交换两个变量的值
public void Swap(ref int a, ref int b)
{
    int temp = a;
    a = b;
    b = temp;
}

// 调用示例
int x = 10, y = 20;
Console.WriteLine($&quot;交换前: x = {x}, y = {y}&quot;);
Swap(ref x, ref y);
Console.WriteLine($&quot;交换后: x = {x}, y = {y}&quot;); // x = 20, y = 10

// ref参数的特点
// - 调用方和函数定义都必须使用ref关键字
// - 变量必须先初始化才能作为ref参数传递
// - 函数内部对参数的修改会直接影响调用方的变量
// - 适用于需要函数修改调用方变量的场景
&lt;/code&gt;&lt;/pre&gt;
&lt;h4&gt;3. 输出参数（out）&lt;/h4&gt;
&lt;p&gt;使用out关键字可以在函数中为参数赋值，并将值返回给调用方。out参数必须在函数返回前赋值，调用时变量不需要初始化。&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;// out参数示例 - 返回多个值
public void Calculate(int a, int b, out int sum, out int product)
{
    sum = a + b;      // 必须在返回前赋值
    product = a * b;  // 必须在返回前赋值
}

// 调用示例
int x = 5, y = 3;
Calculate(x, y, out int sum, out int product); // 变量可以在调用时声明
Console.WriteLine($&quot;和: {sum}, 积: {product}&quot;);

// 或者先声明变量
int result1, result2;
Calculate(x, y, out result1, out result2);
Console.WriteLine($&quot;和: {result1}, 积: {result2}&quot;);

// out参数用于TryParse模式
public bool TryDivide(int dividend, int divisor, out double result)
{
    if (divisor == 0)
    {
        result = 0; // 即使失败也要赋值
        return false;
    }
    result = (double)dividend / divisor;
    return true;
}

// 调用示例
if (TryDivide(10, 3, out double quotient))
{
    Console.WriteLine($&quot;除法结果: {quotient}&quot;);
}
else
{
    Console.WriteLine(&quot;除法失败&quot;);
}

// out参数的特点
// - 调用方和函数定义都必须使用out关键字
// - 变量不需要初始化就可以作为out参数传递
// - 函数必须在返回前为out参数赋值
// - 适用于需要返回多个值的场景，特别是TryParse模式
&lt;/code&gt;&lt;/pre&gt;
&lt;h4&gt;4. 参数数组（params）&lt;/h4&gt;
&lt;p&gt;使用params关键字可以传递可变数量的参数。params参数必须是参数列表中的最后一个参数，且只能有一个params参数。&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;// params参数示例
public int Sum(params int[] numbers)
{
    int total = 0;
    foreach (int number in numbers)
    {
        total += number;
    }
    return total;
}

// 调用示例 - 多种方式
int result1 = Sum(1, 2, 3);           // 6 - 直接传递多个参数
int result2 = Sum(1, 2, 3, 4, 5);     // 15 - 传递任意数量的参数
int result3 = Sum(new int[] {1, 2, 3, 4, 5, 6}); // 21 - 传递数组
int result4 = Sum();                   // 0 - 可以不传参数

// params参数与其他参数结合
public void PrintInfo(string title, params object[] values)
{
    Console.Write($&quot;{title}: &quot;);
    foreach (var value in values)
    {
        Console.Write($&quot;{value} &quot;);
    }
    Console.WriteLine();
}

// 调用示例
PrintInfo(&quot;数字&quot;, 1, 2, 3, 4, 5);
PrintInfo(&quot;信息&quot;, &quot;姓名&quot;, &quot;张三&quot;, &quot;年龄&quot;, 25);

// params参数的特点
// - 必须是参数列表中的最后一个参数
// - 只能有一个params参数
// - 可以传递0个或多个参数
// - 适用于参数数量不确定的场景
&lt;/code&gt;&lt;/pre&gt;
&lt;h4&gt;5. 可选参数和默认值&lt;/h4&gt;
&lt;p&gt;C#允许为参数指定默认值，这样在调用时可以省略这些参数。有默认值的参数必须放在没有默认值的参数之后。&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;// 默认参数示例
public void PrintInfo(string name, int age = 18, string city = &quot;未知&quot;)
{
    Console.WriteLine($&quot;姓名: {name}, 年龄: {age}, 城市: {city}&quot;);
}

// 调用示例
PrintInfo(&quot;张三&quot;);                    // 姓名: 张三, 年龄: 18, 城市: 未知
PrintInfo(&quot;李四&quot;, 25);               // 姓名: 李四, 年龄: 25, 城市: 未知
PrintInfo(&quot;王五&quot;, 30, &quot;北京&quot;);       // 姓名: 王五, 年龄: 30, 城市: 北京

// 默认参数必须是编译时常量
public void SetTimeout(int milliseconds = 1000) // 正确
{
    // ...
}

// 默认参数与params结合
public void Log(string message, bool isError = false, params object[] args)
{
    string prefix = isError ? &quot;[错误]&quot; : &quot;[信息]&quot;;
    Console.WriteLine($&quot;{prefix} {message}&quot;, args);
}

// 调用示例
Log(&quot;用户登录成功&quot;);
Log(&quot;发生错误&quot;, true, &quot;数据库连接失败&quot;);
Log(&quot;处理完成&quot;, false, &quot;共处理&quot;, 100, &quot;条记录&quot;);

// 默认参数的特点
// - 默认值必须是编译时常量
// - 有默认值的参数必须放在没有默认值的参数之后
// - 调用时可以省略有默认值的参数
// - 适用于参数有常用默认值的场景
&lt;/code&gt;&lt;/pre&gt;
&lt;h4&gt;6. 命名参数&lt;/h4&gt;
&lt;p&gt;调用函数时可以使用命名参数，提高代码的可读性，并且可以改变参数的传递顺序。&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;// 函数定义
public void CreateUser(string name, int age, string email, bool isActive = true)
{
    Console.WriteLine($&quot;创建用户: {name}, {age}岁, 邮箱: {email}, 激活: {isActive}&quot;);
}

// 使用命名参数
CreateUser(name: &quot;张三&quot;, age: 25, email: &quot;zhangsan@example.com&quot;);
CreateUser(email: &quot;lisi@example.com&quot;, name: &quot;李四&quot;, age: 30); // 可以改变顺序
CreateUser(&quot;王五&quot;, 28, &quot;wangwu@example.com&quot;, isActive: false); // 混合使用位置参数和命名参数

// 命名参数与默认参数结合
public void SendEmail(string to, string subject, string body = &quot;&quot;, bool isHtml = false)
{
    Console.WriteLine($&quot;发送邮件到: {to}&quot;);
    Console.WriteLine($&quot;主题: {subject}&quot;);
    Console.WriteLine($&quot;内容: {body}&quot;);
    Console.WriteLine($&quot;HTML格式: {isHtml}&quot;);
}

// 调用示例 - 只指定需要的参数
SendEmail(to: &quot;user@example.com&quot;, subject: &quot;测试邮件&quot;);
SendEmail(to: &quot;user@example.com&quot;, subject: &quot;HTML邮件&quot;, isHtml: true);
SendEmail(&quot;user@example.com&quot;, &quot;主题&quot;, &quot;内容&quot;, true); // 也可以不使用命名参数

// 命名参数的特点
// - 提高代码可读性
// - 可以改变参数传递顺序
// - 必须放在位置参数之后（如果混合使用）
// - 适用于参数较多或参数含义不明显的场景
&lt;/code&gt;&lt;/pre&gt;
&lt;h4&gt;7. 参数传递方式对比&lt;/h4&gt;
&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;参数类型&lt;/th&gt;
&lt;th&gt;关键字&lt;/th&gt;
&lt;th&gt;调用语法&lt;/th&gt;
&lt;th&gt;变量初始化&lt;/th&gt;
&lt;th&gt;函数内赋值&lt;/th&gt;
&lt;th&gt;适用场景&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;值参数&lt;/td&gt;
&lt;td&gt;无&lt;/td&gt;
&lt;td&gt;&lt;code&gt;Func(value)&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;需要&lt;/td&gt;
&lt;td&gt;可选&lt;/td&gt;
&lt;td&gt;大多数场景&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;ref参数&lt;/td&gt;
&lt;td&gt;ref&lt;/td&gt;
&lt;td&gt;&lt;code&gt;Func(ref value)&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;需要&lt;/td&gt;
&lt;td&gt;可选&lt;/td&gt;
&lt;td&gt;需要修改调用方变量&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;out参数&lt;/td&gt;
&lt;td&gt;out&lt;/td&gt;
&lt;td&gt;&lt;code&gt;Func(out value)&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;不需要&lt;/td&gt;
&lt;td&gt;必须&lt;/td&gt;
&lt;td&gt;返回多个值，TryParse模式&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;params参数&lt;/td&gt;
&lt;td&gt;params&lt;/td&gt;
&lt;td&gt;&lt;code&gt;Func(1, 2, 3)&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;-&lt;/td&gt;
&lt;td&gt;-&lt;/td&gt;
&lt;td&gt;参数数量不确定&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;默认参数&lt;/td&gt;
&lt;td&gt;=&lt;/td&gt;
&lt;td&gt;&lt;code&gt;Func()&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;-&lt;/td&gt;
&lt;td&gt;-&lt;/td&gt;
&lt;td&gt;参数有常用默认值&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;命名参数&lt;/td&gt;
&lt;td&gt;:&lt;/td&gt;
&lt;td&gt;&lt;code&gt;Func(name: value)&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;-&lt;/td&gt;
&lt;td&gt;-&lt;/td&gt;
&lt;td&gt;提高可读性&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;
&lt;h4&gt;8. 参数验证和异常处理&lt;/h4&gt;
&lt;pre&gt;&lt;code&gt;// 参数验证示例
public double Divide(double dividend, double divisor)
{
    // 参数验证
    if (double.IsNaN(dividend) || double.IsNaN(divisor))
    {
        throw new ArgumentException(&quot;参数不能为NaN&quot;);
    }
    
    if (divisor == 0)
    {
        throw new DivideByZeroException(&quot;除数不能为零&quot;);
    }
    
    return dividend / divisor;
}

// 使用nameof获取参数名（C# 6.0+）
public void SetAge(int age)
{
    if (age &amp;lt; 0 || age &amp;gt; 150)
    {
        throw new ArgumentOutOfRangeException(nameof(age), &quot;年龄必须在0-150之间&quot;);
    }
    // ...
}

// 参数验证的最佳实践
public void ProcessUser(string name, int age, string email)
{
    // 使用ArgumentNullException检查null
    if (string.IsNullOrWhiteSpace(name))
        throw new ArgumentNullException(nameof(name), &quot;姓名不能为空&quot;);
    
    // 使用ArgumentException检查无效值
    if (age &amp;lt; 0 || age &amp;gt; 150)
        throw new ArgumentException(&quot;年龄必须在0-150之间&quot;, nameof(age));
    
    // 使用ArgumentOutOfRangeException检查范围
    if (string.IsNullOrWhiteSpace(email))
        throw new ArgumentException(&quot;邮箱不能为空&quot;, nameof(email));
    
    // 处理逻辑
    Console.WriteLine($&quot;处理用户: {name}, {age}岁, {email}&quot;);
}
&lt;/code&gt;&lt;/pre&gt;
&lt;h3&gt;函数重载&lt;/h3&gt;
&lt;p&gt;C#支持函数重载，即可以定义多个同名但参数列表不同的函数。&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;// 重载的Add函数
public int Add(int a, int b)
{
    return a + b;
}

public double Add(double a, double b)
{
    return a + b;
}

public int Add(int a, int b, int c)
{
    return a + b + c;
}

// 调用示例
int result1 = Add(5, 3);        // 调用第一个Add函数
double result2 = Add(5.5, 3.2); // 调用第二个Add函数
int result3 = Add(1, 2, 3);     // 调用第三个Add函数
&lt;/code&gt;&lt;/pre&gt;
&lt;h3&gt;&amp;lt;a id=&quot;anonymous-function&quot;&amp;gt;&amp;lt;/a&amp;gt;匿名函数与Lambda表达式（基础概念）&lt;/h3&gt;
&lt;p&gt;C#支持匿名函数（Anonymous Functions），即没有显式名称的函数。匿名函数主要用于简化代码，特别是在需要临时使用一个小函数的场景中。&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;注意&lt;/strong&gt;：关于匿名函数、Lambda表达式、Func委托、Action委托和Predicate委托的详细用法和高级特性，请参考&lt;a href=&quot;./2025-12-03-csharp-advanced-2025.md&quot;&gt;C#进阶教程&lt;/a&gt;中的&quot;C#委托与Lambda表达式进阶&quot;章节。&lt;/p&gt;
&lt;h4&gt;1. 基本Lambda表达式语法&lt;/h4&gt;
&lt;p&gt;Lambda表达式是定义匿名函数的简洁方式，使用&lt;code&gt;=&amp;gt;&lt;/code&gt;操作符分隔参数和表达式：&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;// 基本语法：(参数) =&amp;gt; 表达式
Func&amp;lt;int, int&amp;gt; square = x =&amp;gt; x * x;
int result = square(5); // 结果为25

// 多行Lambda
Func&amp;lt;int, int, int&amp;gt; multiply = (a, b) =&amp;gt;
{
    Console.WriteLine($&quot;计算 {a} × {b}&quot;);
    return a * b;
};
&lt;/code&gt;&lt;/pre&gt;
&lt;pre&gt;&lt;code&gt;// 语法格式
delegate(参数列表) { 方法体 }
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;&lt;strong&gt;示例1：基本使用&lt;/strong&gt;&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;// 定义一个接受两个int参数并返回int的匿名函数
Func&amp;lt;int, int, int&amp;gt; addDelegate = delegate(int a, int b)
{
    return a + b;
};

// 调用匿名函数
int result = addDelegate(5, 3); // 结果为8
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;&lt;strong&gt;示例2：作为参数传递&lt;/strong&gt;&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;// 定义一个接受Func委托作为参数的方法
void ProcessNumbers(int a, int b, Func&amp;lt;int, int, int&amp;gt; operation)
{
    int result = operation(a, b);
    Console.WriteLine($&quot;Result: {result}&quot;);
}

// 使用匿名函数作为参数
ProcessNumbers(10, 5, delegate(int x, int y) { return x - y; }); // 输出: Result: 5
ProcessNumbers(10, 5, delegate(int x, int y) { return x * y; }); // 输出: Result: 50
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;&lt;strong&gt;示例3：访问外部变量（闭包）&lt;/strong&gt;&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;int multiplier = 3;
Func&amp;lt;int, int&amp;gt; multiplyBy = delegate(int x) { return x * multiplier; };

int result1 = multiplyBy(5); // 结果为15
int result2 = multiplyBy(10); // 结果为30

// 修改外部变量会影响匿名函数的行为
multiplier = 5;
int result3 = multiplyBy(5); // 结果为25
&lt;/code&gt;&lt;/pre&gt;
&lt;h4&gt;3. Lambda表达式：匿名函数的简写形式&lt;/h4&gt;
&lt;p&gt;C# 3.0引入了Lambda表达式，提供了一种更简洁的方式来编写匿名函数。Lambda表达式的语法为：&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;// 单行Lambda表达式
(参数列表) =&amp;gt; 表达式

// 多行Lambda表达式
(参数列表) =&amp;gt; { 语句块 }
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;&lt;strong&gt;Lambda表达式的简写规则&lt;/strong&gt;：&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;当参数只有一个时，可以省略括号：&lt;code&gt;x =&amp;gt; x * x&lt;/code&gt;&lt;/li&gt;
&lt;li&gt;当编译器可以推断参数类型时，可以省略类型声明：&lt;code&gt;(a, b) =&amp;gt; a + b&lt;/code&gt;&lt;/li&gt;
&lt;li&gt;当Lambda体只有一条返回语句时，可以省略&lt;code&gt;return&lt;/code&gt;关键字和大括号&lt;/li&gt;
&lt;/ol&gt;
&lt;p&gt;&lt;strong&gt;示例1：基本Lambda表达式&lt;/strong&gt;&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;// 简单的Lambda表达式（单行，省略return和大括号）
Func&amp;lt;int, int, int&amp;gt; addLambda = (a, b) =&amp;gt; a + b;
int sum = addLambda(5, 3); // 结果为8

// 多行Lambda表达式（需要大括号和return）
Func&amp;lt;int, int, int&amp;gt; multiplyLambda = (a, b) =&amp;gt;
{
    Console.WriteLine($&quot;Multiplying {a} and {b}&quot;);
    return a * b;
};
int product = multiplyLambda(4, 6); // 结果为24
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;&lt;strong&gt;示例2：各种简写形式&lt;/strong&gt;&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;// 无参数的Lambda
Func&amp;lt;string&amp;gt; getGreeting = () =&amp;gt; &quot;Hello, World!&quot;;

// 单参数，省略括号
Func&amp;lt;int, int&amp;gt; square = x =&amp;gt; x * x;

// 多参数，省略类型
Func&amp;lt;int, int, bool&amp;gt; isGreater = (a, b) =&amp;gt; a &amp;gt; b;

// 带类型的参数
Func&amp;lt;int, string&amp;gt; convertToString = (int x) =&amp;gt; x.ToString();
&lt;/code&gt;&lt;/pre&gt;
&lt;h4&gt;4. 回调函数的概念与应用&lt;/h4&gt;
&lt;p&gt;回调函数（Callback Function）是一种特殊的匿名函数，它作为参数传递给另一个函数，并在特定事件发生或条件满足时被调用。回调函数在异步编程、事件处理、集合操作等场景中广泛应用。&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;回调函数的特点&lt;/strong&gt;：&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;作为参数传递给其他函数&lt;/li&gt;
&lt;li&gt;在特定条件下被调用&lt;/li&gt;
&lt;li&gt;用于处理异步操作的结果&lt;/li&gt;
&lt;li&gt;用于实现事件驱动编程&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;&lt;strong&gt;示例1：集合操作中的回调&lt;/strong&gt;&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;List&amp;lt;int&amp;gt; numbers = new List&amp;lt;int&amp;gt; { 1, 2, 3, 4, 5, 6, 7, 8, 9, 10 };

// 使用Lambda作为回调函数进行过滤
List&amp;lt;int&amp;gt; evenNumbers = numbers.Where(x =&amp;gt; x % 2 == 0).ToList();
Console.WriteLine(string.Join(&quot;, &quot;, evenNumbers)); // 输出: 2, 4, 6, 8, 10

// 使用Lambda作为回调函数进行映射
List&amp;lt;int&amp;gt; squaredNumbers = numbers.Select(x =&amp;gt; x * x).ToList();
Console.WriteLine(string.Join(&quot;, &quot;, squaredNumbers)); // 输出: 1, 4, 9, 16, 25, 36, 49, 64, 81, 100

// 使用Lambda作为回调函数进行排序
List&amp;lt;int&amp;gt; sortedDescending = numbers.OrderByDescending(x =&amp;gt; x).ToList();
Console.WriteLine(string.Join(&quot;, &quot;, sortedDescending)); // 输出: 10, 9, 8, 7, 6, 5, 4, 3, 2, 1
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;&lt;strong&gt;示例2：异步编程中的回调&lt;/strong&gt;&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;// 定义一个接受回调函数的异步方法
void DownloadFile(string url, Action&amp;lt;string&amp;gt; onComplete)
{
    // 模拟异步下载过程
    Console.WriteLine($&quot;开始下载: {url}&quot;);
    Thread.Sleep(2000); // 模拟网络延迟
    string content = $&quot;文件内容: {url}&quot;;
    
    // 下载完成后调用回调函数
    onComplete(content);
}

// 使用Lambda作为回调函数
DownloadFile(&quot;https://example.com/file.txt&quot;, (content) =&amp;gt;
{
    Console.WriteLine(&quot;下载完成！&quot;);
    Console.WriteLine($&quot;文件内容: {content}&quot;);
});

Console.WriteLine(&quot;等待下载完成...&quot;);
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;&lt;strong&gt;示例3：事件处理中的回调&lt;/strong&gt;&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;// 定义一个简单的事件发布者
class Button
{
    // 定义事件
    public event Action OnClick;
    
    // 模拟按钮点击
    public void Click()
    {
        Console.WriteLine(&quot;按钮被点击了！&quot;);
        // 触发事件，调用所有注册的回调函数
        OnClick?.Invoke();
    }
}

// 使用Lambda作为事件处理回调
Button button = new Button();
button.OnClick += () =&amp;gt; Console.WriteLine(&quot;第一个事件处理程序&quot;);
button.OnClick += () =&amp;gt; Console.WriteLine(&quot;第二个事件处理程序&quot;);

// 模拟点击
button.Click();
// 输出:
// 按钮被点击了！
// 第一个事件处理程序
// 第二个事件处理程序
&lt;/code&gt;&lt;/pre&gt;
&lt;h4&gt;5. 匿名函数与回调函数的最佳实践&lt;/h4&gt;
&lt;ol&gt;
&lt;li&gt;&lt;strong&gt;保持简洁&lt;/strong&gt;：匿名函数和回调函数应该尽可能简洁，通常用于实现简单的逻辑&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;避免复杂逻辑&lt;/strong&gt;：如果逻辑复杂，应该考虑将其提取为命名函数&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;注意闭包陷阱&lt;/strong&gt;：当在循环中使用闭包时，要注意变量捕获的时机&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;选择合适的语法&lt;/strong&gt;：优先使用Lambda表达式，因为它更简洁易读&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;注意性能&lt;/strong&gt;：虽然匿名函数方便，但在性能敏感的场景中要注意避免创建过多临时对象&lt;/li&gt;
&lt;/ol&gt;
&lt;h4&gt;6. 匿名函数与Lambda表达式的区别&lt;/h4&gt;
&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;特性&lt;/th&gt;
&lt;th&gt;传统匿名函数（delegate）&lt;/th&gt;
&lt;th&gt;Lambda表达式&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;语法&lt;/td&gt;
&lt;td&gt;更冗长&lt;/td&gt;
&lt;td&gt;更简洁&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;类型推断&lt;/td&gt;
&lt;td&gt;支持有限&lt;/td&gt;
&lt;td&gt;更强大&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;表达式树&lt;/td&gt;
&lt;td&gt;不支持&lt;/td&gt;
&lt;td&gt;支持（使用Expression&amp;lt;TDelegate&amp;gt;）&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;目标类型&lt;/td&gt;
&lt;td&gt;必须是委托类型&lt;/td&gt;
&lt;td&gt;可以是委托类型或表达式树类型&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;可读性&lt;/td&gt;
&lt;td&gt;较低&lt;/td&gt;
&lt;td&gt;较高&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;
&lt;h3&gt;&amp;lt;a id=&quot;func-delegate&quot;&amp;gt;&amp;lt;/a&amp;gt;Func委托（基础概念）&lt;/h3&gt;
&lt;p&gt;&lt;code&gt;Func&lt;/code&gt;是C#中预定义的泛型委托，用于表示具有返回值的方法。&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;注意&lt;/strong&gt;：关于Func委托的详细用法和高级特性，请参考&lt;a href=&quot;./2025-12-03-csharp-advanced-2025.md&quot;&gt;C#进阶教程&lt;/a&gt;中的&quot;Func委托详解&quot;章节。&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;// 简单示例：将整数转换为字符串
Func&amp;lt;int, string&amp;gt; convertToString = x =&amp;gt; x.ToString();
string result = convertToString(123); // 结果为&quot;123&quot;
bool result1 = isEven(4); // 结果为true

// 两个参数，返回int
Func&amp;lt;int, int, int&amp;gt; subtract = (a, b) =&amp;gt; a - b;
int result2 = subtract(10, 3); // 结果为7

// 三个参数，返回string
Func&amp;lt;int, int, int, string&amp;gt; formatSum = (a, b, c) =&amp;gt; $&quot;{a} + {b} + {c} = {a + b + c}&quot;;
string message = formatSum(1, 2, 3); // 结果为&quot;1 + 2 + 3 = 6&quot;
&lt;/code&gt;&lt;/pre&gt;
&lt;h3&gt;&amp;lt;a id=&quot;action-delegate&quot;&amp;gt;&amp;lt;/a&amp;gt;Action委托（基础概念）&lt;/h3&gt;
&lt;p&gt;&lt;code&gt;Action&lt;/code&gt;是C#中预定义的泛型委托，用于表示没有返回值（void）的方法。&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;注意&lt;/strong&gt;：关于Action委托的详细用法和高级特性，请参考&lt;a href=&quot;./2025-12-03-csharp-advanced-2025.md&quot;&gt;C#进阶教程&lt;/a&gt;中的&quot;Action委托详解&quot;章节。&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;// 简单示例：打印消息
Action&amp;lt;string&amp;gt; printMessage = message =&amp;gt; Console.WriteLine(message);
printMessage(&quot;Hello, World!&quot;); // 输出: Hello, World!
displayInfo(&quot;Bob&quot;, 30);

// 三个参数
Action&amp;lt;int, int, int&amp;gt; printSum = (a, b, c) =&amp;gt; Console.WriteLine($&quot;Sum: {a + b + c}&quot;);
printSum(1, 2, 3);
&lt;/code&gt;&lt;/pre&gt;
&lt;h3&gt;&amp;lt;a id=&quot;predicate-delegate&quot;&amp;gt;&amp;lt;/a&amp;gt;Predicate委托（基础概念）&lt;/h3&gt;
&lt;p&gt;&lt;code&gt;Predicate&lt;/code&gt;是C#中预定义的泛型委托，专门用于表示返回bool值的方法，通常用于条件判断。&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;注意&lt;/strong&gt;：关于Predicate委托的详细用法和高级特性，请参考&lt;a href=&quot;./2025-12-03-csharp-advanced-2025.md&quot;&gt;C#进阶教程&lt;/a&gt;中的&quot;Predicate委托详解&quot;章节。&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;// 简单示例：检查数字是否为偶数
Predicate&amp;lt;int&amp;gt; isEven = number =&amp;gt; number % 2 == 0;
bool result = isEven(4); // 结果为true
bool result4 = isNullOrEmpty(&quot;Hello&quot;); // 结果为false

// 检查列表是否包含元素
Predicate&amp;lt;List&amp;lt;int&amp;gt;&amp;gt; hasElements = (list) =&amp;gt; list != null &amp;amp;&amp;amp; list.Count &amp;gt; 0;
List&amp;lt;int&amp;gt; numbers = new List&amp;lt;int&amp;gt; { 1, 2, 3 };
bool result5 = hasElements(numbers); // 结果为true
&lt;/code&gt;&lt;/pre&gt;
&lt;h4&gt;Predicate的常见应用&lt;/h4&gt;
&lt;ol&gt;
&lt;li&gt;&lt;strong&gt;数组和集合的筛选&lt;/strong&gt;&lt;/li&gt;
&lt;/ol&gt;
&lt;pre&gt;&lt;code&gt;// 使用Predicate筛选数组
int[] numbers = { 1, 2, 3, 4, 5, 6, 7, 8, 9, 10 };

// 筛选偶数
Predicate&amp;lt;int&amp;gt; isEven = (x) =&amp;gt; x % 2 == 0;
int[] evenNumbers = Array.FindAll(numbers, isEven);

// 输出结果
Console.WriteLine(&quot;偶数: &quot; + string.Join(&quot;, &quot;, evenNumbers)); // 输出: 偶数: 2, 4, 6, 8, 10

// 筛选大于5的数
Predicate&amp;lt;int&amp;gt; isGreaterThan5 = (x) =&amp;gt; x &amp;gt; 5;
int[] greaterThan5 = Array.FindAll(numbers, isGreaterThan5);
Console.WriteLine(&quot;大于5的数: &quot; + string.Join(&quot;, &quot;, greaterThan5)); // 输出: 大于5的数: 6, 7, 8, 9, 10
&lt;/code&gt;&lt;/pre&gt;
&lt;ol&gt;
&lt;li&gt;&lt;strong&gt;对象集合的条件查询&lt;/strong&gt;&lt;/li&gt;
&lt;/ol&gt;
&lt;pre&gt;&lt;code&gt;// 定义Person类
public class Person
{
    public string Name { get; set; }
    public int Age { get; set; }
}

// 创建Person对象列表
List&amp;lt;Person&amp;gt; people = new List&amp;lt;Person&amp;gt;
{
    new Person { Name = &quot;Alice&quot;, Age = 25 },
    new Person { Name = &quot;Bob&quot;, Age = 30 },
    new Person { Name = &quot;Charlie&quot;, Age = 35 },
    new Person { Name = &quot;David&quot;, Age = 20 }
};

// 查找年龄大于等于30的人
Predicate&amp;lt;Person&amp;gt; isAdult = (p) =&amp;gt; p.Age &amp;gt;= 30;
Person[] adults = Array.FindAll(people.ToArray(), isAdult);

// 输出结果
Console.WriteLine(&quot;年龄大于等于30的人:&quot;);
foreach (Person person in adults)
{
    Console.WriteLine($&quot;  {person.Name}, {person.Age}岁&quot;);
}
&lt;/code&gt;&lt;/pre&gt;
&lt;h3&gt;&amp;lt;a id=&quot;func-action-application&quot;&amp;gt;&amp;lt;/a&amp;gt;Func与Action的应用场景&lt;/h3&gt;
&lt;p&gt;&lt;code&gt;Func&lt;/code&gt;和&lt;code&gt;Action&lt;/code&gt;是C#中预定义的泛型委托，它们在现代C#编程中被广泛使用，特别是在函数式编程和LINQ查询中。&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;注意&lt;/strong&gt;：关于Func与Action的详细应用场景，请参考&lt;a href=&quot;./2025-12-03-csharp-advanced-2025.md&quot;&gt;C#进阶教程&lt;/a&gt;中的&quot;委托与Lambda表达式进阶&quot;章节。&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;// 简单示例：使用Func进行数据转换
Func&amp;lt;int, string&amp;gt; convertToString = x =&amp;gt; x.ToString();
string result = convertToString(123); // 结果为&quot;123&quot;

// 简单示例：使用Action执行操作
Action&amp;lt;string&amp;gt; printMessage = message =&amp;gt; Console.WriteLine(message);
printMessage(&quot;Hello, World!&quot;); // 输出: Hello, World!
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;}&lt;/p&gt;
&lt;p&gt;// 调用示例
bool isEmailValid = ValidateInput(&quot;user@example.com&quot;, email =&amp;gt;
email.Contains(&quot;@&quot;) &amp;amp;&amp;amp; email.Contains(&quot;.&quot;));
Console.WriteLine(isEmailValid); // 输出: True&lt;/p&gt;
&lt;p&gt;bool isPasswordStrong = ValidateInput(&quot;Password123&quot;, password =&amp;gt;
password.Length &amp;gt;= 8 &amp;amp;&amp;amp; password.Any(char.IsDigit) &amp;amp;&amp;amp; password.Any(char.IsUpper));
Console.WriteLine(isPasswordStrong); // 输出: True&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;
#### 3. 实现回调功能

回调函数是指在某个操作完成后被调用的函数。`Func`和`Action`非常适合用于实现回调机制，特别是在异步编程中。

**示例1：异步操作回调**
```csharp
// 定义一个接受回调的异步方法
public void DownloadFile(string url, Action&amp;lt;string, bool&amp;gt; onComplete)
{
    try
    {
        Console.WriteLine($&quot;开始下载文件: {url}&quot;);
        // 模拟网络延迟
        Thread.Sleep(2000);
        string content = $&quot;下载的文件内容: {url}&quot;;
        // 下载成功，调用回调
        onComplete(content, true);
    }
    catch (Exception ex)
    {
        Console.WriteLine($&quot;下载失败: {ex.Message}&quot;);
        // 下载失败，调用回调
        onComplete(null, false);
    }
}

// 使用Action作为回调
DownloadFile(&quot;https://example.com/file.txt&quot;, (content, success) =&amp;gt;
{
    if (success)
    {
        Console.WriteLine(&quot;下载成功！&quot;);
        Console.WriteLine($&quot;文件内容长度: {content.Length}&quot;);
    }
    else
    {
        Console.WriteLine(&quot;下载失败，请稍后重试。&quot;);
    }
});
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;&lt;strong&gt;示例2：使用Func作为回调获取结果&lt;/strong&gt;&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;// 定义一个接受Func回调的方法
public void CalculateWithCallback(int a, int b, Func&amp;lt;int, int, int&amp;gt; callback)
{
    int result = callback(a, b);
    Console.WriteLine($&quot;计算结果: {result}&quot;);
}

// 使用不同的Func回调进行计算
CalculateWithCallback(10, 5, (x, y) =&amp;gt; x + y); // 加法
CalculateWithCallback(10, 5, (x, y) =&amp;gt; x - y); // 减法
CalculateWithCallback(10, 5, (x, y) =&amp;gt; x * y); // 乘法
CalculateWithCallback(10, 5, (x, y) =&amp;gt; x / y); // 除法
&lt;/code&gt;&lt;/pre&gt;
&lt;h4&gt;4. 配合LINQ使用&lt;/h4&gt;
&lt;p&gt;LINQ（Language Integrated Query）广泛使用&lt;code&gt;Func&lt;/code&gt;委托来实现其各种操作符。通过Lambda表达式，我们可以轻松地定义LINQ操作中使用的函数。&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;示例1：基本LINQ操作&lt;/strong&gt;&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;List&amp;lt;int&amp;gt; numbers = new List&amp;lt;int&amp;gt; { 1, 2, 3, 4, 5, 6, 7, 8, 9, 10 };

// 使用Func作为Where方法的参数：筛选偶数
var evenNumbers = numbers.Where(n =&amp;gt; n % 2 == 0);

// 使用Func作为Select方法的参数：将每个数平方
var squaredNumbers = numbers.Select(n =&amp;gt; n * n);

// 使用Func作为OrderByDescending方法的参数：降序排序
var sortedNumbers = numbers.OrderByDescending(n =&amp;gt; n);

// 使用Func作为Sum方法的参数：计算偶数的和
var sumOfEvens = numbers.Where(n =&amp;gt; n % 2 == 0).Sum();

// 输出结果
Console.WriteLine(&quot;偶数: &quot; + string.Join(&quot;, &quot;, evenNumbers)); // 2, 4, 6, 8, 10
Console.WriteLine(&quot;平方数: &quot; + string.Join(&quot;, &quot;, squaredNumbers)); // 1, 4, 9, 16, 25, 36, 49, 64, 81, 100
Console.WriteLine(&quot;降序排列: &quot; + string.Join(&quot;, &quot;, sortedNumbers)); // 10, 9, 8, 7, 6, 5, 4, 3, 2, 1
Console.WriteLine(&quot;偶数的和: &quot; + sumOfEvens); // 30
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;&lt;strong&gt;示例2：复杂对象的LINQ操作&lt;/strong&gt;&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;// 定义一个Person类
public class Person
{
    public string Name { get; set; }
    public int Age { get; set; }
    public string City { get; set; }
}

// 创建Person对象列表
List&amp;lt;Person&amp;gt; people = new List&amp;lt;Person&amp;gt;
{
    new Person { Name = &quot;张三&quot;, Age = 25, City = &quot;北京&quot; },
    new Person { Name = &quot;李四&quot;, Age = 30, City = &quot;上海&quot; },
    new Person { Name = &quot;王五&quot;, Age = 28, City = &quot;北京&quot; },
    new Person { Name = &quot;赵六&quot;, Age = 35, City = &quot;广州&quot; },
    new Person { Name = &quot;孙七&quot;, Age = 22, City = &quot;上海&quot; }
};

// 使用Func进行更复杂的查询
var beijingPeople = people.Where(p =&amp;gt; p.City == &quot;北京&quot;);
var sortedByAge = people.OrderBy(p =&amp;gt; p.Age);
var averageAge = people.Average(p =&amp;gt; p.Age);
var nameList = people.Select(p =&amp;gt; p.Name);

// 输出结果
Console.WriteLine(&quot;北京人: &quot; + string.Join(&quot;, &quot;, beijingPeople.Select(p =&amp;gt; p.Name))); // 张三, 王五
Console.WriteLine(&quot;按年龄排序: &quot; + string.Join(&quot;, &quot;, sortedByAge.Select(p =&amp;gt; $&quot;{p.Name}({p.Age})&quot;) )); // 孙七(22), 张三(25), 王五(28), 李四(30), 赵六(35)
Console.WriteLine(&quot;平均年龄: &quot; + averageAge); // 28
Console.WriteLine(&quot;所有人名: &quot; + string.Join(&quot;, &quot;, nameList)); // 张三, 李四, 王五, 赵六, 孙七
&lt;/code&gt;&lt;/pre&gt;
&lt;h4&gt;5. 事件处理&lt;/h4&gt;
&lt;p&gt;&lt;code&gt;Action&lt;/code&gt;委托常用于定义和处理事件，特别是在不需要传递复杂参数的情况下。&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;示例：使用Action定义事件&lt;/strong&gt;&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;// 定义一个带有Action事件的类
public class TemperatureMonitor
{
    // 使用Action定义事件
    public event Action&amp;lt;int&amp;gt; OnTemperatureChange;
    public event Action OnOverheating;
    
    private int _temperature;
    public int Temperature
    {
        get { return _temperature; }
        set
        {
            if (_temperature != value)
            {
                _temperature = value;
                // 触发温度变化事件
                OnTemperatureChange?.Invoke(_temperature);
                
                // 如果温度超过阈值，触发过热事件
                if (_temperature &amp;gt; 80)
                {
                    OnOverheating?.Invoke();
                }
            }
        }
    }
}

// 使用示例
TemperatureMonitor monitor = new TemperatureMonitor();

// 订阅温度变化事件
monitor.OnTemperatureChange += temp =&amp;gt; 
    Console.WriteLine($&quot;温度变化: {temp}°C&quot;);

// 订阅过热事件
monitor.OnOverheating += () =&amp;gt; 
    Console.WriteLine(&quot;警告：温度过高！&quot;);

// 模拟温度变化
monitor.Temperature = 25;
monitor.Temperature = 60;
monitor.Temperature = 85; // 触发过热事件
monitor.Temperature = 75;

// 输出结果:
// 温度变化: 25°C
// 温度变化: 60°C
// 温度变化: 85°C
// 警告：温度过高！
// 温度变化: 75°C
&lt;/code&gt;&lt;/pre&gt;
&lt;h4&gt;6. 延迟执行和惰性计算&lt;/h4&gt;
&lt;p&gt;&lt;code&gt;Func&lt;/code&gt;委托可以用于实现延迟执行（Lazy Execution）和惰性计算（Lazy Evaluation），这在需要优化性能的场景中非常有用。&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;示例：延迟执行&lt;/strong&gt;&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;// 定义一个接受Func参数的方法，实现延迟执行
public void ExecuteWhenNeeded&amp;lt;T&amp;gt;(Func&amp;lt;T&amp;gt; operation)
{
    // 模拟某些条件判断
    bool shouldExecute = DateTime.Now.Second % 2 == 0;
    
    if (shouldExecute)
    {
        Console.WriteLine(&quot;执行操作...&quot;);
        T result = operation();
        Console.WriteLine($&quot;操作结果: {result}&quot;);
    }
    else
    {
        Console.WriteLine(&quot;条件不满足，不执行操作&quot;);
    }
}

// 调用示例：传递一个需要复杂计算的Func
ExecuteWhenNeeded(() =&amp;gt;
{
    Console.WriteLine(&quot;正在执行复杂计算...&quot;);
    // 模拟复杂计算
    Thread.Sleep(1000);
    int sum = 0;
    for (int i = 0; i &amp;lt; 1000000; i++)
    {
        sum += i;
    }
    return sum;
});
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;&lt;strong&gt;示例：惰性计算&lt;/strong&gt;&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;// 使用Lazy&amp;lt;T&amp;gt;类结合Func实现惰性计算
Lazy&amp;lt;int&amp;gt; lazyValue = new Lazy&amp;lt;int&amp;gt;(() =&amp;gt;
{
    Console.WriteLine(&quot;正在计算惰性值...&quot;);
    // 模拟耗时计算
    Thread.Sleep(1500);
    return 42;
});

Console.WriteLine(&quot;程序启动&quot;);
Console.WriteLine(&quot;惰性值是否已创建: &quot; + lazyValue.IsValueCreated); // False

// 第一次访问Value属性时，才会执行Func
Console.WriteLine(&quot;获取惰性值: &quot; + lazyValue.Value); // 输出计算过程和结果42
Console.WriteLine(&quot;惰性值是否已创建: &quot; + lazyValue.IsValueCreated); // True

// 后续访问Value属性时，直接返回缓存的结果
Console.WriteLine(&quot;再次获取惰性值: &quot; + lazyValue.Value); // 直接输出42，不再计算
&lt;/code&gt;&lt;/pre&gt;
&lt;h4&gt;7. 函数式编程风格&lt;/h4&gt;
&lt;p&gt;&lt;code&gt;Func&lt;/code&gt;和&lt;code&gt;Action&lt;/code&gt;支持函数式编程风格，可以轻松实现高阶函数（接受函数作为参数或返回函数的函数）。&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;示例：函数组合&lt;/strong&gt;&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;// 定义几个简单的函数
Func&amp;lt;int, int&amp;gt; doubleIt = x =&amp;gt; x * 2;
Func&amp;lt;int, int&amp;gt; addFive = x =&amp;gt; x + 5;
Func&amp;lt;int, bool&amp;gt; isEven = x =&amp;gt; x % 2 == 0;

// 组合函数：先加倍，再加5
Func&amp;lt;int, int&amp;gt; doubleAndAddFive = x =&amp;gt; addFive(doubleIt(x));

// 组合函数：如果是偶数则加倍，否则加5
Func&amp;lt;int, int&amp;gt; processNumber = x =&amp;gt; isEven(x) ? doubleIt(x) : addFive(x);

// 调用示例
Console.WriteLine(doubleAndAddFive(10)); // 10 * 2 + 5 = 25
Console.WriteLine(processNumber(10)); // 10是偶数，加倍：20
Console.WriteLine(processNumber(11)); // 11是奇数，加5：16
&lt;/code&gt;&lt;/pre&gt;
&lt;h4&gt;8. 最佳实践&lt;/h4&gt;
&lt;ol&gt;
&lt;li&gt;
&lt;p&gt;&lt;strong&gt;选择合适的委托类型&lt;/strong&gt;：&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;当需要返回值时使用&lt;code&gt;Func&lt;/code&gt;&lt;/li&gt;
&lt;li&gt;当只需要执行操作时使用&lt;code&gt;Action&lt;/code&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;&lt;strong&gt;保持简洁&lt;/strong&gt;：&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;在Lambda表达式中尽量保持逻辑简单&lt;/li&gt;
&lt;li&gt;复杂逻辑应提取为命名函数&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;&lt;strong&gt;注意闭包陷阱&lt;/strong&gt;：&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;在循环中使用Lambda时，要注意变量捕获的时机&lt;/li&gt;
&lt;li&gt;尽量在循环内部创建变量的副本&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;&lt;strong&gt;性能考虑&lt;/strong&gt;：&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;避免在性能敏感的代码中创建过多临时Lambda表达式&lt;/li&gt;
&lt;li&gt;对于频繁调用的委托，可以考虑缓存&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;&lt;strong&gt;代码可读性&lt;/strong&gt;：&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;为Lambda表达式使用有意义的参数名称&lt;/li&gt;
&lt;li&gt;适当使用换行和缩进，提高代码可读性&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;/ol&gt;
&lt;p&gt;通过合理使用&lt;code&gt;Func&lt;/code&gt;和&lt;code&gt;Action&lt;/code&gt;委托，可以编写更加简洁、灵活和可维护的C#代码，充分发挥现代C#语言的优势。&lt;/p&gt;
&lt;h3&gt;默认参数&lt;/h3&gt;
&lt;p&gt;C#允许为函数参数指定默认值，这样在调用时可以省略这些参数。&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;public void PrintInfo(string name, int age = 18, string city = &quot;未知&quot;)
{
    Console.WriteLine($&quot;姓名: {name}, 年龄: {age}, 城市: {city}&quot;);
}

// 调用示例
PrintInfo(&quot;张三&quot;);                    // 姓名: 张三, 年龄: 18, 城市: 未知
PrintInfo(&quot;李四&quot;, 25);               // 姓名: 李四, 年龄: 25, 城市: 未知
PrintInfo(&quot;王五&quot;, 30, &quot;北京&quot;);       // 姓名: 王五, 年龄: 30, 城市: 北京
&lt;/code&gt;&lt;/pre&gt;
&lt;h3&gt;命名参数&lt;/h3&gt;
&lt;p&gt;调用函数时可以使用命名参数，提高代码的可读性。&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;public void CreateUser(string name, int age, string email, bool isActive = true)
{
    Console.WriteLine($&quot;创建用户: {name}, {age}岁, 邮箱: {email}, 激活: {isActive}&quot;);
}

// 调用示例（使用命名参数）
CreateUser(name: &quot;张三&quot;, age: 25, email: &quot;zhangsan@example.com&quot;);
// 输出: 创建用户: 张三, 25岁, 邮箱: zhangsan@example.com, 激活: True

CreateUser(email: &quot;lisi@example.com&quot;, name: &quot;李四&quot;, age: 30);
// 输出: 创建用户: 李四, 30岁, 邮箱: lisi@example.com, 激活: True
&lt;/code&gt;&lt;/pre&gt;
&lt;h3&gt;递归函数&lt;/h3&gt;
&lt;p&gt;函数可以调用自身，这称为递归。递归在解决某些问题时非常有用。&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;// 计算阶乘的递归函数
public int Factorial(int n)
{
    if (n &amp;lt;= 1)
        return 1;
    else
        return n * Factorial(n - 1);
}

// 计算斐波那契数列的递归函数
public int Fibonacci(int n)
{
    if (n &amp;lt;= 1)
        return n;
    else
        return Fibonacci(n - 1) + Fibonacci(n - 2);
}

// 调用示例
Console.WriteLine($&quot;5的阶乘: {Factorial(5)}&quot;);     // 120
Console.WriteLine($&quot;第10个斐波那契数: {Fibonacci(10)}&quot;); // 55
&lt;/code&gt;&lt;/pre&gt;
&lt;h3&gt;Lambda表达式&lt;/h3&gt;
&lt;p&gt;Lambda表达式是一种简洁的函数定义方式，常用于简化代码。&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;// 传统函数定义
public int Add(int a, int b)
{
    return a + b;
}

// 等价的Lambda表达式
Func&amp;lt;int, int, int&amp;gt; add = (a, b) =&amp;gt; a + b;

// 使用Lambda表达式
int result = add(5, 3); // 8
Console.WriteLine($&quot;Lambda计算结果: {result}&quot;); // 输出: Lambda计算结果: 8

// Lambda表达式用于集合操作
List&amp;lt;int&amp;gt; numbers = new List&amp;lt;int&amp;gt; { 1, 2, 3, 4, 5 };
List&amp;lt;int&amp;gt; evenNumbers = numbers.Where(n =&amp;gt; n % 2 == 0).ToList(); // [2, 4]
int sum = numbers.Sum(n =&amp;gt; n); // 15
Console.WriteLine($&quot;偶数: [{string.Join(&quot;, &quot;, evenNumbers)}]&quot;); // 输出: 偶数: [2, 4]
Console.WriteLine($&quot;总和: {sum}&quot;); // 输出: 总和: 15
&lt;/code&gt;&lt;/pre&gt;
&lt;h3&gt;局部函数&lt;/h3&gt;
&lt;p&gt;C# 7.0引入了局部函数，允许在函数内部定义函数。&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;public void ProcessData()
{
    // 局部函数
    int Square(int x) =&amp;gt; x * x;
    
    int Add(int a, int b) =&amp;gt; a + b;
    
    // 使用局部函数
    int result = Add(Square(3), Square(4));
    Console.WriteLine($&quot;结果: {result}&quot;); // 结果: 25
}
&lt;/code&gt;&lt;/pre&gt;
&lt;h3&gt;函数最佳实践&lt;/h3&gt;
&lt;h4&gt;1. 函数应该具有单一职责&lt;/h4&gt;
&lt;pre&gt;&lt;code&gt;// 不好的做法 - 一个函数做多件事情
public void ProcessOrder(Order order)
{
    // 验证订单
    if (order.Items.Count == 0)
        throw new Exception(&quot;订单不能为空&quot;);
    
    // 计算总价
    decimal total = 0;
    foreach (var item in order.Items)
    {
        total += item.Price * item.Quantity;
    }
    
    // 保存订单
    SaveOrder(order);
}

// 好的做法 - 拆分为多个函数
public void ProcessOrder(Order order)
{
    ValidateOrder(order);
    CalculateTotal(order);
    SaveOrder(order);
}

private void ValidateOrder(Order order)
{
    if (order.Items.Count == 0)
        throw new Exception(&quot;订单不能为空&quot;);
}

private void CalculateTotal(Order order)
{
    order.Total = order.Items.Sum(item =&amp;gt; item.Price * item.Quantity);
}

private void SaveOrder(Order order)
{
    // 保存订单逻辑
}
&lt;/code&gt;&lt;/pre&gt;
&lt;h4&gt;2. 函数应该具有良好的命名&lt;/h4&gt;
&lt;pre&gt;&lt;code&gt;// 不好的命名
public bool Check(int x)
{
    return x &amp;gt; 0;
}

// 好的命名
public bool IsPositive(int number)
{
    return number &amp;gt; 0;
}
&lt;/code&gt;&lt;/pre&gt;
&lt;h4&gt;3. 函数应该尽量短小&lt;/h4&gt;
&lt;pre&gt;&lt;code&gt;// 过长的函数
public void ProcessUserData(User user)
{
    // 验证用户数据
    if (user == null)
        throw new ArgumentNullException(nameof(user));
    
    if (string.IsNullOrEmpty(user.Name))
        throw new ArgumentException(&quot;用户名不能为空&quot;);
    
    if (user.Age &amp;lt; 0 || user.Age &amp;gt; 150)
        throw new ArgumentException(&quot;年龄必须在0-150之间&quot;);
    
    // 格式化用户数据
    user.Name = user.Name.Trim();
    user.Email = user.Email?.ToLower();
    
    // 保存用户数据
    userRepository.Save(user);
    
    // 发送欢迎邮件
    if (!string.IsNullOrEmpty(user.Email))
    {
        emailService.SendWelcomeEmail(user.Email);
    }
    
    // 记录日志
    logger.Info($&quot;用户 {user.Name} 已创建&quot;);
}

// 拆分为多个小函数
public void ProcessUserData(User user)
{
    ValidateUser(user);
    FormatUserData(user);
    SaveUser(user);
    SendWelcomeEmail(user);
    LogUserCreation(user);
}

private void ValidateUser(User user)
{
    if (user == null)
        throw new ArgumentNullException(nameof(user));
    
    if (string.IsNullOrEmpty(user.Name))
        throw new ArgumentException(&quot;用户名不能为空&quot;);
    
    if (user.Age &amp;lt; 0 || user.Age &amp;gt; 150)
        throw new ArgumentException(&quot;年龄必须在0-150之间&quot;);
}

private void FormatUserData(User user)
{
    user.Name = user.Name.Trim();
    user.Email = user.Email?.ToLower();
}

private void SaveUser(User user)
{
    userRepository.Save(user);
}

private void SendWelcomeEmail(User user)
{
    if (!string.IsNullOrEmpty(user.Email))
    {
        emailService.SendWelcomeEmail(user.Email);
    }
}

private void LogUserCreation(User user)
{
    logger.Info($&quot;用户 {user.Name} 已创建&quot;);
}
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;函数是C#编程的基础构建块，合理使用函数可以让代码更加模块化、可重用和易于维护。通过掌握函数的各种特性和最佳实践，可以编写出高质量的C#程序。&lt;/p&gt;
&lt;h2&gt;&amp;lt;a id=&quot;class-detail&quot;&amp;gt;&amp;lt;/a&amp;gt;C#类详解&lt;/h2&gt;
&lt;p&gt;类（Class）是C#面向对象编程的核心概念，是一种用户定义的数据类型，封装了数据（字段）和操作这些数据的方法。通过类，可以创建对象（实例），实现数据的封装、继承和多态等面向对象特性。&lt;/p&gt;
&lt;h3&gt;&amp;lt;a id=&quot;class-definition&quot;&amp;gt;&amp;lt;/a&amp;gt;类的定义与结构&lt;/h3&gt;
&lt;p&gt;在C#中，类使用&lt;code&gt;class&lt;/code&gt;关键字定义，包含字段、属性、方法、构造函数、事件等成员。&lt;/p&gt;
&lt;h4&gt;类的基本语法&lt;/h4&gt;
&lt;pre&gt;&lt;code&gt;// 命名空间声明
namespace MyNamespace
{
    // 类的定义
    public class Person
    {
        // 字段（成员变量）
        private string _name;
        private int _age;
        
        // 构造函数
        public Person(string name, int age)
        {
            _name = name;
            _age = age;
        }
        
        // 方法
        public void Greet()
        {
            Console.WriteLine($&quot;Hello, my name is {_name} and I&apos;m {_age} years old.&quot;);
        }
    }
}
&lt;/code&gt;&lt;/pre&gt;
&lt;h4&gt;类的访问修饰符&lt;/h4&gt;
&lt;p&gt;类和类成员可以使用访问修饰符控制其可见性：&lt;/p&gt;
&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;修饰符&lt;/th&gt;
&lt;th&gt;说明&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;public&lt;/td&gt;
&lt;td&gt;公共的，可以在任何地方访问&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;private&lt;/td&gt;
&lt;td&gt;私有的，只能在类内部访问&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;protected&lt;/td&gt;
&lt;td&gt;受保护的，只能在类内部和派生类中访问&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;internal&lt;/td&gt;
&lt;td&gt;内部的，只能在同一程序集中访问&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;protected internal&lt;/td&gt;
&lt;td&gt;受保护内部的，只能在同一程序集或派生类中访问&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;private protected&lt;/td&gt;
&lt;td&gt;私有受保护的，只能在同一程序集的派生类中访问（C# 7.2+）&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;
&lt;h4&gt;创建和使用对象&lt;/h4&gt;
&lt;pre&gt;&lt;code&gt;// 创建对象
Person person1 = new Person(&quot;张三&quot;, 25);

// 调用方法
person1.Greet(); // 输出: Hello, my name is 张三 and I&apos;m 25 years old.

// 使用对象初始化器（C# 3.0+）
Person person2 = new Person(&quot;李四&quot;, 30)
{
    // 可以在这里设置属性
};
&lt;/code&gt;&lt;/pre&gt;
&lt;h3&gt;&amp;lt;a id=&quot;properties-getset&quot;&amp;gt;&amp;lt;/a&amp;gt;属性与访问器（get/set）&lt;/h3&gt;
&lt;p&gt;属性是类的成员，用于封装字段，提供对字段的安全访问。属性通过访问器（getter和setter）控制如何获取和设置字段的值。&lt;/p&gt;
&lt;h4&gt;属性的基本语法&lt;/h4&gt;
&lt;pre&gt;&lt;code&gt;public class Person
{
    // 私有字段
    private string _name;
    private int _age;
    
    // 公共属性
    public string Name
    {
        get { return _name; }  // getter
        set { _name = value; } // setter
    }
    
    public int Age
    {
        get { return _age; }
        set
        {
            // 在setter中可以添加验证逻辑
            if (value &amp;gt; 0 &amp;amp;&amp;amp; value &amp;lt; 150)
            {
                _age = value;
            }
            else
            {
                throw new ArgumentOutOfRangeException(&quot;年龄必须在0到150之间&quot;);
            }
        }
    }
}
&lt;/code&gt;&lt;/pre&gt;
&lt;h4&gt;自动属性（Auto-implemented Properties）&lt;/h4&gt;
&lt;p&gt;C# 3.0引入了自动属性，简化了属性的声明：&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;public class Person
{
    // 自动属性 - 编译器会自动生成私有字段
    public string Name { get; set; }
    public int Age { get; set; }
    
    // 只读自动属性
    public DateTime CreatedAt { get; private set; }
    
    // 构造函数
    public Person()
    {
        CreatedAt = DateTime.Now;
    }
}
&lt;/code&gt;&lt;/pre&gt;
&lt;h4&gt;只读和只写属性&lt;/h4&gt;
&lt;pre&gt;&lt;code&gt;public class Person
{
    private string _ssn; // 社会安全号码，只能设置一次
    private string _id;   // 唯一标识符，只能获取
    
    // 只写属性（很少使用）
    public string SSN
    {
        set { _ssn = value; }
    }
    
    // 只读属性
    public string Id
    {
        get { return _id; }
    }
    
    public Person()
    {
        _id = Guid.NewGuid().ToString(); // 生成唯一ID
    }
}
&lt;/code&gt;&lt;/pre&gt;
&lt;h4&gt;属性的高级特性&lt;/h4&gt;
&lt;h5&gt;1. 计算属性&lt;/h5&gt;
&lt;p&gt;计算属性不存储数据，而是根据其他字段或属性的值计算结果：&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;public class Rectangle
{
    public double Width { get; set; }
    public double Height { get; set; }
    
    // 计算属性 - 面积
    public double Area
    {
        get { return Width * Height; }
    }
    
    // 计算属性 - 周长
    public double Perimeter
    {
        get { return 2 * (Width + Height); }
    }
}
&lt;/code&gt;&lt;/pre&gt;
&lt;h5&gt;2. 延迟加载属性&lt;/h5&gt;
&lt;p&gt;延迟加载属性在首次访问时才计算或加载数据：&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;public class Product
{
    private List&amp;lt;Review&amp;gt; _reviews;
    
    // 延迟加载的评论列表
    public List&amp;lt;Review&amp;gt; Reviews
    {
        get
        {
            if (_reviews == null)
            {
                // 首次访问时从数据库加载数据
                _reviews = LoadReviewsFromDatabase();
            }
            return _reviews;
        }
    }
    
    private List&amp;lt;Review&amp;gt; LoadReviewsFromDatabase()
    {
        // 模拟从数据库加载数据
        return new List&amp;lt;Review&amp;gt;
        {
            new Review { Rating = 5, Comment = &quot;优秀的产品&quot; },
            new Review { Rating = 4, Comment = &quot;良好的产品&quot; }
        };
    }
}
&lt;/code&gt;&lt;/pre&gt;
&lt;h5&gt;3. 表达式主体属性（Expression-bodied Properties）&lt;/h5&gt;
&lt;p&gt;C# 6.0引入了表达式主体属性，简化了属性的定义：&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;public class Rectangle
{
    public double Width { get; set; }
    public double Height { get; set; }
    
    // 表达式主体的计算属性
    public double Area =&amp;gt; Width * Height;
    public double Perimeter =&amp;gt; 2 * (Width + Height);
}
&lt;/code&gt;&lt;/pre&gt;
&lt;h5&gt;4. 索引器（Indexers）&lt;/h5&gt;
&lt;p&gt;索引器允许类或结构体像数组一样被索引访问：&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;public class EmployeeCollection
{
    private List&amp;lt;Employee&amp;gt; _employees = new List&amp;lt;Employee&amp;gt;();
    
    // 索引器 - 按ID访问员工
    public Employee this[int id]
    {
        get { return _employees.FirstOrDefault(e =&amp;gt; e.Id == id); }
    }
    
    // 索引器 - 按姓名访问员工
    public Employee this[string name]
    {
        get { return _employees.FirstOrDefault(e =&amp;gt; e.Name == name); }
    }
    
    public void Add(Employee employee)
    {
        _employees.Add(employee);
    }
}
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;使用索引器：&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;EmployeeCollection employees = new EmployeeCollection();
employees.Add(new Employee { Id = 1, Name = &quot;张三&quot; });
employees.Add(new Employee { Id = 2, Name = &quot;李四&quot; });

// 使用索引器访问
Employee employee1 = employees[1]; // 按ID访问
Employee employee2 = employees[&quot;李四&quot;]; // 按姓名访问
&lt;/code&gt;&lt;/pre&gt;
&lt;h3&gt;&amp;lt;a id=&quot;class-inheritance&quot;&amp;gt;&amp;lt;/a&amp;gt;类的继承与多态&lt;/h3&gt;
&lt;p&gt;继承是面向对象编程的重要特性，允许创建一个新类（派生类）继承现有类（基类）的属性和方法。&lt;/p&gt;
&lt;h4&gt;继承的基本语法&lt;/h4&gt;
&lt;pre&gt;&lt;code&gt;// 基类
public class Animal
{
    public string Name { get; set; }
    
    public Animal(string name)
    {
        Name = name;
    }
    
    // 虚方法 - 可以在派生类中重写
    public virtual void MakeSound()
    {
        Console.WriteLine($&quot;{Name} makes a sound.&quot;);
    }
}

// 派生类 - 使用冒号(:)继承基类
public class Dog : Animal
{
    public Dog(string name) : base(name) { }
    
    // 重写虚方法
    public override void MakeSound()
    {
        Console.WriteLine($&quot;{Name} barks: Woof! Woof!&quot;);
    }
}

// 另一个派生类
public class Cat : Animal
{
    public Cat(string name) : base(name) { }
    
    public override void MakeSound()
    {
        Console.WriteLine($&quot;{Name} meows: Meow! Meow!&quot;);
    }
}
&lt;/code&gt;&lt;/pre&gt;
&lt;h4&gt;多态的实现&lt;/h4&gt;
&lt;p&gt;多态允许使用基类类型的变量引用派生类的对象，并根据实际对象类型调用相应的方法：&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;// 创建对象
Animal myAnimal = new Animal(&quot;Generic Animal&quot;);
Animal myDog = new Dog(&quot;Buddy&quot;);
Animal myCat = new Cat(&quot;Whiskers&quot;);

// 调用方法 - 多态行为
myAnimal.MakeSound(); // 输出: Generic Animal makes a sound.
myDog.MakeSound();    // 输出: Buddy barks: Woof! Woof!
myCat.MakeSound();    // 输出: Whiskers meows: Meow! Meow!

// 类型转换
if (myDog is Dog dog)
{
    // 安全转换为Dog类型
    dog.WagTail(); // 调用Dog类特有的方法
}

Dog anotherDog = myDog as Dog; // 另一种安全转换方式
if (anotherDog != null)
{
    anotherDog.WagTail();
}
&lt;/code&gt;&lt;/pre&gt;
&lt;h4&gt;密封类（Sealed Classes）&lt;/h4&gt;
&lt;p&gt;密封类使用&lt;code&gt;sealed&lt;/code&gt;关键字修饰，不能被继承：&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;public sealed class FinalClass
{
    // 类的成员
}

// 错误：不能继承密封类
// public class DerivedClass : FinalClass { }
&lt;/code&gt;&lt;/pre&gt;
&lt;h3&gt;&amp;lt;a id=&quot;abstract-interface&quot;&amp;gt;&amp;lt;/a&amp;gt;抽象类与接口&lt;/h3&gt;
&lt;h4&gt;抽象类&lt;/h4&gt;
&lt;p&gt;抽象类使用&lt;code&gt;abstract&lt;/code&gt;关键字修饰，不能直接实例化，通常作为基类使用。抽象类可以包含抽象方法（没有实现的方法），派生类必须实现这些方法。&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;// 抽象基类
public abstract class Shape
{
    public string Name { get; set; }
    
    public Shape(string name)
    {
        Name = name;
    }
    
    // 抽象方法 - 必须在派生类中实现
    public abstract double CalculateArea();
    
    // 普通方法
    public void Display()
    {
        Console.WriteLine($&quot;Shape: {Name}, Area: {CalculateArea()}&quot;);
    }
}

// 派生类实现抽象方法
public class Circle : Shape
{
    public double Radius { get; set; }
    
    public Circle(string name, double radius) : base(name)
    {
        Radius = radius;
    }
    
    public override double CalculateArea()
    {
        return Math.PI * Radius * Radius;
    }
}

public class Rectangle : Shape
{
    public double Width { get; set; }
    public double Height { get; set; }
    
    public Rectangle(string name, double width, double height) : base(name)
    {
        Width = width;
        Height = height;
    }
    
    public override double CalculateArea()
    {
        return Width * Height;
    }
}
&lt;/code&gt;&lt;/pre&gt;
&lt;h4&gt;接口&lt;/h4&gt;
&lt;p&gt;接口使用&lt;code&gt;interface&lt;/code&gt;关键字定义，是一种约定，指定了类或结构体必须实现的成员。接口只包含方法、属性、事件或索引器的声明，没有实现。&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;// 接口定义
public interface IDrawable
{
    // 接口方法声明
    void Draw();
    
    // 接口属性声明
    string Color { get; set; }
}

// 类实现接口
public class Circle : IDrawable
{
    public string Color { get; set; }
    public double Radius { get; set; }
    
    public Circle(string color, double radius)
    {
        Color = color;
        Radius = radius;
    }
    
    // 实现接口方法
    public void Draw()
    {
        Console.WriteLine($&quot;Drawing a {Color} circle with radius {Radius}&quot;);
    }
}

// 结构体实现接口
public struct Rectangle : IDrawable
{
    public string Color { get; set; }
    public double Width { get; set; }
    public double Height { get; set; }
    
    public Rectangle(string color, double width, double height)
    {
        Color = color;
        Width = width;
        Height = height;
    }
    
    public void Draw()
    {
        Console.WriteLine($&quot;Drawing a {Color} rectangle with width {Width} and height {Height}&quot;);
    }
}
&lt;/code&gt;&lt;/pre&gt;
&lt;h4&gt;接口与抽象类的区别&lt;/h4&gt;
&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;特性&lt;/th&gt;
&lt;th&gt;抽象类&lt;/th&gt;
&lt;th&gt;接口&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;实现&lt;/td&gt;
&lt;td&gt;可以包含实现&lt;/td&gt;
&lt;td&gt;不能包含实现（C# 8.0以前）&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;构造函数&lt;/td&gt;
&lt;td&gt;可以有构造函数&lt;/td&gt;
&lt;td&gt;不能有构造函数&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;字段&lt;/td&gt;
&lt;td&gt;可以包含字段&lt;/td&gt;
&lt;td&gt;不能包含字段&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;继承&lt;/td&gt;
&lt;td&gt;只能继承一个抽象类&lt;/td&gt;
&lt;td&gt;可以实现多个接口&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;访问修饰符&lt;/td&gt;
&lt;td&gt;可以使用任何访问修饰符&lt;/td&gt;
&lt;td&gt;成员默认为public（不能显式指定）&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;版本控制&lt;/td&gt;
&lt;td&gt;难以扩展（添加新成员会破坏现有实现）&lt;/td&gt;
&lt;td&gt;易于扩展（C# 8.0支持默认实现）&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;
&lt;h3&gt;&amp;lt;a id=&quot;static-class&quot;&amp;gt;&amp;lt;/a&amp;gt;静态类&lt;/h3&gt;
&lt;p&gt;静态类使用&lt;code&gt;static&lt;/code&gt;关键字修饰，不能实例化，所有成员都是静态的。静态类通常用于包含工具方法或扩展方法。&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;public static class MathUtilities
{
    // 静态字段
    public static readonly double PI = 3.141592653589793;
    
    // 静态方法
    public static double CalculateCircleArea(double radius)
    {
        return PI * radius * radius;
    }
    
    public static double CalculateRectangleArea(double width, double height)
    {
        return width * height;
    }
}

// 使用静态类
double circleArea = MathUtilities.CalculateCircleArea(5);
double rectangleArea = MathUtilities.CalculateRectangleArea(4, 6);
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;静态类的特点：&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;不能使用&lt;code&gt;new&lt;/code&gt;关键字实例化&lt;/li&gt;
&lt;li&gt;所有成员（字段、属性、方法、事件等）都必须是静态的&lt;/li&gt;
&lt;li&gt;不能包含实例构造函数，但可以包含静态构造函数&lt;/li&gt;
&lt;li&gt;不能被继承（隐式密封）&lt;/li&gt;
&lt;li&gt;不能实现接口&lt;/li&gt;
&lt;li&gt;只能访问静态成员&lt;/li&gt;
&lt;/ol&gt;
&lt;p&gt;静态类的适用场景：&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;提供工具方法和辅助函数&lt;/li&gt;
&lt;li&gt;定义扩展方法&lt;/li&gt;
&lt;li&gt;封装常量和只读字段&lt;/li&gt;
&lt;li&gt;实现无状态的功能模块&lt;/li&gt;
&lt;/ol&gt;
&lt;h3&gt;类的最佳实践&lt;/h3&gt;
&lt;ol&gt;
&lt;li&gt;&lt;strong&gt;封装数据&lt;/strong&gt;：使用属性而不是公共字段来封装数据&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;单一职责原则&lt;/strong&gt;：一个类应该只有一个改变的理由&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;继承层次简洁&lt;/strong&gt;：避免过深的继承层次（一般不超过3-4层）&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;接口优先&lt;/strong&gt;：优先使用接口而不是抽象类，提高灵活性&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;命名规范&lt;/strong&gt;：
&lt;ul&gt;
&lt;li&gt;类名使用PascalCase（首字母大写）&lt;/li&gt;
&lt;li&gt;字段名使用驼峰命名法，私有字段前缀加下划线&lt;/li&gt;
&lt;li&gt;方法名使用PascalCase&lt;/li&gt;
&lt;li&gt;属性名使用PascalCase&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;使用自动属性&lt;/strong&gt;：对于简单的属性，使用自动属性提高代码简洁性&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;实现IDisposable接口&lt;/strong&gt;：对于使用非托管资源的类，实现IDisposable接口进行资源清理&lt;/li&gt;
&lt;/ol&gt;
&lt;p&gt;类是C#面向对象编程的核心，通过掌握类的各种特性和最佳实践，可以编写出结构清晰、易于维护和扩展的高质量C#程序。&lt;/p&gt;
&lt;h2&gt;&amp;lt;a id=&quot;class-advanced&quot;&amp;gt;&amp;lt;/a&amp;gt;C#类详解（进阶篇）&lt;/h2&gt;
&lt;h3&gt;1. 类的定义与基本结构&lt;/h3&gt;
&lt;p&gt;C#中的类是引用类型，是面向对象编程的基本构建块。类定义了对象的数据结构和行为。&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;[访问修饰符] [修饰符] class 类名 [: 父类, 接口1, 接口2...]
{
    // 成员定义
}
&lt;/code&gt;&lt;/pre&gt;
&lt;h4&gt;1.1 类的访问修饰符&lt;/h4&gt;
&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;访问修饰符&lt;/th&gt;
&lt;th&gt;可见范围&lt;/th&gt;
&lt;th&gt;适用场景&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;public&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;任何位置均可访问&lt;/td&gt;
&lt;td&gt;公共接口、API类&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;internal&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;当前程序集内可访问&lt;/td&gt;
&lt;td&gt;内部工具类、组件间通信&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;protected&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;类内部和派生类中可访问&lt;/td&gt;
&lt;td&gt;需被子类继承的基类&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;private&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;仅类内部可访问&lt;/td&gt;
&lt;td&gt;类的私有实现细节&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;protected internal&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;程序集内或派生类中可访问&lt;/td&gt;
&lt;td&gt;程序集内的继承体系&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;private protected&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;同一程序集中的派生类可访问&lt;/td&gt;
&lt;td&gt;严格控制的继承层次&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;
&lt;h4&gt;1.2 类的修饰符&lt;/h4&gt;
&lt;ul&gt;
&lt;li&gt;&lt;code&gt;abstract&lt;/code&gt;: 抽象类，不能实例化，可包含抽象成员&lt;/li&gt;
&lt;li&gt;&lt;code&gt;sealed&lt;/code&gt;: 密封类，不能被继承&lt;/li&gt;
&lt;li&gt;&lt;code&gt;static&lt;/code&gt;: 静态类，不能实例化，所有成员必须是静态的&lt;/li&gt;
&lt;li&gt;&lt;code&gt;partial&lt;/code&gt;: 分部类，可以将类定义分散到多个文件中&lt;/li&gt;
&lt;li&gt;&lt;code&gt;unsafe&lt;/code&gt;: 不安全代码类，允许使用指针&lt;/li&gt;
&lt;/ul&gt;
&lt;h3&gt;2. 构造函数详解&lt;/h3&gt;
&lt;p&gt;构造函数是创建对象时自动调用的特殊方法，用于初始化对象状态。&lt;/p&gt;
&lt;h4&gt;2.1 构造函数类型&lt;/h4&gt;
&lt;pre&gt;&lt;code&gt;public class Person
{
    public string Name { get; set; }
    public int Age { get; set; }
    public string Email { get; set; }
    
    // 默认构造函数
    public Person()
    {
        Name = &quot;Unknown&quot;;
        Age = 0;
    }
    
    // 参数化构造函数
    public Person(string name, int age)
    {
        Name = name;
        Age = age;
    }
    
    // 带可选参数的构造函数
    public Person(string name, int age, string email = null)
    {
        Name = name;
        Age = age;
        Email = email;
    }
    
    // 静态构造函数
    static Person()
    {
        // 初始化静态成员，只执行一次
    }
}
&lt;/code&gt;&lt;/pre&gt;
&lt;h4&gt;2.2 构造函数链&lt;/h4&gt;
&lt;p&gt;使用&lt;code&gt;this&lt;/code&gt;关键字链接同一类的构造函数：&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;public class Person
{
    public string Name { get; set; }
    public int Age { get; set; }
    public string Email { get; set; }
    
    public Person() : this(&quot;Unknown&quot;)
    {
    }
    
    public Person(string name) : this(name, 0)
    {
    }
    
    public Person(string name, int age) : this(name, age, null)
    {
    }
    
    public Person(string name, int age, string email)
    {
        Name = name;
        Age = age;
        Email = email;
    }
}
&lt;/code&gt;&lt;/pre&gt;
&lt;h4&gt;2.3 继承中的构造函数&lt;/h4&gt;
&lt;p&gt;使用&lt;code&gt;base&lt;/code&gt;关键字调用父类构造函数：&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;public class Employee : Person
{
    public string Department { get; set; }
    public decimal Salary { get; set; }
    
    public Employee(string name, int age, string department) : base(name, age)
    {
        Department = department;
    }
    
    public Employee(string name, int age, string email, string department, decimal salary)
        : base(name, age, email)
    {
        Department = department;
        Salary = salary;
    }
}
&lt;/code&gt;&lt;/pre&gt;
&lt;h3&gt;2.4 析构函数详解&lt;/h3&gt;
&lt;p&gt;析构函数（也称为终结器 Finalizer）用于在对象被垃圾回收前执行清理操作，主要用于释放非托管资源。&lt;/p&gt;
&lt;h4&gt;2.4.1 析构函数基本语法&lt;/h4&gt;
&lt;pre&gt;&lt;code&gt;~ClassName()
{
    // 清理资源的代码
}
&lt;/code&gt;&lt;/pre&gt;
&lt;h4&gt;2.4.2 析构函数的特点&lt;/h4&gt;
&lt;ul&gt;
&lt;li&gt;只能在类中定义，不能在结构中定义&lt;/li&gt;
&lt;li&gt;不能有访问修饰符、参数或返回值&lt;/li&gt;
&lt;li&gt;每个类最多只能有一个析构函数&lt;/li&gt;
&lt;li&gt;不能显式调用析构函数，由垃圾回收器自动调用&lt;/li&gt;
&lt;li&gt;不能继承或重载析构函数&lt;/li&gt;
&lt;/ul&gt;
&lt;h4&gt;2.4.3 析构函数的使用场景&lt;/h4&gt;
&lt;p&gt;析构函数主要用于释放以下类型的资源：&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;&lt;strong&gt;非托管资源&lt;/strong&gt;：如文件句柄、数据库连接、网络套接字等&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;COM对象引用&lt;/strong&gt;：需要释放的COM互操作引用&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;操作系统资源&lt;/strong&gt;：如窗口句柄、图形设备上下文等&lt;/li&gt;
&lt;/ol&gt;
&lt;h4&gt;2.4.4 析构函数示例&lt;/h4&gt;
&lt;pre&gt;&lt;code&gt;public class FileHandler
{
    private FileStream _fileStream;
    
    public FileHandler(string filePath)
    {
        _fileStream = new FileStream(filePath, FileMode.OpenOrCreate);
        Console.WriteLine(&quot;File opened&quot;);
    }
    
    public void WriteData(string data)
    {
        byte[] bytes = System.Text.Encoding.UTF8.GetBytes(data);
        _fileStream.Write(bytes, 0, bytes.Length);
    }
    
    // 析构函数
    ~FileHandler()
    {
        // 释放非托管资源
        if (_fileStream != null)
        {
            _fileStream.Dispose();
            _fileStream = null;
            Console.WriteLine(&quot;File resources released by destructor&quot;);
        }
    }
}
&lt;/code&gt;&lt;/pre&gt;
&lt;h4&gt;2.4.5 析构函数与IDisposable接口&lt;/h4&gt;
&lt;p&gt;对于需要明确控制资源释放的情况，建议实现IDisposable接口而不是仅依赖析构函数：&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;public class ResourceManager : IDisposable
{
    private bool _disposed = false;
    private FileStream _fileStream;
    
    public ResourceManager(string filePath)
    {
        _fileStream = new FileStream(filePath, FileMode.Open);
    }
    
    // 实现IDisposable接口
    public void Dispose()
    {
        Dispose(true);
        // 告诉垃圾回收器不要调用析构函数
        GC.SuppressFinalize(this);
    }
    
    protected virtual void Dispose(bool disposing)
    {
        if (!_disposed)
        {
            if (disposing)
            {
                // 释放托管资源
                if (_fileStream != null)
                {
                    _fileStream.Dispose();
                    _fileStream = null;
                }
            }
            
            // 释放非托管资源
            // ...
            
            _disposed = true;
        }
    }
    
    // 析构函数作为安全网
    ~ResourceManager()
    {
        Dispose(false);
    }
}
&lt;/code&gt;&lt;/pre&gt;
&lt;h4&gt;2.4.6 最佳实践&lt;/h4&gt;
&lt;ol&gt;
&lt;li&gt;&lt;strong&gt;优先使用IDisposable&lt;/strong&gt;：对于需要确定性释放资源的场景，始终实现IDisposable接口&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;析构函数作为安全网&lt;/strong&gt;：仅在必须处理非托管资源时使用析构函数作为最后的安全保障&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;避免长时间阻塞&lt;/strong&gt;：析构函数执行时间不应过长，否则会延迟垃圾回收过程&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;不要依赖执行时间&lt;/strong&gt;：不能确定析构函数何时会被调用，甚至可能不被调用&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;使用using语句&lt;/strong&gt;：对于实现IDisposable的对象，尽量使用using语句确保资源正确释放&lt;/li&gt;
&lt;/ol&gt;
&lt;pre&gt;&lt;code&gt;// 使用using语句自动调用Dispose
using (var manager = new ResourceManager(&quot;data.txt&quot;))
{
    // 使用资源
}
// 离开using块时自动调用manager.Dispose()
&lt;/code&gt;&lt;/pre&gt;
&lt;h3&gt;3. 成员函数深度解析&lt;/h3&gt;
&lt;h4&gt;3.1 方法签名与重载&lt;/h4&gt;
&lt;p&gt;方法签名由方法名、参数类型和参数数量组成，不包括返回类型和参数名：&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;public class Calculator
{
    // 重载示例
    public int Add(int a, int b) =&amp;gt; a + b;
    public double Add(double a, double b) =&amp;gt; a + b;
    public int Add(int a, int b, int c) =&amp;gt; a + b + c;
    
    // 参数数组（可变参数）
    public int Sum(params int[] numbers)
    {
        int total = 0;
        foreach (var num in numbers)
        {
            total += num;
        }
        return total;
    }
}
&lt;/code&gt;&lt;/pre&gt;
&lt;h4&gt;3.2 值参数、引用参数与输出参数&lt;/h4&gt;
&lt;pre&gt;&lt;code&gt;public class ParameterDemo
{
    // 值参数 - 传递副本
    public void ModifyValue(int value)
    {
        value = value * 2;
    }
    
    // 引用参数 - 使用ref关键字，传递引用
    public void ModifyRef(ref int value)
    {
        value = value * 2;
    }
    
    // 输出参数 - 使用out关键字，必须在方法中赋值
    public bool TryParse(string input, out int result)
    {
        return int.TryParse(input, out result);
    }
    
    // 输入参数 - 使用in关键字，防止修改
    public void ProcessReadOnly(in int value)
    {
        // value = 10; // 错误：无法修改in参数
    }
}
&lt;/code&gt;&lt;/pre&gt;
&lt;h3&gt;4. 属性深入理解&lt;/h3&gt;
&lt;h4&gt;4.1 属性类型详解&lt;/h4&gt;
&lt;pre&gt;&lt;code&gt;public class PropertyDemo
{
    // 私有字段
    private string _name;
    private int _age = 18; // 默认值
    private static string _companyName = &quot;XYZ Corp&quot;;
    
    // 完整属性（带字段）
    public string Name
    {
        get { return _name; }
        set 
        { 
            if (string.IsNullOrWhiteSpace(value))
                throw new ArgumentException(&quot;Name cannot be null or empty&quot;);
            _name = value; 
        }
    }
    
    // 自动属性
    public int Age { get; set; }
    
    // 只读属性（只有getter）
    public string ReadOnlyProperty { get; }
    
    // 只写属性（只有setter）
    public string WriteOnlyProperty { private get; set; }
    
    // 静态属性
    public static string CompanyName
    {
        get { return _companyName; }
        set { _companyName = value; }
    }
    
    // 计算属性
    public string FullName =&amp;gt; $&quot;{_name} from {_companyName}&quot;;
    
    // 延迟加载属性
    private Lazy&amp;lt;string&amp;gt; _lazyData = new Lazy&amp;lt;string&amp;gt;(() =&amp;gt; LoadExpensiveData());
    public string LazyData =&amp;gt; _lazyData.Value;
    
    private static string LoadExpensiveData()
    {
        // 模拟耗时操作
        Thread.Sleep(1000);
        return &quot;Expensive data loaded&quot;;
    }
}
&lt;/code&gt;&lt;/pre&gt;
&lt;h4&gt;4.2 属性访问器修饰符&lt;/h4&gt;
&lt;pre&gt;&lt;code&gt;public class AccessorDemo
{
    // getter和setter不同访问级别
    public string PublicName { get; private set; }
    public int ProtectedAge { get; protected set; }
    
    // 内部设置，公共获取
    public string InternalSetter { get; internal set; }
    
    // 构造函数中设置私有setter的属性
    public AccessorDemo(string name)
    {
        PublicName = name;
    }
}
&lt;/code&gt;&lt;/pre&gt;
&lt;h3&gt;5. 索引器（Indexers）&lt;/h3&gt;
&lt;p&gt;索引器允许类或结构的实例通过类似数组的语法进行访问。&lt;/p&gt;
&lt;h4&gt;5.1 基本索引器&lt;/h4&gt;
&lt;pre&gt;&lt;code&gt;public class StringCollection
{
    private List&amp;lt;string&amp;gt; _items = new List&amp;lt;string&amp;gt;();
    
    // 基本索引器
    public string this[int index]
    {
        get
        {
            if (index &amp;lt; 0 || index &amp;gt;= _items.Count)
                throw new IndexOutOfRangeException();
            return _items[index];
        }
        set
        {
            if (index &amp;lt; 0 || index &amp;gt;= _items.Count)
                throw new IndexOutOfRangeException();
            _items[index] = value;
        }
    }
    
    public void Add(string item)
    {
        _items.Add(item);
    }
    
    public int Count =&amp;gt; _items.Count;
}
&lt;/code&gt;&lt;/pre&gt;
&lt;h4&gt;5.2 多参数索引器&lt;/h4&gt;
&lt;pre&gt;&lt;code&gt;public class Matrix
{
    private double[,] _data;
    
    public Matrix(int rows, int cols)
    {
        _data = new double[rows, cols];
    }
    
    // 多参数索引器
    public double this[int row, int col]
    {
        get =&amp;gt; _data[row, col];
        set =&amp;gt; _data[row, col] = value;
    }
}
&lt;/code&gt;&lt;/pre&gt;
&lt;h4&gt;5.3 字符串索引器&lt;/h4&gt;
&lt;pre&gt;&lt;code&gt;public class Configuration
{
    private Dictionary&amp;lt;string, string&amp;gt; _settings = new Dictionary&amp;lt;string, string&amp;gt;();
    
    // 字符串索引器
    public string this[string key]
    {
        get
        {
            return _settings.TryGetValue(key, out string value) ? value : null;
        }
        set
        {
            _settings[key] = value;
        }
    }
}
&lt;/code&gt;&lt;/pre&gt;
&lt;h3&gt;6. 事件与委托&lt;/h3&gt;
&lt;h4&gt;6.1 委托基础&lt;/h4&gt;
&lt;p&gt;委托是引用类型，类似函数指针，用于封装方法：&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;// 委托声明
public delegate void MessageHandler(string message);
public delegate int Calculator(int a, int b);

public class DelegateDemo
{
    public void Run()
    {
        // 实例化委托
        MessageHandler handler1 = ShowMessage;
        handler1(&quot;Hello from delegate&quot;);
        
        // 多播委托
        MessageHandler handler2 = ShowMessage;
        handler2 += LogMessage;
        handler2(&quot;Multiple handlers&quot;);
        
        // 匿名方法
        Calculator add = delegate(int x, int y) { return x + y; };
        
        // Lambda表达式
        Calculator multiply = (x, y) =&amp;gt; x * y;
        
        Console.WriteLine($&quot;Add: {add(5, 3)}&quot;);
        Console.WriteLine($&quot;Multiply: {multiply(5, 3)}&quot;);
    }
    
    private void ShowMessage(string msg)
    {
        Console.WriteLine($&quot;Show: {msg}&quot;);
    }
    
    private void LogMessage(string msg)
    {
        Console.WriteLine($&quot;Log: {msg}&quot;);
    }
}
&lt;/code&gt;&lt;/pre&gt;
&lt;h4&gt;6.2 事件实现&lt;/h4&gt;
&lt;pre&gt;&lt;code&gt;public class EventPublisher
{
    // 使用EventHandler泛型委托
    public event EventHandler&amp;lt;DataChangedEventArgs&amp;gt; DataChanged;
    
    // 自定义事件参数
    public class DataChangedEventArgs : EventArgs
    {
        public string OldValue { get; }
        public string NewValue { get; }
        public DateTime Timestamp { get; }
        
        public DataChangedEventArgs(string oldValue, string newValue)
        {
            OldValue = oldValue;
            NewValue = newValue;
            Timestamp = DateTime.Now;
        }
    }
    
    private string _data;
    
    public string Data
    {
        get =&amp;gt; _data;
        set
        {
            if (_data != value)
            {
                string oldValue = _data;
                _data = value;
                // 触发事件
                OnDataChanged(new DataChangedEventArgs(oldValue, value));
            }
        }
    }
    
    // 事件触发方法（通常protected virtual）
    protected virtual void OnDataChanged(DataChangedEventArgs e)
    {
        // 线程安全的事件触发
        DataChanged?.Invoke(this, e);
    }
}

// 事件订阅者
public class EventSubscriber
{
    private string _name;
    
    public EventSubscriber(string name, EventPublisher publisher)
    {
        _name = name;
        // 订阅事件
        publisher.DataChanged += Publisher_DataChanged;
    }
    
    private void Publisher_DataChanged(object sender, EventPublisher.DataChangedEventArgs e)
    {
        Console.WriteLine($&quot;[{_name}] Data changed from &apos;{e.OldValue}&apos; to &apos;{e.NewValue}&apos; at {e.Timestamp}&quot;);
    }
    
    // 取消订阅
    public void Unsubscribe(EventPublisher publisher)
    {
        publisher.DataChanged -= Publisher_DataChanged;
    }
}
&lt;/code&gt;&lt;/pre&gt;
&lt;h3&gt;7. 静态类与静态成员&lt;/h3&gt;
&lt;h4&gt;7.1 静态类的使用&lt;/h4&gt;
&lt;pre&gt;&lt;code&gt;public static class StringExtensions
{
    // 静态字段
    public static readonly string DefaultSeparator = &quot;, &quot;;
    
    // 静态属性
    public static bool IsNullOrEmpty =&amp;gt; string.IsNullOrEmpty(value);
    
    // 静态方法
    public static string Truncate(this string value, int maxLength)
    {
        if (string.IsNullOrEmpty(value))
            return value;
            
        return value.Length &amp;lt;= maxLength ? value : value.Substring(0, maxLength) + &quot;...&quot;;
    }
    
    public static string Join&amp;lt;T&amp;gt;(IEnumerable&amp;lt;T&amp;gt; items, string separator = null)
    {
        separator ??= DefaultSeparator;
        return string.Join(separator, items);
    }
    
    // 静态构造函数
    static StringExtensions()
    {
        // 初始化静态成员
    }
}
&lt;/code&gt;&lt;/pre&gt;
&lt;h4&gt;7.2 静态类与单例模式&lt;/h4&gt;
&lt;pre&gt;&lt;code&gt;// 单例模式实现
public sealed class Singleton
{
    // 私有构造函数
    private Singleton() { }
    
    // 懒加载单例
    private static readonly Lazy&amp;lt;Singleton&amp;gt; _instance = new Lazy&amp;lt;Singleton&amp;gt;(() =&amp;gt; new Singleton());
    
    // 公共访问点
    public static Singleton Instance =&amp;gt; _instance.Value;
    
    // 实例成员
    public void DoSomething() { }
}
&lt;/code&gt;&lt;/pre&gt;
&lt;h3&gt;8. 继承与多态高级特性&lt;/h3&gt;
&lt;h4&gt;8.1 方法重写与隐藏&lt;/h4&gt;
&lt;pre&gt;&lt;code&gt;public class Shape
{
    public virtual void Draw()
    {
        Console.WriteLine(&quot;Drawing a shape&quot;);
    }
    
    public void Identify()
    {
        Console.WriteLine(&quot;This is a shape&quot;);
    }
}

public class Circle : Shape
{
    public override void Draw()
    {
        Console.WriteLine(&quot;Drawing a circle&quot;);
    }
    
    // 隐藏基类方法（使用new关键字）
    public new void Identify()
    {
        Console.WriteLine(&quot;This is a circle&quot;);
    }
}
&lt;/code&gt;&lt;/pre&gt;
&lt;h4&gt;8.2 密封方法与类&lt;/h4&gt;
&lt;pre&gt;&lt;code&gt;public class BaseClass
{
    public virtual void Method1() { }
    public virtual void Method2() { }
}

public class DerivedClass : BaseClass
{
    // 密封方法，防止进一步重写
    public sealed override void Method1() { }
    
    public override void Method2() { }
}

// 密封类，不能被继承
public sealed class FinalClass : DerivedClass
{
    // 可以重写Method2，因为它不是密封的
    public override void Method2() { }
    
    // 不能重写Method1，因为它被密封了
}
&lt;/code&gt;&lt;/pre&gt;
&lt;h4&gt;8.3 类型转换与is/as运算符&lt;/h4&gt;
&lt;pre&gt;&lt;code&gt;public class TypeConversionDemo
{
    public void ProcessObject(object obj)
    {
        // is运算符 - 检查类型兼容性
        if (obj is string str)
        {
            Console.WriteLine($&quot;String length: {str.Length}&quot;);
        }
        else if (obj is int number)
        {
            Console.WriteLine($&quot;Number squared: {number * number}&quot;);
        }
        
        // as运算符 - 安全转换，失败返回null
        string text = obj as string;
        if (text != null)
        {
            Console.WriteLine($&quot;Text: {text}&quot;);
        }
        
        // 显式类型转换（可能抛出异常）
        try
        {
            int num = (int)obj;
        }
        catch (InvalidCastException)
        {
            Console.WriteLine(&quot;Invalid cast&quot;);
        }
    }
}
&lt;/code&gt;&lt;/pre&gt;
&lt;h3&gt;9. 特殊类与结构体&lt;/h3&gt;
&lt;h4&gt;9.1 分部类&lt;/h4&gt;
&lt;p&gt;分部类允许将一个类的定义分散到多个文件中：&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;// 文件1: Person.cs
public partial class Person
{
    public string Name { get; set; }
    public int Age { get; set; }
    
    public void Introduce()
    {
        Console.WriteLine($&quot;Hi, I&apos;m {Name}, {Age} years old.&quot;);
    }
}

// 文件2: Person.Contact.cs
public partial class Person
{
    public string Email { get; set; }
    public string Phone { get; set; }
    
    public void Contact()
    {
        Console.WriteLine($&quot;Email: {Email}, Phone: {Phone}&quot;);
    }
}
&lt;/code&gt;&lt;/pre&gt;
&lt;h4&gt;9.2 抽象类&lt;/h4&gt;
&lt;p&gt;抽象类不能实例化，用于定义接口和部分实现：&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;public abstract class Document
{
    public string Title { get; set; }
    public DateTime CreatedDate { get; } = DateTime.Now;
    
    // 普通方法
    public void Save()
    {
        Console.WriteLine($&quot;Saving document: {Title}&quot;);
    }
    
    // 抽象方法（必须在派生类中实现）
    public abstract void Print();
    
    // 抽象属性
    public abstract string Content { get; set; }
}

public class Report : Document
{
    public override string Content { get; set; }
    
    public override void Print()
    {
        Console.WriteLine($&quot;Printing report: {Title}\n{Content}&quot;);
    }
}
&lt;/code&gt;&lt;/pre&gt;
&lt;h3&gt;10. 接口深度解析&lt;/h3&gt;
&lt;h4&gt;10.1 接口定义与实现&lt;/h4&gt;
&lt;pre&gt;&lt;code&gt;// 基本接口
public interface ILogger
{
    void Log(string message);
    void LogError(string message, Exception exception = null);
}

// 泛型接口
public interface IRepository&amp;lt;T&amp;gt; where T : class
{
    T GetById(int id);
    IEnumerable&amp;lt;T&amp;gt; GetAll();
    void Add(T entity);
    void Update(T entity);
    void Delete(int id);
}

// 接口继承
public interface IAdvancedLogger : ILogger
{
    void LogWarning(string message);
    void LogInfo(string message);
    void SetLogLevel(LogLevel level);
}

// 枚举用于接口参数
public enum LogLevel { Debug, Info, Warning, Error, Fatal }
&lt;/code&gt;&lt;/pre&gt;
&lt;h4&gt;10.2 接口实现示例&lt;/h4&gt;
&lt;pre&gt;&lt;code&gt;// 文件日志实现
public class FileLogger : IAdvancedLogger
{
    private string _logFilePath;
    private LogLevel _currentLevel = LogLevel.Info;
    
    public FileLogger(string logFilePath)
    {
        _logFilePath = logFilePath;
    }
    
    public void Log(string message)
    {
        WriteToFile($&quot;[LOG] {DateTime.Now}: {message}&quot;);
    }
    
    public void LogError(string message, Exception exception = null)
    {
        if (_currentLevel &amp;lt;= LogLevel.Error)
        {
            string errorMsg = exception != null ? 
                $&quot;[ERROR] {DateTime.Now}: {message}\nException: {exception}&quot; : 
                $&quot;[ERROR] {DateTime.Now}: {message}&quot;;
            WriteToFile(errorMsg);
        }
    }
    
    public void LogWarning(string message)
    {
        if (_currentLevel &amp;lt;= LogLevel.Warning)
        {
            WriteToFile($&quot;[WARNING] {DateTime.Now}: {message}&quot;);
        }
    }
    
    public void LogInfo(string message)
    {
        if (_currentLevel &amp;lt;= LogLevel.Info)
        {
            WriteToFile($&quot;[INFO] {DateTime.Now}: {message}&quot;);
        }
    }
    
    public void SetLogLevel(LogLevel level)
    {
        _currentLevel = level;
    }
    
    private void WriteToFile(string message)
    {
        // 文件写入实现...
    }
}
&lt;/code&gt;&lt;/pre&gt;
&lt;h3&gt;11. 类的生命周期管理&lt;/h3&gt;
&lt;h4&gt;11.1 构造函数与初始化顺序&lt;/h4&gt;
&lt;p&gt;C#对象创建过程的初始化顺序：&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;静态字段初始化&lt;/li&gt;
&lt;li&gt;静态构造函数执行&lt;/li&gt;
&lt;li&gt;实例字段初始化&lt;/li&gt;
&lt;li&gt;基类实例构造函数执行（如有继承）&lt;/li&gt;
&lt;li&gt;派生类实例构造函数执行&lt;/li&gt;
&lt;/ol&gt;
&lt;pre&gt;&lt;code&gt;public class BaseClass
{
    // 静态字段
    public static int StaticBaseField = InitializeStaticField();
    
    // 实例字段
    public int InstanceBaseField = InitializeInstanceField();
    
    static BaseClass()
    {
        Console.WriteLine(&quot;BaseClass static constructor&quot;);
    }
    
    public BaseClass()
    {
        Console.WriteLine(&quot;BaseClass instance constructor&quot;);
    }
    
    private static int InitializeStaticField()
    {
        Console.WriteLine(&quot;Initializing BaseClass static field&quot;);
        return 10;
    }
    
    private int InitializeInstanceField()
    {
        Console.WriteLine(&quot;Initializing BaseClass instance field&quot;);
        return 20;
    }
}
&lt;/code&gt;&lt;/pre&gt;
&lt;h4&gt;11.2 析构函数与资源管理&lt;/h4&gt;
&lt;pre&gt;&lt;code&gt;public class ResourceManager : IDisposable
{
    // 托管资源
    private StreamWriter _writer;
    
    // 非托管资源（示例）
    private bool _disposed = false;
    
    public ResourceManager(string filePath)
    {
        _writer = new StreamWriter(filePath);
    }
    
    // 使用资源
    public void WriteData(string data)
    {
        if (_disposed)
            throw new ObjectDisposedException(&quot;ResourceManager&quot;);
            
        _writer.WriteLine(data);
    }
    
    // 实现IDisposable接口
    public void Dispose()
    {
        Dispose(true);
        // 告诉垃圾回收器不要调用析构函数
        GC.SuppressFinalize(this);
    }
    
    // 受保护的Dispose方法
    protected virtual void Dispose(bool disposing)
    {
        if (!_disposed)
        {
            // 释放托管资源
            if (disposing &amp;amp;&amp;amp; _writer != null)
            {
                _writer.Dispose();
                _writer = null;
            }
            
            // 释放非托管资源
            // ...
            
            _disposed = true;
        }
    }
    
    // 析构函数（Finalizer）
    ~ResourceManager()
    {
        Dispose(false);
    }
}

// 使用using语句自动调用Dispose
public void UseResourceManager()
{
    using (var manager = new ResourceManager(&quot;data.txt&quot;))
    {
        manager.WriteData(&quot;Some important data&quot;);
    } // 自动调用manager.Dispose()
}
&lt;/code&gt;&lt;/pre&gt;
&lt;h3&gt;12. 类设计最佳实践&lt;/h3&gt;
&lt;h4&gt;12.1 设计原则&lt;/h4&gt;
&lt;ol&gt;
&lt;li&gt;&lt;strong&gt;单一职责原则&lt;/strong&gt;：一个类只负责一项功能&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;开闭原则&lt;/strong&gt;：对扩展开放，对修改关闭&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;里氏替换原则&lt;/strong&gt;：子类可以替换父类使用&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;接口隔离原则&lt;/strong&gt;：客户端不应该依赖它不使用的接口&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;依赖倒置原则&lt;/strong&gt;：依赖于抽象，不依赖于具体实现&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;组合优于继承&lt;/strong&gt;：优先使用组合而非继承实现功能复用&lt;/li&gt;
&lt;/ol&gt;
&lt;h4&gt;12.2 编码规范&lt;/h4&gt;
&lt;pre&gt;&lt;code&gt;// 类命名 - PascalCase
public class CustomerManager { }

// 接口命名 - I前缀 + PascalCase
public interface ILogger { }

// 字段命名 - 私有字段使用下划线前缀
private string _firstName;

// 常量命名 - 全大写，下划线分隔
public const int MAX_RETRY_COUNT = 3;

// 方法命名 - PascalCase，动词开头
public void CalculateTotal() { }

// 异步方法命名 - 后缀Async
public async Task&amp;lt;string&amp;gt; GetDataAsync() { }
&lt;/code&gt;&lt;/pre&gt;
&lt;h4&gt;12.3 常用模式实现&lt;/h4&gt;
&lt;pre&gt;&lt;code&gt;// 工厂模式
public class LoggerFactory
{
    public static ILogger CreateLogger(string type)
    {
        switch (type.ToLower())
        {
            case &quot;console&quot;:
                return new ConsoleLogger();
            case &quot;file&quot;:
                return new FileLogger(&quot;app.log&quot;);
            case &quot;null&quot;:
                return new NullLogger();
            default:
                throw new ArgumentException($&quot;Unknown logger type: {type}&quot;);
        }
    }
}

// 观察者模式（使用事件实现）
public class Subject
{
    public event Action&amp;lt;string&amp;gt; StateChanged;
    
    private string _state;
    public string State
    {
        get =&amp;gt; _state;
        set
        {
            _state = value;
            // 通知观察者
            StateChanged?.Invoke(value);
        }
    }
}
&lt;/code&gt;&lt;/pre&gt;
&lt;h3&gt;13. 实际应用示例&lt;/h3&gt;
&lt;pre&gt;&lt;code&gt;// 完整的Employee类实现示例
public class Employee : Person, IComparable&amp;lt;Employee&amp;gt;, ICloneable, IDisposable
{
    public int EmployeeId { get; set; }
    public string Department { get; set; }
    public decimal Salary { get; private set; }
    public DateTime HireDate { get; set; }
    
    // 事件
    public event EventHandler&amp;lt;SalaryChangedEventArgs&amp;gt; SalaryChanged;
    
    public class SalaryChangedEventArgs : EventArgs
    {
        public decimal OldSalary { get; }
        public decimal NewSalary { get; }
        
        public SalaryChangedEventArgs(decimal oldSalary, decimal newSalary)
        {
            OldSalary = oldSalary;
            NewSalary = newSalary;
        }
    }
    
    // 构造函数
    public Employee(string firstName, string lastName, string department)
        : base(firstName, lastName)
    {
        Department = department;
        HireDate = DateTime.Now;
    }
    
    // 方法
    public void SetSalary(decimal salary)
    {
        if (salary &amp;lt; 0)
            throw new ArgumentException(&quot;Salary cannot be negative&quot;);
            
        if (Salary != salary)
        {
            decimal oldSalary = Salary;
            Salary = salary;
            
            // 触发事件
            OnSalaryChanged(new SalaryChangedEventArgs(oldSalary, salary));
        }
    }
    
    protected virtual void OnSalaryChanged(SalaryChangedEventArgs e)
    {
        SalaryChanged?.Invoke(this, e);
    }
    
    // 重写ToString方法
    public override string ToString()
    {
        return $&quot;{FullName} (ID: {EmployeeId}) - {Department}, Salary: ${Salary:N2}&quot;;
    }
    
    // 实现IComparable接口
    public int CompareTo(Employee other)
    {
        if (other == null)
            return 1;
            
        // 按姓名比较
        return FullName.CompareTo(other.FullName);
    }
    
    // 实现ICloneable接口
    public object Clone()
    {
        return new Employee(FirstName, LastName, Department)
        {
            EmployeeId = this.EmployeeId,
            Salary = this.Salary,
            HireDate = this.HireDate
        };
    }
    
    // 实现IDisposable接口
    private bool _disposed = false;
    
    public void Dispose()
    {
        Dispose(true);
        GC.SuppressFinalize(this);
    }
    
    protected virtual void Dispose(bool disposing)
    {
        if (!_disposed)
        {
            // 释放资源
            // ...
            _disposed = true;
        }
    }
    
    ~Employee()
    {
        Dispose(false);
    }
}

// 使用示例
public class EmployeeManager
{
    private List&amp;lt;Employee&amp;gt; _employees = new List&amp;lt;Employee&amp;gt;();
    
    public void AddEmployee(Employee employee)
    {
        employee.SalaryChanged += OnEmployeeSalaryChanged;
        _employees.Add(employee);
    }
    
    private void OnEmployeeSalaryChanged(object sender, Employee.SalaryChangedEventArgs e)
    {
        var emp = sender as Employee;
        Console.WriteLine($&quot;Salary changed for {emp.FullName}: ${e.OldSalary:N2} → ${e.NewSalary:N2}&quot;);
    }
    
    public void RemoveEmployee(int employeeId)
    {
        var employee = _employees.Find(e =&amp;gt; e.EmployeeId == employeeId);
        if (employee != null)
        {
            employee.SalaryChanged -= OnEmployeeSalaryChanged;
            _employees.Remove(employee);
        }
    }
    
    public List&amp;lt;Employee&amp;gt; GetEmployeesByDepartment(string department)
    {
        return _employees.Where(e =&amp;gt; e.Department == department).ToList();
    }
    
    public void SortEmployees()
    {
        _employees.Sort();
    }
}

## &amp;lt;a id=&quot;math-class&quot;&amp;gt;&amp;lt;/a&amp;gt;C# Math类详解

Math类是C#中用于执行基本数学运算的静态类，提供了丰富的数学函数，包括三角函数、对数函数、幂函数、舍入函数等。使用Math类可以简化复杂的数学计算。

### Math类的基本数学运算

Math类提供了许多常用的数学运算方法，这些方法都是静态的，可以直接通过类名调用：

```csharp
using System;

// 基本数学运算
double abs = Math.Abs(-5.5);        // 绝对值
double ceil = Math.Ceiling(5.3);    // 向上取整
double floor = Math.Floor(5.7);     // 向下取整
double round = Math.Round(5.5);     // 四舍五入
double truncate = Math.Truncate(5.789); // 截断小数部分
double max = Math.Max(10, 20);      // 最大值
double min = Math.Min(10, 20);      // 最小值

// 输出结果
Console.WriteLine($&quot;Abs(-5.5): {abs}&quot;);        // 输出: Abs(-5.5): 5.5
Console.WriteLine($&quot;Ceiling(5.3): {ceil}&quot;);     // 输出: Ceiling(5.3): 6
Console.WriteLine($&quot;Floor(5.7): {floor}&quot;);       // 输出: Floor(5.7): 5
Console.WriteLine($&quot;Round(5.5): {round}&quot;);       // 输出: Round(5.5): 6
Console.WriteLine($&quot;Truncate(5.789): {truncate}&quot;); // 输出: Truncate(5.789): 5
Console.WriteLine($&quot;Max(10,20): {max}&quot;);         // 输出: Max(10,20): 20
Console.WriteLine($&quot;Min(10,20): {min}&quot;);         // 输出: Min(10,20): 10
&lt;/code&gt;&lt;/pre&gt;
&lt;h3&gt;幂运算和根运算&lt;/h3&gt;
&lt;p&gt;Math类提供了处理幂运算和根运算的方法：&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;// 幂运算
double pow = Math.Pow(2, 3);        // 2的3次方
double sqrt = Math.Sqrt(16);        // 平方根
double cbrt = Math.Cbrt(27);        // 立方根 (C# 10+)

// 对数运算
double log = Math.Log(Math.E);      // 自然对数
double log10 = Math.Log10(100);     // 常用对数 (以10为底)
double log2 = Math.Log2(8);         // 以2为底的对数 (C# 10+)

// 输出结果
Console.WriteLine($&quot;Pow(2,3): {pow}&quot;);      // 输出: Pow(2,3): 8
Console.WriteLine($&quot;Sqrt(16): {sqrt}&quot;);     // 输出: Sqrt(16): 4
Console.WriteLine($&quot;Cbrt(27): {cbrt}&quot;);     // 输出: Cbrt(27): 3
Console.WriteLine($&quot;Log(e): {log}&quot;);        // 输出: Log(e): 1
Console.WriteLine($&quot;Log10(100): {log10}&quot;);  // 输出: Log10(100): 2
Console.WriteLine($&quot;Log2(8): {log2}&quot;);      // 输出: Log2(8): 3
&lt;/code&gt;&lt;/pre&gt;
&lt;h3&gt;三角函数&lt;/h3&gt;
&lt;p&gt;Math类提供了完整的三角函数支持，注意这些函数的参数都是以弧度为单位。在实际应用中，我们通常习惯使用角度，因此需要进行角度与弧度之间的转换：&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;// 角度与弧度转换的通用公式
// 弧度 = 角度 * π / 180
// 角度 = 弧度 * 180 / π

// 定义转换方法（可选，为了代码可读性）
public static double DegreesToRadians(double degrees)
{
    return degrees * Math.PI / 180.0;
}

public static double RadiansToDegrees(double radians)
{
    return radians * 180.0 / Math.PI;
}

// 直接使用Math类进行转换
const double angle30 = 30.0;
const double angle45 = 45.0;
const double angle60 = 60.0;
const double angle90 = 90.0;

// 角度转弧度
double rad30 = angle30 * Math.PI / 180;  // 30度转弧度
double rad45 = angle45 * Math.PI / 180;  // 45度转弧度
double rad60 = angle60 * Math.PI / 180;  // 60度转弧度
double rad90 = angle90 * Math.PI / 180;  // 90度转弧度

// 三角函数计算（使用角度）
double sin30 = Math.Sin(angle30 * Math.PI / 180);  // sin(30°)
double cos45 = Math.Cos(angle45 * Math.PI / 180);  // cos(45°)
double tan60 = Math.Tan(angle60 * Math.PI / 180);  // tan(60°)

// 三角函数计算（使用弧度）
double sinPiOver6 = Math.Sin(Math.PI / 6);   // sin(π/6) = sin(30°)
double cosPiOver4 = Math.Cos(Math.PI / 4);   // cos(π/4) = cos(45°)
double tanPiOver3 = Math.Tan(Math.PI / 3);   // tan(π/3) = tan(60°)

// 反三角函数（结果为弧度）
double asinValue = Math.Asin(0.5);           // arcsin(0.5) = π/6 弧度
double acosValue = Math.Acos(0.707106781);   // arccos(0.707106781) ≈ π/4 弧度
double atanValue = Math.Atan(1.0);           // arctan(1.0) = π/4 弧度

// 将反三角函数结果转换为角度
double asinDegrees = asinValue * 180 / Math.PI;  // 转换为角度
double acosDegrees = acosValue * 180 / Math.PI;  // 转换为角度
double atanDegrees = atanValue * 180 / Math.PI;  // 转换为角度

// 输出结果
Console.WriteLine($&quot;30度 = {rad30}弧度&quot;);
Console.WriteLine($&quot;45度 = {rad45}弧度&quot;);
Console.WriteLine($&quot;60度 = {rad60}弧度&quot;);
Console.WriteLine($&quot;90度 = {rad90}弧度&quot;);

Console.WriteLine($&quot;sin(30°) = {sin30}&quot;);
Console.WriteLine($&quot;cos(45°) = {cos45}&quot;);
Console.WriteLine($&quot;tan(60°) = {tan60}&quot;);

Console.WriteLine($&quot;arcsin(0.5) = {asinValue}弧度 = {asinDegrees}度&quot;);
Console.WriteLine($&quot;arccos(0.707106781) = {acosValue}弧度 = {acosDegrees}度&quot;);
Console.WriteLine($&quot;arctan(1.0) = {atanValue}弧度 = {atanDegrees}度&quot;);
&lt;/code&gt;&lt;/pre&gt;
&lt;h3&gt;实用的三角函数示例&lt;/h3&gt;
&lt;p&gt;以下是一些常见的三角函数应用场景：&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;// 计算直角三角形的边长
public static void RightTriangleExample()
{
    double angle = 30;  // 角度
    double hypotenuse = 10;  // 斜边长度
    
    // 计算对边和邻边长度
    double opposite = hypotenuse * Math.Sin(angle * Math.PI / 180);
    double adjacent = hypotenuse * Math.Cos(angle * Math.PI / 180);
    
    Console.WriteLine($&quot;角度: {angle}度&quot;);
    Console.WriteLine($&quot;斜边: {hypotenuse}&quot;);
    Console.WriteLine($&quot;对边: {opposite}&quot;);
    Console.WriteLine($&quot;邻边: {adjacent}&quot;);
}

// 计算两点间的角度
public static double CalculateAngle(double x1, double y1, double x2, double y2)
{
    double deltaX = x2 - x1;
    double deltaY = y2 - y1;
    
    // 使用Atan2计算角度（结果为弧度）
    double angleRadians = Math.Atan2(deltaY, deltaX);
    
    // 转换为角度
    double angleDegrees = angleRadians * 180 / Math.PI;
    
    // 确保角度在0-360度范围内
    if (angleDegrees &amp;lt; 0)
        angleDegrees += 360;
    
    return angleDegrees;
}

// 坐标旋转
public static (double newX, double newY) RotatePoint(double x, double y, double angleDegrees)
{
    double angleRadians = angleDegrees * Math.PI / 180;
    double cos = Math.Cos(angleRadians);
    double sin = Math.Sin(angleRadians);
    
    double newX = x * cos - y * sin;
    double newY = x * sin + y * cos;
    
    return (newX, newY);
}

// 使用示例
static void TrigonometryExamples()
{
    Console.WriteLine(&quot;=== 直角三角形示例 ===&quot;);
    RightTriangleExample();
    
    Console.WriteLine(&quot;\n=== 角度计算示例 ===&quot;);
    double angle = CalculateAngle(0, 0, 1, 1);
    Console.WriteLine($&quot;从原点到点(1,1)的角度: {angle}度&quot;);
    
    Console.WriteLine(&quot;\n=== 坐标旋转示例 ===&quot;);
    var (newX, newY) = RotatePoint(1, 0, 90);  // 将点(1,0)绕原点旋转90度
    Console.WriteLine($&quot;点(1,0)绕原点旋转90度后: ({newX}, {newY})&quot;);
}
&lt;/code&gt;&lt;/pre&gt;
&lt;h3&gt;Math类的常量&lt;/h3&gt;
&lt;p&gt;Math类提供了两个重要的数学常量：&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;// 数学常量
double pi = Math.PI;                // 圆周率π
double e = Math.E;                  // 自然对数的底e

// 输出结果
Console.WriteLine($&quot;π: {pi}&quot;);      // 输出: π: 3.141592653589793
Console.WriteLine($&quot;e: {e}&quot;);       // 输出: e: 2.718281828459045
&lt;/code&gt;&lt;/pre&gt;
&lt;h3&gt;随机数生成&lt;/h3&gt;
&lt;p&gt;虽然Random类更适合生成随机数，但Math类也提供了随机数方法：&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;// 随机数 (返回0.0到1.0之间的double值)
double random = Math.Random();

// 生成指定范围的随机数
int randomInt = (int)(Math.Random() * 100); // 0到99的随机整数

double min = 10.0;
double max = 20.0;
double randomRange = min + Math.Random() * (max - min); // min到max之间的随机数

// 输出结果
Console.WriteLine($&quot;Random: {random}&quot;);           // 输出: Random: 0.123456789 (具体值随机)
Console.WriteLine($&quot;RandomInt: {randomInt}&quot;);     // 输出: RandomInt: 45 (具体值随机)
Console.WriteLine($&quot;RandomRange: {randomRange}&quot;); // 输出: RandomRange: 15.6789 (具体值随机)
&lt;/code&gt;&lt;/pre&gt;
&lt;h3&gt;舍入和精度控制&lt;/h3&gt;
&lt;p&gt;Math类提供了多种舍入方法来控制数字的精度：&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;// 舍入方法
double value = 12.345;

// 四舍五入到指定小数位数
double rounded1 = Math.Round(value, 2);           // 保留2位小数
double rounded2 = Math.Round(value, 2, MidpointRounding.AwayFromZero); // 四舍五入

double truncateValue = Math.Truncate(value);      // 截断小数部分

double ceilingValue = Math.Ceiling(value);        // 向上取整
double floorValue = Math.Floor(value);            // 向下取整

// 输出结果
Console.WriteLine($&quot;原始值: {value}&quot;);                    // 输出: 原始值: 12.345
Console.WriteLine($&quot;Round(2位小数): {rounded1}&quot;);        // 输出: Round(2位小数): 12.34
Console.WriteLine($&quot;Round(远离零): {rounded2}&quot;);         // 输出: Round(远离零): 12.35
Console.WriteLine($&quot;Truncate: {truncateValue}&quot;);          // 输出: Truncate: 12
Console.WriteLine($&quot;Ceiling: {ceilingValue}&quot;);           // 输出: Ceiling: 13
Console.WriteLine($&quot;Floor: {floorValue}&quot;);               // 输出: Floor: 12
&lt;/code&gt;&lt;/pre&gt;
&lt;h3&gt;Math类的实际应用示例&lt;/h3&gt;
&lt;p&gt;以下是一些Math类在实际开发中的应用场景：&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;using System;

class MathExamples
{
    // 计算两点间距离
    public static double Distance(double x1, double y1, double x2, double y2)
    {
        return Math.Sqrt(Math.Pow(x2 - x1, 2) + Math.Pow(y2 - y1, 2));
    }
    
    // 判断是否为素数
    public static bool IsPrime(int n)
    {
        if (n &amp;lt;= 1) return false;
        if (n &amp;lt;= 3) return true;
        if (n % 2 == 0 || n % 3 == 0) return false;
        
        int sqrt = (int)Math.Sqrt(n);
        for (int i = 5; i &amp;lt;= sqrt; i += 6)
        {
            if (n % i == 0 || n % (i + 2) == 0) return false;
        }
        return true;
    }
    
    // 计算圆的面积
    public static double CircleArea(double radius)
    {
        return Math.PI * Math.Pow(radius, 2);
    }
    
    // 计算球的体积
    public static double SphereVolume(double radius)
    {
        return (4.0 / 3.0) * Math.PI * Math.Pow(radius, 3);
    }
    
    // 生成指定范围的随机整数
    public static int RandomRange(int min, int max)
    {
        return (int)(Math.Random() * (max - min + 1)) + min;
    }
    
    static void Main()
    {
        // 测试距离计算
        double distance = Distance(0, 0, 3, 4);
        Console.WriteLine($&quot;两点间距离: {distance}&quot;); // 输出: 两点间距离: 5
        
        // 测试素数判断
        Console.WriteLine($&quot;17是素数: {IsPrime(17)}&quot;); // 输出: 17是素数: True
        Console.WriteLine($&quot;18是素数: {IsPrime(18)}&quot;); // 输出: 18是素数: False
        
        // 测试圆面积计算
        double area = CircleArea(5);
        Console.WriteLine($&quot;半径为5的圆面积: {area}&quot;); // 输出: 半径为5的圆面积: 78.53981633974483
        
        // 测试球体积计算
        double volume = SphereVolume(3);
        Console.WriteLine($&quot;半径为3的球体积: {volume}&quot;); // 输出: 半径为3的球体积: 113.09733552923254
        
        // 测试随机数生成
        for (int i = 0; i &amp;lt; 5; i++)
        {
            int randomNum = RandomRange(1, 10);
            Console.Write($&quot;{randomNum} &quot;); // 输出: 3 7 1 9 4 (示例，具体值随机)
        }
        Console.WriteLine();
    }
}
&lt;/code&gt;&lt;/pre&gt;
&lt;h3&gt;Math类使用注意事项&lt;/h3&gt;
&lt;ol&gt;
&lt;li&gt;
&lt;p&gt;&lt;strong&gt;静态方法调用&lt;/strong&gt;：Math类中的所有方法都是静态的，直接通过类名调用，无需创建实例。&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;&lt;strong&gt;参数单位&lt;/strong&gt;：三角函数的参数是弧度而不是角度，需要进行转换时使用&lt;code&gt;角度 * Math.PI / 180&lt;/code&gt;。&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;&lt;strong&gt;返回类型&lt;/strong&gt;：大多数Math方法返回double类型，必要时需要进行类型转换。&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;&lt;strong&gt;精度问题&lt;/strong&gt;：浮点数运算可能存在精度问题，在比较时应使用误差范围而不是直接相等比较。&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;&lt;strong&gt;异常处理&lt;/strong&gt;：某些方法在无效输入时会返回特殊值（如NaN或Infinity）而不是抛出异常。&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;&lt;strong&gt;性能考虑&lt;/strong&gt;：复杂的数学运算可能影响性能，在性能敏感的代码中应考虑优化。&lt;/p&gt;
&lt;/li&gt;
&lt;/ol&gt;
&lt;h3&gt;Math类常用函数速查表&lt;/h3&gt;
&lt;p&gt;为了方便查阅，下面列出了Math类中常用的函数及其参数和作用：&lt;/p&gt;
&lt;h4&gt;基本数学运算&lt;/h4&gt;
&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;函数&lt;/th&gt;
&lt;th&gt;参数&lt;/th&gt;
&lt;th&gt;返回值&lt;/th&gt;
&lt;th&gt;作用说明&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;Abs&lt;/td&gt;
&lt;td&gt;数值类型 value&lt;/td&gt;
&lt;td&gt;同输入类型&lt;/td&gt;
&lt;td&gt;返回指定数字的绝对值。支持int、long、float、double等数值类型&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Max&lt;/td&gt;
&lt;td&gt;数值类型 val1, val2&lt;/td&gt;
&lt;td&gt;同输入类型&lt;/td&gt;
&lt;td&gt;返回两个数字中的较大值。支持int、long、float、double等数值类型&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Min&lt;/td&gt;
&lt;td&gt;数值类型 val1, val2&lt;/td&gt;
&lt;td&gt;同输入类型&lt;/td&gt;
&lt;td&gt;返回两个数字中的较小值。支持int、long、float、double等数值类型&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Sign&lt;/td&gt;
&lt;td&gt;double value&lt;/td&gt;
&lt;td&gt;int&lt;/td&gt;
&lt;td&gt;返回指定数字的符号（正数返回1，负数返回-1，零返回0）&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;
&lt;h4&gt;舍入和精度控制&lt;/h4&gt;
&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;函数&lt;/th&gt;
&lt;th&gt;参数&lt;/th&gt;
&lt;th&gt;返回值&lt;/th&gt;
&lt;th&gt;作用说明&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;Ceiling&lt;/td&gt;
&lt;td&gt;double value&lt;/td&gt;
&lt;td&gt;double&lt;/td&gt;
&lt;td&gt;返回大于或等于指定数字的最小整数&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Floor&lt;/td&gt;
&lt;td&gt;double value&lt;/td&gt;
&lt;td&gt;double&lt;/td&gt;
&lt;td&gt;返回小于或等于指定数字的最大整数&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Round&lt;/td&gt;
&lt;td&gt;double value, (int digits), (MidpointRounding mode)&lt;/td&gt;
&lt;td&gt;double&lt;/td&gt;
&lt;td&gt;将值舍入到最近的整数或指定小数位数，可指定舍入模式&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Truncate&lt;/td&gt;
&lt;td&gt;double value&lt;/td&gt;
&lt;td&gt;double&lt;/td&gt;
&lt;td&gt;将指定数字的小数部分截断&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;
&lt;h4&gt;幂运算和对数运算&lt;/h4&gt;
&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;函数&lt;/th&gt;
&lt;th&gt;参数&lt;/th&gt;
&lt;th&gt;返回值&lt;/th&gt;
&lt;th&gt;作用说明&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;Pow&lt;/td&gt;
&lt;td&gt;double x, double y&lt;/td&gt;
&lt;td&gt;double&lt;/td&gt;
&lt;td&gt;返回x的y次幂 (x^y)&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Sqrt&lt;/td&gt;
&lt;td&gt;double value&lt;/td&gt;
&lt;td&gt;double&lt;/td&gt;
&lt;td&gt;返回指定数字的平方根&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Cbrt&lt;/td&gt;
&lt;td&gt;double value&lt;/td&gt;
&lt;td&gt;double&lt;/td&gt;
&lt;td&gt;返回指定数字的立方根 (C# 10+)&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Exp&lt;/td&gt;
&lt;td&gt;double value&lt;/td&gt;
&lt;td&gt;double&lt;/td&gt;
&lt;td&gt;返回 e 的指定次幂&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Log&lt;/td&gt;
&lt;td&gt;double value, (double base)&lt;/td&gt;
&lt;td&gt;double&lt;/td&gt;
&lt;td&gt;返回指定数字的自然对数或指定底数的对数&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Log10&lt;/td&gt;
&lt;td&gt;double value&lt;/td&gt;
&lt;td&gt;double&lt;/td&gt;
&lt;td&gt;返回指定数字的常用对数（以10为底）&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Log2&lt;/td&gt;
&lt;td&gt;double value&lt;/td&gt;
&lt;td&gt;double&lt;/td&gt;
&lt;td&gt;返回指定数字的以2为底的对数 (C# 10+)&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;
&lt;h4&gt;三角函数&lt;/h4&gt;
&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;函数&lt;/th&gt;
&lt;th&gt;参数&lt;/th&gt;
&lt;th&gt;返回值&lt;/th&gt;
&lt;th&gt;作用说明&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;Sin/Cos/Tan&lt;/td&gt;
&lt;td&gt;double radians&lt;/td&gt;
&lt;td&gt;double&lt;/td&gt;
&lt;td&gt;返回指定弧度的正弦/余弦/正切值&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Asin/Acos/Atan&lt;/td&gt;
&lt;td&gt;double value&lt;/td&gt;
&lt;td&gt;double&lt;/td&gt;
&lt;td&gt;返回指定数字的反正弦/反余弦/反正切值&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Atan2&lt;/td&gt;
&lt;td&gt;double y, double x&lt;/td&gt;
&lt;td&gt;double&lt;/td&gt;
&lt;td&gt;返回由正切值的商所定义的角度&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Sinh/Cosh/Tanh&lt;/td&gt;
&lt;td&gt;double value&lt;/td&gt;
&lt;td&gt;double&lt;/td&gt;
&lt;td&gt;返回指定角度的双曲正弦/余弦/正切值&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;
&lt;h4&gt;常量&lt;/h4&gt;
&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;名称&lt;/th&gt;
&lt;th&gt;类型&lt;/th&gt;
&lt;th&gt;值&lt;/th&gt;
&lt;th&gt;说明&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;PI&lt;/td&gt;
&lt;td&gt;double&lt;/td&gt;
&lt;td&gt;3.141592653589793&lt;/td&gt;
&lt;td&gt;圆周率π&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;E&lt;/td&gt;
&lt;td&gt;double&lt;/td&gt;
&lt;td&gt;2.718281828459045&lt;/td&gt;
&lt;td&gt;自然对数的底数e&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;
&lt;p&gt;通过合理使用Math类，可以大大简化数学计算的代码编写，提高开发效率和代码可读性。&lt;/p&gt;
&lt;h2&gt;&amp;lt;a id=&quot;random-class&quot;&amp;gt;&amp;lt;/a&amp;gt;C# Random类详解&lt;/h2&gt;
&lt;p&gt;在编程中，我们经常需要生成随机数，例如游戏开发中的随机事件、抽奖程序、密码生成等场景。C#提供了Random类来生成伪随机数。&lt;/p&gt;
&lt;h3&gt;Random类的基本使用&lt;/h3&gt;
&lt;p&gt;Random类用于生成随机数序列。需要注意的是，Random类生成的是伪随机数，这意味着它们是通过算法计算出来的，看起来是随机的，但实际上是可以预测的。&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;using System;

// 创建Random对象
Random random = new Random();

// 生成0到Int32.MaxValue-1之间的随机整数
int randomInt = random.Next();

// 生成0到指定值之间的随机整数（不包含指定值）
int randomIntLessThan100 = random.Next(100); // 0到99之间的随机整数

// 生成指定范围内的随机整数（包含最小值，不包含最大值）
int randomIntInRange = random.Next(10, 20); // 10到19之间的随机整数

// 生成0.0到1.0之间的随机浮点数
double randomDouble = random.NextDouble();

// 生成指定范围内的随机浮点数
double min = 5.0;
double max = 15.0;
double randomDoubleInRange = random.NextDouble() * (max - min) + min;

// 输出结果
Console.WriteLine($&quot;随机整数: {randomInt}&quot;);
Console.WriteLine($&quot;0到99之间的随机整数: {randomIntLessThan100}&quot;);
Console.WriteLine($&quot;10到19之间的随机整数: {randomIntInRange}&quot;);
Console.WriteLine($&quot;0.0到1.0之间的随机浮点数: {randomDouble}&quot;);
Console.WriteLine($&quot;5.0到15.0之间的随机浮点数: {randomDoubleInRange}&quot;);
&lt;/code&gt;&lt;/pre&gt;
&lt;h3&gt;Random类的构造函数&lt;/h3&gt;
&lt;p&gt;Random类提供了两种构造函数：&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;&lt;strong&gt;无参构造函数&lt;/strong&gt;：使用系统时间作为种子值&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;有参构造函数&lt;/strong&gt;：使用指定的种子值&lt;/li&gt;
&lt;/ol&gt;
&lt;pre&gt;&lt;code&gt;// 使用系统时间作为种子（推荐用于大多数场景）
Random random1 = new Random();

// 使用指定种子值（相同种子会产生相同的随机数序列）
Random random2 = new Random(12345);

// 演示相同种子的效果
Random r1 = new Random(100);
Random r2 = new Random(100);

Console.WriteLine(&quot;使用相同种子生成的随机数序列:&quot;);
for (int i = 0; i &amp;lt; 5; i++)
{
    Console.WriteLine($&quot;r1: {r1.Next(1, 100)}, r2: {r2.Next(1, 100)}&quot;);
}
&lt;/code&gt;&lt;/pre&gt;
&lt;h3&gt;Random类常用方法详解&lt;/h3&gt;
&lt;p&gt;Random类提供了多种生成随机数的方法：&lt;/p&gt;
&lt;h4&gt;Next()方法&lt;/h4&gt;
&lt;pre&gt;&lt;code&gt;Random random = new Random();

// Next() - 返回非负随机整数
int next1 = random.Next();
Console.WriteLine($&quot;Next(): {next1}&quot;);

// Next(maxValue) - 返回0到maxValue之间的随机整数（不包含maxValue）
int next2 = random.Next(100);
Console.WriteLine($&quot;Next(100): {next2}&quot;);

// Next(minValue, maxValue) - 返回minValue到maxValue之间的随机整数（包含minValue，不包含maxValue）
int next3 = random.Next(50, 100);
Console.WriteLine($&quot;Next(50, 100): {next3}&quot;);
&lt;/code&gt;&lt;/pre&gt;
&lt;h4&gt;NextDouble()方法&lt;/h4&gt;
&lt;pre&gt;&lt;code&gt;Random random = new Random();

// NextDouble() - 返回0.0到1.0之间的随机浮点数
double nextDouble = random.NextDouble();
Console.WriteLine($&quot;NextDouble(): {nextDouble}&quot;);

// 生成指定范围内的随机浮点数
public static double NextDouble(Random random, double min, double max)
{
    return random.NextDouble() * (max - min) + min;
}

double randomValue = NextDouble(random, 10.5, 20.8);
Console.WriteLine($&quot;10.5到20.8之间的随机浮点数: {randomValue}&quot;);
&lt;/code&gt;&lt;/pre&gt;
&lt;h4&gt;NextBytes()方法&lt;/h4&gt;
&lt;pre&gt;&lt;code&gt;Random random = new Random();

// NextBytes() - 用随机数填充字节数组
byte[] buffer = new byte[10];
random.NextBytes(buffer);

Console.WriteLine(&quot;随机字节数组:&quot;);
foreach (byte b in buffer)
{
    Console.Write($&quot;{b} &quot;);
}
Console.WriteLine();
&lt;/code&gt;&lt;/pre&gt;
&lt;h3&gt;Random类的实际应用示例&lt;/h3&gt;
&lt;p&gt;以下是一些Random类在实际开发中的应用场景：&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;using System;
using System.Collections.Generic;

class RandomExamples
{
    private static Random random = new Random();
    
    // 生成随机密码
    public static string GeneratePassword(int length)
    {
        const string chars = &quot;ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789!@#$%^&amp;amp;*&quot;;
        char[] password = new char[length];
        
        for (int i = 0; i &amp;lt; length; i++)
        {
            password[i] = chars[random.Next(chars.Length)];
        }
        
        return new string(password);
    }
    
    // 随机打乱数组
    public static void Shuffle&amp;lt;T&amp;gt;(T[] array)
    {
        for (int i = array.Length - 1; i &amp;gt; 0; i--)
        {
            int j = random.Next(i + 1);
            T temp = array[i];
            array[i] = array[j];
            array[j] = temp;
        }
    }
    
    // 从集合中随机选择元素
    public static T RandomSelect&amp;lt;T&amp;gt;(IList&amp;lt;T&amp;gt; list)
    {
        if (list == null || list.Count == 0)
            throw new ArgumentException(&quot;列表不能为空&quot;);
            
        int index = random.Next(list.Count);
        return list[index];
    }
    
    // 模拟抛硬币
    public static string FlipCoin()
    {
        return random.Next(2) == 0 ? &quot;正面&quot; : &quot;反面&quot;;
    }
    
    // 模拟掷骰子
    public static int RollDice(int sides = 6)
    {
        return random.Next(1, sides + 1);
    }
    
    // 生成随机颜色
    public static string GenerateRandomColor()
    {
        int red = random.Next(256);
        int green = random.Next(256);
        int blue = random.Next(256);
        return $&quot;RGB({red}, {green}, {blue})&quot;;
    }
    
    static void Main()
    {
        Console.WriteLine($&quot;随机密码: {GeneratePassword(12)}&quot;);
        
        string[] cards = { &quot;红桃&quot;, &quot;方块&quot;, &quot;梅花&quot;, &quot;黑桃&quot; };
        Shuffle(cards);
        Console.WriteLine(&quot;洗牌后:&quot;);
        foreach (string card in cards)
        {
            Console.Write($&quot;{card} &quot;);
        }
        Console.WriteLine();
        
        string[] fruits = { &quot;苹果&quot;, &quot;香蕉&quot;, &quot;橙子&quot;, &quot;葡萄&quot;, &quot;草莓&quot; };
        Console.WriteLine($&quot;随机选择的水果: {RandomSelect(fruits)}&quot;);
        
        Console.WriteLine($&quot;抛硬币结果: {FlipCoin()}&quot;);
        Console.WriteLine($&quot;掷骰子结果: {RollDice()}&quot;);
        Console.WriteLine($&quot;随机颜色: {GenerateRandomColor()}&quot;);
    }
}
&lt;/code&gt;&lt;/pre&gt;
&lt;h3&gt;Random类使用注意事项&lt;/h3&gt;
&lt;ol&gt;
&lt;li&gt;
&lt;p&gt;&lt;strong&gt;线程安全性&lt;/strong&gt;：Random类不是线程安全的。在多线程环境中，应为每个线程创建独立的Random实例，或者使用锁机制。&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;&lt;strong&gt;种子值选择&lt;/strong&gt;：使用无参构造函数时，系统会使用当前时间作为种子值。如果在短时间内创建多个Random实例，可能会产生相同的随机数序列。&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;&lt;strong&gt;避免频繁创建实例&lt;/strong&gt;：不应在循环中频繁创建Random实例，而应复用同一个实例。&lt;/p&gt;
&lt;/li&gt;
&lt;/ol&gt;
&lt;pre&gt;&lt;code&gt;// 错误的做法 - 可能产生相同的随机数
for (int i = 0; i &amp;lt; 10; i++)
{
    Random r = new Random();
    Console.Write(r.Next(1, 100) + &quot; &quot;);
}
Console.WriteLine();

// 正确的做法 - 复用同一个实例
Random random = new Random();
for (int i = 0; i &amp;lt; 10; i++)
{
    Console.Write(random.Next(1, 100) + &quot; &quot;);
}
Console.WriteLine();
&lt;/code&gt;&lt;/pre&gt;
&lt;ol&gt;
&lt;li&gt;
&lt;p&gt;&lt;strong&gt;随机数分布&lt;/strong&gt;：NextDouble()方法返回的随机数在0.0到1.0之间是均匀分布的。&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;&lt;strong&gt;安全性考虑&lt;/strong&gt;：Random类生成的是伪随机数，不适合用于加密等安全性要求高的场景。对于这类场景，应使用System.Security.Cryptography命名空间中的类。&lt;/p&gt;
&lt;/li&gt;
&lt;/ol&gt;
&lt;h3&gt;Random类常用方法速查表&lt;/h3&gt;
&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;方法&lt;/th&gt;
&lt;th&gt;参数&lt;/th&gt;
&lt;th&gt;返回值&lt;/th&gt;
&lt;th&gt;作用说明&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;Next&lt;/td&gt;
&lt;td&gt;(int maxValue) 或 (int minValue, int maxValue)&lt;/td&gt;
&lt;td&gt;int&lt;/td&gt;
&lt;td&gt;生成随机整数。无参返回非负整数，一个参数返回0到maxValue-1的值，两个参数返回minValue到maxValue-1的值&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;NextDouble&lt;/td&gt;
&lt;td&gt;无&lt;/td&gt;
&lt;td&gt;double&lt;/td&gt;
&lt;td&gt;返回0.0到1.0之间的随机浮点数&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;NextBytes&lt;/td&gt;
&lt;td&gt;byte[] buffer&lt;/td&gt;
&lt;td&gt;void&lt;/td&gt;
&lt;td&gt;用随机数填充字节数组&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;NextInt64&lt;/td&gt;
&lt;td&gt;(long maxValue) 或 (long minValue, long maxValue)&lt;/td&gt;
&lt;td&gt;long&lt;/td&gt;
&lt;td&gt;生成随机长整数 (C# 10+)&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;NextSingle&lt;/td&gt;
&lt;td&gt;无&lt;/td&gt;
&lt;td&gt;float&lt;/td&gt;
&lt;td&gt;返回0.0到1.0之间的随机单精度浮点数 (C# 10+)&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;
&lt;p&gt;通过合理使用Random类，可以为应用程序添加随机性，增强用户体验，实现游戏逻辑，进行模拟计算等。&lt;/p&gt;
&lt;h2&gt;&amp;lt;a id=&quot;datetime-class&quot;&amp;gt;&amp;lt;/a&amp;gt;C# DateTime类详解&lt;/h2&gt;
&lt;p&gt;在实际开发中，处理日期和时间是一个常见的需求，例如记录用户注册时间、计算时间差、格式化时间显示等。C#提供了DateTime结构来处理日期和时间相关的操作。&lt;/p&gt;
&lt;h3&gt;DateTime类的基本概念&lt;/h3&gt;
&lt;p&gt;DateTime结构表示一个时间点，精确到100纳秒，称为刻度(Ticks)。DateTime的值范围从公元1年1月1日到公元9999年12月31日。&lt;/p&gt;
&lt;h3&gt;DateTime的创建和初始化&lt;/h3&gt;
&lt;p&gt;DateTime可以通过多种方式创建：&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;using System;

// 1. 使用默认构造函数（创建最小日期值）
DateTime defaultDate = new DateTime(); // 0001/1/1 0:00:00

// 2. 指定年、月、日
DateTime date1 = new DateTime(2025, 11, 20);

// 3. 指定年、月、日、时、分、秒
DateTime date2 = new DateTime(2025, 11, 20, 14, 30, 0);

// 4. 指定年、月、日、时、分、秒和毫秒
DateTime date3 = new DateTime(2025, 11, 20, 14, 30, 0, 500);

// 5. 使用静态属性获取特殊日期时间
DateTime now = DateTime.Now;           // 当前本地时间
DateTime utcNow = DateTime.UtcNow;     // 当前UTC时间
DateTime today = DateTime.Today;       // 今天的日期，时间为00:00:00
DateTime min = DateTime.MinValue;      // 最小日期值 0001/1/1 0:00:00
DateTime max = DateTime.MaxValue;      // 最大日期值 9999/12/31 23:59:59

// 输出结果
Console.WriteLine($&quot;默认日期: {defaultDate}&quot;);
// 输出: 默认日期: 0001/1/1 0:00:00

Console.WriteLine($&quot;指定日期: {date1}&quot;);
// 输出: 指定日期: 2025/11/20 0:00:00

Console.WriteLine($&quot;指定日期时间: {date2}&quot;);
// 输出: 指定日期时间: 2025/11/20 14:30:00

Console.WriteLine($&quot;指定日期时间毫秒: {date3}&quot;);
// 输出: 指定日期时间毫秒: 2025/11/20 14:30:00

Console.WriteLine($&quot;当前时间: {now}&quot;);
// 输出: 当前时间: 2025/11/19 12:00:00 (实际值取决于当前时间)

Console.WriteLine($&quot;UTC时间: {utcNow}&quot;);
// 输出: UTC时间: 2025/11/19 4:00:00 (实际值取决于当前UTC时间)

Console.WriteLine($&quot;今天: {today}&quot;);
// 输出: 今天: 2025/11/19 0:00:00 (实际值取决于当前日期)

Console.WriteLine($&quot;最小日期: {min}&quot;);
// 输出: 最小日期: 0001/1/1 0:00:00

Console.WriteLine($&quot;最大日期: {max}&quot;);
// 输出: 最大日期: 9999/12/31 23:59:59
&lt;/code&gt;&lt;/pre&gt;
&lt;h3&gt;DateTime的常用属性&lt;/h3&gt;
&lt;p&gt;DateTime提供了丰富的属性来获取日期时间的各个组成部分：&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;DateTime now = DateTime.Now;

Console.WriteLine($&quot;当前时间: {now}&quot;);
// 输出: 当前时间: 2025/11/19 12:00:00 (实际值取决于当前时间)

Console.WriteLine($&quot;年: {now.Year}&quot;);
// 输出: 年: 2025 (实际值取决于当前年份)

Console.WriteLine($&quot;月: {now.Month}&quot;);
// 输出: 月: 11 (实际值取决于当前月份)

Console.WriteLine($&quot;日: {now.Day}&quot;);
// 输出: 日: 19 (实际值取决于当前日期)

Console.WriteLine($&quot;时: {now.Hour}&quot;);
// 输出: 时: 12 (实际值取决于当前时间)

Console.WriteLine($&quot;分: {now.Minute}&quot;);
// 输出: 分: 0 (实际值取决于当前时间)

Console.WriteLine($&quot;秒: {now.Second}&quot;);
// 输出: 秒: 0 (实际值取决于当前时间)

Console.WriteLine($&quot;毫秒: {now.Millisecond}&quot;);
// 输出: 毫秒: 0 (实际值取决于当前时间)

Console.WriteLine($&quot;星期: {now.DayOfWeek}&quot;);
// 输出: 星期: Wednesday (实际值取决于当前日期)

Console.WriteLine($&quot;一年中的第几天: {now.DayOfYear}&quot;);
// 输出: 一年中的第几天: 323 (实际值取决于当前日期)

Console.WriteLine($&quot;刻度数: {now.Ticks}&quot;);
// 输出: 刻度数: 638123456789012345 (实际值取决于当前时间)

Console.WriteLine($&quot;日期部分: {now.Date}&quot;);
// 输出: 日期部分: 2025/11/19 0:00:00 (实际值取决于当前日期)

Console.WriteLine($&quot;时间部分: {now.TimeOfDay}&quot;);
// 输出: 时间部分: 12:00:00 (实际值取决于当前时间)
&lt;/code&gt;&lt;/pre&gt;
&lt;h3&gt;DateTime的格式化&lt;/h3&gt;
&lt;p&gt;DateTime可以通过ToString方法进行格式化显示：&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;DateTime now = DateTime.Now;

// 标准格式
Console.WriteLine($&quot;默认格式: {now}&quot;);
// 输出: 默认格式: 2025/11/19 12:00:00 (实际值取决于当前时间)

Console.WriteLine($&quot;短日期: {now.ToShortDateString()}&quot;);
// 输出: 短日期: 2025/11/19

Console.WriteLine($&quot;长日期: {now.ToLongDateString()}&quot;);
// 输出: 长日期: 2025年11月19日 (中文环境) 或 Wednesday, November 19, 2025 (英文环境)

Console.WriteLine($&quot;短时间: {now.ToShortTimeString()}&quot;);
// 输出: 短时间: 12:00

Console.WriteLine($&quot;长时间: {now.ToLongTimeString()}&quot;);
// 输出: 长时间: 12:00:00

// 自定义格式
Console.WriteLine($&quot;自定义格式1: {now:yyyy-MM-dd}&quot;);
// 输出: 自定义格式1: 2025-11-19

Console.WriteLine($&quot;自定义格式2: {now:yyyy年MM月dd日}&quot;);
// 输出: 自定义格式2: 2025年11月19日

Console.WriteLine($&quot;自定义格式3: {now:yyyy-MM-dd HH:mm:ss}&quot;);
// 输出: 自定义格式3: 2025-11-19 12:00:00

Console.WriteLine($&quot;自定义格式4: {now:yyyy年MM月dd日 HH时mm分ss秒}&quot;);
// 输出: 自定义格式4: 2025年11月19日 12时00分00秒

// 使用ToString方法
Console.WriteLine($&quot;ToString(&quot;&quot;d&quot;&quot;): {now.ToString(&quot;d&quot;)}&quot;);
// 输出: ToString(&quot;d&quot;): 2025/11/19

Console.WriteLine($&quot;ToString(&quot;&quot;D&quot;&quot;): {now.ToString(&quot;D&quot;)}&quot;);
// 输出: ToString(&quot;D&quot;): 2025年11月19日 (中文环境)

Console.WriteLine($&quot;ToString(&quot;&quot;f&quot;&quot;): {now.ToString(&quot;f&quot;)}&quot;);
// 输出: ToString(&quot;f&quot;): 2025年11月19日 12:00 (中文环境)

Console.WriteLine($&quot;ToString(&quot;&quot;F&quot;&quot;): {now.ToString(&quot;F&quot;)}&quot;);
// 输出: ToString(&quot;F&quot;): 2025年11月19日 12:00:00 (中文环境)

Console.WriteLine($&quot;ToString(&quot;&quot;g&quot;&quot;): {now.ToString(&quot;g&quot;)}&quot;);
// 输出: ToString(&quot;g&quot;): 2025/11/19 12:00

Console.WriteLine($&quot;ToString(&quot;&quot;G&quot;&quot;): {now.ToString(&quot;G&quot;)}&quot;);
// 输出: ToString(&quot;G&quot;): 2025/11/19 12:00:00

Console.WriteLine($&quot;ToString(&quot;&quot;yyyy-MM-dd HH:mm:ss&quot;&quot;): {now.ToString(&quot;yyyy-MM-dd HH:mm:ss&quot;)}&quot;);
// 输出: ToString(&quot;yyyy-MM-dd HH:mm:ss&quot;): 2025-11-19 12:00:00
&lt;/code&gt;&lt;/pre&gt;
&lt;h3&gt;DateTime的计算和比较&lt;/h3&gt;
&lt;p&gt;DateTime支持各种计算和比较操作：&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;DateTime date1 = new DateTime(2025, 1, 1);
DateTime date2 = new DateTime(2025, 12, 31);

// 比较操作
Console.WriteLine($&quot;date1 &amp;lt; date2: {date1 &amp;lt; date2}&quot;);
// 输出: date1 &amp;lt; date2: True

Console.WriteLine($&quot;date1 &amp;gt; date2: {date1 &amp;gt; date2}&quot;);
// 输出: date1 &amp;gt; date2: False

Console.WriteLine($&quot;date1 == date2: {date1 == date2}&quot;);
// 输出: date1 == date2: False

Console.WriteLine($&quot;date1 != date2: {date1 != date2}&quot;);
// 输出: date1 != date2: True

Console.WriteLine($&quot;date1.CompareTo(date2): {date1.CompareTo(date2)}&quot;);
// 输出: date1.CompareTo(date2): -1 (负数表示date1 &amp;lt; date2)

// 计算时间差
TimeSpan difference = date2 - date1;
Console.WriteLine($&quot;时间差: {difference}&quot;);
// 输出: 时间差: 364.00:00:00

Console.WriteLine($&quot;相差天数: {difference.TotalDays}&quot;);
// 输出: 相差天数: 364

// 添加时间
DateTime futureDate = date1.AddYears(1);
DateTime pastDate = date1.AddMonths(-3);
DateTime nextWeek = date1.AddDays(7);
DateTime tomorrow = date1.AddHours(24);
DateTime nextMinute = date1.AddMinutes(1);
DateTime nextSecond = date1.AddSeconds(1);

Console.WriteLine($&quot;原日期: {date1}&quot;);
// 输出: 原日期: 2025/1/1 0:00:00

Console.WriteLine($&quot;增加1年: {futureDate}&quot;);
// 输出: 增加1年: 2026/1/1 0:00:00

Console.WriteLine($&quot;减少3个月: {pastDate}&quot;);
// 输出: 减少3个月: 2024/10/1 0:00:00

Console.WriteLine($&quot;增加7天: {nextWeek}&quot;);
// 输出: 增加7天: 2025/1/8 0:00:00

Console.WriteLine($&quot;增加24小时: {tomorrow}&quot;);
// 输出: 增加24小时: 2025/1/2 0:00:00

Console.WriteLine($&quot;增加1分钟: {nextMinute}&quot;);
// 输出: 增加1分钟: 2025/1/1 0:01:00

Console.WriteLine($&quot;增加1秒: {nextSecond}&quot;);
// 输出: 增加1秒: 2025/1/1 0:00:01
&lt;/code&gt;&lt;/pre&gt;
&lt;h4&gt;时间直接相加相减计算&lt;/h4&gt;
&lt;p&gt;DateTime支持与TimeSpan直接进行加减运算，这是处理时间间隔最灵活的方式：&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;DateTime date1 = new DateTime(2025, 1, 1, 10, 30, 0);
DateTime date2 = new DateTime(2025, 1, 1, 14, 45, 30);

// 1. DateTime与TimeSpan相加
TimeSpan span1 = new TimeSpan(2, 15, 30); // 2小时15分30秒
DateTime result1 = date1 + span1;
Console.WriteLine($&quot;原时间: {date1}&quot;);
// 输出: 原时间: 2025/1/1 10:30:00

Console.WriteLine($&quot;加上2小时15分30秒: {result1}&quot;);
// 输出: 加上2小时15分30秒: 2025/1/1 12:45:30

// 2. DateTime与TimeSpan相减
TimeSpan span2 = new TimeSpan(1, 30, 0); // 1小时30分钟
DateTime result2 = date1 - span2;
Console.WriteLine($&quot;减去1小时30分钟: {result2}&quot;);
// 输出: 减去1小时30分钟: 2025/1/1 9:00:00

// 3. 两个DateTime相减得到TimeSpan
TimeSpan difference = date2 - date1;
Console.WriteLine($&quot;时间差: {difference}&quot;);
// 输出: 时间差: 04:15:30 (4小时15分30秒)

// 4. 使用TimeSpan.FromXXX方法创建时间间隔
DateTime baseDate = new DateTime(2025, 1, 1, 12, 0, 0);

// 使用FromDays添加天数
DateTime afterDays = baseDate + TimeSpan.FromDays(5);
Console.WriteLine($&quot;5天后: {afterDays}&quot;);
// 输出: 5天后: 2025/1/6 12:00:00

// 使用FromHours添加小时
DateTime afterHours = baseDate + TimeSpan.FromHours(3.5);
Console.WriteLine($&quot;3.5小时后: {afterHours}&quot;);
// 输出: 3.5小时后: 2025/1/1 15:30:00

// 使用FromMinutes添加分钟
DateTime afterMinutes = baseDate + TimeSpan.FromMinutes(90);
Console.WriteLine($&quot;90分钟后: {afterMinutes}&quot;);
// 输出: 90分钟后: 2025/1/1 13:30:00

// 使用FromSeconds添加秒
DateTime afterSeconds = baseDate + TimeSpan.FromSeconds(3600);
Console.WriteLine($&quot;3600秒后: {afterSeconds}&quot;);
// 输出: 3600秒后: 2025/1/1 13:00:00

// 使用FromMilliseconds添加毫秒
DateTime afterMs = baseDate + TimeSpan.FromMilliseconds(1500);
Console.WriteLine($&quot;1500毫秒后: {afterMs}&quot;);
// 输出: 1500毫秒后: 2025/1/1 12:00:01

// 使用FromTicks添加刻度
DateTime afterTicks = baseDate + TimeSpan.FromTicks(10000000); // 1秒 = 10,000,000刻度
Console.WriteLine($&quot;10000000刻度后: {afterTicks}&quot;);
// 输出: 10000000刻度后: 2025/1/1 12:00:01

// 5. 组合使用多个TimeSpan
TimeSpan span3 = new TimeSpan(1, 2, 3, 4, 500); // 1天2小时3分4秒500毫秒
DateTime result3 = baseDate + span3;
Console.WriteLine($&quot;加上1天2小时3分4秒500毫秒: {result3}&quot;);
// 输出: 加上1天2小时3分4秒500毫秒: 2025/1/2 14:03:04

// 6. 负数TimeSpan表示向前推时间
TimeSpan negativeSpan = new TimeSpan(-2, -30, 0); // -2小时30分钟
DateTime result4 = baseDate + negativeSpan;
Console.WriteLine($&quot;减去2小时30分钟: {result4}&quot;);
// 输出: 减去2小时30分钟: 2025/1/1 9:30:00

// 7. 计算两个日期之间的完整时间差
DateTime start = new DateTime(2025, 1, 1, 8, 0, 0);
DateTime end = new DateTime(2025, 1, 3, 14, 30, 45);
TimeSpan totalDiff = end - start;
Console.WriteLine($&quot;总时间差: {totalDiff}&quot;);
// 输出: 总时间差: 2.06:30:45 (2天6小时30分45秒)

Console.WriteLine($&quot;总天数: {totalDiff.TotalDays}&quot;);
// 输出: 总天数: 2.2713541666666666

Console.WriteLine($&quot;总小时数: {totalDiff.TotalHours}&quot;);
// 输出: 总小时数: 54.5125

Console.WriteLine($&quot;总分钟数: {totalDiff.TotalMinutes}&quot;);
// 输出: 总分钟数: 3270.75

Console.WriteLine($&quot;总秒数: {totalDiff.TotalSeconds}&quot;);
// 输出: 总秒数: 196245
&lt;/code&gt;&lt;/pre&gt;
&lt;h3&gt;TimeSpan类详解&lt;/h3&gt;
&lt;p&gt;TimeSpan表示一个时间间隔，可以表示正数或负数的时间段。它是处理时间差、时间计算的重要类型。&lt;/p&gt;
&lt;h4&gt;TimeSpan的基本概念&lt;/h4&gt;
&lt;p&gt;TimeSpan可以表示从几纳秒到几百万天的时间间隔，精度为100纳秒（1个刻度）。&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;// TimeSpan的内部表示
// TimeSpan使用以下组件表示时间间隔：
// - Days: 天数部分
// - Hours: 小时部分（0-23）
// - Minutes: 分钟部分（0-59）
// - Seconds: 秒部分（0-59）
// - Milliseconds: 毫秒部分（0-999）
// - Ticks: 总刻度数（1秒 = 10,000,000刻度）
&lt;/code&gt;&lt;/pre&gt;
&lt;h4&gt;TimeSpan的创建和初始化&lt;/h4&gt;
&lt;pre&gt;&lt;code&gt;// 1. 使用构造函数创建TimeSpan
TimeSpan ts1 = new TimeSpan(1, 2, 3); // 1小时2分3秒
TimeSpan ts2 = new TimeSpan(1, 2, 3, 4); // 1天2小时3分4秒
TimeSpan ts3 = new TimeSpan(1, 2, 3, 4, 500); // 1天2小时3分4秒500毫秒
TimeSpan ts4 = new TimeSpan(100000000); // 使用刻度数创建（100000000刻度 = 10秒）

Console.WriteLine($&quot;ts1: {ts1}&quot;); // 输出: ts1: 01:02:03
Console.WriteLine($&quot;ts2: {ts2}&quot;); // 输出: ts2: 1.02:03:04
Console.WriteLine($&quot;ts3: {ts3}&quot;); // 输出: ts3: 1.02:03:04.5000000
Console.WriteLine($&quot;ts4: {ts4}&quot;); // 输出: ts4: 00:00:10

// 2. 使用静态方法创建TimeSpan
TimeSpan fromDays = TimeSpan.FromDays(2.5); // 2.5天
TimeSpan fromHours = TimeSpan.FromHours(12.5); // 12.5小时
TimeSpan fromMinutes = TimeSpan.FromMinutes(90); // 90分钟
TimeSpan fromSeconds = TimeSpan.FromSeconds(3600); // 3600秒 = 1小时
TimeSpan fromMs = TimeSpan.FromMilliseconds(1500); // 1500毫秒 = 1.5秒
TimeSpan fromTicks = TimeSpan.FromTicks(100000000); // 100000000刻度 = 10秒

Console.WriteLine($&quot;FromDays(2.5): {fromDays}&quot;); // 输出: FromDays(2.5): 2.12:00:00
Console.WriteLine($&quot;FromHours(12.5): {fromHours}&quot;); // 输出: FromHours(12.5): 12:30:00
Console.WriteLine($&quot;FromMinutes(90): {fromMinutes}&quot;); // 输出: FromMinutes(90): 01:30:00
Console.WriteLine($&quot;FromSeconds(3600): {fromSeconds}&quot;); // 输出: FromSeconds(3600): 01:00:00
Console.WriteLine($&quot;FromMilliseconds(1500): {fromMs}&quot;); // 输出: FromMilliseconds(1500): 00:00:01.5000000
Console.WriteLine($&quot;FromTicks(100000000): {fromTicks}&quot;); // 输出: FromTicks(100000000): 00:00:10

// 3. 使用静态属性获取常用TimeSpan值
TimeSpan zero = TimeSpan.Zero; // 零时间间隔
TimeSpan max = TimeSpan.MaxValue; // 最大时间间隔
TimeSpan min = TimeSpan.MinValue; // 最小时间间隔（负数）

Console.WriteLine($&quot;Zero: {zero}&quot;); // 输出: Zero: 00:00:00
Console.WriteLine($&quot;MaxValue: {max}&quot;); // 输出: MaxValue: 10675199.02:48:05.4775807
Console.WriteLine($&quot;MinValue: {min}&quot;); // 输出: MinValue: -10675199.02:48:05.4775808
&lt;/code&gt;&lt;/pre&gt;
&lt;h4&gt;TimeSpan的常用属性&lt;/h4&gt;
&lt;pre&gt;&lt;code&gt;TimeSpan ts = new TimeSpan(2, 3, 45, 30, 500); // 2天3小时45分30秒500毫秒

// 获取各个时间组件
Console.WriteLine($&quot;Days: {ts.Days}&quot;); // 输出: Days: 2
Console.WriteLine($&quot;Hours: {ts.Hours}&quot;); // 输出: Hours: 3
Console.WriteLine($&quot;Minutes: {ts.Minutes}&quot;); // 输出: Minutes: 45
Console.WriteLine($&quot;Seconds: {ts.Seconds}&quot;); // 输出: Seconds: 30
Console.WriteLine($&quot;Milliseconds: {ts.Milliseconds}&quot;); // 输出: Milliseconds: 500
Console.WriteLine($&quot;Ticks: {ts.Ticks}&quot;); // 输出: Ticks: 1887305000000

// 获取总时间（以不同单位表示）
Console.WriteLine($&quot;TotalDays: {ts.TotalDays}&quot;); // 输出: TotalDays: 2.1566041666666667
Console.WriteLine($&quot;TotalHours: {ts.TotalHours}&quot;); // 输出: TotalHours: 51.7585
Console.WriteLine($&quot;TotalMinutes: {ts.TotalMinutes}&quot;); // 输出: TotalMinutes: 3105.5083333333333
Console.WriteLine($&quot;TotalSeconds: {ts.TotalSeconds}&quot;); // 输出: TotalSeconds: 186330.5
Console.WriteLine($&quot;TotalMilliseconds: {ts.TotalMilliseconds}&quot;); // 输出: TotalMilliseconds: 186330500

// 其他属性
Console.WriteLine($&quot;是否为负数: {ts &amp;lt; TimeSpan.Zero}&quot;); // 输出: 是否为负数: False
&lt;/code&gt;&lt;/pre&gt;
&lt;h4&gt;TimeSpan的运算操作&lt;/h4&gt;
&lt;pre&gt;&lt;code&gt;TimeSpan ts1 = new TimeSpan(2, 30, 0); // 2小时30分钟
TimeSpan ts2 = new TimeSpan(1, 15, 0); // 1小时15分钟

// 1. TimeSpan相加
TimeSpan sum = ts1 + ts2;
Console.WriteLine($&quot;ts1 + ts2: {sum}&quot;); // 输出: ts1 + ts2: 03:45:00

// 2. TimeSpan相减
TimeSpan diff = ts1 - ts2;
Console.WriteLine($&quot;ts1 - ts2: {diff}&quot;); // 输出: ts1 - ts2: 01:15:00

// 3. TimeSpan乘以数值
TimeSpan multiplied = ts1 * 2;
Console.WriteLine($&quot;ts1 * 2: {multiplied}&quot;); // 输出: ts1 * 2: 05:00:00

// 4. TimeSpan除以数值
TimeSpan divided = ts1 / 2;
Console.WriteLine($&quot;ts1 / 2: {divided}&quot;); // 输出: ts1 / 2: 01:15:00

// 5. 两个TimeSpan相除（得到倍数）
double ratio = ts1 / ts2;
Console.WriteLine($&quot;ts1 / ts2 (倍数): {ratio}&quot;); // 输出: ts1 / ts2 (倍数): 2

// 6. TimeSpan取反
TimeSpan negated = -ts1;
Console.WriteLine($&quot;-ts1: {negated}&quot;); // 输出: -ts1: -02:30:00

// 7. TimeSpan比较
Console.WriteLine($&quot;ts1 &amp;gt; ts2: {ts1 &amp;gt; ts2}&quot;); // 输出: ts1 &amp;gt; ts2: True
Console.WriteLine($&quot;ts1 &amp;lt; ts2: {ts1 &amp;lt; ts2}&quot;); // 输出: ts1 &amp;lt; ts2: False
Console.WriteLine($&quot;ts1 == ts2: {ts1 == ts2}&quot;); // 输出: ts1 == ts2: False
Console.WriteLine($&quot;ts1 != ts2: {ts1 != ts2}&quot;); // 输出: ts1 != ts2: True
&lt;/code&gt;&lt;/pre&gt;
&lt;h4&gt;TimeSpan的常用方法&lt;/h4&gt;
&lt;pre&gt;&lt;code&gt;TimeSpan ts1 = new TimeSpan(2, 30, 45);
TimeSpan ts2 = new TimeSpan(1, 15, 30);

// 1. Add方法 - 添加另一个TimeSpan
TimeSpan added = ts1.Add(ts2);
Console.WriteLine($&quot;ts1.Add(ts2): {added}&quot;); // 输出: ts1.Add(ts2): 03:46:15

// 2. Subtract方法 - 减去另一个TimeSpan
TimeSpan subtracted = ts1.Subtract(ts2);
Console.WriteLine($&quot;ts1.Subtract(ts2): {subtracted}&quot;); // 输出: ts1.Subtract(ts2): 01:15:15

// 3. Negate方法 - 取反
TimeSpan negated = ts1.Negate();
Console.WriteLine($&quot;ts1.Negate(): {negated}&quot;); // 输出: ts1.Negate(): -02:30:45

// 4. Duration方法 - 获取绝对值
TimeSpan negative = new TimeSpan(-2, -30, -45);
TimeSpan duration = negative.Duration();
Console.WriteLine($&quot;Duration(): {duration}&quot;); // 输出: Duration(): 02:30:45

// 5. CompareTo方法 - 比较两个TimeSpan
int compare = ts1.CompareTo(ts2);
Console.WriteLine($&quot;ts1.CompareTo(ts2): {compare}&quot;); // 输出: ts1.CompareTo(ts2): 1 (正数表示ts1 &amp;gt; ts2)

// 6. Equals方法 - 判断是否相等
bool equal = ts1.Equals(ts2);
Console.WriteLine($&quot;ts1.Equals(ts2): {equal}&quot;); // 输出: ts1.Equals(ts2): False

// 7. ToString方法 - 转换为字符串
string str = ts1.ToString();
Console.WriteLine($&quot;ToString(): {str}&quot;); // 输出: ToString(): 02:30:45

// 8. ToString格式化
string formatted1 = ts1.ToString(@&quot;d\.hh\:mm\:ss&quot;);
Console.WriteLine($&quot;格式化1: {formatted1}&quot;); // 输出: 格式化1: 0.02:30:45

string formatted2 = ts1.ToString(@&quot;hh\:mm\:ss&quot;);
Console.WriteLine($&quot;格式化2: {formatted2}&quot;); // 输出: 格式化2: 02:30:45
&lt;/code&gt;&lt;/pre&gt;
&lt;h4&gt;TimeSpan的实际应用示例&lt;/h4&gt;
&lt;pre&gt;&lt;code&gt;// 1. 计算程序执行时间
DateTime startTime = DateTime.Now;
// 模拟一些操作
System.Threading.Thread.Sleep(1500); // 休眠1.5秒
DateTime endTime = DateTime.Now;
TimeSpan elapsed = endTime - startTime;
Console.WriteLine($&quot;程序执行时间: {elapsed.TotalMilliseconds}毫秒&quot;);
// 输出: 程序执行时间: 1500.1234毫秒 (实际值可能略有不同)

// 2. 计算剩余时间
DateTime deadline = new DateTime(2025, 12, 31, 23, 59, 59);
DateTime now = DateTime.Now;
TimeSpan remaining = deadline - now;
Console.WriteLine($&quot;距离截止日期还有: {remaining.Days}天 {remaining.Hours}小时&quot;);
// 输出: 距离截止日期还有: 42天 12小时 (实际值取决于当前时间)

// 3. 计算工作时间
TimeSpan workStart = new TimeSpan(9, 0, 0); // 9:00
TimeSpan workEnd = new TimeSpan(18, 0, 0); // 18:00
TimeSpan workDuration = workEnd - workStart;
Console.WriteLine($&quot;工作时间: {workDuration.TotalHours}小时&quot;);
// 输出: 工作时间: 9小时

// 4. 计算平均时间
TimeSpan[] durations = {
    new TimeSpan(2, 30, 0),
    new TimeSpan(3, 15, 0),
    new TimeSpan(2, 45, 0)
};
TimeSpan total = TimeSpan.Zero;
foreach (var duration in durations)
{
    total += duration;
}
TimeSpan average = TimeSpan.FromTicks(total.Ticks / durations.Length);
Console.WriteLine($&quot;平均时间: {average}&quot;);
// 输出: 平均时间: 02:50:00

// 5. 格式化时间间隔显示
TimeSpan interval = new TimeSpan(2, 3, 45, 30);
string formatted = $&quot;{interval.Days}天{interval.Hours}小时{interval.Minutes}分钟{interval.Seconds}秒&quot;;
Console.WriteLine($&quot;格式化显示: {formatted}&quot;);
// 输出: 格式化显示: 2天3小时45分钟30秒

// 6. 判断时间间隔是否在范围内
TimeSpan minInterval = new TimeSpan(0, 5, 0); // 最少5分钟
TimeSpan maxInterval = new TimeSpan(1, 0, 0); // 最多1小时
TimeSpan checkInterval = new TimeSpan(0, 30, 0); // 30分钟

bool inRange = checkInterval &amp;gt;= minInterval &amp;amp;&amp;amp; checkInterval &amp;lt;= maxInterval;
Console.WriteLine($&quot;30分钟是否在5分钟到1小时之间: {inRange}&quot;);
// 输出: 30分钟是否在5分钟到1小时之间: True
&lt;/code&gt;&lt;/pre&gt;
&lt;h4&gt;TimeSpan的格式化字符串&lt;/h4&gt;
&lt;p&gt;TimeSpan支持自定义格式化字符串：&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;TimeSpan ts = new TimeSpan(2, 3, 45, 30, 500);

// 标准格式
Console.WriteLine($&quot;默认格式: {ts}&quot;); // 输出: 默认格式: 2.03:45:30.5000000
Console.WriteLine($&quot;c格式: {ts.ToString(&quot;c&quot;)}&quot;); // 输出: c格式: 2.03:45:30.5000000
Console.WriteLine($&quot;g格式: {ts.ToString(&quot;g&quot;)}&quot;); // 输出: g格式: 2:3:45:30.5
Console.WriteLine($&quot;G格式: {ts.ToString(&quot;G&quot;)}&quot;); // 输出: G格式: 2:03:45:30.5000000

// 自定义格式
Console.WriteLine($@&quot;d\.hh\:mm\:ss: {ts.ToString(@&quot;d\.hh\:mm\:ss&quot;)}&quot;);
// 输出: d\.hh\:mm\:ss: 2.03:45:30

Console.WriteLine($@&quot;hh\:mm\:ss: {ts.ToString(@&quot;hh\:mm\:ss&quot;)}&quot;);
// 输出: hh\:mm\:ss: 03:45:30

Console.WriteLine($@&quot;d天hh小时mm分钟: {ts.ToString(@&quot;d天hh小时mm分钟&quot;)}&quot;);
// 输出: d天hh小时mm分钟: 2天03小时45分钟
&lt;/code&gt;&lt;/pre&gt;
&lt;h3&gt;DateTime的解析&lt;/h3&gt;
&lt;p&gt;可以将字符串解析为DateTime对象：&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;// 使用Parse方法
try
{
    DateTime parsedDate1 = DateTime.Parse(&quot;2025-11-20&quot;);
    DateTime parsedDate2 = DateTime.Parse(&quot;2025/11/20 14:30:00&quot;);
    DateTime parsedDate3 = DateTime.Parse(&quot;November 20, 2025&quot;);
    
    Console.WriteLine($&quot;解析&quot;2025-11-20&quot;: {parsedDate1}&quot;);
    // 输出: 解析&quot;2025-11-20&quot;: 2025/11/20 0:00:00
    
    Console.WriteLine($&quot;解析&quot;2025/11/20 14:30:00&quot;: {parsedDate2}&quot;);
    // 输出: 解析&quot;2025/11/20 14:30:00&quot;: 2025/11/20 14:30:00
    
    Console.WriteLine($&quot;解析&quot;November 20, 2025&quot;: {parsedDate3}&quot;);
    // 输出: 解析&quot;November 20, 2025&quot;: 2025/11/20 0:00:00
}
catch (FormatException ex)
{
    Console.WriteLine($&quot;解析失败: {ex.Message}&quot;);
}

// 使用TryParse方法（推荐）
if (DateTime.TryParse(&quot;2025-11-20&quot;, out DateTime result1))
{
    Console.WriteLine($&quot;TryParse成功: {result1}&quot;);
    // 输出: TryParse成功: 2025/11/20 0:00:00
}
else
{
    Console.WriteLine(&quot;TryParse失败&quot;);
}

// 使用ParseExact方法
try
{
    DateTime exactDate = DateTime.ParseExact(&quot;2025-11-20 14:30:00&quot;, &quot;yyyy-MM-dd HH:mm:ss&quot;, null);
    Console.WriteLine($&quot;ParseExact: {exactDate}&quot;);
    // 输出: ParseExact: 2025/11/20 14:30:00
}
catch (FormatException ex)
{
    Console.WriteLine($&quot;ParseExact失败: {ex.Message}&quot;);
}

// 使用TryParseExact方法
if (DateTime.TryParseExact(&quot;20/11/2025&quot;, &quot;dd/MM/yyyy&quot;, null, System.Globalization.DateTimeStyles.None, out DateTime result2))
{
    Console.WriteLine($&quot;TryParseExact成功: {result2}&quot;);
    // 输出: TryParseExact成功: 2025/11/20 0:00:00
}
else
{
    Console.WriteLine(&quot;TryParseExact失败&quot;);
}
&lt;/code&gt;&lt;/pre&gt;
&lt;h3&gt;高级时间解析&lt;/h3&gt;
&lt;p&gt;除了基本的解析方法，DateTime还支持更复杂的时间解析场景：&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;// 解析带时区信息的时间字符串
try
{
    // 解析ISO 8601格式的时间
    DateTime isoDate = DateTime.Parse(&quot;2025-11-20T14:30:00&quot;);
    Console.WriteLine($&quot;ISO 8601格式解析: {isoDate}&quot;);
    // 输出: ISO 8601格式解析: 2025/11/20 14:30:00
    
    // 解析带毫秒的时间
    DateTime millisecondDate = DateTime.Parse(&quot;2025-11-20 14:30:00.123&quot;);
    Console.WriteLine($&quot;带毫秒时间解析: {millisecondDate}&quot;);
    // 输出: 带毫秒时间解析: 2025/11/20 14:30:00
}
catch (FormatException ex)
{
    Console.WriteLine($&quot;解析带时区信息失败: {ex.Message}&quot;);
}

// 使用特定文化信息解析
try
{
    // 使用美国英语文化信息解析
    var usCulture = System.Globalization.CultureInfo.GetCultureInfo(&quot;en-US&quot;);
    DateTime usDate = DateTime.Parse(&quot;11/20/2025&quot;, usCulture);
    Console.WriteLine($&quot;美式日期解析: {usDate}&quot;);
    // 输出: 美式日期解析: 2025/11/20 0:00:00
    
    // 使用英国英语文化信息解析
    var ukCulture = System.Globalization.CultureInfo.GetCultureInfo(&quot;en-GB&quot;);
    DateTime ukDate = DateTime.Parse(&quot;20/11/2025&quot;, ukCulture);
    Console.WriteLine($&quot;英式日期解析: {ukDate}&quot;);
    // 输出: 英式日期解析: 2025/11/20 0:00:00
}
catch (FormatException ex)
{
    Console.WriteLine($&quot;文化信息解析失败: {ex.Message}&quot;);
}

// 处理多种可能的格式
public static bool TryParseMultipleFormats(string input, out DateTime result)
{
    string[] formats = {
        &quot;yyyy-MM-dd&quot;,
        &quot;yyyy/MM/dd&quot;,
        &quot;dd-MM-yyyy&quot;,
        &quot;dd/MM/yyyy&quot;,
        &quot;yyyy-MM-dd HH:mm:ss&quot;,
        &quot;yyyy/MM/dd HH:mm:ss&quot;,
        &quot;MM/dd/yyyy&quot;,
        &quot;MMMM dd, yyyy&quot;
    };
    
    return DateTime.TryParseExact(input, formats, null, System.Globalization.DateTimeStyles.None, out result);
}

// 使用示例
string[] testDates = { &quot;2025-11-20&quot;, &quot;20/11/2025&quot;, &quot;11/20/2025&quot;, &quot;November 20, 2025&quot; };
foreach (string testDate in testDates)
{
    if (TryParseMultipleFormats(testDate, out DateTime parsedDate))
    {
        Console.WriteLine($&quot;多格式解析 &apos;{testDate}&apos; 成功: {parsedDate}&quot;);
        // 输出示例:
        // 多格式解析 &apos;2025-11-20&apos; 成功: 2025/11/20 0:00:00
        // 多格式解析 &apos;20/11/2025&apos; 成功: 2025/11/20 0:00:00
        // 多格式解析 &apos;11/20/2025&apos; 成功: 2025/11/20 0:00:00
        // 多格式解析 &apos;November 20, 2025&apos; 成功: 2025/11/20 0:00:00
    }
    else
    {
        Console.WriteLine($&quot;多格式解析 &apos;{testDate}&apos; 失败&quot;);
    }
}
&lt;/code&gt;&lt;/pre&gt;
&lt;h3&gt;时间戳处理&lt;/h3&gt;
&lt;p&gt;在许多应用场景中，特别是与Web API交互时，经常需要处理Unix时间戳。Unix时间戳是从1970年1月1日00:00:00 UTC开始计算的秒数或毫秒数：&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;// Unix时间戳常量
public static readonly DateTime UnixEpoch = new DateTime(1970, 1, 1, 0, 0, 0, DateTimeKind.Utc);

// 将DateTime转换为Unix时间戳（秒）
public static long ToUnixTimestamp(DateTime dateTime)
{
    return (long)(dateTime.ToUniversalTime() - UnixEpoch).TotalSeconds;
}

// 将DateTime转换为Unix时间戳（毫秒）
public static long ToUnixTimestampMilliseconds(DateTime dateTime)
{
    return (long)(dateTime.ToUniversalTime() - UnixEpoch).TotalMilliseconds;
}

// 将Unix时间戳（秒）转换为DateTime
public static DateTime FromUnixTimestamp(long timestamp)
{
    return UnixEpoch.AddSeconds(timestamp).ToLocalTime();
}

// 将Unix时间戳（毫秒）转换为DateTime
public static DateTime FromUnixTimestampMilliseconds(long timestamp)
{
    return UnixEpoch.AddMilliseconds(timestamp).ToLocalTime();
}

// 使用示例
DateTime now = DateTime.Now;
Console.WriteLine($&quot;当前时间: {now}&quot;);
// 输出: 当前时间: 2025/11/19 12:00:00 (实际值取决于当前时间)

// 转换为Unix时间戳
long unixTimestamp = ToUnixTimestamp(now);
long unixTimestampMs = ToUnixTimestampMilliseconds(now);
Console.WriteLine($&quot;Unix时间戳(秒): {unixTimestamp}&quot;);
// 输出: Unix时间戳(秒): 1763452800 (实际值取决于当前时间)

Console.WriteLine($&quot;Unix时间戳(毫秒): {unixTimestampMs}&quot;);
// 输出: Unix时间戳(毫秒): 1763452800000 (实际值取决于当前时间)

// 从Unix时间戳转换回DateTime
DateTime fromUnix = FromUnixTimestamp(unixTimestamp);
DateTime fromUnixMs = FromUnixTimestampMilliseconds(unixTimestampMs);
Console.WriteLine($&quot;从时间戳转换: {fromUnix}&quot;);
// 输出: 从时间戳转换: 2025/11/19 12:00:00 (实际值取决于当前时间)

Console.WriteLine($&quot;从毫秒时间戳转换: {fromUnixMs}&quot;);
// 输出: 从毫秒时间戳转换: 2025/11/19 12:00:00 (实际值取决于当前时间)

// 处理JavaScript时间戳（毫秒）
// JavaScript中的Date.now()返回的是毫秒时间戳
long jsTimestamp = 1758432600000; // 示例时间戳
DateTime jsDateTime = FromUnixTimestampMilliseconds(jsTimestamp);
Console.WriteLine($&quot;JavaScript时间戳转换: {jsDateTime}&quot;);
// 输出: JavaScript时间戳转换: 2025/9/22 0:00:00
&lt;/code&gt;&lt;/pre&gt;
&lt;h3&gt;DateTime的实际应用示例&lt;/h3&gt;
&lt;p&gt;以下是一些DateTime在实际开发中的应用场景：&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;using System;

class DateTimeExamples
{
    // 计算年龄
    public static int CalculateAge(DateTime birthDate)
    {
        DateTime today = DateTime.Today;
        int age = today.Year - birthDate.Year;
        
        // 检查是否还没过生日
        if (birthDate.Date &amp;gt; today.AddYears(-age))
        {
            age--;
        }
        
        return age;
    }
    
    // 计算工作日
    public static int CalculateWorkDays(DateTime startDate, DateTime endDate)
    {
        int workDays = 0;
        DateTime currentDate = startDate;
        
        while (currentDate &amp;lt;= endDate)
        {
            if (currentDate.DayOfWeek != DayOfWeek.Saturday &amp;amp;&amp;amp; 
                currentDate.DayOfWeek != DayOfWeek.Sunday)
            {
                workDays++;
            }
            currentDate = currentDate.AddDays(1);
        }
        
        return workDays;
    }
    
    // 格式化时间显示（如：刚刚、几分钟前、几小时前等）
    public static string FormatTimeAgo(DateTime dateTime)
    {
        TimeSpan timeSpan = DateTime.Now - dateTime;
        
        if (timeSpan.TotalSeconds &amp;lt; 60)
        {
            return &quot;刚刚&quot;;
        }
        else if (timeSpan.TotalMinutes &amp;lt; 60)
        {
            return $&quot;{timeSpan.Minutes}分钟前&quot;;
        }
        else if (timeSpan.TotalHours &amp;lt; 24)
        {
            return $&quot;{timeSpan.Hours}小时前&quot;;
        }
        else if (timeSpan.TotalDays &amp;lt; 30)
        {
            return $&quot;{timeSpan.Days}天前&quot;;
        }
        else
        {
            return dateTime.ToString(&quot;yyyy年MM月dd日&quot;);
        }
    }
    
    // 获取指定月份的最后一天
    public static DateTime GetLastDayOfMonth(int year, int month)
    {
        return new DateTime(year, month, 1).AddMonths(1).AddDays(-1);
    }
    
    static void Main()
    {
        // 测试计算年龄
        DateTime birthDate = new DateTime(1990, 5, 15);
        Console.WriteLine($&quot;出生日期: {birthDate:yyyy年MM月dd日}&quot;);
        Console.WriteLine($&quot;当前年龄: {CalculateAge(birthDate)}岁&quot;);
        
        // 测试计算工作日
        DateTime startDate = new DateTime(2025, 11, 1);
        DateTime endDate = new DateTime(2025, 11, 30);
        Console.WriteLine($&quot;{startDate:yyyy年MM月dd日}到{endDate:yyyy年MM月dd日}的工作日: {CalculateWorkDays(startDate, endDate)}天&quot;);
        
        // 测试时间显示格式化
        DateTime[] testDates = {
            DateTime.Now.AddSeconds(-30),
            DateTime.Now.AddMinutes(-15),
            DateTime.Now.AddHours(-3),
            DateTime.Now.AddDays(-5),
            DateTime.Now.AddDays(-45)
        };
        
        foreach (DateTime date in testDates)
        {
            Console.WriteLine($&quot;{date:yyyy年MM月dd日 HH:mm} -&amp;gt; {FormatTimeAgo(date)}&quot;);
        }
        
        // 测试获取月份最后一天
        Console.WriteLine($&quot;2025年2月的最后一天: {GetLastDayOfMonth(2025, 2):yyyy年MM月dd日}&quot;);
        Console.WriteLine($&quot;2025年12月的最后一天: {GetLastDayOfMonth(2025, 12):yyyy年MM月dd日}&quot;);
    }
}
&lt;/code&gt;&lt;/pre&gt;
&lt;h3&gt;DateTime使用注意事项&lt;/h3&gt;
&lt;ol&gt;
&lt;li&gt;
&lt;p&gt;&lt;strong&gt;时区问题&lt;/strong&gt;：DateTime.Now返回本地时间，DateTime.UtcNow返回UTC时间。在处理跨时区应用时，应明确使用哪种时间。&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;&lt;strong&gt;精度问题&lt;/strong&gt;：DateTime的精度是100纳秒，但在某些平台上可能达不到这个精度。&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;&lt;strong&gt;夏令时问题&lt;/strong&gt;：在涉及夏令时的地区，需要注意时间计算可能出现的问题。&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;&lt;strong&gt;线程安全性&lt;/strong&gt;：DateTime结构是不可变的，因此是线程安全的。&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;&lt;strong&gt;异常处理&lt;/strong&gt;：在解析字符串为DateTime时，应使用TryParse或TryParseExact方法，避免异常处理的开销。&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;&lt;strong&gt;性能考虑&lt;/strong&gt;：频繁创建DateTime对象可能影响性能，在性能敏感的代码中应考虑优化。&lt;/p&gt;
&lt;/li&gt;
&lt;/ol&gt;
&lt;h3&gt;DateTime类常用属性和方法速查表&lt;/h3&gt;
&lt;h4&gt;常用属性&lt;/h4&gt;
&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;属性&lt;/th&gt;
&lt;th&gt;类型&lt;/th&gt;
&lt;th&gt;作用说明&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;Now&lt;/td&gt;
&lt;td&gt;DateTime&lt;/td&gt;
&lt;td&gt;获取当前本地日期和时间&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;UtcNow&lt;/td&gt;
&lt;td&gt;DateTime&lt;/td&gt;
&lt;td&gt;获取当前UTC日期和时间&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Today&lt;/td&gt;
&lt;td&gt;DateTime&lt;/td&gt;
&lt;td&gt;获取当前日期，时间为00:00:00&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;MinValue&lt;/td&gt;
&lt;td&gt;DateTime&lt;/td&gt;
&lt;td&gt;表示DateTime的最小可能值&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;MaxValue&lt;/td&gt;
&lt;td&gt;DateTime&lt;/td&gt;
&lt;td&gt;表示DateTime的最大可能值&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Year&lt;/td&gt;
&lt;td&gt;int&lt;/td&gt;
&lt;td&gt;获取日期的年份部分&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Month&lt;/td&gt;
&lt;td&gt;int&lt;/td&gt;
&lt;td&gt;获取日期的月份部分&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Day&lt;/td&gt;
&lt;td&gt;int&lt;/td&gt;
&lt;td&gt;获取日期的天数部分&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Hour&lt;/td&gt;
&lt;td&gt;int&lt;/td&gt;
&lt;td&gt;获取时间的小时部分&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Minute&lt;/td&gt;
&lt;td&gt;int&lt;/td&gt;
&lt;td&gt;获取时间的分钟部分&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Second&lt;/td&gt;
&lt;td&gt;int&lt;/td&gt;
&lt;td&gt;获取时间的秒部分&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Millisecond&lt;/td&gt;
&lt;td&gt;int&lt;/td&gt;
&lt;td&gt;获取时间的毫秒部分&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;DayOfWeek&lt;/td&gt;
&lt;td&gt;DayOfWeek&lt;/td&gt;
&lt;td&gt;获取星期几&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;DayOfYear&lt;/td&gt;
&lt;td&gt;int&lt;/td&gt;
&lt;td&gt;获取一年中的第几天&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Ticks&lt;/td&gt;
&lt;td&gt;long&lt;/td&gt;
&lt;td&gt;获取表示日期和时间的刻度数&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Date&lt;/td&gt;
&lt;td&gt;DateTime&lt;/td&gt;
&lt;td&gt;获取日期部分&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;TimeOfDay&lt;/td&gt;
&lt;td&gt;TimeSpan&lt;/td&gt;
&lt;td&gt;获取时间部分&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;
&lt;h4&gt;常用方法&lt;/h4&gt;
&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;方法&lt;/th&gt;
&lt;th&gt;参数&lt;/th&gt;
&lt;th&gt;返回值&lt;/th&gt;
&lt;th&gt;作用说明&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;Add(TimeSpan)&lt;/td&gt;
&lt;td&gt;TimeSpan&lt;/td&gt;
&lt;td&gt;DateTime&lt;/td&gt;
&lt;td&gt;在此实例的值上添加指定的时间间隔&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;AddDays(double)&lt;/td&gt;
&lt;td&gt;double&lt;/td&gt;
&lt;td&gt;DateTime&lt;/td&gt;
&lt;td&gt;在此实例的值上添加指定的天数&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;AddHours(double)&lt;/td&gt;
&lt;td&gt;double&lt;/td&gt;
&lt;td&gt;DateTime&lt;/td&gt;
&lt;td&gt;在此实例的值上添加指定的小时数&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;AddMinutes(double)&lt;/td&gt;
&lt;td&gt;double&lt;/td&gt;
&lt;td&gt;DateTime&lt;/td&gt;
&lt;td&gt;在此实例的值上添加指定的分钟数&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;AddMonths(int)&lt;/td&gt;
&lt;td&gt;int&lt;/td&gt;
&lt;td&gt;DateTime&lt;/td&gt;
&lt;td&gt;在此实例的值上添加指定的月数&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;AddSeconds(double)&lt;/td&gt;
&lt;td&gt;double&lt;/td&gt;
&lt;td&gt;DateTime&lt;/td&gt;
&lt;td&gt;在此实例的值上添加指定的秒数&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;AddYears(int)&lt;/td&gt;
&lt;td&gt;int&lt;/td&gt;
&lt;td&gt;DateTime&lt;/td&gt;
&lt;td&gt;在此实例的值上添加指定的年数&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;CompareTo(DateTime)&lt;/td&gt;
&lt;td&gt;DateTime&lt;/td&gt;
&lt;td&gt;int&lt;/td&gt;
&lt;td&gt;将此实例与指定的DateTime对象进行比较&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Equals(DateTime)&lt;/td&gt;
&lt;td&gt;DateTime&lt;/td&gt;
&lt;td&gt;bool&lt;/td&gt;
&lt;td&gt;返回一个值，该值指示此实例是否等于指定的DateTime实例&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;ToString()&lt;/td&gt;
&lt;td&gt;无&lt;/td&gt;
&lt;td&gt;string&lt;/td&gt;
&lt;td&gt;将当前DateTime对象的值转换为其等效的字符串表示形式&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;ToString(string)&lt;/td&gt;
&lt;td&gt;string&lt;/td&gt;
&lt;td&gt;string&lt;/td&gt;
&lt;td&gt;使用指定的格式将当前DateTime对象的值转换为字符串&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Parse(string)&lt;/td&gt;
&lt;td&gt;string&lt;/td&gt;
&lt;td&gt;DateTime&lt;/td&gt;
&lt;td&gt;将日期和时间的字符串表示形式转换为等效的DateTime&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;TryParse(string, out DateTime)&lt;/td&gt;
&lt;td&gt;string, out DateTime&lt;/td&gt;
&lt;td&gt;bool&lt;/td&gt;
&lt;td&gt;将日期和时间的字符串表示形式转换为等效的DateTime，返回是否转换成功&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;ParseExact(string, string, IFormatProvider)&lt;/td&gt;
&lt;td&gt;string, string, IFormatProvider&lt;/td&gt;
&lt;td&gt;DateTime&lt;/td&gt;
&lt;td&gt;使用指定的格式将日期和时间的字符串表示形式转换为等效的DateTime&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;TryParseExact(string, string, IFormatProvider, DateTimeStyles, out DateTime)&lt;/td&gt;
&lt;td&gt;string, string, IFormatProvider, DateTimeStyles, out DateTime&lt;/td&gt;
&lt;td&gt;bool&lt;/td&gt;
&lt;td&gt;使用指定的格式将日期和时间的字符串表示形式转换为等效的DateTime，返回是否转换成功&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;ToShortDateString()&lt;/td&gt;
&lt;td&gt;无&lt;/td&gt;
&lt;td&gt;string&lt;/td&gt;
&lt;td&gt;将当前DateTime对象的值转换为其等效的短日期字符串表示形式&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;ToLongDateString()&lt;/td&gt;
&lt;td&gt;无&lt;/td&gt;
&lt;td&gt;string&lt;/td&gt;
&lt;td&gt;将当前DateTime对象的值转换为其等效的长日期字符串表示形式&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;ToShortTimeString()&lt;/td&gt;
&lt;td&gt;无&lt;/td&gt;
&lt;td&gt;string&lt;/td&gt;
&lt;td&gt;将当前DateTime对象的值转换为其等效的短时间字符串表示形式&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;ToLongTimeString()&lt;/td&gt;
&lt;td&gt;无&lt;/td&gt;
&lt;td&gt;string&lt;/td&gt;
&lt;td&gt;将当前DateTime对象的值转换为其等效的长时间字符串表示形式&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;
&lt;p&gt;通过合理使用DateTime类，可以方便地处理各种日期和时间相关的操作，满足应用程序中对时间处理的需求。&lt;/p&gt;
&lt;h2&gt;&amp;lt;a id=&quot;array-class&quot;&amp;gt;&amp;lt;/a&amp;gt;C# Array类详解&lt;/h2&gt;
&lt;p&gt;数组(Array)是C#中最基本的数据结构之一，用于存储相同类型的元素集合。数组在内存中是连续存储的，可以通过索引快速访问元素。C#提供了Array类来操作数组，同时支持多种创建和操作数组的方法。&lt;/p&gt;
&lt;h3&gt;Array类的基本概念&lt;/h3&gt;
&lt;p&gt;在C#中，数组是引用类型，继承自System.Array类。数组一旦创建，其大小就是固定的。数组的索引从0开始，可以通过索引访问和修改数组元素。&lt;/p&gt;
&lt;h3&gt;Array的创建和初始化&lt;/h3&gt;
&lt;p&gt;C#提供了多种创建和初始化数组的方法：&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;using System;

// 1. 声明数组但不初始化
int[] numbers1;

// 2. 创建指定大小的数组（元素初始化为默认值）
int[] numbers2 = new int[5]; // 创建包含5个int元素的数组，所有元素初始化为0

// 3. 创建并初始化数组
int[] numbers3 = new int[] { 1, 2, 3, 4, 5 };
int[] numbers4 = { 1, 2, 3, 4, 5 }; // 简化语法

// 4. 创建多维数组
int[,] matrix = new int[3, 4]; // 3行4列的二维数组
int[,] matrix2 = { { 1, 2, 3, 4 }, { 5, 6, 7, 8 }, { 9, 10, 11, 12 } };

// 5. 创建交错数组（数组的数组）
int[][] jaggedArray = new int[3][];
jaggedArray[0] = new int[] { 1, 2 };
jaggedArray[1] = new int[] { 3, 4, 5, 6 };
jaggedArray[2] = new int[] { 7, 8, 9 };

// 6. 使用Array类的静态方法创建数组
int[] numbers5 = Array.CreateInstance(typeof(int), 5) as int[];

// 输出数组内容
Console.WriteLine(&quot;一维数组:&quot;);
for (int i = 0; i &amp;lt; numbers3.Length; i++)
{
    Console.Write(numbers3[i] + &quot; &quot;);
}
Console.WriteLine();

Console.WriteLine(&quot;二维数组:&quot;);
for (int i = 0; i &amp;lt; matrix2.GetLength(0); i++)
{
    for (int j = 0; j &amp;lt; matrix2.GetLength(1); j++)
    {
        Console.Write(matrix2[i, j] + &quot;\t&quot;);
    }
    Console.WriteLine();
}

Console.WriteLine(&quot;交错数组:&quot;);
for (int i = 0; i &amp;lt; jaggedArray.Length; i++)
{
    for (int j = 0; j &amp;lt; jaggedArray[i].Length; j++)
    {
        Console.Write(jaggedArray[i][j] + &quot; &quot;);
    }
    Console.WriteLine();
}
&lt;/code&gt;&lt;/pre&gt;
&lt;h3&gt;Array的常用属性&lt;/h3&gt;
&lt;p&gt;Array类提供了多个有用的属性来获取数组的信息：&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;int[] numbers = { 1, 2, 3, 4, 5 };
int[,] matrix = { { 1, 2, 3 }, { 4, 5, 6 } };

// Length属性 - 获取数组中所有元素的总数
Console.WriteLine($&quot;一维数组长度: {numbers.Length}&quot;);
Console.WriteLine($&quot;二维数组总元素数: {matrix.Length}&quot;);

// Rank属性 - 获取数组的维数
Console.WriteLine($&quot;一维数组维数: {numbers.Rank}&quot;);
Console.WriteLine($&quot;二维数组维数: {matrix.Rank}&quot;);

// GetLength方法 - 获取指定维度的长度
Console.WriteLine($&quot;二维数组第1维长度: {matrix.GetLength(0)}&quot;);
Console.WriteLine($&quot;二维数组第2维长度: {matrix.GetLength(1)}&quot;);

// IsFixedSize属性 - 检查数组是否有固定大小
Console.WriteLine($&quot;数组是否有固定大小: {numbers.IsFixedSize}&quot;);

// IsReadOnly属性 - 检查数组是否为只读
Console.WriteLine($&quot;数组是否只读: {numbers.IsReadOnly}&quot;);
&lt;/code&gt;&lt;/pre&gt;
&lt;h3&gt;Array的常用方法&lt;/h3&gt;
&lt;p&gt;Array类提供了丰富的静态和实例方法来操作数组：&lt;/p&gt;
&lt;h4&gt;排序和搜索方法&lt;/h4&gt;
&lt;pre&gt;&lt;code&gt;int[] numbers = { 5, 2, 8, 1, 9, 3 };
string[] names = { &quot;张三&quot;, &quot;李四&quot;, &quot;王五&quot;, &quot;赵六&quot; };

Console.WriteLine(&quot;原始数组:&quot;);
Console.WriteLine($&quot;数字: {string.Join(&quot;, &quot;, numbers)}&quot;);
Console.WriteLine($&quot;姓名: {string.Join(&quot;, &quot;, names)}&quot;);

// Sort方法 - 对数组进行排序
Array.Sort(numbers);
Array.Sort(names);

Console.WriteLine(&quot;排序后:&quot;);
Console.WriteLine($&quot;数字: {string.Join(&quot;, &quot;, numbers)}&quot;);
Console.WriteLine($&quot;姓名: {string.Join(&quot;, &quot;, names)}&quot;);

// 重新初始化数组以演示搜索
numbers = new int[] { 5, 2, 8, 1, 9, 3 };
Array.Sort(numbers); // 先排序，因为BinarySearch要求数组已排序

// BinarySearch方法 - 在已排序数组中搜索元素
int index = Array.BinarySearch(numbers, 8);
if (index &amp;gt;= 0)
{
    Console.WriteLine($&quot;找到元素8，索引为: {index}&quot;);
}
else
{
    Console.WriteLine(&quot;未找到元素8&quot;);
}

// IndexOf方法 - 在数组中搜索元素首次出现的索引
int[] unsortedNumbers = { 5, 2, 8, 1, 9, 3, 8 };
int firstIndex = Array.IndexOf(unsortedNumbers, 8);
Console.WriteLine($&quot;元素8首次出现的索引: {firstIndex}&quot;);

// LastIndexOf方法 - 在数组中搜索元素最后出现的索引
int lastIndex = Array.LastIndexOf(unsortedNumbers, 8);
Console.WriteLine($&quot;元素8最后出现的索引: {lastIndex}&quot;);
&lt;/code&gt;&lt;/pre&gt;
&lt;h4&gt;数组操作方法&lt;/h4&gt;
&lt;pre&gt;&lt;code&gt;int[] sourceArray = { 1, 2, 3, 4, 5 };

// Copy方法 - 复制数组元素
int[] destinationArray = new int[5];
Array.Copy(sourceArray, destinationArray, sourceArray.Length);
Console.WriteLine($&quot;复制后的数组: {string.Join(&quot;, &quot;, destinationArray)}&quot;);

// CopyTo方法 - 将数组元素复制到另一个数组
int[] targetArray = new int[5];
sourceArray.CopyTo(targetArray, 0);
Console.WriteLine($&quot;CopyTo后的数组: {string.Join(&quot;, &quot;, targetArray)}&quot;);

// Clone方法 - 创建数组的浅表副本
int[] clonedArray = (int[])sourceArray.Clone();
Console.WriteLine($&quot;克隆的数组: {string.Join(&quot;, &quot;, clonedArray)}&quot;);

// Clear方法 - 将数组元素设置为默认值
int[] tempArray = { 1, 2, 3, 4, 5 };
Console.WriteLine($&quot;清除前: {string.Join(&quot;, &quot;, tempArray)}&quot;);
Array.Clear(tempArray, 1, 3); // 从索引1开始清除3个元素
Console.WriteLine($&quot;清除后: {string.Join(&quot;, &quot;, tempArray)}&quot;);

// Reverse方法 - 反转数组元素
int[] reverseArray = { 1, 2, 3, 4, 5 };
Console.WriteLine($&quot;反转前: {string.Join(&quot;, &quot;, reverseArray)}&quot;);
Array.Reverse(reverseArray);
Console.WriteLine($&quot;反转后: {string.Join(&quot;, &quot;, reverseArray)}&quot;);

// Resize方法 - 调整数组大小（注意：这是Array类的静态方法，但定义在System命名空间中）
int[] resizeArray = { 1, 2, 3 };
Console.WriteLine($&quot;调整大小前: {string.Join(&quot;, &quot;, resizeArray)} (长度: {resizeArray.Length})&quot;);
Array.Resize(ref resizeArray, 5); // 扩展到5个元素
Console.WriteLine($&quot;扩展后: {string.Join(&quot;, &quot;, resizeArray)} (长度: {resizeArray.Length})&quot;);
Array.Resize(ref resizeArray, 2); // 缩小到2个元素
Console.WriteLine($&quot;缩小后: {string.Join(&quot;, &quot;, resizeArray)} (长度: {resizeArray.Length})&quot;);
&lt;/code&gt;&lt;/pre&gt;
&lt;h3&gt;Array的实际应用示例&lt;/h3&gt;
&lt;p&gt;以下是一些Array在实际开发中的应用场景：&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;using System;
using System.Linq;

class ArrayExamples
{
    // 查找数组中的最大值和最小值
    public static (int max, int min) FindMaxMin(int[] array)
    {
        if (array == null || array.Length == 0)
            throw new ArgumentException(&quot;数组不能为空&quot;);
            
        int max = array[0];
        int min = array[0];
        
        for (int i = 1; i &amp;lt; array.Length; i++)
        {
            if (array[i] &amp;gt; max)
                max = array[i];
            if (array[i] &amp;lt; min)
                min = array[i];
        }
        
        return (max, min);
    }
    
    // 计算数组元素的平均值
    public static double CalculateAverage(int[] array)
    {
        if (array == null || array.Length == 0)
            throw new ArgumentException(&quot;数组不能为空&quot;);
            
        long sum = 0;
        foreach (int value in array)
        {
            sum += value;
        }
        
        return (double)sum / array.Length;
    }
    
    // 移除数组中的重复元素
    public static int[] RemoveDuplicates(int[] array)
    {
        if (array == null || array.Length == 0)
            return new int[0];
            
        // 使用Array.Sort和自定义逻辑
        int[] sortedArray = (int[])array.Clone();
        Array.Sort(sortedArray);
        
        int[] temp = new int[sortedArray.Length];
        int count = 1;
        temp[0] = sortedArray[0];
        
        for (int i = 1; i &amp;lt; sortedArray.Length; i++)
        {
            if (sortedArray[i] != sortedArray[i - 1])
            {
                temp[count] = sortedArray[i];
                count++;
            }
        }
        
        int[] result = new int[count];
        Array.Copy(temp, result, count);
        return result;
    }
    
    // 合并两个数组
    public static T[] MergeArrays&amp;lt;T&amp;gt;(T[] array1, T[] array2)
    {
        T[] result = new T[array1.Length + array2.Length];
        Array.Copy(array1, 0, result, 0, array1.Length);
        Array.Copy(array2, 0, result, array1.Length, array2.Length);
        return result;
    }
    
    static void Main()
    {
        // 测试查找最大值和最小值
        int[] numbers = { 5, 2, 8, 1, 9, 3 };
        var (max, min) = FindMaxMin(numbers);
        Console.WriteLine($&quot;数组 {string.Join(&quot;, &quot;, numbers)} 中的最大值: {max}, 最小值: {min}&quot;);
        
        // 测试计算平均值
        double average = CalculateAverage(numbers);
        Console.WriteLine($&quot;数组的平均值: {average}&quot;);
        
        // 测试移除重复元素
        int[] withDuplicates = { 5, 2, 8, 1, 9, 3, 5, 8, 1 };
        int[] unique = RemoveDuplicates(withDuplicates);
        Console.WriteLine($&quot;原数组: {string.Join(&quot;, &quot;, withDuplicates)}&quot;);
        Console.WriteLine($&quot;去重后: {string.Join(&quot;, &quot;, unique)}&quot;);
        
        // 测试合并数组
        int[] arr1 = { 1, 2, 3 };
        int[] arr2 = { 4, 5, 6 };
        int[] merged = MergeArrays(arr1, arr2);
        Console.WriteLine($&quot;合并数组: {string.Join(&quot;, &quot;, merged)}&quot;);
    }
}
&lt;/code&gt;&lt;/pre&gt;
&lt;h3&gt;Array使用注意事项&lt;/h3&gt;
&lt;ol&gt;
&lt;li&gt;
&lt;p&gt;&lt;strong&gt;数组大小固定&lt;/strong&gt;：数组一旦创建，其大小就不能改变。如果需要动态调整大小，应考虑使用List&amp;lt;T&amp;gt;等集合类。&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;&lt;strong&gt;索引越界检查&lt;/strong&gt;：访问数组元素时必须确保索引在有效范围内，否则会抛出IndexOutOfRangeException异常。&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;&lt;strong&gt;性能考虑&lt;/strong&gt;：数组是性能最高的集合类型之一，因为它们在内存中连续存储，访问速度快。&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;&lt;strong&gt;多维数组与交错数组&lt;/strong&gt;：多维数组是矩形的，而交错数组的每一行可以有不同的长度。&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;&lt;strong&gt;引用类型与值类型&lt;/strong&gt;：存储引用类型的数组实际存储的是引用，而存储值类型的数组存储的是实际值。&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;&lt;strong&gt;内存分配&lt;/strong&gt;：大型数组会在大对象堆(LOH)上分配，可能影响垃圾回收性能。&lt;/p&gt;
&lt;/li&gt;
&lt;/ol&gt;
&lt;h3&gt;Array类常用属性和方法速查表&lt;/h3&gt;
&lt;h4&gt;常用属性&lt;/h4&gt;
&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;属性&lt;/th&gt;
&lt;th&gt;类型&lt;/th&gt;
&lt;th&gt;作用说明&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;Length&lt;/td&gt;
&lt;td&gt;int&lt;/td&gt;
&lt;td&gt;获取数组中所有元素的总数。例如：int[] arr = {1,2,3}; int count = arr.Length; // 返回3&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Rank&lt;/td&gt;
&lt;td&gt;int&lt;/td&gt;
&lt;td&gt;获取数组的维数。一维数组返回1，二维数组返回2，以此类推&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;IsFixedSize&lt;/td&gt;
&lt;td&gt;bool&lt;/td&gt;
&lt;td&gt;获取一个值，该值指示数组是否有固定大小。对于所有Array实例，该值始终为true&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;IsReadOnly&lt;/td&gt;
&lt;td&gt;bool&lt;/td&gt;
&lt;td&gt;获取一个值，该值指示数组是否为只读。对于所有Array实例，该值始终为false&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;IsSynchronized&lt;/td&gt;
&lt;td&gt;bool&lt;/td&gt;
&lt;td&gt;获取一个值，该值指示对数组的访问是否同步（线程安全）。对于所有Array实例，该值始终为false&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;SyncRoot&lt;/td&gt;
&lt;td&gt;object&lt;/td&gt;
&lt;td&gt;获取可用于同步对数组的访问的对象。通常用于多线程环境中的同步操作&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;
&lt;h4&gt;常用静态方法&lt;/h4&gt;
&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;方法&lt;/th&gt;
&lt;th&gt;参数&lt;/th&gt;
&lt;th&gt;返回值&lt;/th&gt;
&lt;th&gt;作用说明&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;BinarySearch(Array, object)&lt;/td&gt;
&lt;td&gt;Array array, object value&lt;/td&gt;
&lt;td&gt;int&lt;/td&gt;
&lt;td&gt;在已排序的一维数组中搜索指定对象。如果找到，返回元素索引；否则返回负数。array必须已排序&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;BinarySearch(Array, object, IComparer)&lt;/td&gt;
&lt;td&gt;Array array, object value, IComparer comparer&lt;/td&gt;
&lt;td&gt;int&lt;/td&gt;
&lt;td&gt;使用指定的IComparer接口在已排序的数组中搜索元素&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;BinarySearch(Array, int, int, object)&lt;/td&gt;
&lt;td&gt;Array array, int index, int length, object value&lt;/td&gt;
&lt;td&gt;int&lt;/td&gt;
&lt;td&gt;在数组的指定范围内搜索元素。index是起始索引，length是搜索的元素个数&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Clear(Array, int, int)&lt;/td&gt;
&lt;td&gt;Array array, int index, int length&lt;/td&gt;
&lt;td&gt;void&lt;/td&gt;
&lt;td&gt;将数组中指定范围的元素设置为该类型默认值。index是起始索引，length是要清除的元素个数&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Copy(Array, Array, int)&lt;/td&gt;
&lt;td&gt;Array sourceArray, Array destinationArray, int length&lt;/td&gt;
&lt;td&gt;void&lt;/td&gt;
&lt;td&gt;从第一个数组复制指定数量的元素到第二个数组。length是要复制的元素个数&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Copy(Array, int, Array, int, int)&lt;/td&gt;
&lt;td&gt;Array sourceArray, int sourceIndex, Array destinationArray, int destinationIndex, int length&lt;/td&gt;
&lt;td&gt;void&lt;/td&gt;
&lt;td&gt;从源数组的指定索引开始，复制指定数量的元素到目标数组的指定索引位置&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;CreateInstance(Type, int)&lt;/td&gt;
&lt;td&gt;Type elementType, int length&lt;/td&gt;
&lt;td&gt;Array&lt;/td&gt;
&lt;td&gt;创建指定类型和一维长度的数组。例如：Array.CreateInstance(typeof(int), 5)创建包含5个int元素的数组&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;CreateInstance(Type, int[])&lt;/td&gt;
&lt;td&gt;Type elementType, int[] lengths&lt;/td&gt;
&lt;td&gt;Array&lt;/td&gt;
&lt;td&gt;创建指定类型和维度长度的多维数组。lengths数组指定每个维度的长度&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;CreateInstance(Type, int[], int[])&lt;/td&gt;
&lt;td&gt;Type elementType, int[] lengths, int[] lowerBounds&lt;/td&gt;
&lt;td&gt;Array&lt;/td&gt;
&lt;td&gt;创建指定类型、维度长度和下界的多维数组&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;IndexOf(Array, object)&lt;/td&gt;
&lt;td&gt;Array array, object value&lt;/td&gt;
&lt;td&gt;int&lt;/td&gt;
&lt;td&gt;在一维数组中搜索指定对象，返回首次出现的索引。如果未找到，返回-1&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;IndexOf(Array, object, int)&lt;/td&gt;
&lt;td&gt;Array array, object value, int startIndex&lt;/td&gt;
&lt;td&gt;int&lt;/td&gt;
&lt;td&gt;从指定索引开始，在一维数组中搜索指定对象&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;IndexOf(Array, object, int, int)&lt;/td&gt;
&lt;td&gt;Array array, object value, int startIndex, int count&lt;/td&gt;
&lt;td&gt;int&lt;/td&gt;
&lt;td&gt;在数组的指定范围内搜索指定对象。startIndex是起始索引，count是搜索的元素个数&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;LastIndexOf(Array, object)&lt;/td&gt;
&lt;td&gt;Array array, object value&lt;/td&gt;
&lt;td&gt;int&lt;/td&gt;
&lt;td&gt;在一维数组中搜索指定对象，返回最后出现的索引。如果未找到，返回-1&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;LastIndexOf(Array, object, int)&lt;/td&gt;
&lt;td&gt;Array array, object value, int startIndex&lt;/td&gt;
&lt;td&gt;int&lt;/td&gt;
&lt;td&gt;从指定索引开始，反向搜索指定对象&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;LastIndexOf(Array, object, int, int)&lt;/td&gt;
&lt;td&gt;Array array, object value, int startIndex, int count&lt;/td&gt;
&lt;td&gt;int&lt;/td&gt;
&lt;td&gt;在数组的指定范围内反向搜索指定对象&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Reverse(Array)&lt;/td&gt;
&lt;td&gt;Array array&lt;/td&gt;
&lt;td&gt;void&lt;/td&gt;
&lt;td&gt;反转整个一维数组中元素的顺序&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Reverse(Array, int, int)&lt;/td&gt;
&lt;td&gt;Array array, int index, int length&lt;/td&gt;
&lt;td&gt;void&lt;/td&gt;
&lt;td&gt;反转数组指定范围内元素的顺序。index是起始索引，length是要反转的元素个数&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Sort(Array)&lt;/td&gt;
&lt;td&gt;Array array&lt;/td&gt;
&lt;td&gt;void&lt;/td&gt;
&lt;td&gt;对一维数组中的元素进行升序排序&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Sort(Array, Array)&lt;/td&gt;
&lt;td&gt;Array keys, Array items&lt;/td&gt;
&lt;td&gt;void&lt;/td&gt;
&lt;td&gt;基于第一个数组中的键对两个数组进行排序，第一个数组包含键，第二个数组包含对应的项&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Sort(Array, IComparer)&lt;/td&gt;
&lt;td&gt;Array array, IComparer comparer&lt;/td&gt;
&lt;td&gt;void&lt;/td&gt;
&lt;td&gt;使用指定的IComparer接口对数组进行排序&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Sort(Array, int, int)&lt;/td&gt;
&lt;td&gt;Array array, int index, int length&lt;/td&gt;
&lt;td&gt;void&lt;/td&gt;
&lt;td&gt;对数组的指定范围内元素进行排序。index是起始索引，length是要排序的元素个数&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Sort(Array, Array, IComparer)&lt;/td&gt;
&lt;td&gt;Array keys, Array items, IComparer comparer&lt;/td&gt;
&lt;td&gt;void&lt;/td&gt;
&lt;td&gt;基于第一个数组中的键对两个数组进行排序，并使用指定的IComparer接口&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Sort(Array, Array, int, int)&lt;/td&gt;
&lt;td&gt;Array keys, Array items, int index, int length&lt;/td&gt;
&lt;td&gt;void&lt;/td&gt;
&lt;td&gt;对两个数组的指定范围内元素进行排序&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Sort(Array, Array, int, int, IComparer)&lt;/td&gt;
&lt;td&gt;Array keys, Array items, int index, int length, IComparer comparer&lt;/td&gt;
&lt;td&gt;void&lt;/td&gt;
&lt;td&gt;对两个数组的指定范围内元素进行排序，并使用指定的IComparer接口&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Sort(Array, int, int, IComparer)&lt;/td&gt;
&lt;td&gt;Array array, int index, int length, IComparer comparer&lt;/td&gt;
&lt;td&gt;void&lt;/td&gt;
&lt;td&gt;对数组的指定范围内元素进行排序，并使用指定的IComparer接口&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Resize&amp;lt;T&amp;gt;(ref T[], int)&lt;/td&gt;
&lt;td&gt;ref T[] array, int newSize&lt;/td&gt;
&lt;td&gt;void&lt;/td&gt;
&lt;td&gt;将数组大小调整为指定大小。这是一个泛型方法，不是Array类的成员，但与数组操作密切相关&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;
&lt;h4&gt;常用实例方法&lt;/h4&gt;
&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;方法&lt;/th&gt;
&lt;th&gt;参数&lt;/th&gt;
&lt;th&gt;返回值&lt;/th&gt;
&lt;th&gt;作用说明&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;Clone()&lt;/td&gt;
&lt;td&gt;无&lt;/td&gt;
&lt;td&gt;object&lt;/td&gt;
&lt;td&gt;创建数组的浅表副本。对于值类型数组，会复制值；对于引用类型数组，会复制引用但不复制引用的对象&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;CopyTo(Array, int)&lt;/td&gt;
&lt;td&gt;Array array, int index&lt;/td&gt;
&lt;td&gt;void&lt;/td&gt;
&lt;td&gt;将当前一维数组的所有元素复制到指定的一维数组中。index是目标数组中的起始索引&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;GetLength(int)&lt;/td&gt;
&lt;td&gt;int dimension&lt;/td&gt;
&lt;td&gt;int&lt;/td&gt;
&lt;td&gt;获取指定维度的长度。dimension是维度索引，从0开始。对于二维数组，0表示行数，1表示列数&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;GetLowerBound(int)&lt;/td&gt;
&lt;td&gt;int dimension&lt;/td&gt;
&lt;td&gt;int&lt;/td&gt;
&lt;td&gt;获取指定维度的下界。对于C#数组，通常返回0&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;GetUpperBound(int)&lt;/td&gt;
&lt;td&gt;int dimension&lt;/td&gt;
&lt;td&gt;int&lt;/td&gt;
&lt;td&gt;获取指定维度的上界。等于该维度长度减1&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;GetValue(int)&lt;/td&gt;
&lt;td&gt;int index&lt;/td&gt;
&lt;td&gt;object&lt;/td&gt;
&lt;td&gt;获取一维数组中指定位置的值。index是元素索引&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;GetValue(int, int)&lt;/td&gt;
&lt;td&gt;int index1, int index2&lt;/td&gt;
&lt;td&gt;object&lt;/td&gt;
&lt;td&gt;获取二维数组中指定位置的值。index1是行索引，index2是列索引&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;GetValue(int, int, int)&lt;/td&gt;
&lt;td&gt;int index1, int index2, int index3&lt;/td&gt;
&lt;td&gt;object&lt;/td&gt;
&lt;td&gt;获取三维数组中指定位置的值&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;GetValue(int[])&lt;/td&gt;
&lt;td&gt;int[] indices&lt;/td&gt;
&lt;td&gt;object&lt;/td&gt;
&lt;td&gt;获取多维数组中指定位置的值。indices数组包含每个维度的索引&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;SetValue(object, int)&lt;/td&gt;
&lt;td&gt;object value, int index&lt;/td&gt;
&lt;td&gt;void&lt;/td&gt;
&lt;td&gt;设置一维数组中指定位置的值。value是要设置的值，index是元素索引&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;SetValue(object, int, int)&lt;/td&gt;
&lt;td&gt;object value, int index1, int index2&lt;/td&gt;
&lt;td&gt;void&lt;/td&gt;
&lt;td&gt;设置二维数组中指定位置的值。index1是行索引，index2是列索引&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;SetValue(object, int, int, int)&lt;/td&gt;
&lt;td&gt;object value, int index1, int index2, int index3&lt;/td&gt;
&lt;td&gt;void&lt;/td&gt;
&lt;td&gt;设置三维数组中指定位置的值&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;SetValue(object, int[])&lt;/td&gt;
&lt;td&gt;object value, int[] indices&lt;/td&gt;
&lt;td&gt;void&lt;/td&gt;
&lt;td&gt;设置多维数组中指定位置的值。indices数组包含每个维度的索引&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;
&lt;p&gt;需要注意的是，Array类本身不直接提供Resize方法。Array.Resize&amp;lt;T&amp;gt;(ref T[] array, int newSize)是一个静态泛型方法，定义在System命名空间中，专门用于调整数组大小。当数组扩展时，新元素被设置为类型的默认值；当数组缩小时，多余的元素会被丢弃。&lt;/p&gt;
&lt;p&gt;在实际开发中，如果需要动态调整数组大小，通常建议使用List&amp;lt;T&amp;gt;集合类，它提供了更好的性能和更丰富的功能。但在某些特定场景下，Array.Resize方法仍然很有用。&lt;/p&gt;
&lt;p&gt;通过合理使用Array类，可以高效地处理各种数据集合操作，满足应用程序中对数据存储和处理的需求。&lt;/p&gt;
&lt;h2&gt;&amp;lt;a id=&quot;string-class&quot;&amp;gt;&amp;lt;/a&amp;gt;C# String类详解&lt;/h2&gt;
&lt;p&gt;字符串(String)是C#中最常用的数据类型之一，用于存储和处理文本数据。C#中的字符串是System.String类的别名，提供了丰富的字符串操作方法。理解字符串的特性和使用方法对于编写高效的C#程序至关重要。&lt;/p&gt;
&lt;h3&gt;String类的基本概念&lt;/h3&gt;
&lt;p&gt;在C#中，字符串是引用类型，但具有值类型的某些特性。字符串是不可变的(immutable)，这意味着一旦创建，字符串的内容就不能被修改。任何看似修改字符串的操作实际上都会创建一个新的字符串对象。&lt;/p&gt;
&lt;h4&gt;字符串的不可变性&lt;/h4&gt;
&lt;pre&gt;&lt;code&gt;string str = &quot;Hello&quot;;
str = str + &quot; World&quot;; // 这里不是修改原字符串，而是创建了一个新字符串

// 演示不可变性
string original = &quot;Hello&quot;;
string modified = original;
modified = modified + &quot; World&quot;;
Console.WriteLine($&quot;原字符串: {original}&quot;); // 输出: 原字符串: Hello
Console.WriteLine($&quot;修改后的字符串: {modified}&quot;); // 输出: 修改后的字符串: Hello World
&lt;/code&gt;&lt;/pre&gt;
&lt;h3&gt;String的创建和初始化&lt;/h3&gt;
&lt;p&gt;C#提供了多种创建和初始化字符串的方法：&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;using System;

// 1. 使用字符串字面量
string str1 = &quot;Hello, World!&quot;;
string str2 = &quot;C#编程&quot;;

// 2. 使用String构造函数
string str3 = new string(&apos;A&apos;, 5); // 创建包含5个&apos;A&apos;字符的字符串: &quot;AAAAA&quot;
char[] chars = { &apos;H&apos;, &apos;e&apos;, &apos;l&apos;, &apos;l&apos;, &apos;o&apos; };
string str4 = new string(chars); // &quot;Hello&quot;

// 3. 使用字符串插值（String Interpolation）
string name = &quot;张三&quot;;
int age = 25;
string str5 = $&quot;姓名: {name}, 年龄: {age}&quot;; // &quot;姓名: 张三, 年龄: 25&quot;

// 4. 使用String.Format方法
string str6 = String.Format(&quot;姓名: {0}, 年龄: {1}&quot;, name, age);

// 5. 使用字符串连接
string str7 = &quot;Hello&quot; + &quot; &quot; + &quot;World&quot;; // &quot;Hello World&quot;

// 6. 使用@符号创建逐字字符串（Verbatim String）
string path1 = &quot;C:\\Users\\Documents\\file.txt&quot;; // 需要转义
string path2 = @&quot;C:\Users\Documents\file.txt&quot;; // 不需要转义
string multiline = @&quot;第一行
第二行
第三行&quot;;

// 7. 空字符串和null
string empty1 = &quot;&quot;; // 空字符串
string empty2 = String.Empty; // 空字符串（推荐）
string nullString = null; // null引用

// 输出结果
Console.WriteLine($&quot;str1: {str1}&quot;);
Console.WriteLine($&quot;str3: {str3}&quot;);
Console.WriteLine($&quot;str5: {str5}&quot;);
Console.WriteLine($&quot;path2: {path2}&quot;);
&lt;/code&gt;&lt;/pre&gt;
&lt;h3&gt;String的常用属性&lt;/h3&gt;
&lt;p&gt;String类提供了多个有用的属性来获取字符串的信息：&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;string text = &quot;Hello, World!&quot;;

// Length属性 - 获取字符串的字符数
Console.WriteLine($&quot;字符串长度: {text.Length}&quot;); // 输出: 13

// 检查字符串是否为空或null
string empty = &quot;&quot;;
string nullStr = null;

Console.WriteLine($&quot;empty是否为空: {String.IsNullOrEmpty(empty)}&quot;); // true
Console.WriteLine($&quot;nullStr是否为null或空: {String.IsNullOrEmpty(nullStr)}&quot;); // true
Console.WriteLine($&quot;empty是否为空白: {String.IsNullOrWhiteSpace(empty)}&quot;); // true
Console.WriteLine($&quot;text是否为空白: {String.IsNullOrWhiteSpace(&quot;   &quot;)}&quot;); // true

// 访问字符串中的字符（通过索引）
char firstChar = text[0]; // &apos;H&apos;
char lastChar = text[text.Length - 1]; // &apos;!&apos;
Console.WriteLine($&quot;第一个字符: {firstChar}&quot;);
Console.WriteLine($&quot;最后一个字符: {lastChar}&quot;);
&lt;/code&gt;&lt;/pre&gt;
&lt;h3&gt;String的常用方法&lt;/h3&gt;
&lt;p&gt;String类提供了丰富的实例方法和静态方法来操作字符串：&lt;/p&gt;
&lt;h4&gt;字符串查找方法&lt;/h4&gt;
&lt;pre&gt;&lt;code&gt;string text = &quot;Hello, World! Hello, C#!&quot;;

// IndexOf方法 - 查找子字符串首次出现的位置
int index1 = text.IndexOf(&quot;Hello&quot;); // 0
int index2 = text.IndexOf(&quot;World&quot;); // 7
int index3 = text.IndexOf(&quot;Java&quot;); // -1 (未找到)

// LastIndexOf方法 - 查找子字符串最后出现的位置
int lastIndex = text.LastIndexOf(&quot;Hello&quot;); // 14

// Contains方法 - 检查字符串是否包含子字符串
bool contains = text.Contains(&quot;World&quot;); // true

// StartsWith方法 - 检查字符串是否以指定子字符串开头
bool startsWith = text.StartsWith(&quot;Hello&quot;); // true

// EndsWith方法 - 检查字符串是否以指定子字符串结尾
bool endsWith = text.EndsWith(&quot;!&quot;); // true

Console.WriteLine($&quot;IndexOf(&apos;Hello&apos;): {index1}&quot;);
Console.WriteLine($&quot;LastIndexOf(&apos;Hello&apos;): {lastIndex}&quot;);
Console.WriteLine($&quot;Contains(&apos;World&apos;): {contains}&quot;);
&lt;/code&gt;&lt;/pre&gt;
&lt;h4&gt;字符串截取和分割方法&lt;/h4&gt;
&lt;pre&gt;&lt;code&gt;string text = &quot;Hello, World, C#, Programming&quot;;

// Substring方法 - 截取子字符串
string sub1 = text.Substring(0, 5); // &quot;Hello&quot; (从索引0开始，长度为5)
string sub2 = text.Substring(7); // &quot;World, C#, Programming&quot; (从索引7到末尾)

// Split方法 - 分割字符串
string[] parts1 = text.Split(&apos;,&apos;); // 按逗号分割
string[] parts2 = text.Split(new char[] { &apos;,&apos;, &apos; &apos; }, StringSplitOptions.RemoveEmptyEntries); // 按多个字符分割并移除空项

Console.WriteLine($&quot;Substring(0, 5): {sub1}&quot;);
Console.WriteLine($&quot;Split结果:&quot;);
foreach (string part in parts1)
{
    Console.WriteLine($&quot;  - {part.Trim()}&quot;); // Trim()移除首尾空白
}

// 使用Split的重载方法
string data = &quot;张三|25|北京|工程师&quot;;
string[] fields = data.Split(&apos;|&apos;);
Console.WriteLine($&quot;姓名: {fields[0]}, 年龄: {fields[1]}, 城市: {fields[2]}, 职业: {fields[3]}&quot;);
&lt;/code&gt;&lt;/pre&gt;
&lt;h4&gt;字符串替换方法&lt;/h4&gt;
&lt;pre&gt;&lt;code&gt;string text = &quot;Hello, World! Hello, C#!&quot;;

// Replace方法 - 替换字符串
string replaced1 = text.Replace(&quot;Hello&quot;, &quot;Hi&quot;); // &quot;Hi, World! Hi, C#!&quot;
string replaced2 = text.Replace(&quot;World&quot;, &quot;Universe&quot;); // &quot;Hello, Universe! Hello, C#!&quot;

// 替换单个字符
string replaced3 = text.Replace(&apos;o&apos;, &apos;O&apos;); // &quot;HellO, WOrld! HellO, C#!&quot;

Console.WriteLine($&quot;替换&apos;Hello&apos;为&apos;Hi&apos;: {replaced1}&quot;);
Console.WriteLine($&quot;替换&apos;o&apos;为&apos;O&apos;: {replaced3}&quot;);

// 移除字符串
string removed = text.Remove(5, 2); // 从索引5开始移除2个字符: &quot;Hello World! Hello, C#!&quot;
Console.WriteLine($&quot;移除字符后: {removed}&quot;);
&lt;/code&gt;&lt;/pre&gt;
&lt;h4&gt;字符串大小写转换方法&lt;/h4&gt;
&lt;pre&gt;&lt;code&gt;string text = &quot;Hello, World!&quot;;

// ToUpper方法 - 转换为大写
string upper = text.ToUpper(); // &quot;HELLO, WORLD!&quot;

// ToLower方法 - 转换为小写
string lower = text.ToLower(); // &quot;hello, world!&quot;

Console.WriteLine($&quot;大写: {upper}&quot;);
Console.WriteLine($&quot;小写: {lower}&quot;);
&lt;/code&gt;&lt;/pre&gt;
&lt;h4&gt;字符串格式化方法&lt;/h4&gt;
&lt;pre&gt;&lt;code&gt;string name = &quot;张三&quot;;
int age = 25;
double salary = 8500.50;

// 使用字符串插值（推荐）
string formatted1 = $&quot;姓名: {name}, 年龄: {age}, 薪资: {salary:C}&quot;;

// 使用String.Format方法
string formatted2 = String.Format(&quot;姓名: {0}, 年龄: {1}, 薪资: {2:C}&quot;, name, age, salary);

// 使用ToString方法格式化
string formatted3 = $&quot;薪资: {salary:F2}&quot;; // 保留2位小数
string formatted4 = $&quot;百分比: {0.25:P}&quot;; // 25.00%
string formatted5 = $&quot;十六进制: {255:X}&quot;; // FF

Console.WriteLine(formatted1);
Console.WriteLine(formatted2);
Console.WriteLine(formatted3);
Console.WriteLine(formatted4);
Console.WriteLine(formatted5);
&lt;/code&gt;&lt;/pre&gt;
&lt;h4&gt;字符串连接方法&lt;/h4&gt;
&lt;pre&gt;&lt;code&gt;string[] words = { &quot;Hello&quot;, &quot;World&quot;, &quot;C#&quot;, &quot;Programming&quot; };

// 使用+运算符连接
string joined1 = words[0] + &quot; &quot; + words[1]; // &quot;Hello World&quot;

// 使用String.Concat方法
string joined2 = String.Concat(words); // &quot;HelloWorldC#Programming&quot;

// 使用String.Join方法（推荐）
string joined3 = String.Join(&quot; &quot;, words); // &quot;Hello World C# Programming&quot;
string joined4 = String.Join(&quot;, &quot;, words); // &quot;Hello, World, C#, Programming&quot;

Console.WriteLine($&quot;Join结果: {joined3}&quot;);
Console.WriteLine($&quot;Join结果(逗号分隔): {joined4}&quot;);
&lt;/code&gt;&lt;/pre&gt;
&lt;h4&gt;字符串修剪方法&lt;/h4&gt;
&lt;pre&gt;&lt;code&gt;string text = &quot;   Hello, World!   &quot;;

// Trim方法 - 移除首尾空白字符
string trimmed = text.Trim(); // &quot;Hello, World!&quot;

// TrimStart方法 - 移除开头空白字符
string trimmedStart = text.TrimStart(); // &quot;Hello, World!   &quot;

// TrimEnd方法 - 移除结尾空白字符
string trimmedEnd = text.TrimEnd(); // &quot;   Hello, World!&quot;

// Trim方法可以指定要移除的字符
string text2 = &quot;***Hello, World!***&quot;;
string trimmed2 = text2.Trim(&apos;*&apos;); // &quot;Hello, World!&quot;

Console.WriteLine($&quot;Trim结果: &apos;{trimmed}&apos;&quot;);
Console.WriteLine($&quot;Trim(&apos;*&apos;)结果: &apos;{trimmed2}&apos;&quot;);
&lt;/code&gt;&lt;/pre&gt;
&lt;h3&gt;StringBuilder类&lt;/h3&gt;
&lt;p&gt;当需要频繁修改字符串时，使用StringBuilder类可以获得更好的性能，因为它不会每次都创建新的字符串对象。&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;using System.Text;

// 创建StringBuilder对象
StringBuilder sb = new StringBuilder();

// Append方法 - 追加字符串
sb.Append(&quot;Hello&quot;);
sb.Append(&quot; &quot;);
sb.Append(&quot;World&quot;);

// AppendLine方法 - 追加字符串并换行
sb.AppendLine();
sb.AppendLine(&quot;C# Programming&quot;);

// AppendFormat方法 - 追加格式化字符串
sb.AppendFormat(&quot;姓名: {0}, 年龄: {1}&quot;, &quot;张三&quot;, 25);

// Insert方法 - 在指定位置插入字符串
sb.Insert(0, &quot;开始: &quot;);

// Remove方法 - 移除指定范围的字符
sb.Remove(0, 4); // 移除前4个字符

// Replace方法 - 替换字符串
sb.Replace(&quot;World&quot;, &quot;Universe&quot;);

// Clear方法 - 清空内容
// sb.Clear();

// ToString方法 - 转换为字符串
string result = sb.ToString();
Console.WriteLine(result);

// StringBuilder的容量管理
StringBuilder sb2 = new StringBuilder(100); // 指定初始容量
StringBuilder sb3 = new StringBuilder(&quot;初始内容&quot;, 100); // 指定初始内容和容量

// 获取和设置容量
Console.WriteLine($&quot;当前长度: {sb2.Length}, 容量: {sb2.Capacity}&quot;);
sb2.EnsureCapacity(200); // 确保容量至少为200
&lt;/code&gt;&lt;/pre&gt;
&lt;h3&gt;字符串比较&lt;/h3&gt;
&lt;p&gt;C#提供了多种字符串比较方法，每种方法适用于不同的场景：&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;string str1 = &quot;Hello&quot;;
string str2 = &quot;hello&quot;;
string str3 = &quot;Hello&quot;;

// == 运算符和 Equals方法 - 区分大小写的比较
bool equal1 = (str1 == str2); // false
bool equal2 = str1.Equals(str2); // false
bool equal3 = str1.Equals(str3); // true

// String.Equals静态方法
bool equal4 = String.Equals(str1, str2); // false
bool equal5 = String.Equals(str1, str2, StringComparison.OrdinalIgnoreCase); // true (忽略大小写)

// CompareTo方法 - 比较字符串（返回负数、0或正数）
int compare1 = str1.CompareTo(str2); // 负数（str1 &amp;lt; str2）
int compare2 = str1.CompareTo(str3); // 0 (相等)

// String.Compare静态方法
int compare3 = String.Compare(str1, str2); // 负数
int compare4 = String.Compare(str1, str2, StringComparison.OrdinalIgnoreCase); // 0 (忽略大小写)

// StringComparison枚举的常用值
// Ordinal: 区分大小写的序数比较（最快）
// OrdinalIgnoreCase: 不区分大小写的序数比较
// CurrentCulture: 使用当前文化信息比较
// InvariantCulture: 使用固定文化信息比较

Console.WriteLine($&quot;str1 == str2: {equal1}&quot;);
Console.WriteLine($&quot;CompareTo结果: {compare1}&quot;);
&lt;/code&gt;&lt;/pre&gt;
&lt;h3&gt;字符串的实际应用示例&lt;/h3&gt;
&lt;p&gt;以下是一些String在实际开发中的应用场景：&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;using System;
using System.Text;
using System.Text.RegularExpressions;

class StringExamples
{
    // 验证邮箱格式
    public static bool IsValidEmail(string email)
    {
        if (String.IsNullOrWhiteSpace(email))
            return false;
        
        // 简单的邮箱验证（实际应用中应使用更严格的验证）
        return email.Contains(&quot;@&quot;) &amp;amp;&amp;amp; 
               email.Contains(&quot;.&quot;) &amp;amp;&amp;amp; 
               email.IndexOf(&quot;@&quot;) &amp;lt; email.LastIndexOf(&quot;.&quot;);
    }
    
    // 提取字符串中的数字
    public static string ExtractNumbers(string input)
    {
        if (String.IsNullOrEmpty(input))
            return &quot;&quot;;
        
        StringBuilder numbers = new StringBuilder();
        foreach (char c in input)
        {
            if (Char.IsDigit(c))
            {
                numbers.Append(c);
            }
        }
        return numbers.ToString();
    }
    
    // 反转字符串
    public static string ReverseString(string input)
    {
        if (String.IsNullOrEmpty(input))
            return input;
        
        char[] chars = input.ToCharArray();
        Array.Reverse(chars);
        return new string(chars);
    }
    
    // 统计字符串中单词的数量
    public static int CountWords(string text)
    {
        if (String.IsNullOrWhiteSpace(text))
            return 0;
        
        string[] words = text.Split(new char[] { &apos; &apos;, &apos;\t&apos;, &apos;\n&apos;, &apos;\r&apos; }, 
                                    StringSplitOptions.RemoveEmptyEntries);
        return words.Length;
    }
    
    // 首字母大写
    public static string CapitalizeFirstLetter(string input)
    {
        if (String.IsNullOrEmpty(input))
            return input;
        
        return Char.ToUpper(input[0]) + input.Substring(1).ToLower();
    }
    
    // 移除HTML标签
    public static string RemoveHtmlTags(string html)
    {
        if (String.IsNullOrEmpty(html))
            return html;
        
        // 简单的HTML标签移除（实际应用中应使用更完善的方法）
        return Regex.Replace(html, &quot;&amp;lt;.*?&amp;gt;&quot;, String.Empty);
    }
    
    // 格式化手机号码（添加分隔符）
    public static string FormatPhoneNumber(string phone)
    {
        if (String.IsNullOrEmpty(phone) || phone.Length != 11)
            return phone;
        
        return $&quot;{phone.Substring(0, 3)}-{phone.Substring(3, 4)}-{phone.Substring(7)}&quot;;
    }
    
    // 检查字符串是否为回文
    public static bool IsPalindrome(string input)
    {
        if (String.IsNullOrEmpty(input))
            return false;
        
        string cleaned = input.Replace(&quot; &quot;, &quot;&quot;).ToLower();
        string reversed = ReverseString(cleaned);
        return cleaned == reversed;
    }
    
    static void Main()
    {
        // 测试邮箱验证
        Console.WriteLine($&quot;&apos;test@example.com&apos; 是否为有效邮箱: {IsValidEmail(&quot;test@example.com&quot;)}&quot;);
        Console.WriteLine($&quot;&apos;invalid-email&apos; 是否为有效邮箱: {IsValidEmail(&quot;invalid-email&quot;)}&quot;);
        
        // 测试提取数字
        string textWithNumbers = &quot;我有3个苹果和5个橙子&quot;;
        Console.WriteLine($&quot;从&apos;{textWithNumbers}&apos;中提取数字: {ExtractNumbers(textWithNumbers)}&quot;);
        
        // 测试反转字符串
        Console.WriteLine($&quot;&apos;Hello&apos;反转后: {ReverseString(&quot;Hello&quot;)}&quot;);
        
        // 测试统计单词
        string sentence = &quot;C# 是一门强大的编程语言&quot;;
        Console.WriteLine($&quot;&apos;{sentence}&apos;中的单词数: {CountWords(sentence)}&quot;);
        
        // 测试首字母大写
        Console.WriteLine($&quot;&apos;hello world&apos;首字母大写: {CapitalizeFirstLetter(&quot;hello world&quot;)}&quot;);
        
        // 测试格式化手机号
        Console.WriteLine($&quot;格式化手机号: {FormatPhoneNumber(&quot;13812345678&quot;)}&quot;);
        
        // 测试回文检查
        Console.WriteLine($&quot;&apos;level&apos;是否为回文: {IsPalindrome(&quot;level&quot;)}&quot;);
        Console.WriteLine($&quot;&apos;hello&apos;是否为回文: {IsPalindrome(&quot;hello&quot;)}&quot;);
    }
}
&lt;/code&gt;&lt;/pre&gt;
&lt;h3&gt;字符串与性能优化&lt;/h3&gt;
&lt;p&gt;由于字符串的不可变性，频繁的字符串操作可能影响性能。以下是一些优化建议：&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;// 1. 使用StringBuilder进行大量字符串拼接
// 不推荐的做法
string result = &quot;&quot;;
for (int i = 0; i &amp;lt; 1000; i++)
{
    result += i.ToString(); // 每次都会创建新字符串
}

// 推荐的做法
StringBuilder sb = new StringBuilder();
for (int i = 0; i &amp;lt; 1000; i++)
{
    sb.Append(i.ToString());
}
string result2 = sb.ToString();

// 2. 使用String.Join代替循环拼接
string[] items = new string[1000];
for (int i = 0; i &amp;lt; 1000; i++)
{
    items[i] = i.ToString();
}
string joined = String.Join(&quot;&quot;, items); // 比循环拼接更高效

// 3. 使用字符串插值代替String.Format（C# 6.0+）
string name = &quot;张三&quot;;
int age = 25;
// 推荐
string msg1 = $&quot;姓名: {name}, 年龄: {age}&quot;;
// 不推荐（较旧的方式）
string msg2 = String.Format(&quot;姓名: {0}, 年龄: {1}&quot;, name, age);
&lt;/code&gt;&lt;/pre&gt;
&lt;h3&gt;String使用注意事项&lt;/h3&gt;
&lt;ol&gt;
&lt;li&gt;
&lt;p&gt;&lt;strong&gt;字符串不可变性&lt;/strong&gt;：字符串一旦创建就不能修改，任何修改操作都会创建新字符串。对于频繁修改的场景，应使用StringBuilder。&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;&lt;strong&gt;null和空字符串的区别&lt;/strong&gt;：&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;code&gt;null&lt;/code&gt;表示没有引用任何字符串对象&lt;/li&gt;
&lt;li&gt;&lt;code&gt;&quot;&quot;&lt;/code&gt;或&lt;code&gt;String.Empty&lt;/code&gt;表示引用了一个空字符串对象&lt;/li&gt;
&lt;li&gt;使用&lt;code&gt;String.IsNullOrEmpty()&lt;/code&gt;或&lt;code&gt;String.IsNullOrWhiteSpace()&lt;/code&gt;进行检查&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;&lt;strong&gt;字符串比较&lt;/strong&gt;：&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;使用&lt;code&gt;==&lt;/code&gt;运算符进行引用比较（对于字符串，实际上是比较值）&lt;/li&gt;
&lt;li&gt;使用&lt;code&gt;String.Equals()&lt;/code&gt;进行值比较&lt;/li&gt;
&lt;li&gt;使用&lt;code&gt;String.Compare()&lt;/code&gt;进行排序比较&lt;/li&gt;
&lt;li&gt;根据场景选择合适的&lt;code&gt;StringComparison&lt;/code&gt;选项&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;&lt;strong&gt;性能考虑&lt;/strong&gt;：&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;避免在循环中进行字符串拼接，使用StringBuilder&lt;/li&gt;
&lt;li&gt;使用字符串插值代替String.Format（C# 6.0+）&lt;/li&gt;
&lt;li&gt;对于大量字符串操作，考虑使用StringBuilder&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;&lt;strong&gt;内存管理&lt;/strong&gt;：&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;字符串是引用类型，存储在堆上&lt;/li&gt;
&lt;li&gt;字符串字面量会被字符串池（String Pool）缓存，相同内容的字符串可能共享同一引用&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;&lt;strong&gt;字符编码&lt;/strong&gt;：&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;C#中的字符串使用UTF-16编码&lt;/li&gt;
&lt;li&gt;处理不同编码时，需要使用&lt;code&gt;Encoding&lt;/code&gt;类进行转换&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;/ol&gt;
&lt;h3&gt;String类常用属性和方法速查表&lt;/h3&gt;
&lt;h4&gt;常用属性&lt;/h4&gt;
&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;属性&lt;/th&gt;
&lt;th&gt;类型&lt;/th&gt;
&lt;th&gt;作用说明&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;Length&lt;/td&gt;
&lt;td&gt;int&lt;/td&gt;
&lt;td&gt;获取字符串中的字符数。例如：&lt;code&gt;string str = &quot;Hello&quot;; int len = str.Length; // 返回5&lt;/code&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Chars[int]&lt;/td&gt;
&lt;td&gt;char&lt;/td&gt;
&lt;td&gt;获取字符串中指定位置的字符。例如：&lt;code&gt;char c = str[0]; // 获取第一个字符&lt;/code&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;
&lt;h4&gt;常用静态方法&lt;/h4&gt;
&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;方法&lt;/th&gt;
&lt;th&gt;参数&lt;/th&gt;
&lt;th&gt;返回值&lt;/th&gt;
&lt;th&gt;作用说明&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;Compare(string, string)&lt;/td&gt;
&lt;td&gt;string strA, string strB&lt;/td&gt;
&lt;td&gt;int&lt;/td&gt;
&lt;td&gt;比较两个字符串，返回负数、0或正数。负数表示strA &amp;lt; strB，0表示相等，正数表示strA &amp;gt; strB&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Compare(string, string, StringComparison)&lt;/td&gt;
&lt;td&gt;string strA, string strB, StringComparison comparisonType&lt;/td&gt;
&lt;td&gt;int&lt;/td&gt;
&lt;td&gt;使用指定的比较规则比较两个字符串&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Concat(params string[])&lt;/td&gt;
&lt;td&gt;params string[] values&lt;/td&gt;
&lt;td&gt;string&lt;/td&gt;
&lt;td&gt;连接多个字符串。例如：&lt;code&gt;String.Concat(&quot;Hello&quot;, &quot; &quot;, &quot;World&quot;)&lt;/code&gt; 返回 &quot;Hello World&quot;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Format(string, params object[])&lt;/td&gt;
&lt;td&gt;string format, params object[] args&lt;/td&gt;
&lt;td&gt;string&lt;/td&gt;
&lt;td&gt;格式化字符串。例如：&lt;code&gt;String.Format(&quot;{0} is {1} years old&quot;, &quot;John&quot;, 25)&lt;/code&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Join(string, string[])&lt;/td&gt;
&lt;td&gt;string separator, string[] value&lt;/td&gt;
&lt;td&gt;string&lt;/td&gt;
&lt;td&gt;使用指定分隔符连接字符串数组。例如：&lt;code&gt;String.Join(&quot;, &quot;, new[]{&quot;a&quot;, &quot;b&quot;, &quot;c&quot;})&lt;/code&gt; 返回 &quot;a, b, c&quot;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;IsNullOrEmpty(string)&lt;/td&gt;
&lt;td&gt;string value&lt;/td&gt;
&lt;td&gt;bool&lt;/td&gt;
&lt;td&gt;检查字符串是否为null或空字符串&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;IsNullOrWhiteSpace(string)&lt;/td&gt;
&lt;td&gt;string value&lt;/td&gt;
&lt;td&gt;bool&lt;/td&gt;
&lt;td&gt;检查字符串是否为null、空字符串或只包含空白字符&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Empty&lt;/td&gt;
&lt;td&gt;string&lt;/td&gt;
&lt;td&gt;string&lt;/td&gt;
&lt;td&gt;表示空字符串的静态字段，等同于&quot;&quot;&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;
&lt;h4&gt;常用实例方法&lt;/h4&gt;
&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;方法&lt;/th&gt;
&lt;th&gt;参数&lt;/th&gt;
&lt;th&gt;返回值&lt;/th&gt;
&lt;th&gt;作用说明&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;Contains(string)&lt;/td&gt;
&lt;td&gt;string value&lt;/td&gt;
&lt;td&gt;bool&lt;/td&gt;
&lt;td&gt;检查字符串是否包含指定的子字符串&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;EndsWith(string)&lt;/td&gt;
&lt;td&gt;string value&lt;/td&gt;
&lt;td&gt;bool&lt;/td&gt;
&lt;td&gt;检查字符串是否以指定的子字符串结尾&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;StartsWith(string)&lt;/td&gt;
&lt;td&gt;string value&lt;/td&gt;
&lt;td&gt;bool&lt;/td&gt;
&lt;td&gt;检查字符串是否以指定的子字符串开头&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;IndexOf(string)&lt;/td&gt;
&lt;td&gt;string value&lt;/td&gt;
&lt;td&gt;int&lt;/td&gt;
&lt;td&gt;返回指定子字符串首次出现的索引位置，如果未找到返回-1&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;IndexOf(string, int)&lt;/td&gt;
&lt;td&gt;string value, int startIndex&lt;/td&gt;
&lt;td&gt;int&lt;/td&gt;
&lt;td&gt;从指定索引开始搜索子字符串&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;LastIndexOf(string)&lt;/td&gt;
&lt;td&gt;string value&lt;/td&gt;
&lt;td&gt;int&lt;/td&gt;
&lt;td&gt;返回指定子字符串最后出现的索引位置&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Substring(int)&lt;/td&gt;
&lt;td&gt;int startIndex&lt;/td&gt;
&lt;td&gt;string&lt;/td&gt;
&lt;td&gt;从指定索引开始截取到字符串末尾的子字符串&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Substring(int, int)&lt;/td&gt;
&lt;td&gt;int startIndex, int length&lt;/td&gt;
&lt;td&gt;string&lt;/td&gt;
&lt;td&gt;从指定索引开始截取指定长度的子字符串&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Replace(string, string)&lt;/td&gt;
&lt;td&gt;string oldValue, string newValue&lt;/td&gt;
&lt;td&gt;string&lt;/td&gt;
&lt;td&gt;将字符串中所有出现的指定子字符串替换为新的子字符串&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Replace(char, char)&lt;/td&gt;
&lt;td&gt;char oldChar, char newChar&lt;/td&gt;
&lt;td&gt;string&lt;/td&gt;
&lt;td&gt;将字符串中所有出现的指定字符替换为新的字符&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Split(params char[])&lt;/td&gt;
&lt;td&gt;params char[] separator&lt;/td&gt;
&lt;td&gt;string[]&lt;/td&gt;
&lt;td&gt;根据指定的分隔符将字符串分割为字符串数组&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Split(char[], StringSplitOptions)&lt;/td&gt;
&lt;td&gt;char[] separator, StringSplitOptions options&lt;/td&gt;
&lt;td&gt;string[]&lt;/td&gt;
&lt;td&gt;根据指定的分隔符和选项分割字符串&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;ToLower()&lt;/td&gt;
&lt;td&gt;无&lt;/td&gt;
&lt;td&gt;string&lt;/td&gt;
&lt;td&gt;将字符串转换为小写&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;ToUpper()&lt;/td&gt;
&lt;td&gt;无&lt;/td&gt;
&lt;td&gt;string&lt;/td&gt;
&lt;td&gt;将字符串转换为大写&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Trim()&lt;/td&gt;
&lt;td&gt;无&lt;/td&gt;
&lt;td&gt;string&lt;/td&gt;
&lt;td&gt;移除字符串首尾的空白字符&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Trim(params char[])&lt;/td&gt;
&lt;td&gt;params char[] trimChars&lt;/td&gt;
&lt;td&gt;string&lt;/td&gt;
&lt;td&gt;移除字符串首尾的指定字符&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;TrimStart()&lt;/td&gt;
&lt;td&gt;无&lt;/td&gt;
&lt;td&gt;string&lt;/td&gt;
&lt;td&gt;移除字符串开头的空白字符&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;TrimEnd()&lt;/td&gt;
&lt;td&gt;无&lt;/td&gt;
&lt;td&gt;string&lt;/td&gt;
&lt;td&gt;移除字符串结尾的空白字符&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Remove(int)&lt;/td&gt;
&lt;td&gt;int startIndex&lt;/td&gt;
&lt;td&gt;string&lt;/td&gt;
&lt;td&gt;从指定索引开始移除到字符串末尾的所有字符&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Remove(int, int)&lt;/td&gt;
&lt;td&gt;int startIndex, int count&lt;/td&gt;
&lt;td&gt;string&lt;/td&gt;
&lt;td&gt;从指定索引开始移除指定数量的字符&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Insert(int, string)&lt;/td&gt;
&lt;td&gt;int startIndex, string value&lt;/td&gt;
&lt;td&gt;string&lt;/td&gt;
&lt;td&gt;在指定索引位置插入字符串&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;PadLeft(int)&lt;/td&gt;
&lt;td&gt;int totalWidth&lt;/td&gt;
&lt;td&gt;string&lt;/td&gt;
&lt;td&gt;在字符串左侧填充空白字符，使总长度达到指定值&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;PadRight(int)&lt;/td&gt;
&lt;td&gt;int totalWidth&lt;/td&gt;
&lt;td&gt;string&lt;/td&gt;
&lt;td&gt;在字符串右侧填充空白字符，使总长度达到指定值&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Equals(string)&lt;/td&gt;
&lt;td&gt;string value&lt;/td&gt;
&lt;td&gt;bool&lt;/td&gt;
&lt;td&gt;检查字符串是否与指定字符串相等&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Equals(string, StringComparison)&lt;/td&gt;
&lt;td&gt;string value, StringComparison comparisonType&lt;/td&gt;
&lt;td&gt;bool&lt;/td&gt;
&lt;td&gt;使用指定的比较规则检查字符串是否相等&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;CompareTo(string)&lt;/td&gt;
&lt;td&gt;string strB&lt;/td&gt;
&lt;td&gt;int&lt;/td&gt;
&lt;td&gt;比较当前字符串与指定字符串，返回负数、0或正数&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;ToCharArray()&lt;/td&gt;
&lt;td&gt;无&lt;/td&gt;
&lt;td&gt;char[]&lt;/td&gt;
&lt;td&gt;将字符串转换为字符数组&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;ToString()&lt;/td&gt;
&lt;td&gt;无&lt;/td&gt;
&lt;td&gt;string&lt;/td&gt;
&lt;td&gt;返回字符串本身（因为已经是字符串）&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;
&lt;h3&gt;StringBuilder类常用方法速查表&lt;/h3&gt;
&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;方法&lt;/th&gt;
&lt;th&gt;参数&lt;/th&gt;
&lt;th&gt;返回值&lt;/th&gt;
&lt;th&gt;作用说明&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;Append(string)&lt;/td&gt;
&lt;td&gt;string value&lt;/td&gt;
&lt;td&gt;StringBuilder&lt;/td&gt;
&lt;td&gt;在StringBuilder末尾追加字符串，返回当前StringBuilder实例以支持链式调用&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;AppendLine()&lt;/td&gt;
&lt;td&gt;无&lt;/td&gt;
&lt;td&gt;StringBuilder&lt;/td&gt;
&lt;td&gt;追加换行符&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;AppendLine(string)&lt;/td&gt;
&lt;td&gt;string value&lt;/td&gt;
&lt;td&gt;StringBuilder&lt;/td&gt;
&lt;td&gt;追加字符串并换行&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;AppendFormat(string, params object[])&lt;/td&gt;
&lt;td&gt;string format, params object[] args&lt;/td&gt;
&lt;td&gt;StringBuilder&lt;/td&gt;
&lt;td&gt;追加格式化字符串&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Insert(int, string)&lt;/td&gt;
&lt;td&gt;int index, string value&lt;/td&gt;
&lt;td&gt;StringBuilder&lt;/td&gt;
&lt;td&gt;在指定索引位置插入字符串&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Remove(int, int)&lt;/td&gt;
&lt;td&gt;int startIndex, int length&lt;/td&gt;
&lt;td&gt;StringBuilder&lt;/td&gt;
&lt;td&gt;从指定索引开始移除指定长度的字符&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Replace(string, string)&lt;/td&gt;
&lt;td&gt;string oldValue, string newValue&lt;/td&gt;
&lt;td&gt;StringBuilder&lt;/td&gt;
&lt;td&gt;替换字符串中所有出现的指定子字符串&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Clear()&lt;/td&gt;
&lt;td&gt;无&lt;/td&gt;
&lt;td&gt;StringBuilder&lt;/td&gt;
&lt;td&gt;清空StringBuilder的内容&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;ToString()&lt;/td&gt;
&lt;td&gt;无&lt;/td&gt;
&lt;td&gt;string&lt;/td&gt;
&lt;td&gt;将StringBuilder转换为字符串&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;EnsureCapacity(int)&lt;/td&gt;
&lt;td&gt;int capacity&lt;/td&gt;
&lt;td&gt;int&lt;/td&gt;
&lt;td&gt;确保容量至少为指定值&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;
&lt;p&gt;通过合理使用String类和StringBuilder类，可以高效地处理各种文本操作，满足应用程序中对字符串处理的需求。在实际开发中，应根据具体场景选择合适的字符串操作方法，并注意性能优化。&lt;/p&gt;
&lt;h2&gt;&amp;lt;a id=&quot;object-class&quot;&amp;gt;&amp;lt;/a&amp;gt;C# Object类详解&lt;/h2&gt;
&lt;p&gt;Object类是C#中所有类型的基类，在C#类型系统中占据核心地位。理解Object类的特性和用法对于深入理解C#的类型系统、继承机制以及类型转换至关重要。&lt;/p&gt;
&lt;h3&gt;Object类的基本概念&lt;/h3&gt;
&lt;p&gt;在C#中，&lt;code&gt;object&lt;/code&gt;是&lt;code&gt;System.Object&lt;/code&gt;类的别名，所有类型（包括值类型和引用类型）都直接或间接继承自Object类。这意味着任何类型的变量都可以赋值给object类型的变量。&lt;/p&gt;
&lt;h4&gt;Object在类型系统中的地位&lt;/h4&gt;
&lt;pre&gt;&lt;code&gt;// Object是所有类型的基类
object obj1 = 100;           // int类型可以赋值给object
object obj2 = &quot;Hello&quot;;       // string类型可以赋值给object
object obj3 = true;          // bool类型可以赋值给object
object obj4 = new List&amp;lt;int&amp;gt;(); // 引用类型可以赋值给object

// 值类型和引用类型都可以转换为object
int number = 42;
string text = &quot;C#&quot;;
DateTime date = DateTime.Now;

object o1 = number;  // 值类型装箱为object
object o2 = text;    // 引用类型直接赋值
object o3 = date;    // 值类型装箱为object

Console.WriteLine($&quot;obj1类型: {obj1.GetType()}&quot;);
Console.WriteLine($&quot;obj2类型: {obj2.GetType()}&quot;);
Console.WriteLine($&quot;obj3类型: {obj3.GetType()}&quot;);
&lt;/code&gt;&lt;/pre&gt;
&lt;h4&gt;为什么需要Object类？&lt;/h4&gt;
&lt;ol&gt;
&lt;li&gt;
&lt;p&gt;&lt;strong&gt;统一类型系统&lt;/strong&gt;：Object类为所有类型提供了统一的基类，使得C#具有统一的类型系统。&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;&lt;strong&gt;多态支持&lt;/strong&gt;：通过Object类型可以实现多态，编写能够处理任意类型的通用代码。&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;&lt;strong&gt;集合存储&lt;/strong&gt;：在泛型出现之前，Object类型用于在集合中存储不同类型的数据。&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;&lt;strong&gt;反射支持&lt;/strong&gt;：Object类提供了GetType()方法，支持运行时类型检查。&lt;/p&gt;
&lt;/li&gt;
&lt;/ol&gt;
&lt;h3&gt;Object类的常用方法&lt;/h3&gt;
&lt;p&gt;Object类提供了几个重要的虚方法，这些方法可以被派生类重写：&lt;/p&gt;
&lt;h4&gt;ToString()方法&lt;/h4&gt;
&lt;p&gt;ToString()方法返回对象的字符串表示。默认实现返回类型的完全限定名。&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;// 默认的ToString()实现
object obj = new object();
Console.WriteLine(obj.ToString()); // 输出: System.Object

// 值类型的ToString()
int number = 42;
Console.WriteLine(number.ToString()); // 输出: 42

// 引用类型的ToString()
string text = &quot;Hello&quot;;
Console.WriteLine(text.ToString()); // 输出: Hello

// 自定义类型的ToString()
public class Person
{
    public string Name { get; set; }
    public int Age { get; set; }
    
    public override string ToString()
    {
        return $&quot;姓名: {Name}, 年龄: {Age}&quot;;
    }
}

Person person = new Person { Name = &quot;张三&quot;, Age = 25 };
Console.WriteLine(person.ToString()); // 输出: 姓名: 张三, 年龄: 25
&lt;/code&gt;&lt;/pre&gt;
&lt;h4&gt;Equals()方法&lt;/h4&gt;
&lt;p&gt;Equals()方法用于比较两个对象是否相等。Object类提供了两个版本的Equals方法：&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;// 实例方法 Equals(object obj)
object obj1 = &quot;Hello&quot;;
object obj2 = &quot;Hello&quot;;
object obj3 = &quot;World&quot;;

bool equal1 = obj1.Equals(obj2); // true (字符串值相等)
bool equal2 = obj1.Equals(obj3); // false

// 静态方法 Equals(object objA, object objB)
bool equal3 = Object.Equals(obj1, obj2); // true
bool equal4 = Object.Equals(obj1, null); // false
bool equal5 = Object.Equals(null, null); // true (两个null相等)

// 值类型的Equals()比较
int a = 100;
int b = 100;
int c = 200;
Console.WriteLine($&quot;a.Equals(b): {a.Equals(b)}&quot;); // true
Console.WriteLine($&quot;a.Equals(c): {a.Equals(c)}&quot;); // false

// 引用类型的Equals()默认比较引用
Person p1 = new Person { Name = &quot;张三&quot;, Age = 25 };
Person p2 = new Person { Name = &quot;张三&quot;, Age = 25 };
Person p3 = p1;

Console.WriteLine($&quot;p1.Equals(p2): {p1.Equals(p2)}&quot;); // false (不同对象，引用不同)
Console.WriteLine($&quot;p1.Equals(p3): {p1.Equals(p3)}&quot;); // true (同一对象)

// 重写Equals()方法实现值比较
public class Person
{
    public string Name { get; set; }
    public int Age { get; set; }
    
    public override bool Equals(object obj)
    {
        if (obj == null || GetType() != obj.GetType())
            return false;
        
        Person other = (Person)obj;
        return Name == other.Name &amp;amp;&amp;amp; Age == other.Age;
    }
    
    public override int GetHashCode()
    {
        return HashCode.Combine(Name, Age);
    }
}
&lt;/code&gt;&lt;/pre&gt;
&lt;h4&gt;GetHashCode()方法&lt;/h4&gt;
&lt;p&gt;GetHashCode()方法返回对象的哈希码，用于在哈希表等数据结构中快速查找对象。&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;// 默认的GetHashCode()实现
object obj1 = new object();
object obj2 = new object();
Console.WriteLine($&quot;obj1的哈希码: {obj1.GetHashCode()}&quot;);
Console.WriteLine($&quot;obj2的哈希码: {obj2.GetHashCode()}&quot;);

// 相同对象的哈希码相同
object obj3 = obj1;
Console.WriteLine($&quot;obj1和obj3的哈希码相同: {obj1.GetHashCode() == obj3.GetHashCode()}&quot;); // true

// 字符串的哈希码基于内容
string str1 = &quot;Hello&quot;;
string str2 = &quot;Hello&quot;;
Console.WriteLine($&quot;相同内容的字符串哈希码相同: {str1.GetHashCode() == str2.GetHashCode()}&quot;); // true

// 值类型的哈希码基于值
int num1 = 100;
int num2 = 100;
Console.WriteLine($&quot;相同值的整数哈希码相同: {num1.GetHashCode() == num2.GetHashCode()}&quot;); // true

// 重写GetHashCode()的示例
public class Person
{
    public string Name { get; set; }
    public int Age { get; set; }
    
    public override int GetHashCode()
    {
        // 使用HashCode.Combine (C# 7.3+)
        return HashCode.Combine(Name, Age);
        
        // 或者使用传统方式
        // int hash = 17;
        // hash = hash * 23 + (Name?.GetHashCode() ?? 0);
        // hash = hash * 23 + Age.GetHashCode();
        // return hash;
    }
}
&lt;/code&gt;&lt;/pre&gt;
&lt;h4&gt;GetType()方法&lt;/h4&gt;
&lt;p&gt;GetType()方法返回对象的运行时类型信息，返回Type对象。&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;// 获取对象的类型
object obj1 = 100;
object obj2 = &quot;Hello&quot;;
object obj3 = new List&amp;lt;int&amp;gt;();

Type type1 = obj1.GetType();
Type type2 = obj2.GetType();
Type type3 = obj3.GetType();

Console.WriteLine($&quot;obj1的类型: {type1.Name}&quot;); // Int32
Console.WriteLine($&quot;obj2的类型: {type2.Name}&quot;); // String
Console.WriteLine($&quot;obj3的类型: {type3.Name}&quot;); // List`1

// 使用typeof运算符比较类型
if (obj1.GetType() == typeof(int))
{
    Console.WriteLine(&quot;obj1是int类型&quot;);
}

// 获取类型的完全限定名
Console.WriteLine($&quot;完全限定名: {type1.FullName}&quot;); // System.Int32
Console.WriteLine($&quot;命名空间: {type1.Namespace}&quot;); // System
&lt;/code&gt;&lt;/pre&gt;
&lt;h4&gt;ReferenceEquals()方法&lt;/h4&gt;
&lt;p&gt;ReferenceEquals()是静态方法，用于比较两个对象的引用是否指向同一个对象。&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;// ReferenceEquals比较引用
object obj1 = new object();
object obj2 = new object();
object obj3 = obj1;

Console.WriteLine($&quot;obj1和obj2引用相同: {Object.ReferenceEquals(obj1, obj2)}&quot;); // false
Console.WriteLine($&quot;obj1和obj3引用相同: {Object.ReferenceEquals(obj1, obj3)}&quot;); // true

// 字符串的特殊情况（字符串驻留）
string str1 = &quot;Hello&quot;;
string str2 = &quot;Hello&quot;;
string str3 = new string(&quot;Hello&quot;.ToCharArray());

Console.WriteLine($&quot;str1和str2引用相同: {Object.ReferenceEquals(str1, str2)}&quot;); // true (字符串驻留)
Console.WriteLine($&quot;str1和str3引用相同: {Object.ReferenceEquals(str1, str3)}&quot;); // false

// null比较
Console.WriteLine($&quot;null和null引用相同: {Object.ReferenceEquals(null, null)}&quot;); // true
&lt;/code&gt;&lt;/pre&gt;
&lt;h3&gt;Object的运算和比较&lt;/h3&gt;
&lt;h4&gt;相等性比较&lt;/h4&gt;
&lt;p&gt;C#提供了多种方式比较对象的相等性：&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;// 1. == 运算符
int a = 100;
int b = 100;
Console.WriteLine($&quot;a == b: {a == b}&quot;); // true (值类型比较值)

string s1 = &quot;Hello&quot;;
string s2 = &quot;Hello&quot;;
Console.WriteLine($&quot;s1 == s2: {s1 == s2}&quot;); // true (字符串重载了==，比较值)

Person p1 = new Person { Name = &quot;张三&quot;, Age = 25 };
Person p2 = new Person { Name = &quot;张三&quot;, Age = 25 };
Console.WriteLine($&quot;p1 == p2: {p1 == p2}&quot;); // false (引用类型默认比较引用)

// 2. Equals()方法
Console.WriteLine($&quot;a.Equals(b): {a.Equals(b)}&quot;); // true
Console.WriteLine($&quot;s1.Equals(s2): {s1.Equals(s2)}&quot;); // true
Console.WriteLine($&quot;p1.Equals(p2): {p1.Equals(p2)}&quot;); // 取决于是否重写了Equals

// 3. Object.ReferenceEquals()方法
Console.WriteLine($&quot;ReferenceEquals(p1, p2): {Object.ReferenceEquals(p1, p2)}&quot;); // false
&lt;/code&gt;&lt;/pre&gt;
&lt;h4&gt;类型比较&lt;/h4&gt;
&lt;pre&gt;&lt;code&gt;// 使用is运算符进行类型检查
object obj = &quot;Hello&quot;;

if (obj is string)
{
    Console.WriteLine(&quot;obj是string类型&quot;);
}

if (obj is int)
{
    Console.WriteLine(&quot;obj是int类型&quot;);
}

// 使用is运算符进行模式匹配 (C# 7.0+)
if (obj is string str)
{
    Console.WriteLine($&quot;转换后的字符串: {str}&quot;);
}

// 使用as运算符进行安全类型转换
string text = obj as string;
if (text != null)
{
    Console.WriteLine($&quot;转换成功: {text}&quot;);
}

int? number = obj as int?;
if (number == null)
{
    Console.WriteLine(&quot;转换失败，返回null&quot;);
}
&lt;/code&gt;&lt;/pre&gt;
&lt;h3&gt;Object的类型转换&lt;/h3&gt;
&lt;h4&gt;向上转型（隐式转换）&lt;/h4&gt;
&lt;p&gt;值类型和引用类型都可以隐式转换为object类型，这个过程对于值类型来说就是装箱。&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;// 值类型向上转型（装箱）
int number = 42;
object obj1 = number; // 隐式装箱

// 引用类型向上转型
string text = &quot;Hello&quot;;
object obj2 = text; // 直接赋值，无装箱

// 自定义类型向上转型
Person person = new Person { Name = &quot;张三&quot;, Age = 25 };
object obj3 = person; // 直接赋值
&lt;/code&gt;&lt;/pre&gt;
&lt;h4&gt;向下转型（显式转换）&lt;/h4&gt;
&lt;p&gt;从object类型转换回具体类型需要显式转换。&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;// 使用强制类型转换
object obj = 100;
int number = (int)obj; // 显式拆箱

object obj2 = &quot;Hello&quot;;
string text = (string)obj2; // 显式转换

// 转换失败会抛出InvalidCastException
try
{
    object obj3 = &quot;Hello&quot;;
    int num = (int)obj3; // 抛出异常
}
catch (InvalidCastException ex)
{
    Console.WriteLine($&quot;转换失败: {ex.Message}&quot;);
}

// 使用as运算符进行安全转换（不会抛出异常）
object obj4 = &quot;Hello&quot;;
string str = obj4 as string; // 成功，返回&quot;Hello&quot;

object obj5 = 100;
string str2 = obj5 as string; // 失败，返回null（因为int不能转换为string）

// 使用is运算符检查类型后再转换
object obj6 = 100;
if (obj6 is int)
{
    int num = (int)obj6; // 安全转换
    Console.WriteLine($&quot;转换成功: {num}&quot;);
}
&lt;/code&gt;&lt;/pre&gt;
&lt;h3&gt;装箱和拆箱详解&lt;/h3&gt;
&lt;p&gt;装箱和拆箱是C#类型系统中的重要概念，理解它们对于编写高性能代码至关重要。&lt;/p&gt;
&lt;h4&gt;什么是装箱（Boxing）？&lt;/h4&gt;
&lt;p&gt;装箱是将值类型转换为object类型或接口类型的过程。装箱时，值类型的值会被复制到堆上，并创建一个对象引用来包装这个值。&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;// 装箱示例
int number = 42;
object obj = number; // 装箱：值类型转换为引用类型

// 装箱的过程：
// 1. 在堆上分配内存
// 2. 将值类型的值复制到堆上
// 3. 返回对象引用

// 验证装箱
Console.WriteLine($&quot;number的类型: {number.GetType()}&quot;); // System.Int32
Console.WriteLine($&quot;obj的类型: {obj.GetType()}&quot;); // System.Int32
Console.WriteLine($&quot;number的值: {number}&quot;); // 42
Console.WriteLine($&quot;obj的值: {obj}&quot;); // 42

// 多个值类型的装箱
int i = 100;
double d = 3.14;
bool b = true;
DateTime dt = DateTime.Now;

object o1 = i;  // 装箱
object o2 = d;  // 装箱
object o3 = b;  // 装箱
object o4 = dt; // 装箱
&lt;/code&gt;&lt;/pre&gt;
&lt;h4&gt;什么是拆箱（Unboxing）？&lt;/h4&gt;
&lt;p&gt;拆箱是将object类型或接口类型转换回值类型的过程。拆箱时，需要显式指定目标类型，并且只能拆箱到原始的值类型。&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;// 拆箱示例
object obj = 42; // 装箱
int number = (int)obj; // 拆箱：引用类型转换回值类型

// 拆箱的过程：
// 1. 检查对象引用是否为null
// 2. 检查对象是否为指定值类型的装箱实例
// 3. 将堆上的值复制回栈上的值类型变量

// 正确的拆箱
object obj1 = 100;
int num1 = (int)obj1; // 正确：拆箱到原始类型

// 错误的拆箱会抛出异常
object obj2 = 100;
try
{
    long num2 = (long)obj2; // 错误：不能拆箱到不同的类型
}
catch (InvalidCastException ex)
{
    Console.WriteLine($&quot;拆箱失败: {ex.Message}&quot;);
}

// 正确的拆箱方式
object obj3 = 100;
if (obj3 is int)
{
    int num3 = (int)obj3; // 安全拆箱
    Console.WriteLine($&quot;拆箱成功: {num3}&quot;);
}
&lt;/code&gt;&lt;/pre&gt;
&lt;h4&gt;装箱和拆箱的性能影响&lt;/h4&gt;
&lt;p&gt;装箱和拆箱会带来性能开销，应尽量避免在性能敏感的代码中使用。&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;// 性能测试：避免装箱
void PerformanceTest()
{
    // 不好的做法：频繁装箱
    int sum1 = 0;
    for (int i = 0; i &amp;lt; 1000000; i++)
    {
        object obj = i; // 装箱
        sum1 += (int)obj; // 拆箱
    }
    
    // 好的做法：避免装箱
    int sum2 = 0;
    for (int i = 0; i &amp;lt; 1000000; i++)
    {
        sum2 += i; // 直接操作值类型
    }
}

// 集合中的装箱问题
// ArrayList会进行装箱（不推荐）
ArrayList list1 = new ArrayList();
list1.Add(1);    // 装箱
list1.Add(2);    // 装箱
int value1 = (int)list1[0]; // 拆箱

// List&amp;lt;T&amp;gt;不会装箱（推荐）
List&amp;lt;int&amp;gt; list2 = new List&amp;lt;int&amp;gt;();
list2.Add(1);    // 无装箱
list2.Add(2);    // 无装箱
int value2 = list2[0]; // 无拆箱
&lt;/code&gt;&lt;/pre&gt;
&lt;h4&gt;装箱和拆箱的最佳实践&lt;/h4&gt;
&lt;pre&gt;&lt;code&gt;// 1. 使用泛型集合代替非泛型集合
// 不推荐
ArrayList list = new ArrayList();
list.Add(100); // 装箱

// 推荐
List&amp;lt;int&amp;gt; genericList = new List&amp;lt;int&amp;gt;();
genericList.Add(100); // 无装箱

// 2. 使用泛型方法
// 不推荐
public void Process(object obj)
{
    if (obj is int)
    {
        int value = (int)obj; // 拆箱
        // 处理value
    }
}

// 推荐
public void Process&amp;lt;T&amp;gt;(T value)
{
    // 直接使用value，无装箱拆箱
}

// 3. 避免在接口中使用值类型
// 不推荐：值类型实现接口会导致装箱
int number = 42;
IComparable comparable = number; // 装箱

// 4. 使用Nullable&amp;lt;T&amp;gt;代替object存储值类型
// 不推荐
object obj = GetNullableInt(); // 可能装箱

// 推荐
int? nullableInt = GetNullableInt(); // 无装箱
&lt;/code&gt;&lt;/pre&gt;
&lt;h3&gt;Object的实际应用示例&lt;/h3&gt;
&lt;p&gt;以下是一些Object在实际开发中的应用场景：&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;using System;
using System.Collections;

class ObjectExamples
{
    // 通用方法：处理任意类型的对象
    public static void ProcessObject(object obj)
    {
        if (obj == null)
        {
            Console.WriteLine(&quot;对象为null&quot;);
            return;
        }
        
        Type type = obj.GetType();
        Console.WriteLine($&quot;对象类型: {type.Name}&quot;);
        Console.WriteLine($&quot;对象值: {obj}&quot;);
        
        // 根据类型进行不同处理
        if (obj is int)
        {
            int value = (int)obj;
            Console.WriteLine($&quot;这是一个整数: {value * 2}&quot;);
        }
        else if (obj is string)
        {
            string text = (string)obj;
            Console.WriteLine($&quot;这是一个字符串，长度: {text.Length}&quot;);
        }
        else if (obj is Person)
        {
            Person person = (Person)obj;
            Console.WriteLine($&quot;这是一个Person对象: {person.Name}&quot;);
        }
    }
    
    // 使用模式匹配处理对象 (C# 7.0+)
    public static void ProcessObjectWithPatternMatching(object obj)
    {
        switch (obj)
        {
            case int i:
                Console.WriteLine($&quot;整数: {i}&quot;);
                break;
            case string s:
                Console.WriteLine($&quot;字符串: {s}&quot;);
                break;
            case Person p:
                Console.WriteLine($&quot;Person: {p.Name}, {p.Age}岁&quot;);
                break;
            case null:
                Console.WriteLine(&quot;对象为null&quot;);
                break;
            default:
                Console.WriteLine($&quot;未知类型: {obj.GetType().Name}&quot;);
                break;
        }
    }
    
    // 深拷贝对象（使用序列化）
    public static T DeepClone&amp;lt;T&amp;gt;(T obj) where T : class
    {
        if (obj == null)
            return null;
        
        // 这里只是示例，实际应使用序列化库
        // 例如：使用System.Text.Json或Newtonsoft.Json
        return obj; // 简化示例
    }
    
    // 比较两个对象是否相等
    public static bool AreEqual(object obj1, object obj2)
    {
        // 处理null情况
        if (obj1 == null &amp;amp;&amp;amp; obj2 == null)
            return true;
        
        if (obj1 == null || obj2 == null)
            return false;
        
        // 使用Equals方法
        return obj1.Equals(obj2);
    }
    
    // 获取对象的哈希码（用于字典等）
    public static int GetObjectHashCode(object obj)
    {
        if (obj == null)
            return 0;
        
        return obj.GetHashCode();
    }
    
    // 类型安全的转换辅助方法
    public static T SafeCast&amp;lt;T&amp;gt;(object obj) where T : class
    {
        return obj as T;
    }
    
    public static T SafeCastValue&amp;lt;T&amp;gt;(object obj) where T : struct
    {
        if (obj is T)
            return (T)obj;
        return default(T);
    }
    
    static void Main()
    {
        // 测试ProcessObject
        ProcessObject(100);
        ProcessObject(&quot;Hello&quot;);
        ProcessObject(new Person { Name = &quot;张三&quot;, Age = 25 });
        ProcessObject(null);
        
        Console.WriteLine();
        
        // 测试模式匹配
        ProcessObjectWithPatternMatching(42);
        ProcessObjectWithPatternMatching(&quot;World&quot;);
        ProcessObjectWithPatternMatching(new Person { Name = &quot;李四&quot;, Age = 30 });
        
        Console.WriteLine();
        
        // 测试相等性比较
        int a = 100, b = 100;
        Console.WriteLine($&quot;AreEqual(100, 100): {AreEqual(a, b)}&quot;);
        
        string s1 = &quot;Hello&quot;, s2 = &quot;Hello&quot;;
        Console.WriteLine($&quot;AreEqual(&apos;Hello&apos;, &apos;Hello&apos;): {AreEqual(s1, s2)}&quot;);
        
        Person p1 = new Person { Name = &quot;张三&quot;, Age = 25 };
        Person p2 = new Person { Name = &quot;张三&quot;, Age = 25 };
        Console.WriteLine($&quot;AreEqual(p1, p2): {AreEqual(p1, p2)}&quot;);
    }
}

// 辅助类
public class Person
{
    public string Name { get; set; }
    public int Age { get; set; }
    
    public override bool Equals(object obj)
    {
        if (obj == null || GetType() != obj.GetType())
            return false;
        
        Person other = (Person)obj;
        return Name == other.Name &amp;amp;&amp;amp; Age == other.Age;
    }
    
    public override int GetHashCode()
    {
        return HashCode.Combine(Name, Age);
    }
    
    public override string ToString()
    {
        return $&quot;Person(姓名: {Name}, 年龄: {Age})&quot;;
    }
}```

### Object与泛型

在C# 2.0引入泛型之前，Object类型被广泛用于创建通用集合和方法。现在应优先使用泛型。

```csharp
// 旧方式：使用Object（不推荐）
public class ObjectList
{
    private ArrayList list = new ArrayList();
    
    public void Add(object item)
    {
        list.Add(item); // 值类型会装箱
    }
    
    public object Get(int index)
    {
        return list[index]; // 需要拆箱
    }
}

// 新方式：使用泛型（推荐）
public class GenericList&amp;lt;T&amp;gt;
{
    private List&amp;lt;T&amp;gt; list = new List&amp;lt;T&amp;gt;();
    
    public void Add(T item)
    {
        list.Add(item); // 无装箱
    }
    
    public T Get(int index)
    {
        return list[index]; // 无拆箱
    }
}

// 使用示例
ObjectList oldList = new ObjectList();
oldList.Add(100); // 装箱
int value1 = (int)oldList.Get(0); // 拆箱

GenericList&amp;lt;int&amp;gt; newList = new GenericList&amp;lt;int&amp;gt;();
newList.Add(100); // 无装箱
int value2 = newList.Get(0); // 无拆箱
&lt;/code&gt;&lt;/pre&gt;
&lt;h3&gt;Object使用注意事项&lt;/h3&gt;
&lt;ol&gt;
&lt;li&gt;
&lt;p&gt;&lt;strong&gt;性能考虑&lt;/strong&gt;：&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;避免在性能敏感的代码中频繁使用装箱和拆箱&lt;/li&gt;
&lt;li&gt;优先使用泛型集合（List&amp;lt;T&amp;gt;）而不是非泛型集合（ArrayList）&lt;/li&gt;
&lt;li&gt;使用泛型方法代替Object参数的方法&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;&lt;strong&gt;类型安全&lt;/strong&gt;：&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;使用as运算符进行安全类型转换，避免InvalidCastException&lt;/li&gt;
&lt;li&gt;使用is运算符检查类型后再进行转换&lt;/li&gt;
&lt;li&gt;在C# 7.0+中使用模式匹配简化类型检查&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;&lt;strong&gt;Equals()和GetHashCode()的重写规则&lt;/strong&gt;：&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;如果重写了Equals()，必须同时重写GetHashCode()&lt;/li&gt;
&lt;li&gt;相等的对象必须具有相同的哈希码&lt;/li&gt;
&lt;li&gt;GetHashCode()应该在对象的生命周期内保持不变&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;&lt;strong&gt;null处理&lt;/strong&gt;：&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;使用Object.ReferenceEquals()比较null更明确&lt;/li&gt;
&lt;li&gt;使用null条件运算符（?.）安全访问对象成员&lt;/li&gt;
&lt;li&gt;使用null合并运算符（??）提供默认值&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;&lt;strong&gt;ToString()的重写&lt;/strong&gt;：&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;为自定义类型重写ToString()提供有意义的字符串表示&lt;/li&gt;
&lt;li&gt;ToString()应该返回可读的、描述性的字符串&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;/ol&gt;
&lt;h3&gt;Object类常用方法速查表&lt;/h3&gt;
&lt;h4&gt;实例方法&lt;/h4&gt;
&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;方法&lt;/th&gt;
&lt;th&gt;参数&lt;/th&gt;
&lt;th&gt;返回值&lt;/th&gt;
&lt;th&gt;作用说明&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;ToString()&lt;/td&gt;
&lt;td&gt;无&lt;/td&gt;
&lt;td&gt;string&lt;/td&gt;
&lt;td&gt;返回对象的字符串表示。默认返回类型的完全限定名，建议为自定义类型重写此方法&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Equals(object)&lt;/td&gt;
&lt;td&gt;object obj&lt;/td&gt;
&lt;td&gt;bool&lt;/td&gt;
&lt;td&gt;比较当前对象与指定对象是否相等。值类型比较值，引用类型默认比较引用。可以重写以实现值比较&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;GetHashCode()&lt;/td&gt;
&lt;td&gt;无&lt;/td&gt;
&lt;td&gt;int&lt;/td&gt;
&lt;td&gt;返回对象的哈希码。如果重写了Equals()，必须同时重写GetHashCode()。相等的对象必须具有相同的哈希码&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;GetType()&lt;/td&gt;
&lt;td&gt;无&lt;/td&gt;
&lt;td&gt;Type&lt;/td&gt;
&lt;td&gt;返回对象的运行时类型信息。返回Type对象，可用于反射操作&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;
&lt;h4&gt;静态方法&lt;/h4&gt;
&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;方法&lt;/th&gt;
&lt;th&gt;参数&lt;/th&gt;
&lt;th&gt;返回值&lt;/th&gt;
&lt;th&gt;作用说明&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;Equals(object, object)&lt;/td&gt;
&lt;td&gt;object objA, object objB&lt;/td&gt;
&lt;td&gt;bool&lt;/td&gt;
&lt;td&gt;比较两个对象是否相等。如果两个参数都为null，返回true；如果只有一个为null，返回false；否则调用objA.Equals(objB)&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;ReferenceEquals(object, object)&lt;/td&gt;
&lt;td&gt;object objA, object objB&lt;/td&gt;
&lt;td&gt;bool&lt;/td&gt;
&lt;td&gt;比较两个对象的引用是否指向同一个对象。这是唯一可靠的方式来判断两个引用是否指向同一对象实例&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;
&lt;h3&gt;装箱和拆箱总结&lt;/h3&gt;
&lt;h4&gt;装箱（Boxing）&lt;/h4&gt;
&lt;ul&gt;
&lt;li&gt;&lt;strong&gt;定义&lt;/strong&gt;：将值类型转换为object类型或接口类型&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;过程&lt;/strong&gt;：在堆上分配内存，复制值类型的值，返回对象引用&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;性能影响&lt;/strong&gt;：会分配堆内存，有性能开销&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;何时发生&lt;/strong&gt;：值类型赋值给object、接口类型，或作为object参数传递&lt;/li&gt;
&lt;/ul&gt;
&lt;h4&gt;拆箱（Unboxing）&lt;/h4&gt;
&lt;ul&gt;
&lt;li&gt;&lt;strong&gt;定义&lt;/strong&gt;：将object类型或接口类型转换回值类型&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;过程&lt;/strong&gt;：检查对象是否为指定类型的装箱实例，将堆上的值复制回栈&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;性能影响&lt;/strong&gt;：需要类型检查，有性能开销&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;注意事项&lt;/strong&gt;：只能拆箱到原始的值类型，否则会抛出InvalidCastException&lt;/li&gt;
&lt;/ul&gt;
&lt;h4&gt;避免装箱和拆箱的最佳实践&lt;/h4&gt;
&lt;ol&gt;
&lt;li&gt;使用泛型集合（List&amp;lt;T&amp;gt;、Dictionary&amp;lt;TKey, TValue&amp;gt;等）代替非泛型集合&lt;/li&gt;
&lt;li&gt;使用泛型方法代替Object参数的方法&lt;/li&gt;
&lt;li&gt;避免值类型实现接口（如果可能）&lt;/li&gt;
&lt;li&gt;使用Nullable&amp;lt;T&amp;gt;代替object存储可空值类型&lt;/li&gt;
&lt;li&gt;在性能敏感的代码中，直接使用具体类型而不是object&lt;/li&gt;
&lt;/ol&gt;
&lt;p&gt;通过深入理解Object类、装箱和拆箱机制，可以编写出更高效、更类型安全的C#代码。在实际开发中，应优先使用泛型来避免装箱和拆箱带来的性能开销。&lt;/p&gt;
&lt;h2&gt;&amp;lt;a id=&quot;arraylist-class&quot;&amp;gt;&amp;lt;/a&amp;gt;C# ArrayList类详解&lt;/h2&gt;
&lt;p&gt;ArrayList是C#中一个非泛型的动态数组集合类，位于System.Collections命名空间中。虽然现在推荐使用泛型集合List&amp;lt;T&amp;gt;，但了解ArrayList仍然有助于理解集合的基本概念和C#的发展历程。&lt;/p&gt;
&lt;h3&gt;ArrayList的基本概念&lt;/h3&gt;
&lt;p&gt;ArrayList是一个可以动态调整大小的数组，可以存储任意类型的对象（object类型）。它是C# 1.0时代的主要集合类，在C# 2.0引入泛型后，逐渐被List&amp;lt;T&amp;gt;取代。&lt;/p&gt;
&lt;h4&gt;为什么需要ArrayList？&lt;/h4&gt;
&lt;p&gt;在泛型出现之前，ArrayList提供了以下优势：&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;&lt;strong&gt;动态大小&lt;/strong&gt;：可以根据需要自动调整容量&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;类型灵活&lt;/strong&gt;：可以存储任意类型的对象&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;丰富的操作&lt;/strong&gt;：提供了添加、删除、查找、排序等方法&lt;/li&gt;
&lt;/ol&gt;
&lt;h4&gt;ArrayList的局限性&lt;/h4&gt;
&lt;ol&gt;
&lt;li&gt;&lt;strong&gt;类型不安全&lt;/strong&gt;：存储的是object类型，需要类型转换&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;性能开销&lt;/strong&gt;：值类型会进行装箱，读取时需要拆箱&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;运行时错误&lt;/strong&gt;：类型转换错误只能在运行时发现&lt;/li&gt;
&lt;/ol&gt;
&lt;h3&gt;ArrayList的创建和初始化&lt;/h3&gt;
&lt;pre&gt;&lt;code&gt;using System.Collections;

// 1. 使用默认构造函数创建空ArrayList
ArrayList list1 = new ArrayList();

// 2. 指定初始容量创建ArrayList
ArrayList list2 = new ArrayList(10); // 初始容量为10

// 3. 使用集合初始化器创建并初始化
ArrayList list3 = new ArrayList { 1, 2, 3, &quot;Hello&quot;, true };

// 4. 从其他集合创建ArrayList
int[] array = { 1, 2, 3, 4, 5 };
ArrayList list4 = new ArrayList(array);

// 5. 使用AddRange方法初始化
ArrayList list5 = new ArrayList();
list5.AddRange(new int[] { 1, 2, 3, 4, 5 });
list5.AddRange(new string[] { &quot;A&quot;, &quot;B&quot;, &quot;C&quot; });

Console.WriteLine($&quot;list3的元素数: {list3.Count}&quot;);
Console.WriteLine($&quot;list4的元素数: {list4.Count}&quot;);
&lt;/code&gt;&lt;/pre&gt;
&lt;h3&gt;ArrayList的常用属性&lt;/h3&gt;
&lt;pre&gt;&lt;code&gt;ArrayList list = new ArrayList { 1, 2, 3, 4, 5 };

// Count属性 - 获取ArrayList中实际包含的元素数量
Console.WriteLine($&quot;元素数量: {list.Count}&quot;); // 5

// Capacity属性 - 获取或设置ArrayList的容量（内部数组的大小）
Console.WriteLine($&quot;当前容量: {list.Capacity}&quot;); // 可能是8或更大

// 设置容量
list.Capacity = 20;
Console.WriteLine($&quot;设置后的容量: {list.Capacity}&quot;); // 20

// IsFixedSize属性 - 检查ArrayList是否有固定大小
Console.WriteLine($&quot;是否有固定大小: {list.IsFixedSize}&quot;); // false

// IsReadOnly属性 - 检查ArrayList是否为只读
Console.WriteLine($&quot;是否只读: {list.IsReadOnly}&quot;); // false

// IsSynchronized属性 - 检查对ArrayList的访问是否同步（线程安全）
Console.WriteLine($&quot;是否同步: {list.IsSynchronized}&quot;); // false
&lt;/code&gt;&lt;/pre&gt;
&lt;h3&gt;ArrayList的常用方法&lt;/h3&gt;
&lt;h4&gt;添加元素&lt;/h4&gt;
&lt;pre&gt;&lt;code&gt;ArrayList list = new ArrayList();

// Add方法 - 在末尾添加单个元素
list.Add(100);
list.Add(&quot;Hello&quot;);
list.Add(true);
list.Add(3.14);

Console.WriteLine($&quot;添加后元素数: {list.Count}&quot;);

// AddRange方法 - 添加多个元素（从集合）
list.AddRange(new int[] { 1, 2, 3 });
list.AddRange(new string[] { &quot;A&quot;, &quot;B&quot;, &quot;C&quot; });

Console.WriteLine($&quot;AddRange后元素数: {list.Count}&quot;);

// Insert方法 - 在指定索引位置插入元素
list.Insert(0, &quot;First&quot;); // 在索引0处插入
list.Insert(2, &quot;Middle&quot;); // 在索引2处插入

// InsertRange方法 - 在指定位置插入多个元素
list.InsertRange(1, new int[] { 10, 20, 30 });

// 输出所有元素
foreach (var item in list)
{
    Console.Write($&quot;{item} &quot;);
}
Console.WriteLine();
&lt;/code&gt;&lt;/pre&gt;
&lt;h4&gt;访问和修改元素&lt;/h4&gt;
&lt;pre&gt;&lt;code&gt;ArrayList list = new ArrayList { 10, 20, 30, 40, 50 };

// 通过索引访问元素（需要类型转换）
int first = (int)list[0]; // 拆箱
string second = list[1] as string; // 如果类型不匹配返回null

// 通过索引修改元素
list[0] = 100; // 如果原来是值类型，新值会装箱
list[1] = &quot;New Value&quot;;

// 遍历ArrayList
for (int i = 0; i &amp;lt; list.Count; i++)
{
    Console.WriteLine($&quot;索引 {i}: {list[i]}&quot;);
}

// 使用foreach遍历
foreach (object item in list)
{
    Console.WriteLine($&quot;元素: {item}&quot;);
}
&lt;/code&gt;&lt;/pre&gt;
&lt;h4&gt;查找元素&lt;/h4&gt;
&lt;pre&gt;&lt;code&gt;ArrayList list = new ArrayList { 10, 20, 30, 20, 40, 20 };

// IndexOf方法 - 查找元素首次出现的索引
int index1 = list.IndexOf(20); // 返回1
int index2 = list.IndexOf(100); // 返回-1（未找到）

// LastIndexOf方法 - 查找元素最后出现的索引
int lastIndex = list.LastIndexOf(20); // 返回5

// Contains方法 - 检查是否包含指定元素
bool contains = list.Contains(30); // true
bool notContains = list.Contains(100); // false

// BinarySearch方法 - 二分查找（要求列表已排序）
ArrayList sortedList = new ArrayList { 10, 20, 30, 40, 50 };
int binaryIndex = sortedList.BinarySearch(30); // 返回2
int notFound = sortedList.BinarySearch(25); // 返回负数

Console.WriteLine($&quot;IndexOf(20): {index1}&quot;);
Console.WriteLine($&quot;LastIndexOf(20): {lastIndex}&quot;);
Console.WriteLine($&quot;Contains(30): {contains}&quot;);
&lt;/code&gt;&lt;/pre&gt;
&lt;h4&gt;删除元素&lt;/h4&gt;
&lt;pre&gt;&lt;code&gt;ArrayList list = new ArrayList { 10, 20, 30, 40, 50, 20 };

// Remove方法 - 删除首次出现的指定元素
list.Remove(20); // 删除第一个20
Console.WriteLine($&quot;Remove后: [{string.Join(&quot;, &quot;, list.ToArray())}]&quot;);

// RemoveAt方法 - 删除指定索引的元素
list.RemoveAt(0); // 删除索引0的元素
Console.WriteLine($&quot;RemoveAt后: [{string.Join(&quot;, &quot;, list.ToArray())}]&quot;);

// RemoveRange方法 - 删除指定范围的元素
ArrayList list2 = new ArrayList { 10, 20, 30, 40, 50 };
list2.RemoveRange(1, 2); // 从索引1开始删除2个元素
Console.WriteLine($&quot;RemoveRange后: [{string.Join(&quot;, &quot;, list2.ToArray())}]&quot;);

// Clear方法 - 清空所有元素
list2.Clear();
Console.WriteLine($&quot;Clear后元素数: {list2.Count}&quot;); // 0
&lt;/code&gt;&lt;/pre&gt;
&lt;h4&gt;排序和反转&lt;/h4&gt;
&lt;pre&gt;&lt;code&gt;ArrayList list = new ArrayList { 50, 20, 30, 10, 40 };

// Sort方法 - 对ArrayList进行排序
Console.WriteLine($&quot;排序前: [{string.Join(&quot;, &quot;, list.ToArray())}]&quot;);
list.Sort();
Console.WriteLine($&quot;排序后: [{string.Join(&quot;, &quot;, list.ToArray())}]&quot;);

// 注意：Sort要求所有元素实现IComparable接口
// 如果元素类型不同，排序可能会失败

// Reverse方法 - 反转ArrayList中元素的顺序
ArrayList list2 = new ArrayList { 1, 2, 3, 4, 5 };
list2.Reverse();
Console.WriteLine($&quot;反转后: [{string.Join(&quot;, &quot;, list2.ToArray())}]&quot;);

// 使用自定义比较器排序
ArrayList list3 = new ArrayList { &quot;apple&quot;, &quot;banana&quot;, &quot;cherry&quot;, &quot;date&quot; };
list3.Sort(new CaseInsensitiveComparer()); // 不区分大小写排序
Console.WriteLine($&quot;自定义排序后: [{string.Join(&quot;, &quot;, list3.ToArray())}]&quot;);
&lt;/code&gt;&lt;/pre&gt;
&lt;h4&gt;复制和转换&lt;/h4&gt;
&lt;pre&gt;&lt;code&gt;ArrayList list = new ArrayList { 10, 20, 30, 40, 50 };

// Clone方法 - 创建ArrayList的浅表副本
ArrayList cloned = (ArrayList)list.Clone();
Console.WriteLine($&quot;原列表: [{string.Join(&quot;, &quot;, list.ToArray())}]&quot;);
Console.WriteLine($&quot;克隆列表: [{string.Join(&quot;, &quot;, cloned.ToArray())}]&quot;);

// ToArray方法 - 将ArrayList转换为数组
object[] array = list.ToArray();
int[] intArray = list.Cast&amp;lt;int&amp;gt;().ToArray(); // 使用LINQ转换类型

// CopyTo方法 - 复制到数组
int[] targetArray = new int[list.Count];
list.CopyTo(targetArray);
Console.WriteLine($&quot;复制到数组: [{string.Join(&quot;, &quot;, targetArray)}]&quot;);

// GetRange方法 - 获取指定范围的元素（返回新的ArrayList）
ArrayList range = list.GetRange(1, 3); // 从索引1开始，获取3个元素
Console.WriteLine($&quot;范围元素: [{string.Join(&quot;, &quot;, range.ToArray())}]&quot;);
&lt;/code&gt;&lt;/pre&gt;
&lt;h3&gt;ArrayList的类型转换和装箱拆箱&lt;/h3&gt;
&lt;p&gt;由于ArrayList存储的是object类型，值类型会进行装箱，读取时需要拆箱：&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;ArrayList list = new ArrayList();

// 值类型装箱
int number = 100;
list.Add(number); // 装箱：int转换为object

// 读取时需要拆箱
int value = (int)list[0]; // 拆箱：object转换为int

// 类型转换错误会在运行时抛出异常
try
{
    string str = (string)list[0]; // 抛出InvalidCastException
}
catch (InvalidCastException ex)
{
    Console.WriteLine($&quot;类型转换错误: {ex.Message}&quot;);
}

// 使用as运算符进行安全转换
string safeStr = list[0] as string; // 返回null，不会抛出异常
if (safeStr != null)
{
    Console.WriteLine($&quot;转换成功: {safeStr}&quot;);
}
else
{
    Console.WriteLine(&quot;转换失败，返回null&quot;);
}

// 使用is运算符检查类型
if (list[0] is int)
{
    int intValue = (int)list[0];
    Console.WriteLine($&quot;是int类型: {intValue}&quot;);
}
&lt;/code&gt;&lt;/pre&gt;
&lt;h3&gt;ArrayList的实际应用示例&lt;/h3&gt;
&lt;pre&gt;&lt;code&gt;using System;
using System.Collections;

class ArrayListExamples
{
    // 存储混合类型的数据
    public static void MixedTypeExample()
    {
        ArrayList mixedList = new ArrayList();
        mixedList.Add(100);           // int
        mixedList.Add(&quot;Hello&quot;);      // string
        mixedList.Add(3.14);         // double
        mixedList.Add(true);         // bool
        mixedList.Add(DateTime.Now);  // DateTime
        
        Console.WriteLine(&quot;混合类型列表:&quot;);
        foreach (var item in mixedList)
        {
            Console.WriteLine($&quot;类型: {item.GetType().Name}, 值: {item}&quot;);
        }
    }
    
    // 动态添加和删除元素
    public static void DynamicOperations()
    {
        ArrayList list = new ArrayList();
        
        // 动态添加
        for (int i = 0; i &amp;lt; 10; i++)
        {
            list.Add(i * i);
        }
        
        Console.WriteLine($&quot;添加后元素数: {list.Count}&quot;);
        
        // 动态删除
        while (list.Count &amp;gt; 5)
        {
            list.RemoveAt(list.Count - 1);
        }
        
        Console.WriteLine($&quot;删除后元素数: {list.Count}&quot;);
        Console.WriteLine($&quot;剩余元素: [{string.Join(&quot;, &quot;, list.ToArray())}]&quot;);
    }
    
    // 查找和过滤
    public static void FindAndFilter()
    {
        ArrayList numbers = new ArrayList { 10, 20, 30, 40, 50, 20, 30 };
        
        // 查找所有20的索引
        ArrayList indices = new ArrayList();
        int index = -1;
        while ((index = numbers.IndexOf(20, index + 1)) != -1)
        {
            indices.Add(index);
        }
        
        Console.WriteLine($&quot;元素20出现的索引: [{string.Join(&quot;, &quot;, indices.ToArray())}]&quot;);
        
        // 过滤大于30的元素
        ArrayList filtered = new ArrayList();
        foreach (int num in numbers)
        {
            if (num &amp;gt; 30)
            {
                filtered.Add(num);
            }
        }
        
        Console.WriteLine($&quot;大于30的元素: [{string.Join(&quot;, &quot;, filtered.ToArray())}]&quot;);
    }
    
    // 排序和搜索
    public static void SortAndSearch()
    {
        ArrayList numbers = new ArrayList { 50, 20, 30, 10, 40 };
        
        // 排序
        numbers.Sort();
        Console.WriteLine($&quot;排序后: [{string.Join(&quot;, &quot;, numbers.ToArray())}]&quot;);
        
        // 二分查找
        int searchValue = 30;
        int index = numbers.BinarySearch(searchValue);
        if (index &amp;gt;= 0)
        {
            Console.WriteLine($&quot;找到 {searchValue}，索引: {index}&quot;);
        }
        else
        {
            Console.WriteLine($&quot;未找到 {searchValue}&quot;);
        }
    }
    
    // 合并两个ArrayList
    public static ArrayList Merge(ArrayList list1, ArrayList list2)
    {
        ArrayList merged = new ArrayList(list1);
        merged.AddRange(list2);
        return merged;
    }
    
    static void Main()
    {
        Console.WriteLine(&quot;=== 混合类型示例 ===&quot;);
        MixedTypeExample();
        
        Console.WriteLine(&quot;\n=== 动态操作示例 ===&quot;);
        DynamicOperations();
        
        Console.WriteLine(&quot;\n=== 查找和过滤示例 ===&quot;);
        FindAndFilter();
        
        Console.WriteLine(&quot;\n=== 排序和搜索示例 ===&quot;);
        SortAndSearch();
        
        Console.WriteLine(&quot;\n=== 合并示例 ===&quot;);
        ArrayList list1 = new ArrayList { 1, 2, 3 };
        ArrayList list2 = new ArrayList { 4, 5, 6 };
        ArrayList merged = Merge(list1, list2);
        Console.WriteLine($&quot;合并结果: [{string.Join(&quot;, &quot;, merged.ToArray())}]&quot;);
    }
}
&lt;/code&gt;&lt;/pre&gt;
&lt;h3&gt;ArrayList vs List&amp;lt;T&amp;gt; 对比&lt;/h3&gt;
&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;特性&lt;/th&gt;
&lt;th&gt;ArrayList&lt;/th&gt;
&lt;th&gt;List&amp;lt;T&amp;gt;&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;类型安全&lt;/td&gt;
&lt;td&gt;否（存储object）&lt;/td&gt;
&lt;td&gt;是（存储指定类型T）&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;装箱拆箱&lt;/td&gt;
&lt;td&gt;值类型会装箱拆箱&lt;/td&gt;
&lt;td&gt;无装箱拆箱&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;性能&lt;/td&gt;
&lt;td&gt;较慢（装箱拆箱开销）&lt;/td&gt;
&lt;td&gt;较快&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;编译时类型检查&lt;/td&gt;
&lt;td&gt;否&lt;/td&gt;
&lt;td&gt;是&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;代码可读性&lt;/td&gt;
&lt;td&gt;较差&lt;/td&gt;
&lt;td&gt;较好&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;推荐使用&lt;/td&gt;
&lt;td&gt;不推荐（遗留代码）&lt;/td&gt;
&lt;td&gt;推荐&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;
&lt;h3&gt;ArrayList使用注意事项&lt;/h3&gt;
&lt;ol&gt;
&lt;li&gt;
&lt;p&gt;&lt;strong&gt;类型安全问题&lt;/strong&gt;：&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;ArrayList存储的是object类型，需要类型转换&lt;/li&gt;
&lt;li&gt;类型转换错误只能在运行时发现&lt;/li&gt;
&lt;li&gt;建议使用List&amp;lt;T&amp;gt;获得编译时类型检查&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;&lt;strong&gt;性能问题&lt;/strong&gt;：&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;值类型会进行装箱，读取时需要拆箱&lt;/li&gt;
&lt;li&gt;装箱和拆箱会带来性能开销&lt;/li&gt;
&lt;li&gt;对于值类型，List&amp;lt;T&amp;gt;性能更好&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;&lt;strong&gt;容量管理&lt;/strong&gt;：&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;ArrayList会自动扩容，但频繁扩容会影响性能&lt;/li&gt;
&lt;li&gt;如果知道大概容量，可以在创建时指定初始容量&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;&lt;strong&gt;线程安全&lt;/strong&gt;：&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;ArrayList不是线程安全的&lt;/li&gt;
&lt;li&gt;多线程环境下需要使用同步机制或使用线程安全的集合&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;&lt;strong&gt;排序限制&lt;/strong&gt;：&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;Sort方法要求元素实现IComparable接口&lt;/li&gt;
&lt;li&gt;混合类型无法排序&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;/ol&gt;
&lt;h3&gt;ArrayList类常用属性和方法速查表&lt;/h3&gt;
&lt;h4&gt;常用属性&lt;/h4&gt;
&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;属性&lt;/th&gt;
&lt;th&gt;类型&lt;/th&gt;
&lt;th&gt;作用说明&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;Count&lt;/td&gt;
&lt;td&gt;int&lt;/td&gt;
&lt;td&gt;获取ArrayList中实际包含的元素数量&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Capacity&lt;/td&gt;
&lt;td&gt;int&lt;/td&gt;
&lt;td&gt;获取或设置ArrayList的容量（内部数组的大小）。容量总是大于或等于Count&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;IsFixedSize&lt;/td&gt;
&lt;td&gt;bool&lt;/td&gt;
&lt;td&gt;获取一个值，该值指示ArrayList是否有固定大小。对于ArrayList，始终返回false&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;IsReadOnly&lt;/td&gt;
&lt;td&gt;bool&lt;/td&gt;
&lt;td&gt;获取一个值，该值指示ArrayList是否为只读。对于ArrayList，始终返回false&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;IsSynchronized&lt;/td&gt;
&lt;td&gt;bool&lt;/td&gt;
&lt;td&gt;获取一个值，该值指示对ArrayList的访问是否同步（线程安全）。对于ArrayList，始终返回false&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;SyncRoot&lt;/td&gt;
&lt;td&gt;object&lt;/td&gt;
&lt;td&gt;获取可用于同步对ArrayList的访问的对象&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;
&lt;h4&gt;常用方法&lt;/h4&gt;
&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;方法&lt;/th&gt;
&lt;th&gt;参数&lt;/th&gt;
&lt;th&gt;返回值&lt;/th&gt;
&lt;th&gt;作用说明&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;Add(object)&lt;/td&gt;
&lt;td&gt;object value&lt;/td&gt;
&lt;td&gt;int&lt;/td&gt;
&lt;td&gt;在ArrayList末尾添加元素，返回添加位置的索引&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;AddRange(ICollection)&lt;/td&gt;
&lt;td&gt;ICollection c&lt;/td&gt;
&lt;td&gt;void&lt;/td&gt;
&lt;td&gt;将集合中的元素添加到ArrayList末尾&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Insert(int, object)&lt;/td&gt;
&lt;td&gt;int index, object value&lt;/td&gt;
&lt;td&gt;void&lt;/td&gt;
&lt;td&gt;在指定索引位置插入元素&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;InsertRange(int, ICollection)&lt;/td&gt;
&lt;td&gt;int index, ICollection c&lt;/td&gt;
&lt;td&gt;void&lt;/td&gt;
&lt;td&gt;在指定索引位置插入集合中的元素&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Remove(object)&lt;/td&gt;
&lt;td&gt;object obj&lt;/td&gt;
&lt;td&gt;void&lt;/td&gt;
&lt;td&gt;删除首次出现的指定元素&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;RemoveAt(int)&lt;/td&gt;
&lt;td&gt;int index&lt;/td&gt;
&lt;td&gt;void&lt;/td&gt;
&lt;td&gt;删除指定索引的元素&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;RemoveRange(int, int)&lt;/td&gt;
&lt;td&gt;int index, int count&lt;/td&gt;
&lt;td&gt;void&lt;/td&gt;
&lt;td&gt;从指定索引开始删除指定数量的元素&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Clear()&lt;/td&gt;
&lt;td&gt;无&lt;/td&gt;
&lt;td&gt;void&lt;/td&gt;
&lt;td&gt;清空ArrayList中的所有元素&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Contains(object)&lt;/td&gt;
&lt;td&gt;object item&lt;/td&gt;
&lt;td&gt;bool&lt;/td&gt;
&lt;td&gt;检查ArrayList是否包含指定元素&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;IndexOf(object)&lt;/td&gt;
&lt;td&gt;object value&lt;/td&gt;
&lt;td&gt;int&lt;/td&gt;
&lt;td&gt;查找元素首次出现的索引，未找到返回-1&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;IndexOf(object, int)&lt;/td&gt;
&lt;td&gt;object value, int startIndex&lt;/td&gt;
&lt;td&gt;int&lt;/td&gt;
&lt;td&gt;从指定索引开始查找元素&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;LastIndexOf(object)&lt;/td&gt;
&lt;td&gt;object value&lt;/td&gt;
&lt;td&gt;int&lt;/td&gt;
&lt;td&gt;查找元素最后出现的索引&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;BinarySearch(object)&lt;/td&gt;
&lt;td&gt;object value&lt;/td&gt;
&lt;td&gt;int&lt;/td&gt;
&lt;td&gt;在已排序的ArrayList中使用二分查找，返回索引或负数&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Sort()&lt;/td&gt;
&lt;td&gt;无&lt;/td&gt;
&lt;td&gt;void&lt;/td&gt;
&lt;td&gt;对ArrayList进行排序（要求元素实现IComparable）&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Sort(IComparer)&lt;/td&gt;
&lt;td&gt;IComparer comparer&lt;/td&gt;
&lt;td&gt;void&lt;/td&gt;
&lt;td&gt;使用指定的比较器对ArrayList进行排序&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Reverse()&lt;/td&gt;
&lt;td&gt;无&lt;/td&gt;
&lt;td&gt;void&lt;/td&gt;
&lt;td&gt;反转ArrayList中元素的顺序&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;ToArray()&lt;/td&gt;
&lt;td&gt;无&lt;/td&gt;
&lt;td&gt;object[]&lt;/td&gt;
&lt;td&gt;将ArrayList转换为object数组&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;CopyTo(Array)&lt;/td&gt;
&lt;td&gt;Array array&lt;/td&gt;
&lt;td&gt;void&lt;/td&gt;
&lt;td&gt;将ArrayList的元素复制到数组中&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;CopyTo(Array, int)&lt;/td&gt;
&lt;td&gt;Array array, int index&lt;/td&gt;
&lt;td&gt;void&lt;/td&gt;
&lt;td&gt;从指定索引开始复制到数组&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;GetRange(int, int)&lt;/td&gt;
&lt;td&gt;int index, int count&lt;/td&gt;
&lt;td&gt;ArrayList&lt;/td&gt;
&lt;td&gt;返回包含指定范围元素的新ArrayList&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Clone()&lt;/td&gt;
&lt;td&gt;无&lt;/td&gt;
&lt;td&gt;object&lt;/td&gt;
&lt;td&gt;创建ArrayList的浅表副本&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;TrimToSize()&lt;/td&gt;
&lt;td&gt;无&lt;/td&gt;
&lt;td&gt;void&lt;/td&gt;
&lt;td&gt;将容量设置为实际元素数量，释放多余内存&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;
&lt;h3&gt;何时使用ArrayList？&lt;/h3&gt;
&lt;p&gt;虽然现在推荐使用List&amp;lt;T&amp;gt;，但在以下情况下可能仍需要使用ArrayList：&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;&lt;strong&gt;遗留代码维护&lt;/strong&gt;：维护使用ArrayList的旧代码&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;需要存储混合类型&lt;/strong&gt;：虽然不推荐，但如果确实需要存储不同类型且无法使用泛型&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;学习目的&lt;/strong&gt;：理解集合的基本概念和C#的发展历程&lt;/li&gt;
&lt;/ol&gt;
&lt;h3&gt;迁移到List&amp;lt;T&amp;gt;的建议&lt;/h3&gt;
&lt;p&gt;如果现有代码使用ArrayList，建议迁移到List&amp;lt;T&amp;gt;：&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;// 旧代码（使用ArrayList）
ArrayList oldList = new ArrayList();
oldList.Add(100);
oldList.Add(200);
int value = (int)oldList[0]; // 需要拆箱

// 新代码（使用List&amp;lt;T&amp;gt;）
List&amp;lt;int&amp;gt; newList = new List&amp;lt;int&amp;gt;();
newList.Add(100);
newList.Add(200);
int value2 = newList[0]; // 无拆箱，类型安全
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;通过了解ArrayList，可以更好地理解C#集合的发展历程，以及为什么泛型集合（如List&amp;lt;T&amp;gt;）是更好的选择。在实际开发中，应优先使用List&amp;lt;T&amp;gt;等泛型集合来获得更好的类型安全性和性能。&lt;/p&gt;
&lt;h2&gt;&amp;lt;a id=&quot;list-class&quot;&amp;gt;&amp;lt;/a&amp;gt;C# List类详解&lt;/h2&gt;
&lt;p&gt;List&amp;lt;T&amp;gt;是C#中最常用的泛型集合类，位于System.Collections.Generic命名空间中。它提供了类型安全的动态数组功能，是ArrayList的泛型替代品，也是现代C#开发中推荐使用的集合类型。&lt;/p&gt;
&lt;h3&gt;List&amp;lt;T&amp;gt;的基本概念&lt;/h3&gt;
&lt;p&gt;List&amp;lt;T&amp;gt;是一个泛型动态数组，可以存储指定类型T的元素。与ArrayList相比，List&amp;lt;T&amp;gt;具有以下优势：&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;&lt;strong&gt;类型安全&lt;/strong&gt;：编译时进行类型检查，避免运行时类型错误&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;性能优异&lt;/strong&gt;：值类型无需装箱拆箱，性能更好&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;代码清晰&lt;/strong&gt;：类型明确，代码更易读易维护&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;IntelliSense支持&lt;/strong&gt;：IDE可以提供更好的代码提示&lt;/li&gt;
&lt;/ol&gt;
&lt;h4&gt;为什么使用List&amp;lt;T&amp;gt;？&lt;/h4&gt;
&lt;pre&gt;&lt;code&gt;// ArrayList的问题
ArrayList list1 = new ArrayList();
list1.Add(100);
list1.Add(&quot;Hello&quot;); // 可以添加不同类型，但类型不安全
int value = (int)list1[0]; // 需要类型转换，可能出错

// List&amp;lt;T&amp;gt;的优势
List&amp;lt;int&amp;gt; list2 = new List&amp;lt;int&amp;gt;();
list2.Add(100);
// list2.Add(&quot;Hello&quot;); // 编译错误，类型安全
int value2 = list2[0]; // 无需类型转换，直接使用
&lt;/code&gt;&lt;/pre&gt;
&lt;h3&gt;List&amp;lt;T&amp;gt;的创建和初始化&lt;/h3&gt;
&lt;pre&gt;&lt;code&gt;using System.Collections.Generic;

// 1. 使用默认构造函数创建空List
List&amp;lt;int&amp;gt; list1 = new List&amp;lt;int&amp;gt;();

// 2. 指定初始容量创建List（性能优化）
List&amp;lt;int&amp;gt; list2 = new List&amp;lt;int&amp;gt;(100); // 初始容量为100

// 3. 使用集合初始化器创建并初始化
List&amp;lt;int&amp;gt; list3 = new List&amp;lt;int&amp;gt; { 1, 2, 3, 4, 5 };
List&amp;lt;string&amp;gt; list4 = new List&amp;lt;string&amp;gt; { &quot;Apple&quot;, &quot;Banana&quot;, &quot;Cherry&quot; };

// 4. 从数组创建List
int[] array = { 1, 2, 3, 4, 5 };
List&amp;lt;int&amp;gt; list5 = new List&amp;lt;int&amp;gt;(array);

// 5. 从其他集合创建List
List&amp;lt;int&amp;gt; list6 = new List&amp;lt;int&amp;gt;(list3);

// 6. 使用var关键字（类型推断）
var list7 = new List&amp;lt;string&amp;gt; { &quot;A&quot;, &quot;B&quot;, &quot;C&quot; };

// 7. 使用AddRange方法初始化
List&amp;lt;int&amp;gt; list8 = new List&amp;lt;int&amp;gt;();
list8.AddRange(new int[] { 1, 2, 3, 4, 5 });

Console.WriteLine($&quot;list3的元素数: {list3.Count}&quot;);
Console.WriteLine($&quot;list4的元素数: {list4.Count}&quot;);
&lt;/code&gt;&lt;/pre&gt;
&lt;h3&gt;List&amp;lt;T&amp;gt;的常用属性&lt;/h3&gt;
&lt;pre&gt;&lt;code&gt;List&amp;lt;int&amp;gt; list = new List&amp;lt;int&amp;gt; { 1, 2, 3, 4, 5 };

// Count属性 - 获取List中实际包含的元素数量
Console.WriteLine($&quot;元素数量: {list.Count}&quot;); // 5

// Capacity属性 - 获取或设置List的容量（内部数组的大小）
Console.WriteLine($&quot;当前容量: {list.Capacity}&quot;); // 可能是8或更大

// 设置容量（如果知道大概数量，可以提前设置以提高性能）
list.Capacity = 20;
Console.WriteLine($&quot;设置后的容量: {list.Capacity}&quot;); // 20

// 注意：Capacity总是 &amp;gt;= Count
&lt;/code&gt;&lt;/pre&gt;
&lt;h3&gt;List&amp;lt;T&amp;gt;的常用方法&lt;/h3&gt;
&lt;h4&gt;添加元素&lt;/h4&gt;
&lt;pre&gt;&lt;code&gt;List&amp;lt;int&amp;gt; list = new List&amp;lt;int&amp;gt;();

// Add方法 - 在末尾添加单个元素
list.Add(10);
list.Add(20);
list.Add(30);

Console.WriteLine($&quot;添加后元素数: {list.Count}&quot;); // 输出: 添加后元素数: 3

// AddRange方法 - 添加多个元素（从集合）
list.AddRange(new int[] { 40, 50, 60 });
list.AddRange(new List&amp;lt;int&amp;gt; { 70, 80, 90 });

Console.WriteLine($&quot;AddRange后元素数: {list.Count}&quot;); // 输出: AddRange后元素数: 9

// Insert方法 - 在指定索引位置插入元素
list.Insert(0, 5); // 在索引0处插入5
list.Insert(2, 15); // 在索引2处插入15

// InsertRange方法 - 在指定位置插入多个元素
list.InsertRange(1, new int[] { 11, 12, 13 });

// 输出所有元素
Console.WriteLine($&quot;列表内容: [{string.Join(&quot;, &quot;, list)}]&quot;);
// 输出: 列表内容: [5, 11, 12, 13, 10, 15, 20, 30, 40, 50, 60, 70, 80, 90]
&lt;/code&gt;&lt;/pre&gt;
&lt;h4&gt;访问和修改元素&lt;/h4&gt;
&lt;pre&gt;&lt;code&gt;List&amp;lt;int&amp;gt; list = new List&amp;lt;int&amp;gt; { 10, 20, 30, 40, 50 };

// 通过索引访问元素（类型安全，无需转换）
int first = list[0]; // 10
int second = list[1]; // 20
Console.WriteLine($&quot;第一个元素: {first}, 第二个元素: {second}&quot;); // 输出: 第一个元素: 10, 第二个元素: 20

// 通过索引修改元素
list[0] = 100;
list[1] = 200;
Console.WriteLine($&quot;修改后: [{string.Join(&quot;, &quot;, list)}]&quot;); // 输出: 修改后: [100, 200, 30, 40, 50]

// 遍历List - 使用for循环
for (int i = 0; i &amp;lt; list.Count; i++)
{
    Console.WriteLine($&quot;索引 {i}: {list[i]}&quot;);
}
// 输出:
// 索引 0: 100
// 索引 1: 200
// 索引 2: 30
// 索引 3: 40
// 索引 4: 50

// 遍历List - 使用foreach（推荐）
foreach (int item in list)
{
    Console.WriteLine($&quot;元素: {item}&quot;);
}
// 输出:
// 元素: 100
// 元素: 200
// 元素: 30
// 元素: 40
// 元素: 50

// 遍历List - 使用LINQ
list.ForEach(item =&amp;gt; Console.WriteLine($&quot;LINQ遍历: {item}&quot;));
// 输出:
// LINQ遍历: 100
// LINQ遍历: 200
// LINQ遍历: 30
// LINQ遍历: 40
// LINQ遍历: 50

// 获取最后一个元素
int last = list[list.Count - 1];
Console.WriteLine($&quot;最后一个元素: {last}&quot;); // 输出: 最后一个元素: 50

// 获取第一个元素
int first2 = list[0];
Console.WriteLine($&quot;第一个元素: {first2}&quot;); // 输出: 第一个元素: 100
&lt;/code&gt;&lt;/pre&gt;
&lt;h4&gt;查找元素&lt;/h4&gt;
&lt;pre&gt;&lt;code&gt;List&amp;lt;int&amp;gt; list = new List&amp;lt;int&amp;gt; { 10, 20, 30, 20, 40, 20 };

// IndexOf方法 - 查找元素首次出现的索引
int index1 = list.IndexOf(20); // 返回1
int index2 = list.IndexOf(100); // 返回-1（未找到）
Console.WriteLine($&quot;IndexOf(20): {index1}&quot;); // 输出: IndexOf(20): 1
Console.WriteLine($&quot;IndexOf(100): {index2}&quot;); // 输出: IndexOf(100): -1

// IndexOf重载 - 从指定位置开始查找
int index3 = list.IndexOf(20, 2); // 从索引2开始查找，返回3
Console.WriteLine($&quot;IndexOf(20, 2): {index3}&quot;); // 输出: IndexOf(20, 2): 3

// LastIndexOf方法 - 查找元素最后出现的索引
int lastIndex = list.LastIndexOf(20); // 返回5
Console.WriteLine($&quot;LastIndexOf(20): {lastIndex}&quot;); // 输出: LastIndexOf(20): 5

// Contains方法 - 检查是否包含指定元素
bool contains = list.Contains(30); // true
bool notContains = list.Contains(100); // false
Console.WriteLine($&quot;Contains(30): {contains}&quot;); // 输出: Contains(30): True
Console.WriteLine($&quot;Contains(100): {notContains}&quot;); // 输出: Contains(100): False

// Find方法 - 查找第一个满足条件的元素
int found = list.Find(x =&amp;gt; x &amp;gt; 25); // 返回30（第一个大于25的元素）
Console.WriteLine($&quot;Find(&amp;gt;25): {found}&quot;); // 输出: Find(&amp;gt;25): 30

// FindLast方法 - 查找最后一个满足条件的元素
int foundLast = list.FindLast(x =&amp;gt; x &amp;gt; 25); // 返回40
Console.WriteLine($&quot;FindLast(&amp;gt;25): {foundLast}&quot;); // 输出: FindLast(&amp;gt;25): 40

// FindAll方法 - 查找所有满足条件的元素
List&amp;lt;int&amp;gt; foundAll = list.FindAll(x =&amp;gt; x &amp;gt; 25); // 返回[30, 40]
Console.WriteLine($&quot;FindAll(&amp;gt;25): [{string.Join(&quot;, &quot;, foundAll)}]&quot;); // 输出: FindAll(&amp;gt;25): [30, 40]

// FindIndex方法 - 查找第一个满足条件的元素的索引
int foundIndex = list.FindIndex(x =&amp;gt; x &amp;gt; 25); // 返回2
Console.WriteLine($&quot;FindIndex(&amp;gt;25): {foundIndex}&quot;); // 输出: FindIndex(&amp;gt;25): 2

// FindLastIndex方法 - 查找最后一个满足条件的元素的索引
int foundLastIndex = list.FindLastIndex(x =&amp;gt; x &amp;gt; 25); // 返回4
Console.WriteLine($&quot;FindLastIndex(&amp;gt;25): {foundLastIndex}&quot;); // 输出: FindLastIndex(&amp;gt;25): 4

// Exists方法 - 检查是否存在满足条件的元素
bool exists = list.Exists(x =&amp;gt; x &amp;gt; 50); // false
Console.WriteLine($&quot;Exists(&amp;gt;50): {exists}&quot;); // 输出: Exists(&amp;gt;50): False
&lt;/code&gt;&lt;/pre&gt;
&lt;h4&gt;删除元素&lt;/h4&gt;
&lt;pre&gt;&lt;code&gt;List&amp;lt;int&amp;gt; list = new List&amp;lt;int&amp;gt; { 10, 20, 30, 40, 50, 20 };

// Remove方法 - 删除首次出现的指定元素
bool removed = list.Remove(20); // 返回true，删除第一个20
Console.WriteLine($&quot;Remove后: [{string.Join(&quot;, &quot;, list)}]&quot;);

// RemoveAt方法 - 删除指定索引的元素
list.RemoveAt(0); // 删除索引0的元素
Console.WriteLine($&quot;RemoveAt后: [{string.Join(&quot;, &quot;, list)}]&quot;);

// RemoveRange方法 - 删除指定范围的元素
List&amp;lt;int&amp;gt; list2 = new List&amp;lt;int&amp;gt; { 10, 20, 30, 40, 50 };
list2.RemoveRange(1, 2); // 从索引1开始删除2个元素
Console.WriteLine($&quot;RemoveRange后: [{string.Join(&quot;, &quot;, list2)}]&quot;);

// RemoveAll方法 - 删除所有满足条件的元素
List&amp;lt;int&amp;gt; list3 = new List&amp;lt;int&amp;gt; { 10, 20, 30, 40, 50 };
int removedCount = list3.RemoveAll(x =&amp;gt; x &amp;gt; 25); // 删除所有大于25的元素
Console.WriteLine($&quot;RemoveAll删除了{removedCount}个元素: [{string.Join(&quot;, &quot;, list3)}]&quot;);

// Clear方法 - 清空所有元素
list3.Clear();
Console.WriteLine($&quot;Clear后元素数: {list3.Count}&quot;); // 0
&lt;/code&gt;&lt;/pre&gt;
&lt;h4&gt;排序和反转&lt;/h4&gt;
&lt;pre&gt;&lt;code&gt;List&amp;lt;int&amp;gt; list = new List&amp;lt;int&amp;gt; { 50, 20, 30, 10, 40 };

// Sort方法 - 对List进行排序（升序）
Console.WriteLine($&quot;排序前: [{string.Join(&quot;, &quot;, list)}]&quot;);
list.Sort();
Console.WriteLine($&quot;排序后: [{string.Join(&quot;, &quot;, list)}]&quot;);

// Sort重载 - 使用自定义比较器排序（降序）
list.Sort((x, y) =&amp;gt; y.CompareTo(x)); // 降序排序
Console.WriteLine($&quot;降序排序: [{string.Join(&quot;, &quot;, list)}]&quot;);

// Sort重载 - 使用Comparison委托
list.Sort(CompareNumbers);
Console.WriteLine($&quot;自定义排序: [{string.Join(&quot;, &quot;, list)}]&quot;);

// Reverse方法 - 反转List中元素的顺序
List&amp;lt;int&amp;gt; list2 = new List&amp;lt;int&amp;gt; { 1, 2, 3, 4, 5 };
list2.Reverse();
Console.WriteLine($&quot;反转后: [{string.Join(&quot;, &quot;, list2)}]&quot;);

// 字符串列表排序
List&amp;lt;string&amp;gt; names = new List&amp;lt;string&amp;gt; { &quot;张三&quot;, &quot;李四&quot;, &quot;王五&quot;, &quot;赵六&quot; };
names.Sort();
Console.WriteLine($&quot;字符串排序: [{string.Join(&quot;, &quot;, names)}]&quot;);

// 自定义对象排序
List&amp;lt;Person&amp;gt; people = new List&amp;lt;Person&amp;gt;
{
    new Person { Name = &quot;张三&quot;, Age = 25 },
    new Person { Name = &quot;李四&quot;, Age = 30 },
    new Person { Name = &quot;王五&quot;, Age = 20 }
};

people.Sort((p1, p2) =&amp;gt; p1.Age.CompareTo(p2.Age)); // 按年龄排序
Console.WriteLine(&quot;按年龄排序:&quot;);
foreach (var person in people)
{
    Console.WriteLine($&quot;  {person.Name}: {person.Age}岁&quot;);
}
// 输出:
// 按年龄排序:
//   王五: 20岁
//   张三: 25岁
//   李四: 30岁

// 辅助方法
static int CompareNumbers(int x, int y)
{
    return x.CompareTo(y);
}
&lt;/code&gt;&lt;/pre&gt;
&lt;h4&gt;转换和复制&lt;/h4&gt;
&lt;pre&gt;&lt;code&gt;List&amp;lt;int&amp;gt; list = new List&amp;lt;int&amp;gt; { 10, 20, 30, 40, 50 };

// ToArray方法 - 将List转换为数组
int[] array = list.ToArray();
Console.WriteLine($&quot;转换为数组: [{string.Join(&quot;, &quot;, array)}]&quot;);
// 输出: 转换为数组: [10, 20, 30, 40, 50]

// CopyTo方法 - 复制到数组
int[] targetArray = new int[list.Count];
list.CopyTo(targetArray);
Console.WriteLine($&quot;复制到数组: [{string.Join(&quot;, &quot;, targetArray)}]&quot;);
// 输出: 复制到数组: [10, 20, 30, 40, 50]

// CopyTo重载 - 从指定索引开始复制
int[] targetArray2 = new int[10];
list.CopyTo(targetArray2, 2); // 从索引2开始复制
Console.WriteLine($&quot;从索引2复制: [{string.Join(&quot;, &quot;, targetArray2)}]&quot;);
// 输出: 从索引2复制: [0, 0, 10, 20, 30, 40, 50, 0, 0, 0]

// GetRange方法 - 获取指定范围的元素（返回新的List）
List&amp;lt;int&amp;gt; range = list.GetRange(1, 3); // 从索引1开始，获取3个元素
Console.WriteLine($&quot;范围元素: [{string.Join(&quot;, &quot;, range)}]&quot;);
// 输出: 范围元素: [20, 30, 40]

// ConvertAll方法 - 将List中的元素转换为另一种类型
List&amp;lt;string&amp;gt; stringList = list.ConvertAll(x =&amp;gt; x.ToString());
Console.WriteLine($&quot;转换为字符串: [{string.Join(&quot;, &quot;, stringList)}]&quot;);
// 输出: 转换为字符串: [10, 20, 30, 40, 50]

// 使用LINQ进行转换
List&amp;lt;double&amp;gt; doubleList = list.Select(x =&amp;gt; (double)x).ToList();
Console.WriteLine($&quot;转换为double: [{string.Join(&quot;, &quot;, doubleList)}]&quot;);
// 输出: 转换为double: [10, 20, 30, 40, 50]
&lt;/code&gt;&lt;/pre&gt;
&lt;h3&gt;List&amp;lt;T&amp;gt;与LINQ&lt;/h3&gt;
&lt;p&gt;List&amp;lt;T&amp;gt;与LINQ完美集成，提供了强大的查询和操作能力：&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;List&amp;lt;int&amp;gt; numbers = new List&amp;lt;int&amp;gt; { 1, 2, 3, 4, 5, 6, 7, 8, 9, 10 };

// Where - 过滤元素
List&amp;lt;int&amp;gt; evens = numbers.Where(x =&amp;gt; x % 2 == 0).ToList();
Console.WriteLine($&quot;偶数: [{string.Join(&quot;, &quot;, evens)}]&quot;);

// Select - 投影转换
List&amp;lt;string&amp;gt; strings = numbers.Select(x =&amp;gt; $&quot;数字{x}&quot;).ToList();
Console.WriteLine($&quot;转换后: [{string.Join(&quot;, &quot;, strings)}]&quot;);

// OrderBy / OrderByDescending - 排序
List&amp;lt;int&amp;gt; sorted = numbers.OrderByDescending(x =&amp;gt; x).ToList();
Console.WriteLine($&quot;降序: [{string.Join(&quot;, &quot;, sorted)}]&quot;);

// First / Last - 获取第一个/最后一个元素
int first = numbers.First();
int last = numbers.Last();
int firstEven = numbers.First(x =&amp;gt; x % 2 == 0);
Console.WriteLine($&quot;第一个元素: {first}, 最后一个元素: {last}, 第一个偶数: {firstEven}&quot;);
// 输出: 第一个元素: 1, 最后一个元素: 10, 第一个偶数: 2

// Any / All - 检查条件
bool hasEven = numbers.Any(x =&amp;gt; x % 2 == 0); // true
bool allPositive = numbers.All(x =&amp;gt; x &amp;gt; 0); // true
Console.WriteLine($&quot;是否有偶数: {hasEven}, 是否都为正数: {allPositive}&quot;);
// 输出: 是否有偶数: True, 是否都为正数: True

// Count - 计数
int evenCount = numbers.Count(x =&amp;gt; x % 2 == 0); // 5
Console.WriteLine($&quot;偶数个数: {evenCount}&quot;); // 输出: 偶数个数: 5

// Sum / Average / Max / Min - 聚合操作
int sum = numbers.Sum(); // 55
double average = numbers.Average(); // 5.5
int max = numbers.Max(); // 10
int min = numbers.Min(); // 1
Console.WriteLine($&quot;总和: {sum}, 平均值: {average}, 最大值: {max}, 最小值: {min}&quot;);
// 输出: 总和: 55, 平均值: 5.5, 最大值: 10, 最小值: 1

// Distinct - 去重
List&amp;lt;int&amp;gt; withDuplicates = new List&amp;lt;int&amp;gt; { 1, 2, 2, 3, 3, 3, 4 };
List&amp;lt;int&amp;gt; distinct = withDuplicates.Distinct().ToList();
Console.WriteLine($&quot;去重后: [{string.Join(&quot;, &quot;, distinct)}]&quot;);

// GroupBy - 分组
var grouped = numbers.GroupBy(x =&amp;gt; x % 2 == 0 ? &quot;偶数&quot; : &quot;奇数&quot;);
foreach (var group in grouped)
{
    Console.WriteLine($&quot;{group.Key}: [{string.Join(&quot;, &quot;, group)}]&quot;);
}
&lt;/code&gt;&lt;/pre&gt;
&lt;h3&gt;List&amp;lt;T&amp;gt;的实际应用示例&lt;/h3&gt;
&lt;pre&gt;&lt;code&gt;using System;
using System.Collections.Generic;
using System.Linq;

class ListExamples
{
    // 学生成绩管理
    public static void StudentGradeExample()
    {
        List&amp;lt;Student&amp;gt; students = new List&amp;lt;Student&amp;gt;
        {
            new Student { Name = &quot;张三&quot;, Score = 85 },
            new Student { Name = &quot;李四&quot;, Score = 92 },
            new Student { Name = &quot;王五&quot;, Score = 78 },
            new Student { Name = &quot;赵六&quot;, Score = 96 },
            new Student { Name = &quot;孙七&quot;, Score = 88 }
        };
        
        // 按分数排序
        students.Sort((s1, s2) =&amp;gt; s2.Score.CompareTo(s1.Score));
        
        Console.WriteLine(&quot;学生成绩排名:&quot;);
        for (int i = 0; i &amp;lt; students.Count; i++)
        {
            Console.WriteLine($&quot;{i + 1}. {students[i].Name}: {students[i].Score}分&quot;);
        }
        
        // 计算平均分
        double average = students.Average(s =&amp;gt; s.Score);
        Console.WriteLine($&quot;平均分: {average:F2}&quot;);
        
        // 查找高分学生（&amp;gt;=90）
        List&amp;lt;Student&amp;gt; highScorers = students.Where(s =&amp;gt; s.Score &amp;gt;= 90).ToList();
        Console.WriteLine($&quot;高分学生: {string.Join(&quot;, &quot;, highScorers.Select(s =&amp;gt; s.Name))}&quot;);
    }
    
    // 分页功能
    public static List&amp;lt;T&amp;gt; GetPage&amp;lt;T&amp;gt;(List&amp;lt;T&amp;gt; list, int pageNumber, int pageSize)
    {
        int skip = (pageNumber - 1) * pageSize;
        return list.Skip(skip).Take(pageSize).ToList();
    }
    
    // 去重并保持顺序
    public static List&amp;lt;T&amp;gt; RemoveDuplicates&amp;lt;T&amp;gt;(List&amp;lt;T&amp;gt; list)
    {
        return list.Distinct().ToList();
    }
    
    // 合并两个List
    public static List&amp;lt;T&amp;gt; Merge&amp;lt;T&amp;gt;(List&amp;lt;T&amp;gt; list1, List&amp;lt;T&amp;gt; list2)
    {
        List&amp;lt;T&amp;gt; merged = new List&amp;lt;T&amp;gt;(list1);
        merged.AddRange(list2);
        return merged;
    }
    
    // 查找和替换
    public static void FindAndReplace&amp;lt;T&amp;gt;(List&amp;lt;T&amp;gt; list, T oldValue, T newValue)
    {
        int index = list.IndexOf(oldValue);
        while (index != -1)
        {
            list[index] = newValue;
            index = list.IndexOf(oldValue, index + 1);
        }
    }
    
    // 批量操作
    public static void BatchProcess(List&amp;lt;int&amp;gt; numbers)
    {
        // 过滤、转换、聚合
        var result = numbers
            .Where(x =&amp;gt; x &amp;gt; 0)
            .Select(x =&amp;gt; x * 2)
            .Where(x =&amp;gt; x &amp;gt; 10)
            .OrderByDescending(x =&amp;gt; x)
            .ToList();
        
        Console.WriteLine($&quot;处理结果: [{string.Join(&quot;, &quot;, result)}]&quot;);
    }
    
    static void Main()
    {
        Console.WriteLine(&quot;=== 学生成绩示例 ===&quot;);
        StudentGradeExample();
        
        Console.WriteLine(&quot;\n=== 分页示例 ===&quot;);
        List&amp;lt;int&amp;gt; numbers = Enumerable.Range(1, 20).ToList();
        List&amp;lt;int&amp;gt; page1 = GetPage(numbers, 1, 5);
        Console.WriteLine($&quot;第1页: [{string.Join(&quot;, &quot;, page1)}]&quot;);
        
        Console.WriteLine(&quot;\n=== 去重示例 ===&quot;);
        List&amp;lt;int&amp;gt; withDupes = new List&amp;lt;int&amp;gt; { 1, 2, 2, 3, 3, 3, 4 };
        List&amp;lt;int&amp;gt; unique = RemoveDuplicates(withDupes);
        Console.WriteLine($&quot;去重后: [{string.Join(&quot;, &quot;, unique)}]&quot;);
        
        Console.WriteLine(&quot;\n=== 批量处理示例 ===&quot;);
        List&amp;lt;int&amp;gt; data = new List&amp;lt;int&amp;gt; { -5, 2, 8, 15, 3, 20, -10 };
        BatchProcess(data);
    }
}

// 辅助类
class Student
{
    public string Name { get; set; }
    public int Score { get; set; }
}
&lt;/code&gt;&lt;/pre&gt;
&lt;h3&gt;List&amp;lt;T&amp;gt;的性能优化&lt;/h3&gt;
&lt;pre&gt;&lt;code&gt;// 1. 指定初始容量（如果知道大概数量）
List&amp;lt;int&amp;gt; list1 = new List&amp;lt;int&amp;gt;(1000); // 避免频繁扩容

// 2. 使用Capacity属性优化
List&amp;lt;int&amp;gt; list2 = new List&amp;lt;int&amp;gt;();
list2.Capacity = 1000; // 提前设置容量

// 3. 使用TrimExcess方法释放多余内存
List&amp;lt;int&amp;gt; list3 = new List&amp;lt;int&amp;gt;(1000);
// ... 添加元素
list3.TrimExcess(); // 将容量减少到实际元素数量

// 4. 批量操作优于逐个操作
// 不推荐
List&amp;lt;int&amp;gt; list4 = new List&amp;lt;int&amp;gt;();
for (int i = 0; i &amp;lt; 1000; i++)
{
    list4.Add(i); // 可能多次扩容
}

// 推荐
List&amp;lt;int&amp;gt; list5 = new List&amp;lt;int&amp;gt;(1000);
for (int i = 0; i &amp;lt; 1000; i++)
{
    list5.Add(i); // 只需一次分配
}

// 或者使用AddRange
List&amp;lt;int&amp;gt; list6 = new List&amp;lt;int&amp;gt;();
list6.AddRange(Enumerable.Range(0, 1000));
&lt;/code&gt;&lt;/pre&gt;
&lt;h3&gt;List&amp;lt;T&amp;gt;使用注意事项&lt;/h3&gt;
&lt;ol&gt;
&lt;li&gt;
&lt;p&gt;&lt;strong&gt;类型安全&lt;/strong&gt;：&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;List&amp;lt;T&amp;gt;是类型安全的，编译时检查类型&lt;/li&gt;
&lt;li&gt;不能添加不兼容类型的元素&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;&lt;strong&gt;性能考虑&lt;/strong&gt;：&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;如果知道大概容量，指定初始容量可以提高性能&lt;/li&gt;
&lt;li&gt;频繁插入/删除中间元素时，考虑使用LinkedList&amp;lt;T&amp;gt;&lt;/li&gt;
&lt;li&gt;只读场景考虑使用IReadOnlyList&amp;lt;T&amp;gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;&lt;strong&gt;线程安全&lt;/strong&gt;：&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;List&amp;lt;T&amp;gt;不是线程安全的&lt;/li&gt;
&lt;li&gt;多线程环境下需要使用锁或线程安全的集合&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;&lt;strong&gt;空值处理&lt;/strong&gt;：&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;对于引用类型T，可以存储null&lt;/li&gt;
&lt;li&gt;对于值类型T，不能存储null（除非使用Nullable&amp;lt;T&amp;gt;）&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;&lt;strong&gt;容量管理&lt;/strong&gt;：&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;Capacity会自动增长，但频繁扩容会影响性能&lt;/li&gt;
&lt;li&gt;使用TrimExcess可以释放多余内存&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;/ol&gt;
&lt;h3&gt;List&amp;lt;T&amp;gt;类常用属性和方法速查表&lt;/h3&gt;
&lt;h4&gt;常用属性&lt;/h4&gt;
&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;属性&lt;/th&gt;
&lt;th&gt;类型&lt;/th&gt;
&lt;th&gt;作用说明&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;Count&lt;/td&gt;
&lt;td&gt;int&lt;/td&gt;
&lt;td&gt;获取List中实际包含的元素数量&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Capacity&lt;/td&gt;
&lt;td&gt;int&lt;/td&gt;
&lt;td&gt;获取或设置List的容量（内部数组的大小）。容量总是大于或等于Count&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Item[int]&lt;/td&gt;
&lt;td&gt;T&lt;/td&gt;
&lt;td&gt;获取或设置指定索引处的元素。这是索引器，可以通过list[index]访问&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;
&lt;h4&gt;常用方法&lt;/h4&gt;
&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;方法&lt;/th&gt;
&lt;th&gt;参数&lt;/th&gt;
&lt;th&gt;返回值&lt;/th&gt;
&lt;th&gt;作用说明&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;Add(T)&lt;/td&gt;
&lt;td&gt;T item&lt;/td&gt;
&lt;td&gt;void&lt;/td&gt;
&lt;td&gt;在List末尾添加元素&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;AddRange(IEnumerable&amp;lt;T&amp;gt;)&lt;/td&gt;
&lt;td&gt;IEnumerable&amp;lt;T&amp;gt; collection&lt;/td&gt;
&lt;td&gt;void&lt;/td&gt;
&lt;td&gt;将集合中的元素添加到List末尾&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Insert(int, T)&lt;/td&gt;
&lt;td&gt;int index, T item&lt;/td&gt;
&lt;td&gt;void&lt;/td&gt;
&lt;td&gt;在指定索引位置插入元素&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;InsertRange(int, IEnumerable&amp;lt;T&amp;gt;)&lt;/td&gt;
&lt;td&gt;int index, IEnumerable&amp;lt;T&amp;gt; collection&lt;/td&gt;
&lt;td&gt;void&lt;/td&gt;
&lt;td&gt;在指定索引位置插入集合中的元素&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Remove(T)&lt;/td&gt;
&lt;td&gt;T item&lt;/td&gt;
&lt;td&gt;bool&lt;/td&gt;
&lt;td&gt;删除首次出现的指定元素，返回是否成功&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;RemoveAt(int)&lt;/td&gt;
&lt;td&gt;int index&lt;/td&gt;
&lt;td&gt;void&lt;/td&gt;
&lt;td&gt;删除指定索引的元素&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;RemoveRange(int, int)&lt;/td&gt;
&lt;td&gt;int index, int count&lt;/td&gt;
&lt;td&gt;void&lt;/td&gt;
&lt;td&gt;从指定索引开始删除指定数量的元素&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;RemoveAll(Predicate&amp;lt;T&amp;gt;)&lt;/td&gt;
&lt;td&gt;Predicate&amp;lt;T&amp;gt; match&lt;/td&gt;
&lt;td&gt;int&lt;/td&gt;
&lt;td&gt;删除所有满足条件的元素，返回删除的数量&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Clear()&lt;/td&gt;
&lt;td&gt;无&lt;/td&gt;
&lt;td&gt;void&lt;/td&gt;
&lt;td&gt;清空List中的所有元素&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Contains(T)&lt;/td&gt;
&lt;td&gt;T item&lt;/td&gt;
&lt;td&gt;bool&lt;/td&gt;
&lt;td&gt;检查List是否包含指定元素&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;IndexOf(T)&lt;/td&gt;
&lt;td&gt;T item&lt;/td&gt;
&lt;td&gt;int&lt;/td&gt;
&lt;td&gt;查找元素首次出现的索引，未找到返回-1&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;IndexOf(T, int)&lt;/td&gt;
&lt;td&gt;T item, int index&lt;/td&gt;
&lt;td&gt;int&lt;/td&gt;
&lt;td&gt;从指定索引开始查找元素&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;LastIndexOf(T)&lt;/td&gt;
&lt;td&gt;T item&lt;/td&gt;
&lt;td&gt;int&lt;/td&gt;
&lt;td&gt;查找元素最后出现的索引&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;BinarySearch(T)&lt;/td&gt;
&lt;td&gt;T item&lt;/td&gt;
&lt;td&gt;int&lt;/td&gt;
&lt;td&gt;在已排序的List中使用二分查找&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Sort()&lt;/td&gt;
&lt;td&gt;无&lt;/td&gt;
&lt;td&gt;void&lt;/td&gt;
&lt;td&gt;对List进行排序（要求T实现IComparable）&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Sort(Comparison&amp;lt;T&amp;gt;)&lt;/td&gt;
&lt;td&gt;Comparison&amp;lt;T&amp;gt; comparison&lt;/td&gt;
&lt;td&gt;void&lt;/td&gt;
&lt;td&gt;使用指定的比较委托对List进行排序&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Sort(IComparer&amp;lt;T&amp;gt;)&lt;/td&gt;
&lt;td&gt;IComparer&amp;lt;T&amp;gt; comparer&lt;/td&gt;
&lt;td&gt;void&lt;/td&gt;
&lt;td&gt;使用指定的比较器对List进行排序&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Reverse()&lt;/td&gt;
&lt;td&gt;无&lt;/td&gt;
&lt;td&gt;void&lt;/td&gt;
&lt;td&gt;反转List中元素的顺序&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;ToArray()&lt;/td&gt;
&lt;td&gt;无&lt;/td&gt;
&lt;td&gt;T[]&lt;/td&gt;
&lt;td&gt;将List转换为数组&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;CopyTo(T[])&lt;/td&gt;
&lt;td&gt;T[] array&lt;/td&gt;
&lt;td&gt;void&lt;/td&gt;
&lt;td&gt;将List的元素复制到数组中&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;CopyTo(T[], int)&lt;/td&gt;
&lt;td&gt;T[] array, int arrayIndex&lt;/td&gt;
&lt;td&gt;void&lt;/td&gt;
&lt;td&gt;从指定索引开始复制到数组&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;GetRange(int, int)&lt;/td&gt;
&lt;td&gt;int index, int count&lt;/td&gt;
&lt;td&gt;List&amp;lt;T&amp;gt;&lt;/td&gt;
&lt;td&gt;返回包含指定范围元素的新List&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Find(Predicate&amp;lt;T&amp;gt;)&lt;/td&gt;
&lt;td&gt;Predicate&amp;lt;T&amp;gt; match&lt;/td&gt;
&lt;td&gt;T&lt;/td&gt;
&lt;td&gt;查找第一个满足条件的元素&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;FindLast(Predicate&amp;lt;T&amp;gt;)&lt;/td&gt;
&lt;td&gt;Predicate&amp;lt;T&amp;gt; match&lt;/td&gt;
&lt;td&gt;T&lt;/td&gt;
&lt;td&gt;查找最后一个满足条件的元素&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;FindAll(Predicate&amp;lt;T&amp;gt;)&lt;/td&gt;
&lt;td&gt;Predicate&amp;lt;T&amp;gt; match&lt;/td&gt;
&lt;td&gt;List&amp;lt;T&amp;gt;&lt;/td&gt;
&lt;td&gt;查找所有满足条件的元素&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;FindIndex(Predicate&amp;lt;T&amp;gt;)&lt;/td&gt;
&lt;td&gt;Predicate&amp;lt;T&amp;gt; match&lt;/td&gt;
&lt;td&gt;int&lt;/td&gt;
&lt;td&gt;查找第一个满足条件的元素的索引&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;FindLastIndex(Predicate&amp;lt;T&amp;gt;)&lt;/td&gt;
&lt;td&gt;Predicate&amp;lt;T&amp;gt; match&lt;/td&gt;
&lt;td&gt;int&lt;/td&gt;
&lt;td&gt;查找最后一个满足条件的元素的索引&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Exists(Predicate&amp;lt;T&amp;gt;)&lt;/td&gt;
&lt;td&gt;Predicate&amp;lt;T&amp;gt; match&lt;/td&gt;
&lt;td&gt;bool&lt;/td&gt;
&lt;td&gt;检查是否存在满足条件的元素&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;TrueForAll(Predicate&amp;lt;T&amp;gt;)&lt;/td&gt;
&lt;td&gt;Predicate&amp;lt;T&amp;gt; match&lt;/td&gt;
&lt;td&gt;bool&lt;/td&gt;
&lt;td&gt;检查是否所有元素都满足条件&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;ForEach(Action&amp;lt;T&amp;gt;)&lt;/td&gt;
&lt;td&gt;Action&amp;lt;T&amp;gt; action&lt;/td&gt;
&lt;td&gt;void&lt;/td&gt;
&lt;td&gt;对每个元素执行指定操作&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;ConvertAll&amp;lt;TOutput&amp;gt;(Converter&amp;lt;T, TOutput&amp;gt;)&lt;/td&gt;
&lt;td&gt;Converter&amp;lt;T, TOutput&amp;gt; converter&lt;/td&gt;
&lt;td&gt;List&amp;lt;TOutput&amp;gt;&lt;/td&gt;
&lt;td&gt;将List中的元素转换为另一种类型&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;TrimExcess()&lt;/td&gt;
&lt;td&gt;无&lt;/td&gt;
&lt;td&gt;void&lt;/td&gt;
&lt;td&gt;将容量减少到实际元素数量（如果容量大于Count的90%）&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;
&lt;h3&gt;List&amp;lt;T&amp;gt;与其他集合类的选择&lt;/h3&gt;
&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;场景&lt;/th&gt;
&lt;th&gt;推荐集合&lt;/th&gt;
&lt;th&gt;说明&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;动态数组，频繁随机访问&lt;/td&gt;
&lt;td&gt;List&amp;lt;T&amp;gt;&lt;/td&gt;
&lt;td&gt;最常用&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;频繁在开头/中间插入删除&lt;/td&gt;
&lt;td&gt;LinkedList&amp;lt;T&amp;gt;&lt;/td&gt;
&lt;td&gt;链表结构&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;需要唯一元素&lt;/td&gt;
&lt;td&gt;HashSet&amp;lt;T&amp;gt;&lt;/td&gt;
&lt;td&gt;哈希集合&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;需要排序且唯一&lt;/td&gt;
&lt;td&gt;SortedSet&amp;lt;T&amp;gt;&lt;/td&gt;
&lt;td&gt;有序集合&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;键值对存储&lt;/td&gt;
&lt;td&gt;Dictionary&amp;lt;TKey, TValue&amp;gt;&lt;/td&gt;
&lt;td&gt;字典&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;先进先出&lt;/td&gt;
&lt;td&gt;Queue&amp;lt;T&amp;gt;&lt;/td&gt;
&lt;td&gt;队列&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;后进先出&lt;/td&gt;
&lt;td&gt;Stack&amp;lt;T&amp;gt;&lt;/td&gt;
&lt;td&gt;栈&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;只读访问&lt;/td&gt;
&lt;td&gt;IReadOnlyList&amp;lt;T&amp;gt;&lt;/td&gt;
&lt;td&gt;只读列表&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;
&lt;p&gt;List&amp;lt;T&amp;gt;是C#开发中最重要和最常用的集合类之一。通过熟练掌握List&amp;lt;T&amp;gt;的各种方法和特性，可以高效地处理各种数据集合操作，编写出类型安全、性能优异的C#代码。&lt;/p&gt;
&lt;h2&gt;&amp;lt;a id=&quot;hashset-class&quot;&amp;gt;&amp;lt;/a&amp;gt;C# HashSet类详解&lt;/h2&gt;
&lt;p&gt;HashSet&amp;lt;T&amp;gt;是C#中一个高性能的泛型集合类，位于System.Collections.Generic命名空间中。它提供了一组唯一的元素，基于哈希表实现，具有快速的插入、删除和查找操作。&lt;/p&gt;
&lt;h3&gt;HashSet&amp;lt;T&amp;gt;的基本概念&lt;/h3&gt;
&lt;p&gt;HashSet&amp;lt;T&amp;gt;是一个不允许重复元素的集合，类似于数学中的集合概念：&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;&lt;strong&gt;唯一性保证&lt;/strong&gt;：HashSet&amp;lt;T&amp;gt;中不会包含重复的元素&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;无序集合&lt;/strong&gt;：元素没有特定的顺序，插入顺序不被保留&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;哈希表实现&lt;/strong&gt;：基于哈希表的数据结构，提供O(1)时间复杂度的操作&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;类型安全&lt;/strong&gt;：泛型实现，提供编译时类型检查&lt;/li&gt;
&lt;/ol&gt;
&lt;h4&gt;为什么使用HashSet&amp;lt;T&amp;gt;？&lt;/h4&gt;
&lt;p&gt;在以下场景中，HashSet&amp;lt;T&amp;gt;是理想的选择：&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;// 场景1：需要快速检查元素是否存在
HashSet&amp;lt;string&amp;gt; uniqueNames = new HashSet&amp;lt;string&amp;gt;();
// 添加元素不会有重复
uniqueNames.Add(&quot;张三&quot;);
uniqueNames.Add(&quot;李四&quot;);
uniqueNames.Add(&quot;张三&quot;); // 不会被添加，因为已经存在

// 场景2：需要获取唯一元素集合
List&amp;lt;string&amp;gt; namesWithDuplicates = new List&amp;lt;string&amp;gt; { &quot;张三&quot;, &quot;李四&quot;, &quot;张三&quot;, &quot;王五&quot;, &quot;李四&quot; };
HashSet&amp;lt;string&amp;gt; uniqueNamesSet = new HashSet&amp;lt;string&amp;gt;(namesWithDuplicates);
// uniqueNamesSet 现在包含 { &quot;张三&quot;, &quot;李四&quot;, &quot;王五&quot; }
&lt;/code&gt;&lt;/pre&gt;
&lt;h3&gt;HashSet&amp;lt;T&amp;gt;的创建和初始化&lt;/h3&gt;
&lt;p&gt;HashSet&amp;lt;T&amp;gt;提供了多种创建和初始化方式：&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;// 方式1：创建空的HashSet
HashSet&amp;lt;int&amp;gt; numbers1 = new HashSet&amp;lt;int&amp;gt;();

// 方式2：使用初始容量创建
HashSet&amp;lt;int&amp;gt; numbers2 = new HashSet&amp;lt;int&amp;gt;(100);

// 方式3：使用集合初始化器
HashSet&amp;lt;string&amp;gt; names1 = new HashSet&amp;lt;string&amp;gt; { &quot;张三&quot;, &quot;李四&quot;, &quot;王五&quot; };

// 方式4：从现有集合创建
List&amp;lt;int&amp;gt; numbersList = new List&amp;lt;int&amp;gt; { 1, 2, 3, 2, 4 };
HashSet&amp;lt;int&amp;gt; numbers3 = new HashSet&amp;lt;int&amp;gt;(numbersList);

// 方式5：使用比较器创建（自定义相等性判断）
HashSet&amp;lt;string&amp;gt; caseInsensitiveNames = new HashSet&amp;lt;string&amp;gt;(StringComparer.OrdinalIgnoreCase);
caseInsensitiveNames.Add(&quot;张三&quot;);
caseInsensitiveNames.Add(&quot;张三&quot;); // 不会被添加，因为忽略大小写
&lt;/code&gt;&lt;/pre&gt;
&lt;h3&gt;HashSet&amp;lt;T&amp;gt;的常用属性&lt;/h3&gt;
&lt;p&gt;HashSet&amp;lt;T&amp;gt;提供了以下常用属性：&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;HashSet&amp;lt;string&amp;gt; names = new HashSet&amp;lt;string&amp;gt; { &quot;张三&quot;, &quot;李四&quot;, &quot;王五&quot; };

// Count：获取集合中的元素数量
int count = names.Count; // 3

// Comparer：获取用于比较集合中元素的相等性比较器
IEqualityComparer&amp;lt;string&amp;gt; comparer = names.Comparer;
&lt;/code&gt;&lt;/pre&gt;
&lt;h3&gt;HashSet&amp;lt;T&amp;gt;的常用方法&lt;/h3&gt;
&lt;p&gt;HashSet&amp;lt;T&amp;gt;提供了丰富的方法来操作集合：&lt;/p&gt;
&lt;h4&gt;基本操作&lt;/h4&gt;
&lt;pre&gt;&lt;code&gt;HashSet&amp;lt;int&amp;gt; numbers = new HashSet&amp;lt;int&amp;gt;();

// 添加元素
numbers.Add(1);
numbers.Add(2);
numbers.Add(3);

// 添加多个元素
numbers.UnionWith(new int[] { 4, 5, 6 });

// 检查元素是否存在
bool contains3 = numbers.Contains(3); // true
bool contains7 = numbers.Contains(7); // false

// 删除元素
bool removed = numbers.Remove(2); // true

// 清空集合
numbers.Clear();
&lt;/code&gt;&lt;/pre&gt;
&lt;h4&gt;集合运算&lt;/h4&gt;
&lt;p&gt;HashSet&amp;lt;T&amp;gt;支持标准的集合运算：&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;HashSet&amp;lt;int&amp;gt; set1 = new HashSet&amp;lt;int&amp;gt; { 1, 2, 3, 4, 5 };
HashSet&amp;lt;int&amp;gt; set2 = new HashSet&amp;lt;int&amp;gt; { 3, 4, 5, 6, 7 };

// 交集（Intersection）：获取两个集合都包含的元素
HashSet&amp;lt;int&amp;gt; intersection = new HashSet&amp;lt;int&amp;gt;(set1);
intersection.IntersectWith(set2); // { 3, 4, 5 }

// 并集（Union）：获取两个集合的所有元素
HashSet&amp;lt;int&amp;gt; union = new HashSet&amp;lt;int&amp;gt;(set1);
union.UnionWith(set2); // { 1, 2, 3, 4, 5, 6, 7 }

// 差集（Difference）：获取set1中有但set2中没有的元素
HashSet&amp;lt;int&amp;gt; difference = new HashSet&amp;lt;int&amp;gt;(set1);
difference.ExceptWith(set2); // { 1, 2 }

// 对称差集（Symmetric Difference）：获取两个集合中不相同的元素
HashSet&amp;lt;int&amp;gt; symmetricDiff = new HashSet&amp;lt;int&amp;gt;(set1);
symmetricDiff.SymmetricExceptWith(set2); // { 1, 2, 6, 7 }
&lt;/code&gt;&lt;/pre&gt;
&lt;h4&gt;子集和超集&lt;/h4&gt;
&lt;pre&gt;&lt;code&gt;HashSet&amp;lt;int&amp;gt; subset = new HashSet&amp;lt;int&amp;gt; { 1, 2, 3 };
HashSet&amp;lt;int&amp;gt; superset = new HashSet&amp;lt;int&amp;gt; { 1, 2, 3, 4, 5 };

// 检查是否是子集
bool isSubset = subset.IsSubsetOf(superset); // true

// 检查是否是超集
bool isSuperset = superset.IsSupersetOf(subset); // true

// 检查是否是真子集（不相等）
bool isProperSubset = subset.IsProperSubsetOf(superset); // true

// 检查是否是真超集（不相等）
bool isProperSuperset = superset.IsProperSupersetOf(subset); // true
&lt;/code&gt;&lt;/pre&gt;
&lt;h3&gt;HashSet&amp;lt;T&amp;gt;与LINQ&lt;/h3&gt;
&lt;p&gt;HashSet&amp;lt;T&amp;gt;与LINQ完美集成，提供了强大的查询能力：&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;HashSet&amp;lt;int&amp;gt; numbers = new HashSet&amp;lt;int&amp;gt; { 1, 2, 3, 4, 5, 6, 7, 8, 9, 10 };

// 筛选元素
HashSet&amp;lt;int&amp;gt; evenNumbers = new HashSet&amp;lt;int&amp;gt;(numbers.Where(x =&amp;gt; x % 2 == 0));

// 转换元素
HashSet&amp;lt;string&amp;gt; numberStrings = new HashSet&amp;lt;string&amp;gt;(numbers.Select(x =&amp;gt; $&quot;数字{x}&quot;));

// 排序（注意：HashSet本身无序，但可以转换为有序集合）
List&amp;lt;int&amp;gt; sortedNumbers = numbers.OrderBy(x =&amp;gt; x).ToList();

// 分组
var groupedByParity = numbers.GroupBy(x =&amp;gt; x % 2 == 0 ? &quot;偶数&quot; : &quot;奇数&quot;);
&lt;/code&gt;&lt;/pre&gt;
&lt;h3&gt;HashSet&amp;lt;T&amp;gt;的实际应用示例&lt;/h3&gt;
&lt;h4&gt;示例1：去重和元素检查&lt;/h4&gt;
&lt;pre&gt;&lt;code&gt;public static class UniqueChecker
{
    public static HashSet&amp;lt;T&amp;gt; RemoveDuplicates&amp;lt;T&amp;gt;(List&amp;lt;T&amp;gt; items)
    {
        return new HashSet&amp;lt;T&amp;gt;(items);
    }
    
    public static bool ContainsDuplicates&amp;lt;T&amp;gt;(List&amp;lt;T&amp;gt; items)
    {
        HashSet&amp;lt;T&amp;gt; uniqueItems = new HashSet&amp;lt;T&amp;gt;();
        foreach (T item in items)
        {
            if (!uniqueItems.Add(item))
            {
                return true; // 发现重复元素
            }
        }
        return false; // 没有重复元素
    }
}

// 使用示例
List&amp;lt;string&amp;gt; names = new List&amp;lt;string&amp;gt; { &quot;张三&quot;, &quot;李四&quot;, &quot;张三&quot;, &quot;王五&quot; };
HashSet&amp;lt;string&amp;gt; uniqueNames = UniqueChecker.RemoveDuplicates(names);
bool hasDuplicates = UniqueChecker.ContainsDuplicates(names);
&lt;/code&gt;&lt;/pre&gt;
&lt;h4&gt;示例2：集合运算&lt;/h4&gt;
&lt;pre&gt;&lt;code&gt;public static class SetOperations
{
    public static HashSet&amp;lt;T&amp;gt; GetIntersection&amp;lt;T&amp;gt;(IEnumerable&amp;lt;T&amp;gt; set1, IEnumerable&amp;lt;T&amp;gt; set2)
    {
        HashSet&amp;lt;T&amp;gt; result = new HashSet&amp;lt;T&amp;gt;(set1);
        result.IntersectWith(set2);
        return result;
    }
    
    public static HashSet&amp;lt;T&amp;gt; GetUnion&amp;lt;T&amp;gt;(IEnumerable&amp;lt;T&amp;gt; set1, IEnumerable&amp;lt;T&amp;gt; set2)
    {
        HashSet&amp;lt;T&amp;gt; result = new HashSet&amp;lt;T&amp;gt;(set1);
        result.UnionWith(set2);
        return result;
    }
    
    public static HashSet&amp;lt;T&amp;gt; GetDifference&amp;lt;T&amp;gt;(IEnumerable&amp;lt;T&amp;gt; set1, IEnumerable&amp;lt;T&amp;gt; set2)
    {
        HashSet&amp;lt;T&amp;gt; result = new HashSet&amp;lt;T&amp;gt;(set1);
        result.ExceptWith(set2);
        return result;
    }
}

// 使用示例
List&amp;lt;int&amp;gt; list1 = new List&amp;lt;int&amp;gt; { 1, 2, 3, 4, 5 };
List&amp;lt;int&amp;gt; list2 = new List&amp;lt;int&amp;gt; { 3, 4, 5, 6, 7 };
HashSet&amp;lt;int&amp;gt; intersection = SetOperations.GetIntersection(list1, list2);
&lt;/code&gt;&lt;/pre&gt;
&lt;h3&gt;HashSet&amp;lt;T&amp;gt;的性能优化&lt;/h3&gt;
&lt;p&gt;HashSet&amp;lt;T&amp;gt;的性能主要取决于哈希函数的质量和初始容量的设置：&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;// 优化1：合理设置初始容量
HashSet&amp;lt;int&amp;gt; numbers1 = new HashSet&amp;lt;int&amp;gt;(10000); // 避免频繁扩容

// 优化2：使用高效的哈希函数
// 对于自定义类型，实现GetHashCode和Equals方法
public class Person
{
    public string Name { get; set; }
    public int Age { get; set; }
    
    public override int GetHashCode()
    {
        return HashCode.Combine(Name, Age); // .NET Core 2.1+ 提供的哈希组合方法
    }
    
    public override bool Equals(object obj)
    {
        if (obj is Person person)
        {
            return Name == person.Name &amp;amp;&amp;amp; Age == person.Age;
        }
        return false;
    }
}

// 优化3：选择合适的比较器
HashSet&amp;lt;string&amp;gt; caseInsensitiveSet = new HashSet&amp;lt;string&amp;gt;(StringComparer.OrdinalIgnoreCase);
&lt;/code&gt;&lt;/pre&gt;
&lt;h3&gt;HashSet&amp;lt;T&amp;gt;使用注意事项&lt;/h3&gt;
&lt;ol&gt;
&lt;li&gt;&lt;strong&gt;无序性&lt;/strong&gt;：HashSet&amp;lt;T&amp;gt;不保证元素的顺序，插入顺序可能与遍历顺序不同&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;null值处理&lt;/strong&gt;：对于引用类型，可以存储null，但只能有一个null&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;线程安全&lt;/strong&gt;：HashSet&amp;lt;T&amp;gt;不是线程安全的，多线程环境下需要手动同步&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;值类型性能&lt;/strong&gt;：对于值类型，HashSet&amp;lt;T&amp;gt;的性能通常比List&amp;lt;T&amp;gt;好，特别是在检查元素存在时&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;内存占用&lt;/strong&gt;：HashSet&amp;lt;T&amp;gt;的内存占用通常比List&amp;lt;T&amp;gt;大，因为需要存储哈希表结构&lt;/li&gt;
&lt;/ol&gt;
&lt;h3&gt;HashSet&amp;lt;T&amp;gt;类常用属性和方法速查表&lt;/h3&gt;
&lt;h4&gt;常用属性&lt;/h4&gt;
&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;属性&lt;/th&gt;
&lt;th&gt;类型&lt;/th&gt;
&lt;th&gt;作用说明&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;Count&lt;/td&gt;
&lt;td&gt;int&lt;/td&gt;
&lt;td&gt;获取HashSet中实际包含的元素数量&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Comparer&lt;/td&gt;
&lt;td&gt;IEqualityComparer&amp;lt;T&amp;gt;&lt;/td&gt;
&lt;td&gt;获取用于比较集合中元素的相等性比较器&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;
&lt;h4&gt;常用方法&lt;/h4&gt;
&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;方法&lt;/th&gt;
&lt;th&gt;参数&lt;/th&gt;
&lt;th&gt;返回值&lt;/th&gt;
&lt;th&gt;作用说明&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;Add(T)&lt;/td&gt;
&lt;td&gt;T item&lt;/td&gt;
&lt;td&gt;bool&lt;/td&gt;
&lt;td&gt;添加元素，如果元素不存在则返回true，否则返回false&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Contains(T)&lt;/td&gt;
&lt;td&gt;T item&lt;/td&gt;
&lt;td&gt;bool&lt;/td&gt;
&lt;td&gt;检查HashSet是否包含指定元素&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Remove(T)&lt;/td&gt;
&lt;td&gt;T item&lt;/td&gt;
&lt;td&gt;bool&lt;/td&gt;
&lt;td&gt;删除元素，如果元素存在则返回true，否则返回false&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Clear()&lt;/td&gt;
&lt;td&gt;无&lt;/td&gt;
&lt;td&gt;void&lt;/td&gt;
&lt;td&gt;清空HashSet中的所有元素&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;UnionWith(IEnumerable&amp;lt;T&amp;gt;)&lt;/td&gt;
&lt;td&gt;IEnumerable&amp;lt;T&amp;gt; other&lt;/td&gt;
&lt;td&gt;void&lt;/td&gt;
&lt;td&gt;将指定集合中的元素添加到当前HashSet&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;IntersectWith(IEnumerable&amp;lt;T&amp;gt;)&lt;/td&gt;
&lt;td&gt;IEnumerable&amp;lt;T&amp;gt; other&lt;/td&gt;
&lt;td&gt;void&lt;/td&gt;
&lt;td&gt;只保留当前HashSet和指定集合中都存在的元素&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;ExceptWith(IEnumerable&amp;lt;T&amp;gt;)&lt;/td&gt;
&lt;td&gt;IEnumerable&amp;lt;T&amp;gt; other&lt;/td&gt;
&lt;td&gt;void&lt;/td&gt;
&lt;td&gt;从当前HashSet中移除指定集合中的所有元素&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;SymmetricExceptWith(IEnumerable&amp;lt;T&amp;gt;)&lt;/td&gt;
&lt;td&gt;IEnumerable&amp;lt;T&amp;gt; other&lt;/td&gt;
&lt;td&gt;void&lt;/td&gt;
&lt;td&gt;只保留当前HashSet或指定集合中存在的元素（不同时存在的元素）&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;IsSubsetOf(IEnumerable&amp;lt;T&amp;gt;)&lt;/td&gt;
&lt;td&gt;IEnumerable&amp;lt;T&amp;gt; other&lt;/td&gt;
&lt;td&gt;bool&lt;/td&gt;
&lt;td&gt;检查当前HashSet是否是指定集合的子集&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;IsSupersetOf(IEnumerable&amp;lt;T&amp;gt;)&lt;/td&gt;
&lt;td&gt;IEnumerable&amp;lt;T&amp;gt; other&lt;/td&gt;
&lt;td&gt;bool&lt;/td&gt;
&lt;td&gt;检查当前HashSet是否是指定集合的超集&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;IsProperSubsetOf(IEnumerable&amp;lt;T&amp;gt;)&lt;/td&gt;
&lt;td&gt;IEnumerable&amp;lt;T&amp;gt; other&lt;/td&gt;
&lt;td&gt;bool&lt;/td&gt;
&lt;td&gt;检查当前HashSet是否是指定集合的真子集&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;IsProperSupersetOf(IEnumerable&amp;lt;T&amp;gt;)&lt;/td&gt;
&lt;td&gt;IEnumerable&amp;lt;T&amp;gt; other&lt;/td&gt;
&lt;td&gt;bool&lt;/td&gt;
&lt;td&gt;检查当前HashSet是否是指定集合的真超集&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Overlaps(IEnumerable&amp;lt;T&amp;gt;)&lt;/td&gt;
&lt;td&gt;IEnumerable&amp;lt;T&amp;gt; other&lt;/td&gt;
&lt;td&gt;bool&lt;/td&gt;
&lt;td&gt;检查当前HashSet和指定集合是否有共同元素&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;SetEquals(IEnumerable&amp;lt;T&amp;gt;)&lt;/td&gt;
&lt;td&gt;IEnumerable&amp;lt;T&amp;gt; other&lt;/td&gt;
&lt;td&gt;bool&lt;/td&gt;
&lt;td&gt;检查当前HashSet和指定集合是否包含相同的元素&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;
&lt;h2&gt;&amp;lt;a id=&quot;dictionary-class&quot;&amp;gt;&amp;lt;/a&amp;gt;C# Dictionary类详解&lt;/h2&gt;
&lt;p&gt;Dictionary&amp;lt;TKey, TValue&amp;gt;是C#中最常用的键值对集合类，位于System.Collections.Generic命名空间中。它基于哈希表实现，提供了快速的键查找能力。&lt;/p&gt;
&lt;h3&gt;Dictionary&amp;lt;TKey, TValue&amp;gt;的基本概念&lt;/h3&gt;
&lt;p&gt;Dictionary&amp;lt;TKey, TValue&amp;gt;是一个键值对集合，具有以下特点：&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;&lt;strong&gt;键值映射&lt;/strong&gt;：每个元素由一个键（Key）和一个值（Value）组成&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;键唯一性&lt;/strong&gt;：字典中的键必须唯一，不能重复&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;快速查找&lt;/strong&gt;：基于哈希表实现，提供O(1)时间复杂度的查找操作&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;类型安全&lt;/strong&gt;：泛型实现，提供编译时类型检查&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;无序集合&lt;/strong&gt;：元素没有特定的顺序，插入顺序不被保留&lt;/li&gt;
&lt;/ol&gt;
&lt;h4&gt;为什么使用Dictionary&amp;lt;TKey, TValue&amp;gt;？&lt;/h4&gt;
&lt;p&gt;在以下场景中，Dictionary&amp;lt;TKey, TValue&amp;gt;是理想的选择：&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;// 场景1：需要快速通过键查找值
Dictionary&amp;lt;string, int&amp;gt; studentScores = new Dictionary&amp;lt;string, int&amp;gt;();
studentScores.Add(&quot;张三&quot;, 90);
studentScores.Add(&quot;李四&quot;, 85);
studentScores.Add(&quot;王五&quot;, 95);

// 快速查找分数
int zhangSanScore = studentScores[&quot;张三&quot;]; // 90

// 场景2：需要存储键值对数据
Dictionary&amp;lt;int, string&amp;gt; monthNames = new Dictionary&amp;lt;int, string&amp;gt;
{
    { 1, &quot;一月&quot; },
    { 2, &quot;二月&quot; },
    { 3, &quot;三月&quot; }
};

// 场景3：需要计数和统计
Dictionary&amp;lt;string, int&amp;gt; wordCount = new Dictionary&amp;lt;string, int&amp;gt;();
string text = &quot;Hello World Hello C#&quot;;
string[] words = text.Split(&apos; &apos;);
foreach (string word in words)
{
    if (wordCount.ContainsKey(word))
    {
        wordCount[word]++;
    }
    else
    {
        wordCount[word] = 1;
    }
}
&lt;/code&gt;&lt;/pre&gt;
&lt;h3&gt;Dictionary&amp;lt;TKey, TValue&amp;gt;的创建和初始化&lt;/h3&gt;
&lt;p&gt;Dictionary&amp;lt;TKey, TValue&amp;gt;提供了多种创建和初始化方式：&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;// 方式1：创建空的Dictionary
Dictionary&amp;lt;int, string&amp;gt; dict1 = new Dictionary&amp;lt;int, string&amp;gt;();

// 方式2：使用初始容量创建
Dictionary&amp;lt;int, string&amp;gt; dict2 = new Dictionary&amp;lt;int, string&amp;gt;(100);

// 方式3：使用集合初始化器
Dictionary&amp;lt;string, int&amp;gt; studentScores1 = new Dictionary&amp;lt;string, int&amp;gt;
{
    { &quot;张三&quot;, 90 },
    { &quot;李四&quot;, 85 },
    { &quot;王五&quot;, 95 }
};

// 方式4：从现有集合创建
List&amp;lt;KeyValuePair&amp;lt;string, int&amp;gt;&amp;gt; scorePairs = new List&amp;lt;KeyValuePair&amp;lt;string, int&amp;gt;&amp;gt;
{
    new KeyValuePair&amp;lt;string, int&amp;gt;(&quot;张三&quot;, 90),
    new KeyValuePair&amp;lt;string, int&amp;gt;(&quot;李四&quot;, 85)
};
Dictionary&amp;lt;string, int&amp;gt; studentScores2 = new Dictionary&amp;lt;string, int&amp;gt;(scorePairs);

// 方式5：使用比较器创建（自定义键的比较方式）
Dictionary&amp;lt;string, int&amp;gt; caseInsensitiveDict = new Dictionary&amp;lt;string, int&amp;gt;(StringComparer.OrdinalIgnoreCase);
caseInsensitiveDict.Add(&quot;张三&quot;, 90);
caseInsensitiveDict.Add(&quot;张三&quot;, 85); // 会抛出异常，因为键不区分大小写
&lt;/code&gt;&lt;/pre&gt;
&lt;h3&gt;Dictionary&amp;lt;TKey, TValue&amp;gt;的常用属性&lt;/h3&gt;
&lt;p&gt;Dictionary&amp;lt;TKey, TValue&amp;gt;提供了以下常用属性：&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;Dictionary&amp;lt;string, int&amp;gt; studentScores = new Dictionary&amp;lt;string, int&amp;gt;
{
    { &quot;张三&quot;, 90 },
    { &quot;李四&quot;, 85 },
    { &quot;王五&quot;, 95 }
};

// Count：获取字典中的键值对数量
int count = studentScores.Count;

// Comparer：获取用于比较键的相等性比较器
IEqualityComparer&amp;lt;string&amp;gt; keyComparer = studentScores.Comparer;

// Keys：获取字典中所有键的集合
ICollection&amp;lt;string&amp;gt; keys = studentScores.Keys;

// Values：获取字典中所有值的集合
ICollection&amp;lt;int&amp;gt; values = studentScores.Values;
&lt;/code&gt;&lt;/pre&gt;
&lt;h3&gt;Dictionary&amp;lt;TKey, TValue&amp;gt;的常用方法&lt;/h3&gt;
&lt;p&gt;Dictionary&amp;lt;TKey, TValue&amp;gt;提供了丰富的方法来操作键值对：&lt;/p&gt;
&lt;h4&gt;基本操作&lt;/h4&gt;
&lt;pre&gt;&lt;code&gt;Dictionary&amp;lt;string, int&amp;gt; studentScores = new Dictionary&amp;lt;string, int&amp;gt;();

// 添加键值对
studentScores.Add(&quot;张三&quot;, 90);

// 使用索引器添加或修改
studentScores[&quot;李四&quot;] = 85;
studentScores[&quot;张三&quot;] = 95; // 修改现有值

// 检查键是否存在
bool hasZhangSan = studentScores.ContainsKey(&quot;张三&quot;);
bool has90Score = studentScores.ContainsValue(90);

// 获取值（安全方式）
if (studentScores.TryGetValue(&quot;王五&quot;, out int wangWuScore))
{
    Console.WriteLine($&quot;王五的分数：{wangWuScore}&quot;);
}
else
{
    Console.WriteLine(&quot;王五不存在&quot;);
}

// 删除键值对
studentScores.Remove(&quot;李四&quot;);

// 清空字典
studentScores.Clear();
&lt;/code&gt;&lt;/pre&gt;
&lt;h4&gt;遍历操作&lt;/h4&gt;
&lt;pre&gt;&lt;code&gt;Dictionary&amp;lt;string, int&amp;gt; studentScores = new Dictionary&amp;lt;string, int&amp;gt;
{
    { &quot;张三&quot;, 90 },
    { &quot;李四&quot;, 85 },
    { &quot;王五&quot;, 95 }
};

// 遍历所有键值对
foreach (KeyValuePair&amp;lt;string, int&amp;gt; pair in studentScores)
{
    Console.WriteLine($&quot;姓名：{pair.Key}，分数：{pair.Value}&quot;);
}

// 遍历所有键
foreach (string name in studentScores.Keys)
{
    Console.WriteLine($&quot;姓名：{name}&quot;);
}

// 遍历所有值
foreach (int score in studentScores.Values)
{
    Console.WriteLine($&quot;分数：{score}&quot;);
}

// 使用LINQ遍历
studentScores.ToList().ForEach(pair =&amp;gt; 
    Console.WriteLine($&quot;姓名：{pair.Key}，分数：{pair.Value}&quot;)
);
&lt;/code&gt;&lt;/pre&gt;
&lt;h3&gt;Dictionary&amp;lt;TKey, TValue&amp;gt;与LINQ&lt;/h3&gt;
&lt;p&gt;Dictionary&amp;lt;TKey, TValue&amp;gt;与LINQ完美集成，提供了强大的查询和操作能力：&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;Dictionary&amp;lt;string, int&amp;gt; studentScores = new Dictionary&amp;lt;string, int&amp;gt;
{
    { &quot;张三&quot;, 90 },
    { &quot;李四&quot;, 85 },
    { &quot;王五&quot;, 95 },
    { &quot;赵六&quot;, 88 }
};

// 筛选键值对
var highScorers = studentScores.Where(pair =&amp;gt; pair.Value &amp;gt;= 90);

// 转换键值对
var studentInfo = studentScores.Select(pair =&amp;gt; 
    new { Name = pair.Key, Grade = pair.Value &amp;gt;= 90 ? &quot;优秀&quot; : &quot;良好&quot; });

// 排序
var sortedByScore = studentScores.OrderByDescending(pair =&amp;gt; pair.Value);

// 分组
var groupedByScoreRange = studentScores.GroupBy(pair =&amp;gt; 
    pair.Value &amp;gt;= 90 ? &quot;高分&quot; : (pair.Value &amp;gt;= 80 ? &quot;中等&quot; : &quot;低分&quot;));

// 聚合
int totalScore = studentScores.Sum(pair =&amp;gt; pair.Value);
double averageScore = studentScores.Average(pair =&amp;gt; pair.Value);
int maxScore = studentScores.Max(pair =&amp;gt; pair.Value);
int minScore = studentScores.Min(pair =&amp;gt; pair.Value);
&lt;/code&gt;&lt;/pre&gt;
&lt;h3&gt;Dictionary&amp;lt;TKey, TValue&amp;gt;的高阶方法详解&lt;/h3&gt;
&lt;p&gt;Dictionary&amp;lt;TKey, TValue&amp;gt;结合LINQ提供了强大的高阶方法，可以实现复杂的数据查询、转换和操作。下面详细介绍这些常用的高阶方法：&lt;/p&gt;
&lt;h4&gt;1. 排序方法&lt;/h4&gt;
&lt;h5&gt;OrderBy 方法&lt;/h5&gt;
&lt;p&gt;&lt;strong&gt;签名&lt;/strong&gt;：&lt;code&gt;IOrderedEnumerable&amp;lt;KeyValuePair&amp;lt;TKey, TValue&amp;gt;&amp;gt; OrderBy&amp;lt;TKey, TValue, TKeySelector&amp;gt;(this IEnumerable&amp;lt;KeyValuePair&amp;lt;TKey, TValue&amp;gt;&amp;gt; source, Func&amp;lt;KeyValuePair&amp;lt;TKey, TValue&amp;gt;, TKeySelector&amp;gt; keySelector)&lt;/code&gt;&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;返回值&lt;/strong&gt;：&lt;code&gt;IOrderedEnumerable&amp;lt;KeyValuePair&amp;lt;TKey, TValue&amp;gt;&amp;gt;&lt;/code&gt; - 已排序的键值对序列&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;功能&lt;/strong&gt;：根据指定的键选择器对字典的键值对进行升序排序&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;Dictionary&amp;lt;string, int&amp;gt; studentScores = new Dictionary&amp;lt;string, int&amp;gt;
{
    { &quot;张三&quot;, 90 },
    { &quot;李四&quot;, 85 },
    { &quot;王五&quot;, 95 },
    { &quot;赵六&quot;, 88 }
};

// 按分数升序排序
var sortedByScore = studentScores.OrderBy(pair =&amp;gt; pair.Value);

// 按姓名排序
var sortedByName = studentScores.OrderBy(pair =&amp;gt; pair.Key);

// 遍历排序后的结果
foreach (var pair in sortedByScore)
{
    Console.WriteLine($&quot;{pair.Key}: {pair.Value}&quot;);
}
&lt;/code&gt;&lt;/pre&gt;
&lt;h5&gt;OrderByDescending 方法&lt;/h5&gt;
&lt;p&gt;&lt;strong&gt;签名&lt;/strong&gt;：&lt;code&gt;IOrderedEnumerable&amp;lt;KeyValuePair&amp;lt;TKey, TValue&amp;gt;&amp;gt; OrderByDescending&amp;lt;TKey, TValue, TKeySelector&amp;gt;(this IEnumerable&amp;lt;KeyValuePair&amp;lt;TKey, TValue&amp;gt;&amp;gt; source, Func&amp;lt;KeyValuePair&amp;lt;TKey, TValue&amp;gt;, TKeySelector&amp;gt; keySelector)&lt;/code&gt;&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;返回值&lt;/strong&gt;：&lt;code&gt;IOrderedEnumerable&amp;lt;KeyValuePair&amp;lt;TKey, TValue&amp;gt;&amp;gt;&lt;/code&gt; - 已降序排序的键值对序列&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;功能&lt;/strong&gt;：根据指定的键选择器对字典的键值对进行降序排序&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;// 按分数降序排序
var sortedByScoreDesc = studentScores.OrderByDescending(pair =&amp;gt; pair.Value);

// 获取最高分的学生
var highestScoreStudent = studentScores.OrderByDescending(pair =&amp;gt; pair.Value).FirstOrDefault();
&lt;/code&gt;&lt;/pre&gt;
&lt;h5&gt;ThenBy/ThenByDescending 方法&lt;/h5&gt;
&lt;p&gt;&lt;strong&gt;签名&lt;/strong&gt;：&lt;code&gt;IOrderedEnumerable&amp;lt;KeyValuePair&amp;lt;TKey, TValue&amp;gt;&amp;gt; ThenBy(...) / ThenByDescending(...)&lt;/code&gt;&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;返回值&lt;/strong&gt;：&lt;code&gt;IOrderedEnumerable&amp;lt;KeyValuePair&amp;lt;TKey, TValue&amp;gt;&amp;gt;&lt;/code&gt; - 进一步排序的键值对序列&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;功能&lt;/strong&gt;：在已排序的基础上进行二级排序&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;// 先按分数降序，分数相同时按姓名排序
var sortedByScoreThenName = studentScores
    .OrderByDescending(pair =&amp;gt; pair.Value)
    .ThenBy(pair =&amp;gt; pair.Key);
&lt;/code&gt;&lt;/pre&gt;
&lt;h4&gt;2. 筛选方法&lt;/h4&gt;
&lt;h5&gt;Where 方法&lt;/h5&gt;
&lt;p&gt;&lt;strong&gt;签名&lt;/strong&gt;：&lt;code&gt;IEnumerable&amp;lt;KeyValuePair&amp;lt;TKey, TValue&amp;gt;&amp;gt; Where&amp;lt;TKey, TValue&amp;gt;(this IEnumerable&amp;lt;KeyValuePair&amp;lt;TKey, TValue&amp;gt;&amp;gt; source, Func&amp;lt;KeyValuePair&amp;lt;TKey, TValue&amp;gt;, bool&amp;gt; predicate)&lt;/code&gt;&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;返回值&lt;/strong&gt;：&lt;code&gt;IEnumerable&amp;lt;KeyValuePair&amp;lt;TKey, TValue&amp;gt;&amp;gt;&lt;/code&gt; - 符合条件的键值对序列&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;功能&lt;/strong&gt;：根据条件筛选字典中的键值对&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;// 筛选分数大于等于90的学生
var highScorers = studentScores.Where(pair =&amp;gt; pair.Value &amp;gt;= 90);

// 筛选姓&quot;张&quot;的学生
var zhangStudents = studentScores.Where(pair =&amp;gt; pair.Key.StartsWith(&quot;张&quot;));

// 组合条件筛选
var specificStudents = studentScores.Where(pair =&amp;gt; pair.Value &amp;gt; 85 &amp;amp;&amp;amp; pair.Key.Length &amp;gt; 2);
&lt;/code&gt;&lt;/pre&gt;
&lt;h4&gt;3. 投影转换方法&lt;/h4&gt;
&lt;h5&gt;Select 方法&lt;/h5&gt;
&lt;p&gt;&lt;strong&gt;签名&lt;/strong&gt;：&lt;code&gt;IEnumerable&amp;lt;TResult&amp;gt; Select&amp;lt;TKey, TValue, TResult&amp;gt;(this IEnumerable&amp;lt;KeyValuePair&amp;lt;TKey, TValue&amp;gt;&amp;gt; source, Func&amp;lt;KeyValuePair&amp;lt;TKey, TValue&amp;gt;, TResult&amp;gt; selector)&lt;/code&gt;&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;返回值&lt;/strong&gt;：&lt;code&gt;IEnumerable&amp;lt;TResult&amp;gt;&lt;/code&gt; - 转换后的结果序列&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;功能&lt;/strong&gt;：将字典中的键值对转换为新的形式&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;// 转换为匿名类型
var studentInfo = studentScores.Select(pair =&amp;gt; new {
    Name = pair.Key,
    Score = pair.Value,
    Grade = pair.Value &amp;gt;= 90 ? &quot;优秀&quot; : (pair.Value &amp;gt;= 80 ? &quot;良好&quot; : &quot;及格&quot;)
});

// 转换为字符串数组
var studentDescriptions = studentScores.Select(pair =&amp;gt; $&quot;{pair.Key}的分数是{pair.Value}&quot;);

// 只选择值
var scores = studentScores.Select(pair =&amp;gt; pair.Value).ToList();
&lt;/code&gt;&lt;/pre&gt;
&lt;h5&gt;SelectMany 方法&lt;/h5&gt;
&lt;p&gt;&lt;strong&gt;签名&lt;/strong&gt;：&lt;code&gt;IEnumerable&amp;lt;TResult&amp;gt; SelectMany&amp;lt;TKey, TValue, TResult&amp;gt;(this IEnumerable&amp;lt;KeyValuePair&amp;lt;TKey, TValue&amp;gt;&amp;gt; source, Func&amp;lt;KeyValuePair&amp;lt;TKey, TValue&amp;gt;, IEnumerable&amp;lt;TResult&amp;gt;&amp;gt; selector)&lt;/code&gt;&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;返回值&lt;/strong&gt;：&lt;code&gt;IEnumerable&amp;lt;TResult&amp;gt;&lt;/code&gt; - 展平后的结果序列&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;功能&lt;/strong&gt;：将每个键值对转换为序列，然后将这些序列展平&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;Dictionary&amp;lt;string, List&amp;lt;string&amp;gt;&amp;gt; studentCourses = new Dictionary&amp;lt;string, List&amp;lt;string&amp;gt;&amp;gt;
{
    { &quot;张三&quot;, new List&amp;lt;string&amp;gt; { &quot;数学&quot;, &quot;物理&quot; } },
    { &quot;李四&quot;, new List&amp;lt;string&amp;gt; { &quot;化学&quot;, &quot;生物&quot; } }
};

// 获取所有课程列表
var allCourses = studentCourses.SelectMany(pair =&amp;gt; pair.Value).Distinct();
&lt;/code&gt;&lt;/pre&gt;
&lt;h4&gt;4. 分组方法&lt;/h4&gt;
&lt;h5&gt;GroupBy 方法&lt;/h5&gt;
&lt;p&gt;&lt;strong&gt;签名&lt;/strong&gt;：&lt;code&gt;IEnumerable&amp;lt;IGrouping&amp;lt;TGroupKey, KeyValuePair&amp;lt;TKey, TValue&amp;gt;&amp;gt;&amp;gt; GroupBy&amp;lt;TKey, TValue, TGroupKey&amp;gt;(this IEnumerable&amp;lt;KeyValuePair&amp;lt;TKey, TValue&amp;gt;&amp;gt; source, Func&amp;lt;KeyValuePair&amp;lt;TKey, TValue&amp;gt;, TGroupKey&amp;gt; keySelector)&lt;/code&gt;&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;返回值&lt;/strong&gt;：&lt;code&gt;IEnumerable&amp;lt;IGrouping&amp;lt;TGroupKey, KeyValuePair&amp;lt;TKey, TValue&amp;gt;&amp;gt;&amp;gt;&lt;/code&gt; - 分组后的结果序列&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;功能&lt;/strong&gt;：根据指定的键选择器对字典的键值对进行分组&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;// 按分数段分组
var groupedByScoreRange = studentScores.GroupBy(pair =&amp;gt; {
    if (pair.Value &amp;gt;= 90) return &quot;优秀&quot;;
    if (pair.Value &amp;gt;= 80) return &quot;良好&quot;;
    return &quot;及格&quot;;
});

// 遍历分组结果
foreach (var group in groupedByScoreRange)
{
    Console.WriteLine($&quot;{group.Key}学生：&quot;);
    foreach (var student in group)
    {
        Console.WriteLine($&quot;  - {student.Key}: {student.Value}&quot;);
    }
}
&lt;/code&gt;&lt;/pre&gt;
&lt;h4&gt;5. 转换为字典或查找表&lt;/h4&gt;
&lt;h5&gt;ToDictionary 方法&lt;/h5&gt;
&lt;p&gt;&lt;strong&gt;签名&lt;/strong&gt;：&lt;code&gt;Dictionary&amp;lt;TResultKey, TResultValue&amp;gt; ToDictionary&amp;lt;TKey, TValue, TResultKey, TResultValue&amp;gt;(this IEnumerable&amp;lt;KeyValuePair&amp;lt;TKey, TValue&amp;gt;&amp;gt; source, Func&amp;lt;KeyValuePair&amp;lt;TKey, TValue&amp;gt;, TResultKey&amp;gt; keySelector, Func&amp;lt;KeyValuePair&amp;lt;TKey, TValue&amp;gt;, TResultValue&amp;gt; valueSelector)&lt;/code&gt;&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;返回值&lt;/strong&gt;：&lt;code&gt;Dictionary&amp;lt;TResultKey, TResultValue&amp;gt;&lt;/code&gt; - 新的字典&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;功能&lt;/strong&gt;：将查询结果转换为新的字典&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;// 分数翻倍并创建新字典
var doubledScores = studentScores.ToDictionary(
    pair =&amp;gt; pair.Key,
    pair =&amp;gt; pair.Value * 2
);

// 筛选后创建新字典
var highScoreDict = studentScores
    .Where(pair =&amp;gt; pair.Value &amp;gt;= 90)
    .ToDictionary(pair =&amp;gt; pair.Key, pair =&amp;gt; pair.Value);
&lt;/code&gt;&lt;/pre&gt;
&lt;h5&gt;ToLookup 方法&lt;/h5&gt;
&lt;p&gt;&lt;strong&gt;签名&lt;/strong&gt;：&lt;code&gt;ILookup&amp;lt;TGroupKey, TValue&amp;gt; ToLookup&amp;lt;TKey, TValue, TGroupKey&amp;gt;(this IEnumerable&amp;lt;KeyValuePair&amp;lt;TKey, TValue&amp;gt;&amp;gt; source, Func&amp;lt;KeyValuePair&amp;lt;TKey, TValue&amp;gt;, TGroupKey&amp;gt; keySelector, Func&amp;lt;KeyValuePair&amp;lt;TKey, TValue&amp;gt;, TValue&amp;gt; elementSelector)&lt;/code&gt;&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;返回值&lt;/strong&gt;：&lt;code&gt;ILookup&amp;lt;TGroupKey, TValue&amp;gt;&lt;/code&gt; - 查找表对象&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;功能&lt;/strong&gt;：创建一个查找表，类似于允许重复键的字典&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;// 创建按分数段分组的查找表
var scoreLookup = studentScores.ToLookup(
    pair =&amp;gt; pair.Value &amp;gt;= 90 ? &quot;优秀&quot; : (pair.Value &amp;gt;= 80 ? &quot;良好&quot; : &quot;及格&quot;),
    pair =&amp;gt; pair.Key
);

// 查询优秀学生
var excellentStudents = scoreLookup[&quot;优秀&quot;];
&lt;/code&gt;&lt;/pre&gt;
&lt;h4&gt;6. 查找和获取元素方法&lt;/h4&gt;
&lt;h5&gt;First/FirstOrDefault 方法&lt;/h5&gt;
&lt;p&gt;&lt;strong&gt;签名&lt;/strong&gt;：&lt;code&gt;KeyValuePair&amp;lt;TKey, TValue&amp;gt; First&amp;lt;TKey, TValue&amp;gt;(...) / KeyValuePair&amp;lt;TKey, TValue&amp;gt;? FirstOrDefault&amp;lt;TKey, TValue&amp;gt;(...)&lt;/code&gt;&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;返回值&lt;/strong&gt;：&lt;code&gt;KeyValuePair&amp;lt;TKey, TValue&amp;gt;&lt;/code&gt; 或 &lt;code&gt;KeyValuePair&amp;lt;TKey, TValue&amp;gt;?&lt;/code&gt;&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;功能&lt;/strong&gt;：获取序列中的第一个元素，或满足条件的第一个元素&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;// 获取第一个学生
var firstStudent = studentScores.First();

// 获取第一个分数大于90的学生，如果没有则返回默认值
var highScorer = studentScores.FirstOrDefault(pair =&amp;gt; pair.Value &amp;gt; 90);

if (highScorer.HasValue)
{
    Console.WriteLine($&quot;找到高分学生：{highScorer.Value.Key}&quot;);
}
&lt;/code&gt;&lt;/pre&gt;
&lt;h5&gt;Last/LastOrDefault 方法&lt;/h5&gt;
&lt;p&gt;&lt;strong&gt;签名&lt;/strong&gt;：&lt;code&gt;KeyValuePair&amp;lt;TKey, TValue&amp;gt; Last&amp;lt;TKey, TValue&amp;gt;(...) / KeyValuePair&amp;lt;TKey, TValue&amp;gt;? LastOrDefault&amp;lt;TKey, TValue&amp;gt;(...)&lt;/code&gt;&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;返回值&lt;/strong&gt;：&lt;code&gt;KeyValuePair&amp;lt;TKey, TValue&amp;gt;&lt;/code&gt; 或 &lt;code&gt;KeyValuePair&amp;lt;TKey, TValue&amp;gt;?&lt;/code&gt;&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;功能&lt;/strong&gt;：获取序列中的最后一个元素，或满足条件的最后一个元素&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;// 获取排序后的最后一名学生
var lastStudent = studentScores.OrderBy(pair =&amp;gt; pair.Value).LastOrDefault();
&lt;/code&gt;&lt;/pre&gt;
&lt;h5&gt;Single/SingleOrDefault 方法&lt;/h5&gt;
&lt;p&gt;&lt;strong&gt;签名&lt;/strong&gt;：&lt;code&gt;KeyValuePair&amp;lt;TKey, TValue&amp;gt; Single&amp;lt;TKey, TValue&amp;gt;(...) / KeyValuePair&amp;lt;TKey, TValue&amp;gt;? SingleOrDefault&amp;lt;TKey, TValue&amp;gt;(...)&lt;/code&gt;&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;返回值&lt;/strong&gt;：&lt;code&gt;KeyValuePair&amp;lt;TKey, TValue&amp;gt;&lt;/code&gt; 或 &lt;code&gt;KeyValuePair&amp;lt;TKey, TValue&amp;gt;?&lt;/code&gt;&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;功能&lt;/strong&gt;：获取序列中唯一的元素，如果有多个或零个匹配项则抛出异常&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;// 获取唯一叫张三的学生信息
var zhangSan = studentScores.SingleOrDefault(pair =&amp;gt; pair.Key == &quot;张三&quot;);
&lt;/code&gt;&lt;/pre&gt;
&lt;h4&gt;7. 聚合方法&lt;/h4&gt;
&lt;h5&gt;Aggregate 方法&lt;/h5&gt;
&lt;p&gt;&lt;strong&gt;签名&lt;/strong&gt;：&lt;code&gt;TAccumulate Aggregate&amp;lt;TKey, TValue, TAccumulate&amp;gt;(this IEnumerable&amp;lt;KeyValuePair&amp;lt;TKey, TValue&amp;gt;&amp;gt; source, TAccumulate seed, Func&amp;lt;TAccumulate, KeyValuePair&amp;lt;TKey, TValue&amp;gt;, TAccumulate&amp;gt; func)&lt;/code&gt;&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;返回值&lt;/strong&gt;：&lt;code&gt;TAccumulate&lt;/code&gt; - 聚合后的结果&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;功能&lt;/strong&gt;：对序列应用累加器函数，执行自定义聚合操作&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;// 拼接所有学生信息
string studentSummary = studentScores.Aggregate(
    &quot;学生成绩汇总：&quot;,
    (current, pair) =&amp;gt; current + $&quot;{pair.Key}({pair.Value}), &quot;
);

// 计算总分
int totalScore = studentScores.Aggregate(0, (sum, pair) =&amp;gt; sum + pair.Value);
&lt;/code&gt;&lt;/pre&gt;
&lt;h5&gt;Count 方法&lt;/h5&gt;
&lt;p&gt;&lt;strong&gt;签名&lt;/strong&gt;：&lt;code&gt;int Count&amp;lt;TKey, TValue&amp;gt;(this IEnumerable&amp;lt;KeyValuePair&amp;lt;TKey, TValue&amp;gt;&amp;gt; source, Func&amp;lt;KeyValuePair&amp;lt;TKey, TValue&amp;gt;, bool&amp;gt; predicate)&lt;/code&gt;&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;返回值&lt;/strong&gt;：&lt;code&gt;int&lt;/code&gt; - 满足条件的元素数量&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;功能&lt;/strong&gt;：计算满足条件的键值对数量&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;// 统计分数大于90的学生数量
int highScorerCount = studentScores.Count(pair =&amp;gt; pair.Value &amp;gt; 90);
&lt;/code&gt;&lt;/pre&gt;
&lt;h5&gt;Sum/Average/Max/Min 方法&lt;/h5&gt;
&lt;p&gt;&lt;strong&gt;签名&lt;/strong&gt;：&lt;code&gt;TSource Sum&amp;lt;TKey, TValue, TSource&amp;gt;(...) / double Average&amp;lt;TKey, TValue&amp;gt;(...) / TSource Max&amp;lt;TKey, TValue, TSource&amp;gt;(...) / TSource Min&amp;lt;TKey, TValue, TSource&amp;gt;(...)&lt;/code&gt;&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;返回值&lt;/strong&gt;：根据方法不同返回数值类型&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;功能&lt;/strong&gt;：计算数值序列的总和、平均值、最大值或最小值&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;// 计算统计数据
int total = studentScores.Sum(pair =&amp;gt; pair.Value);
double average = studentScores.Average(pair =&amp;gt; pair.Value);
int max = studentScores.Max(pair =&amp;gt; pair.Value);
int min = studentScores.Min(pair =&amp;gt; pair.Value);
&lt;/code&gt;&lt;/pre&gt;
&lt;h4&gt;8. 集合判断方法&lt;/h4&gt;
&lt;h5&gt;Any/All 方法&lt;/h5&gt;
&lt;p&gt;&lt;strong&gt;签名&lt;/strong&gt;：&lt;code&gt;bool Any&amp;lt;TKey, TValue&amp;gt;(...) / bool All&amp;lt;TKey, TValue&amp;gt;(...)&lt;/code&gt;&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;返回值&lt;/strong&gt;：&lt;code&gt;bool&lt;/code&gt; - 布尔值结果&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;功能&lt;/strong&gt;：判断是否存在满足条件的元素，或所有元素是否都满足条件&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;// 判断是否有优秀学生
bool hasExcellentStudents = studentScores.Any(pair =&amp;gt; pair.Value &amp;gt;= 90);

// 判断是否所有学生都及格
bool allPassed = studentScores.All(pair =&amp;gt; pair.Value &amp;gt;= 60);
&lt;/code&gt;&lt;/pre&gt;
&lt;h5&gt;Contains 方法&lt;/h5&gt;
&lt;p&gt;&lt;strong&gt;签名&lt;/strong&gt;：&lt;code&gt;bool Contains&amp;lt;TKey, TValue&amp;gt;(this IEnumerable&amp;lt;KeyValuePair&amp;lt;TKey, TValue&amp;gt;&amp;gt; source, KeyValuePair&amp;lt;TKey, TValue&amp;gt; item, IEqualityComparer&amp;lt;KeyValuePair&amp;lt;TKey, TValue&amp;gt;&amp;gt;? comparer)&lt;/code&gt;&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;返回值&lt;/strong&gt;：&lt;code&gt;bool&lt;/code&gt; - 是否包含指定元素&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;功能&lt;/strong&gt;：判断序列是否包含指定的键值对&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;// 检查是否包含特定键值对
bool containsSpecificPair = studentScores.Contains(
    new KeyValuePair&amp;lt;string, int&amp;gt;(&quot;张三&quot;, 90));
&lt;/code&gt;&lt;/pre&gt;
&lt;h4&gt;9. 分页和切片方法&lt;/h4&gt;
&lt;h5&gt;Skip/Take 方法&lt;/h5&gt;
&lt;p&gt;&lt;strong&gt;签名&lt;/strong&gt;：&lt;code&gt;IEnumerable&amp;lt;KeyValuePair&amp;lt;TKey, TValue&amp;gt;&amp;gt; Skip&amp;lt;TKey, TValue&amp;gt;(...) / IEnumerable&amp;lt;KeyValuePair&amp;lt;TKey, TValue&amp;gt;&amp;gt; Take&amp;lt;TKey, TValue&amp;gt;(...)&lt;/code&gt;&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;返回值&lt;/strong&gt;：&lt;code&gt;IEnumerable&amp;lt;KeyValuePair&amp;lt;TKey, TValue&amp;gt;&amp;gt;&lt;/code&gt; - 处理后的序列&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;功能&lt;/strong&gt;：跳过指定数量的元素，或获取指定数量的元素&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;// 分页获取学生信息（每页2条）
int pageSize = 2;
int pageIndex = 1;
var pageStudents = studentScores
    .OrderBy(pair =&amp;gt; pair.Key)
    .Skip(pageIndex * pageSize)
    .Take(pageSize);
&lt;/code&gt;&lt;/pre&gt;
&lt;h5&gt;SkipWhile/TakeWhile 方法&lt;/h5&gt;
&lt;p&gt;&lt;strong&gt;签名&lt;/strong&gt;：&lt;code&gt;IEnumerable&amp;lt;KeyValuePair&amp;lt;TKey, TValue&amp;gt;&amp;gt; SkipWhile&amp;lt;TKey, TValue&amp;gt;(...) / IEnumerable&amp;lt;KeyValuePair&amp;lt;TKey, TValue&amp;gt;&amp;gt; TakeWhile&amp;lt;TKey, TValue&amp;gt;(...)&lt;/code&gt;&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;返回值&lt;/strong&gt;：&lt;code&gt;IEnumerable&amp;lt;KeyValuePair&amp;lt;TKey, TValue&amp;gt;&amp;gt;&lt;/code&gt; - 处理后的序列&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;功能&lt;/strong&gt;：跳过满足条件的元素，或获取满足条件的元素，直到条件不满足为止&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;// 获取分数连续大于85的学生，直到遇到第一个不满足条件的
var连续高分学生 = studentScores
    .OrderBy(pair =&amp;gt; pair.Key)
    .TakeWhile(pair =&amp;gt; pair.Value &amp;gt; 85);
&lt;/code&gt;&lt;/pre&gt;
&lt;h4&gt;10. 高阶方法的组合使用&lt;/h4&gt;
&lt;p&gt;Dictionary&amp;lt;TKey, TValue&amp;gt;的高阶方法可以灵活组合，实现复杂的数据处理逻辑：&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;// 示例1：获取前三名学生并转换为新格式
var topThreeStudents = studentScores
    .OrderByDescending(pair =&amp;gt; pair.Value)
    .Take(3)
    .Select((pair, index) =&amp;gt; new {
        Rank = index + 1,
        Name = pair.Key,
        Score = pair.Value
    });

// 示例2：复杂分组和统计
var scoreStats = studentScores
    .GroupBy(pair =&amp;gt; pair.Value &amp;gt;= 90 ? &quot;优秀&quot; : (pair.Value &amp;gt;= 80 ? &quot;良好&quot; : &quot;及格&quot;))
    .Select(group =&amp;gt; new {
        Category = group.Key,
        Count = group.Count(),
        Names = string.Join(&quot;, &quot;, group.Select(pair =&amp;gt; pair.Key)),
        AverageScore = group.Average(pair =&amp;gt; pair.Value)
    });

// 示例3：根据条件筛选并转换为多级字典
var studentDictionary = studentScores
    .Where(pair =&amp;gt; pair.Value &amp;gt; 80)
    .GroupBy(pair =&amp;gt; pair.Value &amp;gt;= 90 ? &quot;A&quot; : &quot;B&quot;)
    .ToDictionary(
        group =&amp;gt; group.Key,
        group =&amp;gt; group.ToDictionary(pair =&amp;gt; pair.Key, pair =&amp;gt; pair.Value)
    );
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;通过这些高阶方法的组合使用，可以极大地简化字典数据的处理和转换操作，提高代码的可读性和可维护性。&lt;/p&gt;
&lt;h3&gt;Dictionary&amp;lt;TKey, TValue&amp;gt;的实际应用示例&lt;/h3&gt;
&lt;h4&gt;示例1：学生成绩管理&lt;/h4&gt;
&lt;pre&gt;&lt;code&gt;public class StudentScoreManager
{
    private Dictionary&amp;lt;string, int&amp;gt; _studentScores = new Dictionary&amp;lt;string, int&amp;gt;();
    
    public void AddStudent(string name, int score)
    {
        _studentScores[name] = score;
    }
    
    public bool RemoveStudent(string name)
    {
        return _studentScores.Remove(name);
    }
    
    public int? GetScore(string name)
    {
        if (_studentScores.TryGetValue(name, out int score))
        {
            return score;
        }
        return null;
    }
    
    public List&amp;lt;string&amp;gt; GetStudentsByScoreRange(int minScore, int maxScore)
    {
        return _studentScores.Where(pair =&amp;gt; pair.Value &amp;gt;= minScore &amp;amp;&amp;amp; pair.Value &amp;lt;= maxScore)
                            .Select(pair =&amp;gt; pair.Key)
                            .ToList();
    }
    
    public Dictionary&amp;lt;string, int&amp;gt; GetTopStudents(int topCount)
    {
        return _studentScores.OrderByDescending(pair =&amp;gt; pair.Value)
                            .Take(topCount)
                            .ToDictionary(pair =&amp;gt; pair.Key, pair =&amp;gt; pair.Value);
    }
}

// 使用示例
StudentScoreManager manager = new StudentScoreManager();
manager.AddStudent(&quot;张三&quot;, 90);
manager.AddStudent(&quot;李四&quot;, 85);
manager.AddStudent(&quot;王五&quot;, 95);
List&amp;lt;string&amp;gt; highScorers = manager.GetStudentsByScoreRange(90, 100);
&lt;/code&gt;&lt;/pre&gt;
&lt;h4&gt;示例2：缓存实现&lt;/h4&gt;
&lt;pre&gt;&lt;code&gt;public class SimpleCache&amp;lt;TKey, TValue&amp;gt;
{
    private Dictionary&amp;lt;TKey, CacheItem&amp;gt; _cache = new Dictionary&amp;lt;TKey, CacheItem&amp;gt;();
    private TimeSpan _defaultExpiration;
    
    public SimpleCache(TimeSpan defaultExpiration)
    {
        _defaultExpiration = defaultExpiration;
    }
    
    private class CacheItem
    {
        public TValue Value { get; set; }
        public DateTime ExpirationTime { get; set; }
    }
    
    public void Add(TKey key, TValue value)
    {
        Add(key, value, _defaultExpiration);
    }
    
    public void Add(TKey key, TValue value, TimeSpan expiration)
    {
        _cache[key] = new CacheItem
        {
            Value = value,
            ExpirationTime = DateTime.Now + expiration
        };
    }
    
    public bool TryGetValue(TKey key, out TValue value)
    {
        if (_cache.TryGetValue(key, out CacheItem item))
        {
            if (item.ExpirationTime &amp;gt; DateTime.Now)
            {
                value = item.Value;
                return true;
            }
            else
            {
                _cache.Remove(key); // 移除过期项
            }
        }
        value = default;
        return false;
    }
    
    public void Clear()
    {
        _cache.Clear();
    }
    
    public int Count =&amp;gt; _cache.Count;
}

// 使用示例
SimpleCache&amp;lt;string, string&amp;gt; cache = new SimpleCache&amp;lt;string, string&amp;gt;(TimeSpan.FromMinutes(5));
cache.Add(&quot;user:1&quot;, &quot;张三&quot;);
cache.Add(&quot;user:2&quot;, &quot;李四&quot;, TimeSpan.FromHours(1));
if (cache.TryGetValue(&quot;user:1&quot;, out string userName))
{
    Console.WriteLine($&quot;用户名：{userName}&quot;);
}
&lt;/code&gt;&lt;/pre&gt;
&lt;h3&gt;Dictionary&amp;lt;TKey, TValue&amp;gt;的性能优化&lt;/h3&gt;
&lt;p&gt;Dictionary&amp;lt;TKey, TValue&amp;gt;的性能主要取决于键的哈希函数和初始容量的设置：&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;// 优化1：合理设置初始容量
Dictionary&amp;lt;int, string&amp;gt; dict1 = new Dictionary&amp;lt;int, string&amp;gt;(10000); // 避免频繁扩容

// 优化2：使用高效的键类型
// 推荐使用int、string等内置类型作为键，它们有高效的哈希函数
// 避免使用复杂对象作为键，如果必须使用，请确保实现高效的GetHashCode和Equals方法

// 优化3：实现高效的哈希函数（自定义键类型）
public class Product
{
    public int Id { get; set; }
    public string Name { get; set; }
    
    public override int GetHashCode()
    {
        return Id.GetHashCode(); // 只使用唯一标识符作为哈希值
    }
    
    public override bool Equals(object obj)
    {
        if (obj is Product product)
        {
            return Id == product.Id;
        }
        return false;
    }
}

// 优化4：选择合适的比较器
Dictionary&amp;lt;string, int&amp;gt; caseInsensitiveDict = new Dictionary&amp;lt;string, int&amp;gt;(StringComparer.OrdinalIgnoreCase);
&lt;/code&gt;&lt;/pre&gt;
&lt;h3&gt;Dictionary&amp;lt;TKey, TValue&amp;gt;使用注意事项&lt;/h3&gt;
&lt;ol&gt;
&lt;li&gt;&lt;strong&gt;键的唯一性&lt;/strong&gt;：同一个键只能在字典中出现一次，重复添加会抛出ArgumentException&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;键的null值&lt;/strong&gt;：对于引用类型键，不能为null，否则会抛出ArgumentNullException&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;值的null值&lt;/strong&gt;：对于引用类型值，可以为null，值类型值不能为null（除非使用Nullable&amp;lt;T&amp;gt;）&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;线程安全&lt;/strong&gt;：Dictionary&amp;lt;TKey, TValue&amp;gt;不是线程安全的，多线程环境下需要手动同步&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;无序性&lt;/strong&gt;：Dictionary&amp;lt;TKey, TValue&amp;gt;不保证元素的顺序，插入顺序可能与遍历顺序不同&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;性能考虑&lt;/strong&gt;：
&lt;ul&gt;
&lt;li&gt;当字典大小接近或超过初始容量时，会自动扩容，这会影响性能&lt;/li&gt;
&lt;li&gt;键的哈希函数质量直接影响字典的性能&lt;/li&gt;
&lt;li&gt;使用TryGetValue比先ContainsKey再索引访问更高效&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;/ol&gt;
&lt;h3&gt;Dictionary&amp;lt;TKey, TValue&amp;gt;类常用属性和方法速查表&lt;/h3&gt;
&lt;h4&gt;常用属性&lt;/h4&gt;
&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;属性&lt;/th&gt;
&lt;th&gt;类型&lt;/th&gt;
&lt;th&gt;作用说明&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;Count&lt;/td&gt;
&lt;td&gt;int&lt;/td&gt;
&lt;td&gt;获取Dictionary中实际包含的键值对数量&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Comparer&lt;/td&gt;
&lt;td&gt;IEqualityComparer&amp;lt;TKey&amp;gt;&lt;/td&gt;
&lt;td&gt;获取用于比较键的相等性比较器&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Keys&lt;/td&gt;
&lt;td&gt;ICollection&amp;lt;TKey&amp;gt;&lt;/td&gt;
&lt;td&gt;获取Dictionary中所有键的集合&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Values&lt;/td&gt;
&lt;td&gt;ICollection&amp;lt;TValue&amp;gt;&lt;/td&gt;
&lt;td&gt;获取Dictionary中所有值的集合&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Item[TKey]&lt;/td&gt;
&lt;td&gt;TValue&lt;/td&gt;
&lt;td&gt;获取或设置指定键对应的值。这是索引器，可以通过dict[key]访问&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;
&lt;h4&gt;常用方法&lt;/h4&gt;
&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;方法&lt;/th&gt;
&lt;th&gt;参数&lt;/th&gt;
&lt;th&gt;返回值&lt;/th&gt;
&lt;th&gt;作用说明&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;Add(TKey, TValue)&lt;/td&gt;
&lt;td&gt;TKey key, TValue value&lt;/td&gt;
&lt;td&gt;void&lt;/td&gt;
&lt;td&gt;添加键值对，如果键已存在则抛出异常&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;ContainsKey(TKey)&lt;/td&gt;
&lt;td&gt;TKey key&lt;/td&gt;
&lt;td&gt;bool&lt;/td&gt;
&lt;td&gt;检查Dictionary是否包含指定键&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;ContainsValue(TValue)&lt;/td&gt;
&lt;td&gt;TValue value&lt;/td&gt;
&lt;td&gt;bool&lt;/td&gt;
&lt;td&gt;检查Dictionary是否包含指定值&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;TryGetValue(TKey, out TValue)&lt;/td&gt;
&lt;td&gt;TKey key, out TValue value&lt;/td&gt;
&lt;td&gt;bool&lt;/td&gt;
&lt;td&gt;尝试获取指定键对应的值，如果键存在则返回true，否则返回false&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Remove(TKey)&lt;/td&gt;
&lt;td&gt;TKey key&lt;/td&gt;
&lt;td&gt;bool&lt;/td&gt;
&lt;td&gt;删除指定键对应的键值对，如果键存在则返回true，否则返回false&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Clear()&lt;/td&gt;
&lt;td&gt;无&lt;/td&gt;
&lt;td&gt;void&lt;/td&gt;
&lt;td&gt;清空Dictionary中的所有键值对&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;GetEnumerator()&lt;/td&gt;
&lt;td&gt;无&lt;/td&gt;
&lt;td&gt;IEnumerator&amp;lt;KeyValuePair&amp;lt;TKey, TValue&amp;gt;&amp;gt;&lt;/td&gt;
&lt;td&gt;返回用于遍历Dictionary的枚举器&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;
&lt;h2&gt;&amp;lt;a id=&quot;iterator-detail&quot;&amp;gt;&amp;lt;/a&amp;gt;C#迭代器详解&lt;/h2&gt;
&lt;p&gt;迭代器（Iterator）是C#中用于遍历集合或自定义数据结构的一种机制。它允许开发者以统一的方式访问集合中的元素，而无需了解集合的底层实现细节。C#的迭代器主要通过&lt;code&gt;foreach&lt;/code&gt;循环和&lt;code&gt;yield&lt;/code&gt;关键字来实现。&lt;/p&gt;
&lt;h3&gt;迭代器的基本概念&lt;/h3&gt;
&lt;p&gt;在C#中，迭代器是一个可以产生序列值的方法、属性或索引器。迭代器使用&lt;code&gt;yield return&lt;/code&gt;语句返回序列中的下一个元素，使用&lt;code&gt;yield break&lt;/code&gt;语句终止迭代。&lt;/p&gt;
&lt;h4&gt;迭代器的核心接口&lt;/h4&gt;
&lt;p&gt;C#中迭代器的实现基于以下两个核心接口：&lt;/p&gt;
&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;接口&lt;/th&gt;
&lt;th&gt;说明&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;IEnumerable&amp;lt;T&amp;gt;&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;定义了获取泛型枚举器的方法&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;IEnumerator&amp;lt;T&amp;gt;&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;定义了泛型枚举器的方法，包括遍历集合的核心方法&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;
&lt;p&gt;这两个接口的关系如下：&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;public interface IEnumerable&amp;lt;out T&amp;gt;
{
    IEnumerator&amp;lt;T&amp;gt; GetEnumerator();
}

public interface IEnumerator&amp;lt;out T&amp;gt; : IDisposable, IEnumerator
{
    T Current { get; }
}
&lt;/code&gt;&lt;/pre&gt;
&lt;h3&gt;foreach循环与迭代器&lt;/h3&gt;
&lt;p&gt;&lt;code&gt;foreach&lt;/code&gt;循环是C#中用于遍历实现了&lt;code&gt;IEnumerable&lt;/code&gt;或&lt;code&gt;IEnumerable&amp;lt;T&amp;gt;&lt;/code&gt;接口的集合的语法糖。它内部会调用集合的&lt;code&gt;GetEnumerator()&lt;/code&gt;方法获取枚举器，然后使用枚举器遍历集合。&lt;/p&gt;
&lt;h4&gt;使用foreach循环遍历集合&lt;/h4&gt;
&lt;pre&gt;&lt;code&gt;// 遍历数组
int[] numbers = { 1, 2, 3, 4, 5 };
foreach (int number in numbers)
{
    Console.WriteLine(number);
}

// 遍历List
List&amp;lt;string&amp;gt; names = new List&amp;lt;string&amp;gt; { &quot;张三&quot;, &quot;李四&quot;, &quot;王五&quot; };
foreach (string name in names)
{
    Console.WriteLine(name);
}

// 遍历Dictionary
Dictionary&amp;lt;int, string&amp;gt; dict = new Dictionary&amp;lt;int, string&amp;gt;
{
    { 1, &quot;苹果&quot; },
    { 2, &quot;香蕉&quot; },
    { 3, &quot;橙子&quot; }
};

foreach (KeyValuePair&amp;lt;int, string&amp;gt; kvp in dict)
{
    Console.WriteLine($&quot;Key: {kvp.Key}, Value: {kvp.Value}&quot;);
}

// 只遍历Dictionary的键
foreach (int key in dict.Keys)
{
    Console.WriteLine($&quot;Key: {key}&quot;);
}

// 只遍历Dictionary的值
foreach (string value in dict.Values)
{
    Console.WriteLine($&quot;Value: {value}&quot;);
}
&lt;/code&gt;&lt;/pre&gt;
&lt;h4&gt;foreach循环的工作原理&lt;/h4&gt;
&lt;p&gt;&lt;code&gt;foreach&lt;/code&gt;循环在编译时会被转换为以下代码：&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;IEnumerator&amp;lt;string&amp;gt; enumerator = names.GetEnumerator();
try
{
    while (enumerator.MoveNext())
    {
        string name = enumerator.Current;
        Console.WriteLine(name);
    }
}
finally
{
    if (enumerator != null)
    {
        enumerator.Dispose();
    }
}
&lt;/code&gt;&lt;/pre&gt;
&lt;h3&gt;自定义迭代器的实现&lt;/h3&gt;
&lt;p&gt;在C#中，可以通过&lt;code&gt;yield&lt;/code&gt;关键字实现自定义迭代器，而无需手动实现&lt;code&gt;IEnumerable&lt;/code&gt;和&lt;code&gt;IEnumerator&lt;/code&gt;接口。&lt;/p&gt;
&lt;h4&gt;使用yield return实现迭代器方法&lt;/h4&gt;
&lt;pre&gt;&lt;code&gt;public class NumberGenerator
{
    // 生成从start到end的整数序列
    public IEnumerable&amp;lt;int&amp;gt; GenerateNumbers(int start, int end)
    {
        for (int i = start; i &amp;lt;= end; i++)
        {
            yield return i;
        }
    }
    
    // 生成斐波那契数列
    public IEnumerable&amp;lt;long&amp;gt; GenerateFibonacci(int count)
    {
        long a = 0;
        long b = 1;
        
        for (int i = 0; i &amp;lt; count; i++)
        {
            yield return a;
            long temp = a;
            a = b;
            b = temp + b;
        }
    }
    
    // 生成偶数列
    public IEnumerable&amp;lt;int&amp;gt; GenerateEvenNumbers(int max)
    {
        int i = 0;
        while (i &amp;lt;= max)
        {
            yield return i;
            i += 2;
        }
    }
}
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;使用自定义迭代器：&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;NumberGenerator generator = new NumberGenerator();

// 生成从1到5的整数序列
foreach (int number in generator.GenerateNumbers(1, 5))
{
    Console.WriteLine(number); // 输出: 1, 2, 3, 4, 5
}

// 生成前10个斐波那契数
foreach (long fib in generator.GenerateFibonacci(10))
{
    Console.WriteLine(fib); // 输出: 0, 1, 1, 2, 3, 5, 8, 13, 21, 34
}

// 生成小于等于10的偶数列
foreach (int even in generator.GenerateEvenNumbers(10))
{
    Console.WriteLine(even); // 输出: 0, 2, 4, 6, 8, 10
}
&lt;/code&gt;&lt;/pre&gt;
&lt;h4&gt;迭代器属性和索引器&lt;/h4&gt;
&lt;p&gt;除了方法，还可以在属性和索引器中实现迭代器：&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;public class Student
{
    private string[] _courses = { &quot;数学&quot;, &quot;英语&quot;, &quot;物理&quot;, &quot;化学&quot; };
    
    // 迭代器属性
    public IEnumerable&amp;lt;string&amp;gt; Courses
    {
        get
        {
            foreach (string course in _courses)
            {
                yield return course;
            }
        }
    }
    
    // 迭代器索引器
    public IEnumerable&amp;lt;string&amp;gt; this[int start, int end]
    {
        get
        {
            for (int i = start; i &amp;lt;= end &amp;amp;&amp;amp; i &amp;lt; _courses.Length; i++)
            {
                yield return _courses[i];
            }
        }
    }
}
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;使用迭代器属性和索引器：&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;Student student = new Student();

// 使用迭代器属性
foreach (string course in student.Courses)
{
    Console.WriteLine(course); // 输出: 数学, 英语, 物理, 化学
}

// 使用迭代器索引器
foreach (string course in student[1, 2])
{
    Console.WriteLine(course); // 输出: 英语, 物理
}
&lt;/code&gt;&lt;/pre&gt;
&lt;h3&gt;yield关键字的深入理解&lt;/h3&gt;
&lt;p&gt;&lt;code&gt;yield&lt;/code&gt;关键字是C#迭代器的核心，它有以下两种形式：&lt;/p&gt;
&lt;h4&gt;yield return语句&lt;/h4&gt;
&lt;p&gt;&lt;code&gt;yield return&lt;/code&gt;语句用于返回序列中的下一个元素，并将当前位置保存，以便下次调用时从该位置继续执行。&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;public IEnumerable&amp;lt;int&amp;gt; GenerateNumbers()
{
    yield return 1; // 返回1，保存当前位置
    Console.WriteLine(&quot;After first yield return&quot;);
    yield return 2; // 返回2，保存当前位置
    Console.WriteLine(&quot;After second yield return&quot;);
    yield return 3; // 返回3，保存当前位置
}
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;当调用这个方法时，代码不会立即执行，而是返回一个迭代器对象。只有当调用迭代器的&lt;code&gt;MoveNext()&lt;/code&gt;方法时，才会执行到下一个&lt;code&gt;yield return&lt;/code&gt;语句。&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;IEnumerable&amp;lt;int&amp;gt; numbers = GenerateNumbers();
Console.WriteLine(&quot;Before foreach&quot;);

foreach (int number in numbers)
{
    Console.WriteLine($&quot;Number: {number}&quot;);
}

// 输出:
// Before foreach
// Number: 1
// After first yield return
// Number: 2
// After second yield return
// Number: 3
&lt;/code&gt;&lt;/pre&gt;
&lt;h4&gt;yield break语句&lt;/h4&gt;
&lt;p&gt;&lt;code&gt;yield break&lt;/code&gt;语句用于终止迭代，不再产生任何元素。&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;public IEnumerable&amp;lt;int&amp;gt; GenerateNumbersWithBreak(int max)
{
    for (int i = 1; i &amp;lt;= 10; i++)
    {
        if (i &amp;gt; max)
        {
            yield break; // 终止迭代
        }
        yield return i;
    }
}
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;使用带有yield break的迭代器：&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;foreach (int number in GenerateNumbersWithBreak(5))
{
    Console.WriteLine(number); // 输出: 1, 2, 3, 4, 5
}
&lt;/code&gt;&lt;/pre&gt;
&lt;h3&gt;迭代器的高级特性&lt;/h3&gt;
&lt;h4&gt;迭代器与延迟执行&lt;/h4&gt;
&lt;p&gt;迭代器具有延迟执行的特性，即只有在需要序列中的元素时才会执行相应的代码。这种特性可以提高性能，特别是在处理大型或无限序列时。&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;// 生成无限序列的迭代器
public IEnumerable&amp;lt;int&amp;gt; GenerateInfiniteSequence()
{
    int i = 0;
    while (true)
    {
        yield return i++;
    }
}

// 使用Take方法限制获取的元素数量
foreach (int number in GenerateInfiniteSequence().Take(10))
{
    Console.WriteLine(number); // 输出: 0, 1, 2, 3, 4, 5, 6, 7, 8, 9
}
&lt;/code&gt;&lt;/pre&gt;
&lt;h4&gt;迭代器与LINQ&lt;/h4&gt;
&lt;p&gt;LINQ（Language Integrated Query）大量使用了迭代器和延迟执行的特性。LINQ的查询操作符（如&lt;code&gt;Where&lt;/code&gt;、&lt;code&gt;Select&lt;/code&gt;、&lt;code&gt;OrderBy&lt;/code&gt;等）都是通过迭代器实现的。&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;List&amp;lt;int&amp;gt; numbers = new List&amp;lt;int&amp;gt; { 1, 2, 3, 4, 5, 6, 7, 8, 9, 10 };

// 使用LINQ查询偶数
var evenNumbers = numbers.Where(n =&amp;gt; n % 2 == 0);

// 使用LINQ查询并转换结果
var squaredNumbers = numbers.Select(n =&amp;gt; n * n);

// 组合多个LINQ操作
var result = numbers.Where(n =&amp;gt; n % 2 == 0).Select(n =&amp;gt; n * n).OrderByDescending(n =&amp;gt; n);

// 执行查询（延迟执行，直到迭代时才执行）
foreach (int number in result)
{
    Console.WriteLine(number); // 输出: 100, 64, 36, 16, 4
}
&lt;/code&gt;&lt;/pre&gt;
&lt;h4&gt;自定义集合的迭代器实现&lt;/h4&gt;
&lt;p&gt;当创建自定义集合类时，可以通过实现&lt;code&gt;IEnumerable&amp;lt;T&amp;gt;&lt;/code&gt;接口和使用&lt;code&gt;yield&lt;/code&gt;关键字来提供迭代功能。&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;public class CustomCollection&amp;lt;T&amp;gt; : IEnumerable&amp;lt;T&amp;gt;
{
    private List&amp;lt;T&amp;gt; _items = new List&amp;lt;T&amp;gt;();
    
    public void Add(T item)
    {
        _items.Add(item);
    }
    
    // 实现IEnumerable&amp;lt;T&amp;gt;接口的GetEnumerator方法
    public IEnumerator&amp;lt;T&amp;gt; GetEnumerator()
    {
        foreach (T item in _items)
        {
            yield return item;
        }
    }
    
    // 实现非泛型IEnumerable接口的GetEnumerator方法（显式实现）
    IEnumerator IEnumerable.GetEnumerator()
    {
        return GetEnumerator();
    }
}
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;使用自定义集合：&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;CustomCollection&amp;lt;string&amp;gt; collection = new CustomCollection&amp;lt;string&amp;gt;();
collection.Add(&quot;苹果&quot;);
collection.Add(&quot;香蕉&quot;);
collection.Add(&quot;橙子&quot;);

// 使用foreach循环遍历自定义集合
foreach (string item in collection)
{
    Console.WriteLine(item); // 输出: 苹果, 香蕉, 橙子
}
&lt;/code&gt;&lt;/pre&gt;
&lt;h3&gt;迭代器的性能考虑&lt;/h3&gt;
&lt;p&gt;虽然迭代器提供了便利的遍历机制，但在使用时也需要考虑性能问题：&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;&lt;strong&gt;延迟执行的开销&lt;/strong&gt;：每次调用迭代器都会创建一个新的迭代器对象，这会带来一定的开销。&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;多次迭代的开销&lt;/strong&gt;：如果多次遍历同一个迭代器返回的序列，每次都会重新执行迭代器方法。&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;内存使用&lt;/strong&gt;：对于大型集合，使用迭代器可以减少内存使用，因为它不需要一次性加载所有元素。&lt;/li&gt;
&lt;/ol&gt;
&lt;h4&gt;优化迭代器性能的方法&lt;/h4&gt;
&lt;pre&gt;&lt;code&gt;// 避免在迭代器中执行昂贵的操作
public IEnumerable&amp;lt;int&amp;gt; GenerateNumbers()
{
    // 不推荐：在迭代器中执行昂贵的操作
    // ExpensiveOperation();
    
    for (int i = 1; i &amp;lt;= 10; i++)
    {
        yield return i;
    }
}

// 对于需要多次遍历的序列，考虑将结果缓存
IEnumerable&amp;lt;int&amp;gt; numbers = GenerateNumbers().ToList(); // 缓存结果到List

// 遍历多次，只执行一次迭代器方法
foreach (int number in numbers) { /* ... */ }
foreach (int number in numbers) { /* ... */ }
&lt;/code&gt;&lt;/pre&gt;
&lt;h3&gt;迭代器的最佳实践&lt;/h3&gt;
&lt;ol&gt;
&lt;li&gt;&lt;strong&gt;使用泛型接口&lt;/strong&gt;：优先使用&lt;code&gt;IEnumerable&amp;lt;T&amp;gt;&lt;/code&gt;和&lt;code&gt;IEnumerator&amp;lt;T&amp;gt;&lt;/code&gt;接口，而不是非泛型接口，以避免装箱和拆箱操作。&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;保持迭代器简洁&lt;/strong&gt;：迭代器方法应该只关注生成序列，避免在迭代器中执行与生成序列无关的操作。&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;处理资源释放&lt;/strong&gt;：如果迭代器使用了需要释放的资源（如文件句柄、数据库连接等），应该实现&lt;code&gt;IDisposable&lt;/code&gt;接口。&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;避免修改集合&lt;/strong&gt;：在迭代过程中修改集合可能会导致&lt;code&gt;InvalidOperationException&lt;/code&gt;。&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;使用yield break终止迭代&lt;/strong&gt;：当需要提前终止迭代时，使用&lt;code&gt;yield break&lt;/code&gt;语句，而不是抛出异常。&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;考虑延迟执行的影响&lt;/strong&gt;：了解延迟执行的特性，避免在迭代器中执行有副作用的操作。&lt;/li&gt;
&lt;/ol&gt;
&lt;h3&gt;迭代器的应用场景&lt;/h3&gt;
&lt;p&gt;迭代器在C#中有广泛的应用场景，包括：&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;&lt;strong&gt;遍历集合&lt;/strong&gt;：使用&lt;code&gt;foreach&lt;/code&gt;循环遍历各种集合类型。&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;生成序列&lt;/strong&gt;：生成各种数学序列、日期序列等。&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;处理大数据&lt;/strong&gt;：处理大型文件、数据库结果集等，避免一次性加载所有数据。&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;实现LINQ操作符&lt;/strong&gt;：LINQ的查询操作符（如&lt;code&gt;Where&lt;/code&gt;、&lt;code&gt;Select&lt;/code&gt;、&lt;code&gt;OrderBy&lt;/code&gt;等）都是通过迭代器实现的。&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;创建自定义集合&lt;/strong&gt;：为自定义集合类提供迭代功能。&lt;/li&gt;
&lt;/ol&gt;
&lt;p&gt;迭代器是C#中强大而灵活的特性，通过掌握迭代器的使用和实现，可以编写出更加高效、简洁和可维护的代码。&lt;/p&gt;
</content:encoded></item><item><title>电脑设置事项清单(搁置，有待完善)</title><link>https://meteor-comet.github.io/posts/new-pc-todo-2023/</link><guid isPermaLink="true">https://meteor-comet.github.io/posts/new-pc-todo-2023/</guid><description>电脑系统设置与效率提升全攻略</description><pubDate>Sat, 23 Dec 2023 00:00:00 GMT</pubDate><content:encoded>&lt;h2&gt;新设备设置&lt;/h2&gt;
&lt;p&gt;&lt;strong&gt;修改默认保存路径&lt;/strong&gt;&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;将文档、图片、下载等文件夹的默认保存路径从C盘修改为其他盘（如D盘或E盘），避免系统盘空间不足。&lt;/li&gt;
&lt;li&gt;操作方法：右键&quot;文档&quot;等文件夹 → 属性 → 位置 → 移动。&lt;/li&gt;
&lt;/ul&gt;
&lt;h2&gt;Win10更改AppData默认存储路径方法&lt;/h2&gt;
&lt;p&gt;Win10电脑的AppData文件夹用于存放软件的配置文件和临时文件，默认在C盘。随着使用时间增长，AppData会占用大量C盘空间。你可以通过以下方法将其迁移到其他盘：&lt;/p&gt;
&lt;h3&gt;一、修改Windows用户账户文件夹路径&lt;/h3&gt;
&lt;ol&gt;
&lt;li&gt;
&lt;p&gt;打开注册表编辑器（Win+R 输入 &lt;code&gt;regedit&lt;/code&gt;）。&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;定位到：
&lt;code&gt;计算机\HKEY_LOCAL_MACHINE\SOFTWARE\Microsoft\Windows NT\CurrentVersion\ProfileList\S-1-5-21-...&lt;/code&gt;&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;路径最后一段（S-1-5-21-...）通常是最长的那一项。&lt;/li&gt;
&lt;li&gt;在右侧找到 &lt;code&gt;ProfileImagePath&lt;/code&gt; 字段，修改为你想要的新用户目录路径。&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;&lt;img src=&quot;/images/in-post/post-big-data-20250108/profilelist-admin.png&quot; alt=&quot;ProfileList注册表截图&quot; /&gt;&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;修改后，用户目录（含AppData）会整体迁移，无需单独修改AppData路径。&lt;/p&gt;
&lt;/li&gt;
&lt;/ol&gt;
&lt;h3&gt;二、单独更改AppData的默认存储路径&lt;/h3&gt;
&lt;ol&gt;
&lt;li&gt;打开注册表编辑器（Win+R 输入 &lt;code&gt;regedit&lt;/code&gt;）。&lt;/li&gt;
&lt;li&gt;建议先在&quot;文件&quot;菜单中使用&quot;导出&quot;功能备份注册表。&lt;/li&gt;
&lt;li&gt;定位到：
&lt;code&gt;HKEY_CURRENT_USER\Software\Microsoft\Windows\CurrentVersion\Explorer\Shell Folders&lt;/code&gt;&lt;/li&gt;
&lt;li&gt;在右侧找到 &lt;code&gt;AppData&lt;/code&gt;，将其路径修改为自定义位置（如D:\AppData）。&lt;/li&gt;
&lt;li&gt;将原AppData目录下的所有数据复制到新位置。&lt;/li&gt;
&lt;li&gt;重启电脑即可生效（部分数据可能无法复制，但一般不会影响正常使用）。&lt;/li&gt;
&lt;/ol&gt;
&lt;h3&gt;三、可选：更改ProgramData默认路径&lt;/h3&gt;
&lt;ul&gt;
&lt;li&gt;注册表地址：
&lt;code&gt;计算机\HKEY_LOCAL_MACHINE\SOFTWARE\Microsoft\Windows NT\CurrentVersion\ProfileList&lt;/code&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;blockquote&gt;
&lt;p&gt;修改注册表有风险，操作前请务必备份，谨慎操作。&lt;/p&gt;
&lt;/blockquote&gt;
&lt;hr /&gt;
&lt;p&gt;&lt;strong&gt;参考与版权声明：&lt;/strong&gt;
本文部分内容参考自 CSDN 博主 justlpf 的原创文章，遵循 CC 4.0 BY-SA 版权协议。
原文链接：https://blog.csdn.net/justlpf/article/details/128200123&lt;/p&gt;
</content:encoded></item><item><title>SQL Server 与 T-SQL 深度学习笔记与实战记录</title><link>https://meteor-comet.github.io/posts/tsql-learning/</link><guid isPermaLink="true">https://meteor-comet.github.io/posts/tsql-learning/</guid><description>从基础CRUD到存储过程、事务、索引、游标与并发控制的万字高阶实战解析</description><pubDate>Sun, 15 Oct 2023 00:00:00 GMT</pubDate><content:encoded>&lt;h1&gt;SQL Server 与 T-SQL 深度学习笔记与实战记录&lt;/h1&gt;
&lt;p&gt;无论你是刚刚接触数据库的新手，还是想要系统复习 T-SQL 及高级特性（如：复杂联表、窗口函数、存储过程调优、事务全隔离级别并发、游标细节、各类锁机制等）的开发者，这份学习笔记都能为你提供一条从入门到骨灰级的清晰路径。全篇几乎囊括了所有后端研发必考必用的核心知识点。&lt;/p&gt;
&lt;h2&gt;万字高阶目录&lt;/h2&gt;
&lt;ul&gt;
&lt;li&gt;&lt;a href=&quot;#1-%E6%B7%B1%E5%85%A5%E7%90%86%E8%A7%A3-sql-server-%E4%B8%8E-t-sql-%E4%BD%93%E7%B3%BB%E6%9E%B6%E6%9E%84&quot;&gt;1. 深入理解 SQL Server 与 T-SQL 体系架构&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;#2-ddl-%E8%BF%9B%E9%98%B6%E6%95%B0%E6%8D%AE%E5%BA%93%E8%A1%A8%E8%AE%BE%E8%AE%A1%E4%B8%8E-6-%E5%A4%A7%E7%B2%BE%E5%AF%86%E7%BA%A6%E6%9D%9F&quot;&gt;2. DDL 进阶：数据库、表设计与 6 大精密约束&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;#3-dml-%E6%B7%B1%E5%BA%A6%E5%AE%9E%E6%88%98crud-%E4%B8%8E-%E9%AB%98%E7%BA%A7%E6%95%B0%E6%8D%AE%E6%93%8D%E7%BA%B5&quot;&gt;3. DML 深度实战：CRUD 与 高级数据操纵&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;#4-dql-%E7%8E%8B%E8%80%85%E5%A4%8D%E6%9D%82%E6%9F%A5%E8%AF%A2%E5%85%B3%E8%81%94%E5%AD%90%E6%9F%A5%E8%AF%A2%E4%B8%8E%E7%AA%97%E5%8F%A3%E5%87%BD%E6%95%B0&quot;&gt;4. DQL 王者：复杂查询、关联、子查询与窗口函数&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;#5-t-sql-%E7%BC%96%E7%A8%8B%E7%B2%BE%E9%AB%93%E5%8F%98%E9%87%8F%E6%8E%A7%E5%88%B6%E6%B5%81%E5%86%85%E7%BD%AE%E5%87%BD%E6%95%B0%E4%B8%8E%E6%B8%B8%E6%A0%87-cursor&quot;&gt;5. T-SQL 编程精髓：变量、控制流、内置函数与游标&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;#6-%E6%A8%A1%E5%9D%97%E5%8C%96%E5%B0%81%E8%A3%85%E8%A7%86%E5%9B%BE-view-%E4%B8%8E-%E8%87%AA%E5%AE%9A%E4%B9%89%E5%87%BD%E6%95%B0-udf&quot;&gt;6. 模块化封装：视图 (View) 与 自定义函数 (UDF)&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;#7-%E6%80%A7%E8%83%BD%E6%A0%B8%E5%BF%83%E5%AD%98%E5%82%A8%E8%BF%87%E7%A8%8B-stored-procedure-%E4%B8%8E%E5%8A%A8%E6%80%81-sql&quot;&gt;7. 性能核心：存储过程 (Stored Procedure) 与动态 SQL&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;#8-%E6%95%B0%E6%8D%AE%E5%BA%93%E7%9A%84%E5%BF%83%E8%84%8F%E4%BA%8B%E5%8A%A1-transaction-%E4%B8%8E-4%E5%A4%A7%E9%9A%94%E7%A6%BB%E7%BA%A7%E5%88%AB%E9%94%81&quot;&gt;8. 数据库的心脏：事务 (Transaction) 与 4大隔离级别(锁)&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;#9-%E9%9A%90%E5%BD%A2%E7%9C%8B%E9%97%A8%E7%8B%97%E8%A7%A6%E5%8F%91%E5%99%A8-trigger-%E6%B7%B1%E5%BA%A6%E5%89%96%E6%9E%90&quot;&gt;9. 隐形看门狗：触发器 (Trigger) 深度剖析&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;#10-%E6%9E%81%E8%87%B4%E8%B0%83%E4%BC%98%E7%B4%A2%E5%BC%95-index-%E7%9A%84%E8%81%9A%E7%B0%87%E6%9C%AC%E8%B4%A8%E4%B8%8E%E6%89%A7%E8%A1%8C%E8%AE%A1%E5%88%92%E8%B0%83%E4%BC%98&quot;&gt;10. 极致调优：索引 (Index) 的聚簇本质与执行计划调优&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;#11-%E9%AB%98%E9%98%B6%E6%9F%A5%E8%AF%A2%E8%89%BA%E6%9C%AFcte-%E9%80%92%E5%BD%92pivot-%E8%A1%8C%E5%88%97%E8%BD%AC%E6%8D%A2%E4%B8%8E-crossouter-apply&quot;&gt;11. 高阶查询艺术：CTE 递归、PIVOT 行列转换与 CROSS/OUTER APPLY&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;#12-%E9%AB%98%E9%98%B6%E6%95%B0%E6%8D%AE%E6%93%8D%E7%BA%B5mergeupsert%E4%B8%8E%E4%B8%B4%E6%97%B6%E8%A1%A8%E8%A1%A8%E5%8F%98%E9%87%8F%E4%BD%93%E7%B3%BB&quot;&gt;12. 高阶数据操纵：MERGE（Upsert）与临时表/表变量体系&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;#13-%E6%9E%81%E5%AE%A2%E9%98%B2%E8%BA%AB%E6%9C%AF%E9%9A%8F%E5%BF%83%E6%89%80%E6%AC%B2%E7%9A%84%E5%8A%A8%E6%80%81-sql-%E4%B8%8E%E6%89%A7%E8%A1%8C%E6%8F%90%E7%A4%BA-query-hints&quot;&gt;13. 极客防身术：随心所欲的动态 SQL 与执行提示 (Query Hints)&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;hr /&gt;
&lt;h2&gt;1. 深入理解 SQL Server 与 T-SQL 体系架构&lt;/h2&gt;
&lt;h3&gt;1.1 什么是 SQL Server 和 T-SQL？&lt;/h3&gt;
&lt;ul&gt;
&lt;li&gt;&lt;strong&gt;SQL Server&lt;/strong&gt; 是微软开发的企业级关系型数据库管理系统（RDBMS）。它的网络层使用 TDS 协议，底层拥有独立的 SQLOS 操作系统用来管理内存、线程调度和 I/O，极大地榨干了 Windows Server 环境的物理机性能。&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;T-SQL (Transact-SQL)&lt;/strong&gt; 是微软在标准 SQL (ANSI/ISO) 基础上做出的图灵完备级别扩展。普通的 SQL 仅仅是“结构化查询指令”，而 T-SQL 是一门&lt;strong&gt;完整的编程语言&lt;/strong&gt;，包含局部变量声明、&lt;code&gt;IF-ELSE&lt;/code&gt; / &lt;code&gt;WHILE&lt;/code&gt; 控制流、&lt;code&gt;TRY...CATCH&lt;/code&gt; 异常捕获、数学与字符串函数、以及专有的数据类型处理能力。&lt;/li&gt;
&lt;/ul&gt;
&lt;h3&gt;1.2 系统数据库简介&lt;/h3&gt;
&lt;p&gt;安装完毕后，引擎会自带 4 个核心的系统库（千万不能乱删）：&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;strong&gt;master&lt;/strong&gt;：掌控全局的命门。记录了所有用户登录名、端点配置、系统内所有其他数据库的信息及物理文件位置。一旦顺坏引擎将无法启动。&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;model&lt;/strong&gt;：模具库。你在服务器上每次 &lt;code&gt;CREATE DATABASE&lt;/code&gt; 创建新库，引擎其实都是粗暴地把 &lt;code&gt;model&lt;/code&gt; 库的内容复制一份过去。&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;msdb&lt;/strong&gt;：调度中心库。给 SQL Server Agent 服务使用，保存了所有的定时作业计划 (Jobs)、告警邮件和数据库备份还原的历史记录。&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;tempdb&lt;/strong&gt;：内存垃圾站。存放所有局部临时表 (&lt;code&gt;#Temp&lt;/code&gt;)、全局临时表 (&lt;code&gt;##Temp&lt;/code&gt;)，还有大量涉及 &lt;code&gt;GROUP BY&lt;/code&gt; 或 &lt;code&gt;ORDER BY&lt;/code&gt; 时内存不够用溢出到硬盘上的中间排序变量。&lt;strong&gt;它是高并发下最容易成为系统 I/O 瓶颈的组件&lt;/strong&gt;。&lt;/li&gt;
&lt;/ul&gt;
&lt;hr /&gt;
&lt;h2&gt;2. DDL 进阶：数据库、表设计与 6 大精密约束&lt;/h2&gt;
&lt;p&gt;DDL (Data Definition Language) 是定义数据结构的语言，一旦执行就会被记录在系统表并真实分配硬盘簇分配。&lt;/p&gt;
&lt;h3&gt;2.1 数据库的高级创建方式&lt;/h3&gt;
&lt;p&gt;在生产环境，绝不能简单的 &lt;code&gt;CREATE DATABASE foo&lt;/code&gt;，你需要指定 MDF（主数据文件）和 LDF（日志文件）的物理大小和自动增长率，防止磁盘空间突然被碎片撑爆：&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;-- 企业级正规建库脚本
CREATE DATABASE CorpERP
ON PRIMARY 
(
    NAME = &apos;CorpERP_Data&apos;,               -- 逻辑名
    FILENAME = &apos;D:\SQLData\CorpERP.mdf&apos;, -- 物理文件全路径
    SIZE = 100MB,                        -- 初始大小
    MAXSIZE = 500GB,                     -- 最大限制（防止写满整盘）
    FILEGROWTH = 10%                     -- 自动扩容步长参数
)
LOG ON 
(
    NAME = &apos;CorpERP_Log&apos;,
    FILENAME = &apos;E:\ SQLLogs\CorpERP_Log.ldf&apos;, -- 建议日志单独放到高速读写的纯净磁盘（如 SSD）
    SIZE = 50MB,
    MAXSIZE = 200GB,
    FILEGROWTH = 50MB                    -- 日志暴涨时建议不要按百分比算，按固定兆数扩张减少碎片
);
GO
&lt;/code&gt;&lt;/pre&gt;
&lt;h3&gt;2.2 表的 6 大核弹级约束 (Constraints)的设计&lt;/h3&gt;
&lt;p&gt;创建健壮的架构，&lt;strong&gt;数据类型精准&lt;/strong&gt;与&lt;strong&gt;约束严格&lt;/strong&gt;缺一不可。&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;USE CorpERP;
GO

-- 1. 创建部门主表
CREATE TABLE Department (
    -- [1] 主键约束 (PRIMARY KEY): 唯一 + 绝不为空 + 默认生成聚集物理索引树
    -- [2] 标识/自增属性 (IDENTITY(起始值, 步长)): 让引擎自己接管分配独立ID
    DeptId INT IDENTITY(1,1) PRIMARY KEY, 
    -- [3] 唯一键约束 (UNIQUE): 保证全表部门名不可重复，且自带一个唯一的非聚集索引
    DeptName NVARCHAR(50) NOT NULL UNIQUE,
    ManagerName NVARCHAR(50) NULL
);

-- 2. 创建高度约束的员工表
CREATE TABLE Employee (
    EmpId BIGINT IDENTITY(1000, 1) PRIMARY KEY, -- 大企业建议用 BIGINT 防止越界
    EmpName NVARCHAR(50) NOT NULL,
    
    -- [4] 检查约束 (CHECK): 由引擎在插入/修改时把控取值范围逻辑，杜绝脏数据
    Age TINYINT CHECK (Age &amp;gt;= 18 AND Age &amp;lt;= 65),  
    
    -- [5] 默认值约束 (DEFAULT): 插入时如果不给此列赋值，则启用默认配置
    Gender CHAR(2) DEFAULT &apos;男&apos;, 
    Salary DECIMAL(18, 2) NOT NULL, -- 金额：总长度18位，包含小数点后2位精确
    
    JoinDate DATETIME2 DEFAULT CURRENT_TIMESTAMP, -- DATETIME2 精度比 DATETIME 要更高
    IsDeleted BIT DEFAULT 0, -- 【软删除标记】BIT = 相当于布尔值 True/False
    
    DeptId INT NOT NULL, 

    -- [6] 外键约束 (FOREIGN KEY): 数据血统关系最强硬的防线！
    -- 如果有员工还挂着这个 DeptId，那么在部门表是绝对无法直接 Delete 删掉那个部门的。
    CONSTRAINT FK_Employee_Department 
        FOREIGN KEY (DeptId) 
        REFERENCES Department(DeptId)
        -- ON DELETE CASCADE -- 可选级联操作：如果填了这个，部门一旦被删，旗下所有关联员工被系统瞬间全部同时除名。
);
GO
&lt;/code&gt;&lt;/pre&gt;
&lt;h3&gt;2.3 修改现存结构的利器 (ALTER)&lt;/h3&gt;
&lt;pre&gt;&lt;code&gt;-- 比如：忘了加某列，现在在线无感增加
ALTER TABLE Employee ADD Email VARCHAR(100) NULL;

-- 比如：给某列加上 CHECK 约束
ALTER TABLE Employee ADD CONSTRAINT CK_Employee_Salary CHECK (Salary &amp;gt;= 3000.00);

-- 清理外键关联后，强制删除全表结构
-- DROP TABLE Employee;
&lt;/code&gt;&lt;/pre&gt;
&lt;hr /&gt;
&lt;h2&gt;3. DML 深度实战：CRUD 与 高级数据操纵&lt;/h2&gt;
&lt;p&gt;DML 负责所有涉及到数据进出的交互。&lt;/p&gt;
&lt;h3&gt;3.1 猛烈的数据充填：Insert 的所有形态&lt;/h3&gt;
&lt;pre&gt;&lt;code&gt;-- 形态A：单条 / 极简多条批量插入 (SQL 2008 之后支持)
INSERT INTO Department (DeptName, ManagerName)
VALUES 
(&apos;研发核心部&apos;, &apos;Tony&apos;),
(&apos;财务风控部&apos;, &apos;Amy&apos;),
(&apos;市场部&apos;, &apos;Bob&apos;);

-- 形态B：不指定特定列，按表字段顺序强行插入。如果表加列该写法必报错 (极不推荐！)
-- INSERT INTO Department VALUES (&apos;行政部&apos;, &apos;Lily&apos;);

-- 形态C：查出某个表，并塞进一个已存在的结构同样的表 (INSERT INTO ... SELECT)
INSERT INTO EmployeeBackup (EmpName, Salary, DeptId)
SELECT EmpName, Salary, DeptId FROM Employee WHERE Age &amp;gt; 50;

-- 形态D：凭空创造新表并塞满数据 (SELECT ... INTO)。
-- 这种写法超级快，常用于制作一次性的数据报表镜像或跨库拉取日志：
SELECT * INTO Employee_Mirror_2025 
FROM Employee 
WHERE IsDeleted = 0;
&lt;/code&gt;&lt;/pre&gt;
&lt;h3&gt;3.2 冷酷的抹杀：Delete 与 Truncate&lt;/h3&gt;
&lt;pre&gt;&lt;code&gt;-- 条件删除 (慢，按日志逐行打标记伪删除然后物理覆盖)
DELETE FROM Employee WHERE EmpId = 1005 AND IsDeleted = 1;

-- 【面试常考】TRUNCATE 与 DELETE 对于全表清除的区别：
-- 1. Truncate 极其暴力，不写单独回滚事务日志，只释放数据页，瞬间清空几亿条数据。
-- 2. Truncate 不需要加上 WHERE。
-- 3. Truncate 会将 IDENTITY 自增标识的种子无情重置为 1 ! 
-- 4. 如果这张表被别人用来做外键参考了，由于引擎为了保护引用完整性，直接禁止你执行 Truncate。
-- TRUNCATE TABLE Employee_Mirror_2025; 
&lt;/code&gt;&lt;/pre&gt;
&lt;h3&gt;3.3 精心雕琢的更新：Update&lt;/h3&gt;
&lt;pre&gt;&lt;code&gt;-- 普通的定向涨薪
UPDATE Employee 
SET Salary = Salary * 1.05
WHERE DeptId = 1 AND Age &amp;gt; 30;

-- 【高阶联表更新】: 只通过另一张发来的核算表，去更新本体的薪资
UPDATE e
SET e.Salary = temp.NewSalary
FROM Employee AS e
INNER JOIN TempPayRaiseTable AS temp ON e.EmpId = temp.TargetEmpId
WHERE e.IsDeleted = 0;
&lt;/code&gt;&lt;/pre&gt;
&lt;hr /&gt;
&lt;h2&gt;4. DQL 王者：复杂查询、关联、子查询与窗口函数&lt;/h2&gt;
&lt;p&gt;DQL（Data Query Language）即 &lt;code&gt;SELECT&lt;/code&gt; 家族，这是占日常工作 90% 的灵魂。&lt;/p&gt;
&lt;h3&gt;4.1 基础查询、条件、模糊匹配与重命名&lt;/h3&gt;
&lt;pre&gt;&lt;code&gt;-- AS 取别名：方便前端接收。如果不想要原名，可以用 &apos;&apos; 包裹。
SELECT EmpName AS &apos;姓名&apos;, Salary AS &apos;月薪&apos; FROM Employee;

-- 强大的条件匹配与运算
SELECT * FROM Employee 
WHERE Age BETWEEN 25 AND 35             -- 【范围】等同于 &amp;gt;=25 AND &amp;lt;=35
  AND DeptId IN (1, 3, 5)               -- 【集合】只取这个数组里面的
  AND EmpName LIKE &apos;张_%&apos;                 -- 【模糊】姓张，且名字至少两个字（_代表必然有一个单字符），%代表后面的都无所谓。
  AND ManagerName IS NOT NULL;          -- 【NULL处理】SQL里比对 NULL 绝对不能用 = 号！只能用 IS

-- 排序组合分页与顶尖截取
-- 获取薪水排名前 10 的活人，如果在平薪的情况下再按照更年轻的进行排序
SELECT TOP 10 * FROM Employee 
WHERE IsDeleted = 0
ORDER BY Salary DESC, Age ASC;

-- 【SQL 2012+ 分页新主宰：OFFSET FETCH】
-- 查询出第 3 页的数据（假设每页 20 条），它完全替代了以前丑陋的 ROW_NUMBER() 分页法
SELECT * FROM Employee
ORDER BY EmpId 
OFFSET 40 ROWS           -- 跳过前 40 行 (即抛弃第 1 到 2 页)
FETCH NEXT 20 ROWS ONLY; -- 再拿出紧接着的 20 行数据 (即这才是第 3 页)
&lt;/code&gt;&lt;/pre&gt;
&lt;h3&gt;4.2 聚合的分水岭：GROUP BY 与 HAVING&lt;/h3&gt;
&lt;p&gt;一旦你使用了聚合算子：&lt;code&gt;COUNT()&lt;/code&gt;, &lt;code&gt;SUM()&lt;/code&gt;, &lt;code&gt;AVG()&lt;/code&gt;, &lt;code&gt;MAX()&lt;/code&gt;, &lt;code&gt;MIN()&lt;/code&gt;，你就进入了压缩统计模式。
&lt;strong&gt;死门规则：&lt;code&gt;SELECT&lt;/code&gt; 后面如果有独立的列名（非聚合函数），那么这个列名必须出现在 &lt;code&gt;GROUP BY&lt;/code&gt; 后面，否则立马语法报错。&lt;/strong&gt;&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;-- 统计各个部门各种性别在职的人头数、平均薪水、以及该组合下挑出的最高的那笔工资
SELECT 
    DeptId, 
    Gender,
    COUNT(1) AS &apos;TotalWorkers&apos;,   -- COUNT(1) 和 COUNT(*) 性能在现代编译器下基本无异
    AVG(Salary) AS &apos;AvgSalary&apos;,
    MAX(Salary) AS &apos;TopSalaryOutThere&apos;
FROM Employee
WHERE IsDeleted = 0               -- 【第一层过滤】WHERE，它是发生在 GROUP BY 【聚类之前】 剔除没用的散落死数据的
GROUP BY DeptId, Gender
HAVING COUNT(1) &amp;gt;= 5              -- 【第二层过滤】HAVING，它必须写在【聚类之后】，用于对刚刚压缩算好的总数进行进一步的高级筛选！
ORDER BY TotalWorkers DESC;
&lt;/code&gt;&lt;/pre&gt;
&lt;h3&gt;4.3 表与表的交织：JOIN（连接）家族全覆盖&lt;/h3&gt;
&lt;p&gt;商业系统不可能把所有字段平铺丢在一个表里（这完全违反了 3NF 数据库第三范式）。通过挂载的主外键拆分出去的表，查询时必须重构连接。&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;-- A. 内连接 (INNER JOIN)：门当户对。只会挑选在两张表中靠 ON 都能双花匹配的那些完美的数据带出来。如果某员工没有部门，他就被剔除丢弃。
SELECT e.EmpName, d.DeptName
FROM Employee e
INNER JOIN Department d ON e.DeptId = d.DeptId;

-- B. 左外连接 (LEFT JOIN / LEFT OUTER JOIN)：以和为贵，以左为尊。
-- 强制保留左表(Employee)里的每一行！哪怕右表(Department)里的那个ID刚好被删了，他左边的名字也照样出来，只是这行关于右边 DeptName 的格子会显示 NULL 补齐。
SELECT e.EmpName, ISNULL(d.DeptName, &apos;暂无安排&apos;) AS &apos;所属部门&apos; 
FROM Employee e
LEFT JOIN Department d ON e.DeptId = d.DeptId;

-- C. 右外联 (RIGHT JOIN)：与 LEFT JOIN 同理，反向保右不保左。

-- D. 全外连接 (FULL OUTER JOIN)：极其罕见。不管你俩有没有配对上，所有的行孤儿和寡母全在这一张表里合体集结，没有的数据相互铺 NULL。

-- E. 交叉连接 (CROSS JOIN)：更罕见。笛卡尔积乘法。不要带 ON。如果表 A 有 1000 人，表 B 有 10 个部门，那最后由于连出来的结果是 1000*10 = 10000 行纯组排列的数据爆炸。
&lt;/code&gt;&lt;/pre&gt;
&lt;h3&gt;4.4 子查询、CTE 与 分析利器: 窗口函数 (Window Functions)&lt;/h3&gt;
&lt;p&gt;&lt;strong&gt;传统的子查询写法有些丑陋且难读：&lt;/strong&gt;&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;-- 需要知道哪些人赚的钱超过了财务部的平均工资
SELECT EmpName, Salary 
FROM Employee 
WHERE Salary &amp;gt; (
    SELECT AVG(Salary) FROM Employee 
    WHERE DeptId = (SELECT DeptId FROM Department WHERE DeptName = &apos;财务部&apos;)
);
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;&lt;strong&gt;利用 CTE (公共表表达式) 进行化繁为简的魔法：&lt;/strong&gt;
CTE 实质上是在当前批处理运行内存中定义的一张高可读的逻辑小表，你后面可以直接拿它像用真实表一样用。&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;WITH FinanceInfo AS (
    SELECT DeptId FROM Department WHERE DeptName = &apos;财务部&apos;
),
AvgSalTable AS (
    SELECT AVG(Salary) AS FinanceAvg FROM Employee 
    WHERE DeptId = (SELECT DeptId FROM FinanceInfo)
)
SELECT e.EmpName, e.Salary, a.FinanceAvg
FROM Employee e
CROSS JOIN AvgSalTable a
WHERE e.Salary &amp;gt; a.FinanceAvg;
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;&lt;strong&gt;【核心工具】窗口函数 OVER (PARTITION BY)：避免分组查询丢失明细数据。&lt;/strong&gt;
以前做聚合，比如算个平均分，所有人的行全给压缩折叠成一行结果返回了。
加上 &lt;code&gt;OVER...&lt;/code&gt; 后，你不仅能在每行的旁边算出你要的那个区间的排位或平均分，还能保留所有详细行不被压缩消灭！&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;-- 查询所有人原本的薪水，并额外在每行的末尾，算出一个该行所处在本部门群体内部的前后大榜排名：
-- 这如果用老语法做可能要成百上千行的极其变态的临时表。
SELECT 
    EmpId, 
    EmpName, 
    DeptId, 
    Salary,
    -- ROW_NUMBER()：排名函数。
    -- PARTITION BY DeptId 说的是针对部门切分开来算，ORDER BY 说的是根据薪水降序。
    ROW_NUMBER() OVER(PARTITION BY DeptId ORDER BY Salary DESC) AS &apos;本部门内薪资排名&apos;,
    
    -- 额外在这附赠一个该组的所有人平均值，当做一个普通的列平铺在旁边作为对比！
    AVG(Salary) OVER(PARTITION BY DeptId) AS &apos;部门平均标杆&apos;
FROM Employee
WHERE IsDeleted = 0;
&lt;/code&gt;&lt;/pre&gt;
&lt;hr /&gt;
&lt;h2&gt;5. T-SQL 编程精髓：变量、控制流、内置函数与游标 (Cursor)&lt;/h2&gt;
&lt;h3&gt;5.1 批处理与内置标量函数&lt;/h3&gt;
&lt;p&gt;SQL Server 自带许多好用到哭的杀器函数。&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;
&lt;p&gt;&lt;strong&gt;字符串魔法：&lt;/strong&gt;&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;SELECT LEN(&apos;HelloWorld&apos;);            -- 提取长度: 10
SELECT SUBSTRING(&apos;abcdefg&apos;, 2, 3);   -- 万金油截取（起始点是按 1 计算的而不是 0）: 返回 &apos;bcd&apos;
SELECT CHARINDEX(&apos;T&apos;, &apos;HelloTWorld&apos;);-- 查找位置：返回 6
SELECT REPLACE(&apos;A,B,C&apos;, &apos;,&apos;, &apos;|&apos;);   -- 将逗号翻新成竖线：A|B|C
-- 去除前后空格（2017+ 支持了组合去两端：TRIM，以前是 LTRIM 和 RTRIM 结合使用）
SELECT TRIM(N&apos;   这前后的空格都能被清掉   &apos;); 
&lt;/code&gt;&lt;/pre&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;&lt;strong&gt;日期魔法：&lt;/strong&gt;&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;DECLARE @Now DATETIME = GETDATE(); -- 获取世界末端当前刻画
SELECT YEAR(@Now), MONTH(@Now), DAY(@Now);
SELECT DATEADD(MONTH, 2, @Now);      -- 增加 2 个月。你要减就是传 -2
SELECT DATEDIFF(DAY, &apos;2020-01-01&apos;, @Now); -- 计算两个日期间跨越了多少天

-- 史上最难缠也是必用的格式化：FORMAT (Sql 2012+ 引入)
SELECT FORMAT(GETDATE(), &apos;yyyy/MM/dd HH:mm:ss&apos;); -- 完全等价于 C# 的 ToString 格式
&lt;/code&gt;&lt;/pre&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;&lt;strong&gt;转换魔法 (&lt;code&gt;CAST&lt;/code&gt; 和 &lt;code&gt;CONVERT&lt;/code&gt;)：&lt;/strong&gt;&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;SELECT &apos;我有 &apos; + CAST(100 AS VARCHAR(10)) + &apos; 元&apos;;
-- CONVERT 在老式的风格下更强劲：支持第三个参数专门针对特定日期的转化规则风格码
SELECT CONVERT(NVARCHAR(20), GETDATE(), 120); -- 120是特定的 ODBC yyyy-mm-dd hh:mi:ss 格式
&lt;/code&gt;&lt;/pre&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;h3&gt;5.2 流程控制：IF-ELSE, WHILE 与 CASE 分流机制&lt;/h3&gt;
&lt;p&gt;T-SQL 完全支持条件编程体系。&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;DECLARE @TargetSalary INT =ROUND(RAND() * 20000, 0); -- 产生个随机薪资用来玩
PRINT &apos;生成的随即目标是：&apos; + CAST(@TargetSalary AS VARCHAR);

IF @TargetSalary &amp;lt; 5000
    PRINT &apos;这个数很小不影响大局。&apos;;
ELSE IF @TargetSalary BETWEEN 5000 AND 12000
BEGIN
    PRINT &apos;落入中等核算区间。&apos;;
    -- 这里可以写很长的多行 UPDATE 语句等批量核算操作
END
ELSE
    PRINT &apos;超额阈值预警！&apos;;

-- ========== CASE WHEN ... THEN ... END : 分流数据改造王者 ==========
-- 它属于查询改造级的高手。用于在一行内部，直接对输出呈现的东西做条件翻转翻译变形。
SELECT EmpName, Salary,
    CASE 
        WHEN Salary &amp;gt;= 20000 THEN &apos;企业高管&apos;
        WHEN Salary &amp;gt;= 10000 AND Salary &amp;lt; 20000 THEN &apos;中层骨干&apos;
        WHEN Salary &amp;lt; 10000 THEN &apos;基层建设者&apos;
        ELSE &apos;统计异常&apos; -- 如果连上面的条件都没拦得住的最终安全垫
    END AS &apos;薪资地位等级&apos;
FROM Employee;
&lt;/code&gt;&lt;/pre&gt;
&lt;h3&gt;5.3 在错误中涅槃：TRY...CATCH&lt;/h3&gt;
&lt;p&gt;和 C# 一样，T-SQL 的一切核心运行脚本都应该套在这个无情的结构中。它可以确保即便是发生了除 0、字段转换爆死、插入重复主键，存储过程也不会被粗暴弹红字打断抛给前端，而是交由你主动收网：&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;BEGIN TRY
    -- 声明并插入一个已经存在的名字导致主键约束大崩溃...
    INSERT INTO Department (DeptId, DeptName) VALUES (1, &apos;违规植入&apos;);
END TRY
BEGIN CATCH
    -- 所有关于这次引擎报错的核心参数都在这这这
    PRINT &apos;出现预期外的极度严重操作异常...&apos;;
    PRINT &apos;错误级别 (Severity): &apos; + CAST(ERROR_SEVERITY() AS VARCHAR);
    PRINT &apos;错误代号 (Number): &apos;   + CAST(ERROR_NUMBER() AS VARCHAR);
    PRINT &apos;错误所在的行数 (Line): &apos; + CAST(ERROR_LINE() AS VARCHAR);
    PRINT &apos;原汁原味的引擎错误红字文本: &apos; + ERROR_MESSAGE();
    
    -- 下一步，你通常可以将这些详情强行偷偷再 INSERT 入你们公司的专用日志查错临时表
END CATCH
&lt;/code&gt;&lt;/pre&gt;
&lt;h3&gt;5.4 性能毒药还是神级救火兵？游标 (Cursor) 完全解析&lt;/h3&gt;
&lt;p&gt;无论是什么 ORM 都强调&lt;strong&gt;绝不使用游标&lt;/strong&gt;。为什么？
正常情况下我们写 &lt;code&gt;UPDATE Employee SET Age = Age + 1&lt;/code&gt; 是全量内存锁运算一扫而过（集合论操作 Set-based）。属于降维打击的高速模式。&lt;/p&gt;
&lt;p&gt;但&lt;strong&gt;游标（Cursor）&lt;strong&gt;是一种死板的低级行为。它极其缓慢（由于强锁消耗、CPU高压跳转、一行一行的 Fetch 验证）。它是&lt;/strong&gt;逼迫引擎脱离集合操作，而是把你指定的几千行记录取出来，通过循环控制“第一行、第二行”，一行一行的扫过去执行！&lt;/strong&gt;&lt;/p&gt;
&lt;p&gt;通常只有极其特殊无法使用集合完成（比如：调用外部特殊的 CLR 程序接口每行交互验证一遍，或极度错杂逻辑无法汇集为一条 &lt;code&gt;JOIN UPDATE&lt;/code&gt; 时）才作为最后的底牌使用：&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;-- 定义几个临时承载内存数据的杯子
DECLARE @CurrentEmpId BIGINT;
DECLARE @CurrentSalary DECIMAL(18,2);

-- 1. 【宣告游标】告诉数据库：给我划出一片内存专门给我的遍历使用
DECLARE emp_cursor CURSOR FOR 
    SELECT EmpId, Salary FROM Employee WHERE IsDeleted = 0; 

-- 2. 【开启游标】正式在内存生成指向第一行头顶的一个虚拟只读指针
OPEN emp_cursor;

-- 3. 【提线推进】拉伸第一次：取出实际数据倾倒放进我们刚才声明好的杯子里准备计算
FETCH NEXT FROM emp_cursor INTO @CurrentEmpId, @CurrentSalary;

-- 4. 【开启死亡大循环】
-- 全局的状态变量 @@FETCH_STATUS 只要返回 0，意思就是：不仅刚才那句 FETCH 取到了货而且这底下还有活儿没干完
WHILE @@FETCH_STATUS = 0 
BEGIN
    -- 【在这写你的单行究极复杂的业务】
    PRINT &apos;当前扫描到 ID = &apos; + CAST(@CurrentEmpId AS VARCHAR) + &apos; 该兄弟工资有：&apos; + CAST(@CurrentSalary AS VARCHAR);
    
    -- ... 可以去别的什么遥远的系统拉取什么特殊的发票配置信息
    -- ... IF 一下
    -- 修改这单行特定的数据
    
    -- 【最重要的：提线向前再走一格】！如果忘了这句，你就是死循环爆服！
    FETCH NEXT FROM emp_cursor INTO @CurrentEmpId, @CurrentSalary;
END

-- 5. 【极其严重警告：必须扫尾关闭及释放】如果不执行这句不仅内存爆光整个锁全不放
CLOSE emp_cursor;
DEALLOCATE emp_cursor;
&lt;/code&gt;&lt;/pre&gt;
&lt;hr /&gt;
&lt;h2&gt;6. 模块化封装：视图 (View) 与 自定义函数 (UDF)&lt;/h2&gt;
&lt;p&gt;为了解决“每天有一百个地方写着同样的三百行超级连表 &lt;code&gt;JOIN&lt;/code&gt; 和冗长聚合”，T-SQL 提供了优雅的复用解决方案。&lt;/p&gt;
&lt;h3&gt;6.1 视图 (View) 的美学&lt;/h3&gt;
&lt;p&gt;就像透过特定角度切割的一扇窗。它是一张虚拟存在的影子表，本身一般不在硬盘存储实体。&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;-- CREATE VIEW：给这个虚假的表定制造型组合
CREATE VIEW v_SeniorEmployeesWithDept
AS
SELECT 
    e.EmpId, e.EmpName, e.Salary,
    d.DeptName
FROM Employee e
INNER JOIN Department d ON e.DeptId = d.DeptId
WHERE e.Age &amp;gt; 40 AND e.IsDeleted = 0;
GO

-- 前端或是其他开发人员使用时，极其整洁。
-- 绝大多数情况下，不要去试图对视图底层执行 INSERT、UPDATE，哪怕有些特殊配置允许这样做。它只是一种用来查询的数据隔离防盗门。
SELECT * FROM v_SeniorEmployeesWithDept;
&lt;/code&gt;&lt;/pre&gt;
&lt;h3&gt;6.2 用户自定义函数 (UDF: User Defined Function) 深度解密&lt;/h3&gt;
&lt;p&gt;如果说视图是一张固定的窗，那么函数就是个可输入参数然后制造返回值的黑盒小机器。&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;分为两类：&lt;/strong&gt;&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;&lt;strong&gt;标量函数 (Scalar Function)&lt;/strong&gt;：返回一枚标量单一值。（如字符串、整形、布尔时间等）。可以在所有 &lt;code&gt;SELECT&lt;/code&gt; 列内部和 &lt;code&gt;WHERE&lt;/code&gt; 后方完美当做工具人拼合。&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;表值函数 (Table-Valued Function)&lt;/strong&gt;：它返回的一整个数据集就像表格一样的变量，甚至还能带内联运算，调用时把它直接放在 &lt;code&gt;FROM OOOO()&lt;/code&gt; 后面做连表或者展示。由于能接收参数灵活性极高，在老项目开发中被称为参数化大视图！&lt;/li&gt;
&lt;/ol&gt;
&lt;p&gt;&lt;strong&gt;注意：UDF 内不能执行带有副作用的物理操作！（即里面不能出现任何导致数据真实落地的增删改，哪怕是创建真实的临时物理表都不行）。&lt;/strong&gt;&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;-- 标量函数范例：输入入职日期，给你吐出现实算出的“工龄”数字
CREATE FUNCTION fn_CalculateWorkYears (@JoinDate DATETIME)
RETURNS INT
AS
BEGIN
    RETURN DATEDIFF(YEAR, @JoinDate, GETDATE());
END;
GO

-- 外部愉快调用: (在调用标量时引擎有个死规矩: 一般都得加上系统所属名 dbo.)
SELECT EmpName, dbo.fn_CalculateWorkYears(JoinDate) AS &apos;WorkYears&apos; FROM Employee;

-------------------------------------------------------------------------------------------------

-- 内联表值函数 (TVF) 范例：接收一个薪资要求，只找出超过薪资的完整多列集合结构返回
CREATE FUNCTION fn_GetHighPaidWorkers (@Threshold DECIMAL(18,2))
RETURNS TABLE
AS
RETURN 
(
    SELECT EmpId, EmpName, Salary 
    FROM Employee 
    WHERE Salary &amp;gt;= @Threshold AND IsDeleted = 0
);
GO

-- 外部把它当做一个需要带进去门票的高端虚表来用:
SELECT * FROM fn_GetHighPaidWorkers(20000.00); 
&lt;/code&gt;&lt;/pre&gt;
&lt;hr /&gt;
&lt;h2&gt;7. 性能核心：存储过程 (Stored Procedure) 与动态 SQL&lt;/h2&gt;
&lt;p&gt;这是真正的性能之王。你所有的核心高频调用业务逻辑几乎都最终会被打包封存在这。
它是一大段具有完整 C# 功能逻辑特征（带事务带游标带判断带报错捕捉带返回结构）的大型执行体。&lt;/p&gt;
&lt;p&gt;而且它被第一次建立和运行的时候，引擎会把对里面成千上百个表交织算好的最优解路线图存到内部内存之中（&lt;strong&gt;执行计划缓存 Execution Plan Caching&lt;/strong&gt;）。
下次即便是换了传入参数，引擎不会再从零开始扫描计算这长篇大论，直接挂着最优图谱极速出片完成。相比把长串 SQL 字符通过 C# ORM 无尽传输有不可磨灭的低延时优势。&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;-- 我们来写一个标准的带全套操作特性的高阶存储过程
CREATE PROCEDURE sp_HandleEmployeeBonus
    @InDeptId INT,                        -- 接收普通的传递进来的参数
    @InBaseBonus DECIMAL(18,2),
    @OutTotalGrantedBonus DECIMAL(18,2) OUTPUT -- 输出型特殊参数（做完事之后带出给调用方读取的）
AS
BEGIN
    SET NOCOUNT ON; -- 切忌别漏了！让引擎别在那浪费网速在那傻发&quot;(影响 1 行)&quot; 这种极其占用 IO 流量返回无用信息包了。

    -- 初始化输出总金额计数
    SET @OutTotalGrantedBonus = 0;

    BEGIN TRY
        -- 进行逻辑筛选，没有必要进入修改循环了就快速阻断。
        IF NOT EXISTS(SELECT 1 FROM Employee WHERE DeptId = @InDeptId AND IsDeleted = 0)
        BEGIN
            PRINT &apos;该部门一个活人都没有，终止发奖！&apos;;
            RETURN;
        END
        
        -- 计算总奖金额给外围
        SELECT @OutTotalGrantedBonus = SUM(Salary * 0.1M + @InBaseBonus) 
        FROM Employee WHERE DeptId = @InDeptId;

        -- 执行批量的真切物理修改！
        UPDATE Employee 
        SET Salary = Salary + (Salary * 0.1M + @InBaseBonus)
        WHERE DeptId = @InDeptId AND IsDeleted = 0;
        
        PRINT &apos;存储过程执行完成发薪完毕&apos;;
    END TRY
    BEGIN CATCH
        -- 【此处本应写更高级别的回滚报错收集代码，请参考后续事务篇章】
        PRINT &apos;执行严重失败! &apos; + ERROR_MESSAGE();
        SET @OutTotalGrantedBonus = -1;
    END CATCH
END;
GO

-- ================= C# 或客户端调用它的模拟过程 ==================
DECLARE @ResultTotalBonus DECIMAL(18,2); -- 建一个参数准备接收

-- EXEC 指令挂接大名然后传入匹配字典名称
EXEC sp_HandleEmployeeBonus 
    @InDeptId = 1, 
    @InBaseBonus = 500.00, 
    @OutTotalGrantedBonus = @ResultTotalBonus OUTPUT; -- 这里这个特性的对应不能忘写 OUTPUT

SELECT &apos;本次发放到账总数计算报告为：&apos; + CAST(@ResultTotalBonus AS VARCHAR);
&lt;/code&gt;&lt;/pre&gt;
&lt;h4&gt;高阶：动态抛出拼接的 SQL 语句 (Dynamic SQL)&lt;/h4&gt;
&lt;p&gt;有时候，我们的列名表名，甚至查询条件极度错综，比如用户在一个报表上选了二十个复选框和五十个维度组合，很难用静态查出。必须要在存储过程中执行靠字符串组合运算出的“临时变量中的 SQL 语句片段”。&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;-- 核心使用自带专杀拼接的系统子过程： sp_executesql （千万别用直接的 EXEC(@string) 因为没有执行防注入化）
DECLARE @DynamicSQL NVARCHAR(MAX);
DECLARE @TableName NVARCHAR(50) = &apos;Employee&apos;; -- 甚至连表名都是用户界面传进来的字符串
DECLARE @SearchName NVARCHAR(50) = &apos;Tony&apos;;

-- 极度惊心动魄地利用变量凑出了这段纯文本 T-SQL 字符串
SET @DynamicSQL = N&apos;SELECT * FROM &apos; + QUOTENAME(@TableName) + N&apos; WHERE EmpName = @NameParam&apos;;

-- 直接通过系统底层命令运行这行字符串，并且安全地把内嵌参数的引用抛过去建立防注入防弹衣！
EXEC sp_executesql 
    @stmt = @DynamicSQL, 
    @params = N&apos;@NameParam NVARCHAR(50)&apos;, 
    @NameParam = @SearchName;  -- 给那个抛过去的参数填充真的子弹内容
&lt;/code&gt;&lt;/pre&gt;
&lt;hr /&gt;
&lt;h2&gt;8. 数据库的心脏：事务 (Transaction) 与 4大隔离级别(锁)&lt;/h2&gt;
&lt;p&gt;**ACID（原子性 Atomicity、一致性、隔离性 Isolation、持久性 Durability）&lt;strong&gt;是一切关系型数据库信仰体系中万神殿的神祗准则。
当你处理转账、处理淘宝买东西减去库存增加物流这种&lt;/strong&gt;不可分割的任务包裹（Transaction）**时候，必须要对数据库显式下达 “开启全系统级一致性锁控回滚保护”。要么全部这十三张表修改完成，要么有一张表出错整个这一摊子全给我撤销复原！&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;CREATE PROCEDURE sp_SecureMoneyTransfer
    @FromAcc INT,
    @ToAcc INT,
    @Amount DECIMAL(18,2)
AS
BEGIN
    SET NOCOUNT ON;
    
    -- 1. 显性宣告：锁控护卫系统启动 (从这一秒开始执行的修改在没有走到底下提交时都是悬在锁里锁死的，其他人想读这些行的内容统统被阻挡冻结在门外排队！)
    BEGIN TRANSACTION;

    BEGIN TRY
        -- 判断 A 的钱够不够
        IF (SELECT Balance FROM Accounts WHERE Id = @FromAcc) &amp;lt; @Amount
            THROW 50001, &apos;余额绝对不够啦&apos;, 1; -- THROW 是现代 T-SQL 版直接把程序砸懵强制把代码跳转扔进下方 CATCH 的抛砖神技
            
        -- A 扣款
        UPDATE Accounts SET Balance = Balance - @Amount WHERE Id = @FromAcc;
        -- B 入账 (此时突然 B 账户由于某人手残加的外键约束问题直接爆掉了红色异常崩溃报错了！！)
        UPDATE Accounts SET Balance = Balance + @Amount WHERE Id = @ToAcc;

        -- 如果天下太平没出什么幺蛾子，那么恭喜全部放行写入物理介质真正提交完本！
        COMMIT TRANSACTION;
        PRINT &apos;转账全链路成功结束。&apos;;
    END TRY
    BEGIN CATCH
        -- 【这是事务中最核心保护的一课】刚才如果 B 账户代码由于引擎级错误直接崩溃到了这里被你兜住，之前那一步被扣了钱的 A 的可怜的钱怎么救回来？
        
        -- @@TRANCOUNT 这个全局变量如果大于 0 意味着 “当前身上还挂着个已经开始但没收摊的锁包”
        IF @@TRANCOUNT &amp;gt; 0
        BEGIN
            -- 无情撤回：时间倒流，数据库所有之前被锁死的这批修改全部完美复原回它什么事都没发生之前的最初的样子。并将加锁阻塞池清空释放其他人。
            ROLLBACK TRANSACTION; 
        END
        
        PRINT &apos;极度危机！事务已经熔断回滚。具体错误报告：&apos; + ERROR_MESSAGE();
    END CATCH
END;
&lt;/code&gt;&lt;/pre&gt;
&lt;h4&gt;并发问题与四大隔离级别深度干解：&lt;/h4&gt;
&lt;p&gt;当你高并发下，同时有个事务A和事务B对同一批数据狂轰滥炸操作。
你需要自己手动向引擎输入指令告诉他采取多强度的看守大门措施。这就是隔离级别调整（&lt;code&gt;SET TRANSACTION ISOLATION LEVEL XXX&lt;/code&gt;）。&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;&lt;strong&gt;READ UNCOMMITTED (未提交读)：最垃圾不要脸的裸奔。&lt;/strong&gt; A 事务对某行刚写了个数据但还没敲定 &lt;code&gt;Commit&lt;/code&gt;，甚至极大概率两秒之后他就直接不要给撤销 &lt;code&gt;RollBack&lt;/code&gt; 了，在此期间 B 事务跑过来查这种悬在半空的鬼魂数据，竟然也能查出来并且拿去使用了。&lt;strong&gt;引发的核心灾难现象：【脏读 Dirty Read】&lt;/strong&gt;。适用于仅仅只要求读取快比如查询总用户统计排行榜这种完全不敏感的地方。&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;READ COMMITTED (提交读) [SQL SERVER 的默认默认天尊]：&lt;/strong&gt; 它堵上了脏读的漏洞，A只要没 &lt;code&gt;Commit&lt;/code&gt; 数据就是上锁绝对不可视的。只有 &lt;code&gt;Commit&lt;/code&gt; 落定的真实数据才能被 B 取到读出。但有**【不可重复读】**的现象：就是在一个极长的查询 B 事务里，刚查了一遍这个人的名字叫 “张三”，转头做别的事做了十秒此时有个叫 A 的修改线程刚好冲过来把它名字改成“李四”了。当这个 B 事务中第二句再次回头确认这人的名字时，会惊异发现在我同一个事务里面，查同个东西怎么变性了！&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;REPEATABLE READ (可重复读)：&lt;/strong&gt; 只要是 B 所在的查询事务在查的数据行，他引擎就给加死了一把&lt;strong&gt;读取不许改长串护城锁&lt;/strong&gt;。就是 B 只要在查询期不结束这数据，A 这个时候想跑过来修改成李四直接被卡死排队堵在门外永远无法执行修改，直到 B 自己查完所有的事撤除长锁放行。彻底保护了 B 同一轮里面无论什么时候回头再查，叫“张三”的人绝对不可能被人偷改变成别人的现象。但挡不住 A 在旁边利用空间凭空给总库里**&lt;code&gt;INSERT&lt;/code&gt; 插入新加了一个全新的人**。这就叫诡异的**【幻读 Phantom Read】**。&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;SERIALIZABLE (可串行化极限绝密锁)：性能坟墓。&lt;/strong&gt; 凡是对这堆符合某区间要求的几十万行表甚至大面积块查的过程中，不仅不准你进去改已存在的数据，你也休想在这个这片区域给我增加新插入一条符合这个区间的新数据！隔离级别全宇宙满点最强，并发性能全宇宙卡成死猪！用于极严苛的特殊计费汇总强锁定节点！&lt;/li&gt;
&lt;/ol&gt;
&lt;hr /&gt;
&lt;h2&gt;9. 隐形看门狗：触发器 (Trigger) 深度剖析&lt;/h2&gt;
&lt;p&gt;触发器就是个隐藏很深的特殊形式的存储过程。当有人对某表做了它暗中监视的 &lt;code&gt;INSERT / UPDATE / DELETE&lt;/code&gt; 动作后，根本不用人显式调用它，系统会强行在主查询锁结束的刹那&lt;strong&gt;静默召唤出它的伴生执行附体&lt;/strong&gt;。&lt;/p&gt;
&lt;p&gt;虽然现代系统出于降低复杂排查链路难度不再提倡滥用业务触发器。但它是做&lt;strong&gt;防核心删除日志兜底审计的最佳组件&lt;/strong&gt;。&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;CREATE TRIGGER trg_EmployeeDeleteAudit
ON Employee
AFTER DELETE -- 表明只有 Delete 指令发出确认并干掉行后，我狗子立刻就会激活扑出来
AS
BEGIN
    -- 【特殊系统表】：inserted (存放修改后的新数据)， deleted (存放已被覆盖的老数据或被删数据)。这两个虚拟表仅在触发器执行的上下文中短暂存在。
    
    -- 本触发器监视的明明就是把几十个离职人员强制从数据库干除这个命令！所以引擎给我的魔法表 deleted 中这会装满了那整整被杀的五十个人员信息。
    
    -- 趁还没来得及消失的零点几秒，在后门利用这个删留下的亡语把所有的尸体信息悄悄塞给归档备忘录表
    INSERT INTO Employee_DismissedArchive (EmpId, Name, SalaryWas, DeleteTime)
    SELECT
        EmpId,
        EmpName,
        Salary,
        GETDATE()     -- 利用这种关联式神速复制技巧进行暗中的记录
    FROM deleted;
    
    PRINT &apos;离职人员清理监控：底层数据库核心档案移交已通过无形触发器执行完成。&apos;;
    -- 请注意如果内部这种日志移交业务本身报错了，刚才的那批在界面发生的前端删除动作也会因发生全连锁雪崩全盘倒退被强制撤回。这就是同属一条底层锁血命的好处和拖累坏处所在。
END;
&lt;/code&gt;&lt;/pre&gt;
&lt;hr /&gt;
&lt;h2&gt;10. 极致调优：索引 (Index) 的聚簇本质与执行计划调优&lt;/h2&gt;
&lt;p&gt;当在极其庞大的数据表（例如数亿级别）执行 Select 查询遇到由于全表扫描引发的严重性能降级时，首要的优化手段通常是——&lt;strong&gt;分析执行计划并增加有效的索引&lt;/strong&gt;。&lt;/p&gt;
&lt;p&gt;不加索引扫表，在计算机领域的代号叫：&lt;strong&gt;Table Scan (无差别的全表瞎扫)&lt;/strong&gt;。即使引擎拥有八百个计算核心也会在这里翻船。&lt;/p&gt;
&lt;h3&gt;10.1 聚集决定此生的物理身躯 (Clustered Index)&lt;/h3&gt;
&lt;ul&gt;
&lt;li&gt;&lt;strong&gt;实质&lt;/strong&gt;：一张表的聚集索引只有一个！因为它只能代表这本超级巨型的 5 亿字典里面的这几页究竟是按照第一页往后真实怎么用纸张按照什么顺序去装订叠放的物理身位图谱。而且这个底层数据结构是一个 B+Tree（二叉平衡演进多叉大树）。&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;触发原因&lt;/strong&gt;：一旦你指明哪一列是唯一主键标记为强约束 （&lt;code&gt;PRIMARY KEY&lt;/code&gt;），引擎它就是顺水推舟在建库一瞬间自动帮你把聚集索引也同时安放挂在这上面。而且通常由于系统建议大家主键都是利用递增连续增加数字 &lt;code&gt;IDENTITY(1,1)&lt;/code&gt;，所以底层的数据就像排队打卡的人一样一个个紧挨在屁股后头极其规整舒适没有碎片（Page Split 爆裂）。如果一旦哪个无知开发把主键设置成随意字母无规律生成的 &lt;code&gt;GUID&lt;/code&gt;。由于排序强制插入中间，那底层的聚集这棵物理树就如同被炸弹不停重排。&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;查询恐怖威力&lt;/strong&gt;：查找极其快，比如你的员工号（主键），找主键=五千万员工号，只需顺树跳跃几下磁盘IO就命中了直接掏出，这就叫极速的 &lt;strong&gt;Clustered Index Seek（深水炸弹精准寻址）&lt;/strong&gt;，相比 Table Scan 百慢倍率的差距！！&lt;/li&gt;
&lt;/ul&gt;
&lt;h3&gt;10.2 后天辅挂的额外地图旁支 (Non-Clustered Index 非聚集索引)&lt;/h3&gt;
&lt;ul&gt;
&lt;li&gt;&lt;strong&gt;实质&lt;/strong&gt;：比如我们总是在 WHERE 里面搜索员工邮箱 （Email），这显然它可不是主键物理表不是按它排列的。于是你需要再外挂新建一本辅书小日记！日记里面只有按照 a-z 清清楚楚首字母排序的一千万单列清爽的所有的“邮件列表结构拼音字符”，并给每个拼音字符后面拿一根针挂上线连接告诉系统这个邮箱是其实是存放在那本大物理书的哪个具体的 8KB 长条形抽屉（Row Pointer 地址关联）。&lt;/li&gt;
&lt;li&gt;**查询过程现象：**先神速翻小日记直接命中（&lt;strong&gt;Index Seek 非聚集深层寻读&lt;/strong&gt;）--&amp;gt; 但我光拿个邮箱又没有这人他长什么样的全部别的数据资料啊 --&amp;gt; 去利用末尾那根关联线返回原主聚集大表直接抽走（这个耗时稍微有点点长的经典过程绝招，它术语被称之为：&lt;strong&gt;Key Lookup！也就是神龙脱骨折返查表（RID/键查找）&lt;/strong&gt;）的致命问题。&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;怎么建立这本强力辅助字典：&lt;/strong&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;pre&gt;&lt;code&gt;-- 建树！将极大提高所有带 WHERE Email = &apos;?&apos; 及 ORDER BY Email 结尾相关操作的十万倍性能
CREATE NONCLUSTERED INDEX IX_Employee_EmailSearchLookup 
ON Employee (Email ASC);

-- 再建树！！如果既搜邮箱又搜部门
CREATE NONCLUSTERED INDEX IX_Employee_MailWithDeptCombiner
ON Employee (Email ASC, DeptId DESC);

-- 这句话查询立马变得顺滑：
-- SELECT * FROM Employee WHERE Email = &apos;jack@exp.net&apos; AND DeptId = 5;
&lt;/code&gt;&lt;/pre&gt;
&lt;h3&gt;10.3 “覆盖索引术” (Covering Index) (防 Lookup 原大表回表绝杀技)&lt;/h3&gt;
&lt;p&gt;假如有个 API，它经常查邮箱只求一并带出它的电话。但由于我只建了一个只包含单独邮件这一条树分支的小日记表索引。上面提到我要去翻那本原体大书（去发费极度高昂磁盘消耗利用 &lt;code&gt;Key Lookup&lt;/code&gt; 去原表查出附加电话字段）。
如果能够建立的时候&lt;strong&gt;只把电话这一字段强行也顺便复印在每个邮箱小日记索引项的结尾不就能免回原表大抽查了吗！&lt;/strong&gt;。这种专门克制回表带附属随从的小技巧，被称为&lt;strong&gt;包含列 (INCLUDE)&lt;/strong&gt; 加持覆盖。&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;-- 把这招砸向上面刚才建小树的操作进行覆写 (WITH DROP_EXISTING 高级重建)
CREATE NONCLUSTERED INDEX IX_Employee_EmailSearchLookup
ON Employee (Email ASC)
INCLUDE (Phone, Status) -- 注意！不要包含几百个字段的大肉块。这非常增加平时插入数据的拖慢开销（因为增删需要时间更新你的这本超重的日记）
WITH (DROP_EXISTING = ON);

-- 从此以后如下这行极为常见高发频的接口底层查询，因为他不仅通过 Seek 闪电找到了邮件定位区间，所有顺带要求的这俩参数也就在它挂件上一起存在直接带回：
-- 这次他这查询再也不会发生任何一次 Key Lookup 原表提取物理抽拉拖累：性能达到了全网理论封顶极限水平！
-- SELECT Phone, Status FROM Employee WHERE Email = &apos;jack@exp.net&apos;;
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;对于索引的极致把控是在一个表上不多建一个垃圾索引，也绝不漏掉一个有几十万查询量 &lt;code&gt;Join/Where&lt;/code&gt; 支撑的核心复合查询带包含索引，这考验着高级数据库选手的顶级功底经验。&lt;/p&gt;
&lt;hr /&gt;
&lt;h2&gt;11. 高阶查询艺术：CTE 递归、PIVOT 行列转换与 CROSS/OUTER APPLY&lt;/h2&gt;
&lt;p&gt;真正的报表查询和复杂运算往往超出了常规 &lt;code&gt;JOIN&lt;/code&gt; 的能力范畴，我们需要极其高段位的操作手段。&lt;/p&gt;
&lt;h3&gt;11.1 无限套娃：递归 CTE (Recursive CTE)&lt;/h3&gt;
&lt;p&gt;在构建菜单树、组织架构图、评论盖楼等具有“无限层级引用”的场景中，传统的自连接无法穷尽所有的层级。T-SQL 的递归 CTE 可以完美地通过一次查询把所有层深的树状图全部遍历拉出。&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;-- 测试用：创建一个自己引用自己的员工上下级树表
-- CREATE TABLE OrgChart (EmpId INT, EmpName NVARCHAR(50), BossId INT NULL);
-- INSERT INTO OrgChart VALUES (1, &apos;大老板&apos;, NULL), (2, &apos;销售总监&apos;, 1), (3, &apos;研发总监&apos;, 1), (4, &apos;销售经理A&apos;, 2), (5, &apos;前端程序员&apos;, 3);

-- 使用递归 CTE 拉出包含树层级和全部从属路线的高级结果集
WITH RecursiveOrgCTE AS 
(
    -- 1. [锚点成员] 定位起点：也就是找到所有根本没有上司的顶级大老板作为第 1 级
    SELECT EmpId, EmpName, BossId, 1 AS TierLevel, CAST(EmpName AS NVARCHAR(MAX)) AS &apos;Chain&apos;
    FROM OrgChart 
    WHERE BossId IS NULL

    UNION ALL 

    -- 2. [递归成员] 不断自我循环：拿着上面的结果 CTE 的表名 (RecursiveOrgCTE) 去内部连表！
    SELECT o.EmpId, o.EmpName, o.BossId, 
           cte.TierLevel + 1 AS TierLevel, -- 层级逐级递增
           cte.Chain + &apos; -&amp;gt; &apos; + o.EmpName AS &apos;Chain&apos; -- 把名字像珠子一样顺势串起来
    FROM OrgChart o
    INNER JOIN RecursiveOrgCTE cte ON o.BossId = cte.EmpId -- 将员工的 BossId 指向上一步拿出来的老大的 EmpId
)
SELECT * FROM RecursiveOrgCTE
ORDER BY TierLevel ASC;
&lt;/code&gt;&lt;/pre&gt;
&lt;h3&gt;11.2 PIVOT 与 UNPIVOT：数据透视表行列魔术&lt;/h3&gt;
&lt;p&gt;当我们接到 DBA 需求，要求把“竖着存储的各月份销售额清单”硬生生用 SQL 语句变成“每一个月份变成一个单独的一字排开的动态横向列头”时（常用于财务年终导出报表），&lt;code&gt;PIVOT&lt;/code&gt; 是绝对王者。&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;-- 原始表大概长这样： 销售员(Name) | 月份(Month) | 业绩(Amount) [是竖向无穷多行的]
-- 希望输出长这样：  | Name | 1月 | 2月 | 3月 | 4月 |

SELECT * FROM 
(
    SELECT Name, Month, Amount 
    FROM SalesRecord 
    WHERE Year = 2025
) AS SourceTable -- 原片基础提取集
PIVOT 
(
    -- PIVOT 魔术引擎启动：把所有相同的 Month 下的 Amount 进行挤压求和
    SUM(Amount) 
    -- 强行把原本作为内容存在的 [1月, 2月] 数据值变成了真正的列名字母！
    FOR Month IN ([1月], [2月], [3月], [4月], [5月], [6月]) 
) AS PivotTable;
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;&lt;em&gt;(注意：反向操作则是使用 &lt;code&gt;UNPIVOT&lt;/code&gt;，将横向的宽表炸毁还原成符合第一范式的无穷纵列表。)&lt;/em&gt;&lt;/p&gt;
&lt;h3&gt;11.3 APPLY 算子：CROSS APPLY 与 OUTER APPLY&lt;/h3&gt;
&lt;p&gt;很多时候我们需要在连表时（比如通过 &lt;code&gt;JOIN&lt;/code&gt; 某一个&lt;strong&gt;返回表格结构结果的函数 TVF&lt;/strong&gt;），把主外表某一列带入进后面那张函数表当做条件参数，&lt;code&gt;JOIN&lt;/code&gt; 根本做不到，这时候必须动用 &lt;code&gt;APPLY&lt;/code&gt;。&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;strong&gt;&lt;code&gt;CROSS APPLY&lt;/code&gt;&lt;/strong&gt;: 相当于 &lt;code&gt;INNER JOIN&lt;/code&gt; 的强化版，左边找不到右边的匹配就整行丢弃。&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;&lt;code&gt;OUTER APPLY&lt;/code&gt;&lt;/strong&gt;: 相当于 &lt;code&gt;LEFT JOIN&lt;/code&gt; 的强化版，就算函数没返回，左边主表数据依然而然健在，并把右表空白全铺 &lt;code&gt;NULL&lt;/code&gt;。&lt;/li&gt;
&lt;/ul&gt;
&lt;pre&gt;&lt;code&gt;-- 获取每个部门下，工资排名稳居前两名（Top 2）的绝顶高手的资料，并拼接在一起。
-- 这个用单纯的 JOIN 是极其难写的，有了 APPLY 以及传递参数就变得极为简单：

SELECT d.DeptName, TopEmp.EmpName, TopEmp.Salary
FROM Department d
CROSS APPLY 
(
    -- 每从外部读取到一条 Department 部门信息，执行器都会携带它的 d.DeptId 钻进这个子盒子里执行一遍这个拿 Top 2 的独立运算！
    SELECT TOP 2 EmpName, Salary 
    FROM Employee e 
    WHERE e.DeptId = d.DeptId AND e.IsDeleted = 0
    ORDER BY Salary DESC
) AS TopEmp;
&lt;/code&gt;&lt;/pre&gt;
&lt;hr /&gt;
&lt;h2&gt;12. 高阶数据操纵：MERGE（Upsert）与 临时表/表变量体系&lt;/h2&gt;
&lt;h3&gt;12.1 MERGE 语句：处理 Upsert 操作（有则更新，无则插入）&lt;/h3&gt;
&lt;p&gt;早期在处理“数据存在则 &lt;code&gt;UPDATE&lt;/code&gt;，不存在则 &lt;code&gt;INSERT&lt;/code&gt;”这样的需求时，通常需要分多步执行逻辑判断。而 &lt;code&gt;MERGE&lt;/code&gt; 语句提供了一种标准、高效的方式，可以直接在一个操作单元中将源表的数据比对并覆盖到目标表中。&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;-- 常用于定时同步任务，将昨天的销售新流水增量集合（Source）合并贴死到主库汇总表（Target）里
MERGE INTO MainSummaryTable AS target
USING DailyImportTable AS source 
    ON target.UniqueId = source.UniqueId
WHEN MATCHED THEN
    -- 如果找到了两边共有的身份证 ID，就做定向刷新
    UPDATE SET target.TotalSales = target.TotalSales + source.LastSales,
               target.LastUpdate = GETDATE()
WHEN NOT MATCHED BY TARGET THEN 
    -- 目标主机上根本没这个人，判定为纯全新增加新员工
    INSERT (UniqueId, TotalSales, LastUpdate) 
    VALUES (source.UniqueId, source.LastSales, GETDATE())
WHEN NOT MATCHED BY SOURCE THEN
    -- 甚至支持反向处理：如果是主库里有但新进表里反而消失了的人员（可能离职了），在此地强行打上死亡标记
    UPDATE SET target.IsActive = 0; 
    -- (注意：最后必须跟上一个英文半角分号)
&lt;/code&gt;&lt;/pre&gt;
&lt;h3&gt;12.2 #Temp 临时表 vs @Table 表变量：生命周期的抉择&lt;/h3&gt;
&lt;p&gt;SQL Server 提供了两种短命数据表容器，了解差异能大幅减少内存耗尽并极大提高几万数据处理时的性能：&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;
&lt;p&gt;&lt;strong&gt;本地临时表 (以 &lt;code&gt;#&lt;/code&gt; 开头，如 &lt;code&gt;#TempOrders&lt;/code&gt;)&lt;/strong&gt;
存在于系统的 &lt;code&gt;tempdb&lt;/code&gt; 硬盘块（部分驻留内存）。
&lt;strong&gt;绝对优势&lt;/strong&gt;：你可以为临时表建立多栏的高级&lt;strong&gt;物理聚集索引&lt;/strong&gt;（Primary Key）和统计信息字典。在大几十万数据量级时使用它进行二次 &lt;code&gt;JOIN&lt;/code&gt; 过滤是极为靠谱的快操作。它的生命周期伴随你的那个调用 Session 结束自动火葬销毁。&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;&lt;strong&gt;表变量 (以 &lt;code&gt;@&lt;/code&gt; 声明，如 &lt;code&gt;DECLARE @TblOrders TABLE (...)&lt;/code&gt;)&lt;/strong&gt;
纯粹生存在当前批处理进程极易挥发的直接内存中。
&lt;strong&gt;绝对优势&lt;/strong&gt;：它甚至不发生写锁、不写大量的回滚事务日志负担。但&lt;strong&gt;缺点极其严重&lt;/strong&gt;：它没有统计信息指引图谱！优化器看到一亿条的表变量也会傻乎乎把它估算成了&lt;strong&gt;只有 1 行数据&lt;/strong&gt;。如果在大型关联时用它，极容易逼得执行计划错乱死锁崩溃。适合存储不到 1 百或者几百条的超小型临时数组 ID。&lt;/p&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;pre&gt;&lt;code&gt;-- 1. 表变量：轻盈小巧 (不用 DROP)
DECLARE @LocalIdList TABLE (Id INT PRIMARY KEY, Val NVARCHAR(20));
INSERT INTO @LocalIdList VALUES (1, &apos;A&apos;), (2, &apos;B&apos;);

-- 2. 临时物理表：能抗下几十万数据大旗 (用完最佳习惯还是手工 DROP TABLE #HeavyDataTable 释放硬盘)
CREATE TABLE #HeavyDataTable (
    OrderNo VARCHAR(50) NOT NULL PRIMARY KEY INDEX IX_OrderTree, -- 直建树状索引！
    Amt DECIMAL(18,2)
);
INSERT INTO #HeavyDataTable SELECT OrderNo, Amount FROM OldHistory WHERE CreateY = 2025;
&lt;/code&gt;&lt;/pre&gt;
&lt;hr /&gt;
&lt;h2&gt;13. 极客防身术：随心所欲的动态 SQL 与执行提示 (Query Hints)&lt;/h2&gt;
&lt;h3&gt;13.1 掌控优化器大脑：查询提示 Query Hints&lt;/h3&gt;
&lt;p&gt;我们深知 SQL 是一门“说明你想干什么，路线图让底下的老爷爷（执行器）自动计算”的声明语句。
但某些特殊情况底下老爷爷确实脑抽了导致了极度荒谬的极慢的死锁路线，我们可以利用语句末尾的 &lt;code&gt;OPTION&lt;/code&gt; 强制进行执行级别的脑干劫持干预。&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;strong&gt;&lt;code&gt;WITH (NOLOCK)&lt;/code&gt;&lt;/strong&gt;: 用于打破共享锁等机制。在查询语句后添加该提示，指示引擎无需申请共享行锁或页锁。虽然非常容易产生“脏读”（读取未提交的修改），但在统计允许误差范围的基础报表查询时，它可以极大改善并发和检索性能。&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;&lt;code&gt;OPTION (RECOMPILE)&lt;/code&gt;&lt;/strong&gt;: 解决“参数嗅探（Parameter Sniffing）”问题的有效方式。如果存储过程曾由于特殊参数而缓存了一套低效的执行计划，在此命令的干预下引擎会在每次执行时&lt;strong&gt;丢弃原缓存计划并根据当次参数重新编译最佳查询路线&lt;/strong&gt;。&lt;/li&gt;
&lt;/ul&gt;
&lt;pre&gt;&lt;code&gt;-- 强行穿刺读取那些正在被执行 UPDATE 锁着的数据项 (极其不安全，但极快无阻塞)
SELECT COUNT(1) FROM HeavyLogs WITH (NOLOCK) WHERE CreateDate &amp;gt; &apos;2025-01-01&apos;;

-- 当天重新强制分析新传入的参数 @DeptId，废除曾经该存储过程缓存的全部老式查询路线规划
SELECT * FROM Employee WHERE DeptId = @DeptId 
OPTION (RECOMPILE); 
&lt;/code&gt;&lt;/pre&gt;
&lt;hr /&gt;
&lt;p&gt;至此，关于所有 T-SQL 从开辟疆土的基础核心，讲到了索引内聚并发锁，甚至是一网打尽了 &lt;code&gt;CTE 递归&lt;/code&gt;、&lt;code&gt;MERGE 合体&lt;/code&gt;、&lt;code&gt;PIVOT 数据透视&lt;/code&gt; 以及强制突破优化器的脑干封印指令 &lt;code&gt;Query Hints&lt;/code&gt;。整篇笔记已然升华为超强火力压制的万字长篇大论封顶句号。&lt;/p&gt;
&lt;p&gt;能够完全消化、吸收并能真切地在 C# 代码及数据库内部实施以上防线技术的开发者，已经具备在复杂的大型高并发、高可用电商及后端中枢业务生态体系下，扛下绝对首席核心关系型数据库架构以及深度调优专家的技术水准水准了！&lt;/p&gt;
</content:encoded></item><item><title>Docker 实践指南</title><link>https://meteor-comet.github.io/posts/docker-complete-guide-2023/</link><guid isPermaLink="true">https://meteor-comet.github.io/posts/docker-complete-guide-2023/</guid><description>容器化 / 虚拟化 / 部署 / DevOps / 云原生</description><pubDate>Fri, 30 Jun 2023 00:00:00 GMT</pubDate><content:encoded>&lt;h2&gt;1. Docker概述&lt;/h2&gt;
&lt;h3&gt;1.1 什么是Docker&lt;/h3&gt;
&lt;p&gt;Docker是一个开源的容器化平台，允许开发者将应用程序和其依赖项打包到一个轻量级、可移植的容器中。&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;Docker的核心特性：&lt;/strong&gt;&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;strong&gt;一致性&lt;/strong&gt;：在任何环境中运行都保持一致&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;轻量级&lt;/strong&gt;：比传统虚拟机更轻量&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;快速部署&lt;/strong&gt;：秒级启动和停止&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;版本控制&lt;/strong&gt;：镜像版本化管理&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;资源隔离&lt;/strong&gt;：容器间相互隔离&lt;/li&gt;
&lt;/ul&gt;
&lt;h3&gt;1.2 Docker架构&lt;/h3&gt;
&lt;pre&gt;&lt;code&gt;┌─────────────────────────────────────────────────────────────┐
│                    Docker Client                            │
├─────────────────────────────────────────────────────────────┤
│                    Docker Daemon                            │
├─────────────────────────────────────────────────────────────┤
│  Registry  │  Images  │  Containers  │  Networks  │  Volumes │
└─────────────────────────────────────────────────────────────┘
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;&lt;strong&gt;核心组件：&lt;/strong&gt;&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;strong&gt;Docker Client&lt;/strong&gt;：命令行工具，与Docker Daemon通信&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Docker Daemon&lt;/strong&gt;：后台服务，管理容器生命周期&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Registry&lt;/strong&gt;：镜像仓库（Docker Hub、私有仓库）&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Images&lt;/strong&gt;：只读模板，用于创建容器&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Containers&lt;/strong&gt;：运行中的镜像实例&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Networks&lt;/strong&gt;：容器间通信网络&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Volumes&lt;/strong&gt;：持久化数据存储&lt;/li&gt;
&lt;/ul&gt;
&lt;h3&gt;1.3 容器与虚拟机对比&lt;/h3&gt;
&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;特性&lt;/th&gt;
&lt;th&gt;容器&lt;/th&gt;
&lt;th&gt;虚拟机&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;启动时间&lt;/td&gt;
&lt;td&gt;秒级&lt;/td&gt;
&lt;td&gt;分钟级&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;资源占用&lt;/td&gt;
&lt;td&gt;轻量&lt;/td&gt;
&lt;td&gt;较重&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;隔离级别&lt;/td&gt;
&lt;td&gt;进程级&lt;/td&gt;
&lt;td&gt;系统级&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;镜像大小&lt;/td&gt;
&lt;td&gt;MB级&lt;/td&gt;
&lt;td&gt;GB级&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;性能&lt;/td&gt;
&lt;td&gt;接近原生&lt;/td&gt;
&lt;td&gt;有损耗&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;
&lt;hr /&gt;
&lt;h2&gt;2. Docker安装与配置&lt;/h2&gt;
&lt;h3&gt;2.1 安装Docker&lt;/h3&gt;
&lt;pre&gt;&lt;code&gt;# Ubuntu/Debian
sudo apt update
sudo apt install docker.io docker-compose
sudo systemctl start docker
sudo systemctl enable docker

# CentOS/RHEL
sudo yum install docker
sudo systemctl start docker
sudo systemctl enable docker

# macOS
brew install docker
# 或下载 Docker Desktop

# Windows
# 下载 Docker Desktop for Windows
&lt;/code&gt;&lt;/pre&gt;
&lt;h3&gt;2.2 配置Docker&lt;/h3&gt;
&lt;pre&gt;&lt;code&gt;# 将用户添加到docker组（避免每次使用sudo）
sudo usermod -aG docker $USER
# 重新登录生效

# 配置镜像加速器（中国用户）
bash &amp;lt;(curl -sSL https://xuanyuan.cloud/docker.sh)

# 验证安装
docker --version
docker run hello-world
&lt;/code&gt;&lt;/pre&gt;
&lt;pre&gt;&lt;code&gt;Docker version 20.10.21, build baeda1f
Hello from Docker!
This message shows that your installation appears to be working correctly.
&lt;/code&gt;&lt;/pre&gt;
&lt;hr /&gt;
&lt;h2&gt;3. 镜像操作&lt;/h2&gt;
&lt;h3&gt;3.1 镜像基础命令&lt;/h3&gt;
&lt;pre&gt;&lt;code&gt;# 搜索镜像
docker search nginx

# 拉取镜像
docker pull nginx:latest
docker pull ubuntu:20.04
docker pull python:3.9-slim

# 查看本地镜像
docker images
docker image ls

# 查看镜像详细信息
docker inspect nginx:latest

# 删除镜像
docker rmi nginx:latest
docker image rm ubuntu:20.04

# 强制删除镜像（即使有容器在使用）
docker rmi -f nginx:latest

# 清理未使用的镜像
docker image prune -a
&lt;/code&gt;&lt;/pre&gt;
&lt;pre&gt;&lt;code&gt;# docker images
REPOSITORY   TAG       IMAGE ID       CREATED        SIZE
nginx        latest    f652ca386ed0   2 weeks ago    141MB
ubuntu       20.04     ba6acccedd29   3 weeks ago    72.8MB
python       3.9-slim  a7b92c8b0b1c   4 weeks ago    113MB

# docker search nginx
NAME                           DESCRIPTION                                     STARS     OFFICIAL   AUTOMATED
nginx                          Official build of Nginx.                       18500     [OK]
jwilder/nginx-proxy           Automated Nginx reverse proxy for docker c...   2089                 [OK]
richarvey/nginx-php-fpm       Container running Nginx + PHP-FPM capable o...   820                  [OK]
&lt;/code&gt;&lt;/pre&gt;
&lt;h3&gt;3.2 镜像标签和推送&lt;/h3&gt;
&lt;pre&gt;&lt;code&gt;# 给镜像打标签
docker tag nginx:latest my-nginx:v1.0
docker tag ubuntu:20.04 mycompany/ubuntu:latest

# 推送镜像到仓库
docker push mycompany/ubuntu:latest

# 从私有仓库拉取镜像
docker pull registry.example.com/myapp:v1.0

# 登录Docker Hub
docker login
# 输入用户名和密码

# 登出
docker logout
&lt;/code&gt;&lt;/pre&gt;
&lt;h3&gt;3.3 镜像历史和信息&lt;/h3&gt;
&lt;pre&gt;&lt;code&gt;# 查看镜像构建历史
docker history nginx:latest

# 查看镜像详细信息
docker inspect nginx:latest | grep -A 10 &quot;Config&quot;

# 导出镜像为tar文件
docker save -o nginx.tar nginx:latest

# 从tar文件导入镜像
docker load -i nginx.tar

# 查看镜像占用空间
docker system df
&lt;/code&gt;&lt;/pre&gt;
&lt;hr /&gt;
&lt;h2&gt;4. 容器管理&lt;/h2&gt;
&lt;h3&gt;4.1 容器基本操作&lt;/h3&gt;
&lt;pre&gt;&lt;code&gt;# 运行容器
docker run nginx:latest
docker run -d nginx:latest  # 后台运行
docker run -it ubuntu:20.04 /bin/bash  # 交互式运行

# 查看运行中的容器
docker ps
docker container ls

# 查看所有容器（包括停止的）
docker ps -a

# 停止容器
docker stop &amp;lt;container_id&amp;gt;
docker stop &amp;lt;container_name&amp;gt;

# 启动已停止的容器
docker start &amp;lt;container_id&amp;gt;

# 重启容器
docker restart &amp;lt;container_id&amp;gt;

# 删除容器
docker rm &amp;lt;container_id&amp;gt;
docker container prune  # 删除所有停止的容器
&lt;/code&gt;&lt;/pre&gt;
&lt;pre&gt;&lt;code&gt;# docker ps
CONTAINER ID   IMAGE     COMMAND                  CREATED         STATUS         PORTS     NAMES
abc123def456   nginx     &quot;/docker-entrypoint.…&quot;   2 minutes ago   Up 2 minutes   80/tcp    nginx-container

# docker ps -a
CONTAINER ID   IMAGE          COMMAND                  CREATED         STATUS                     PORTS     NAMES
abc123def456   nginx          &quot;/docker-entrypoint.…&quot;   2 minutes ago   Up 2 minutes               80/tcp    nginx-container
def456ghi789   ubuntu:20.04   &quot;/bin/bash&quot;              5 minutes ago   Exited (0) 3 minutes ago             ubuntu-test
&lt;/code&gt;&lt;/pre&gt;
&lt;h3&gt;4.2 容器高级操作&lt;/h3&gt;
&lt;pre&gt;&lt;code&gt;# 进入运行中的容器
docker exec -it &amp;lt;container_id&amp;gt; /bin/bash
docker exec -it nginx-container /bin/sh

# 查看容器日志
docker logs &amp;lt;container_id&amp;gt;
docker logs -f &amp;lt;container_id&amp;gt;  # 实时查看日志
docker logs --tail 100 &amp;lt;container_id&amp;gt;  # 查看最后100行

# 查看容器资源使用情况
docker stats

# 复制文件到容器
docker cp local_file.txt &amp;lt;container_id&amp;gt;:/path/in/container/

# 从容器复制文件
docker cp &amp;lt;container_id&amp;gt;:/path/in/container/file.txt ./

# 提交容器为镜像
docker commit &amp;lt;container_id&amp;gt; my-nginx:v1.1
&lt;/code&gt;&lt;/pre&gt;
&lt;h3&gt;4.3 容器端口映射与网络&lt;/h3&gt;
&lt;pre&gt;&lt;code&gt;# 端口映射
docker run -d -p 8080:80 nginx:latest  # 主机8080端口映射到容器80端口
docker run -d -p 3000:3000 -p 8080:80 myapp:latest  # 多端口映射

# 指定容器名称
docker run -d --name my-nginx -p 8080:80 nginx:latest

# 设置环境变量
docker run -d -e MYSQL_ROOT_PASSWORD=123456 mysql:8.0

# 挂载数据卷
docker run -d -v /host/path:/container/path nginx:latest
docker run -d -v nginx_data:/usr/share/nginx/html nginx:latest

# 使用自定义网络
docker network create my-network
docker run -d --network my-network --name web nginx:latest
docker run -d --network my-network --name db mysql:8.0
&lt;/code&gt;&lt;/pre&gt;
&lt;hr /&gt;
&lt;h2&gt;5. Dockerfile与镜像构建&lt;/h2&gt;
&lt;h3&gt;5.1 Dockerfile基础语法&lt;/h3&gt;
&lt;pre&gt;&lt;code&gt;# 基础镜像
FROM ubuntu:20.04

# 维护者信息
LABEL maintainer=&quot;your-email@example.com&quot;

# 设置工作目录
WORKDIR /app

# 复制文件
COPY requirements.txt .
COPY src/ ./src/

# 安装依赖
RUN apt-get update &amp;amp;&amp;amp; apt-get install -y \
    python3 \
    python3-pip \
    &amp;amp;&amp;amp; rm -rf /var/lib/apt/lists/*

RUN pip3 install -r requirements.txt

# 暴露端口
EXPOSE 8080

# 设置环境变量
ENV FLASK_APP=app.py
ENV FLASK_ENV=production

# 启动命令
CMD [&quot;python3&quot;, &quot;app.py&quot;]
&lt;/code&gt;&lt;/pre&gt;
&lt;h3&gt;5.2 多阶段构建&lt;/h3&gt;
&lt;pre&gt;&lt;code&gt;# 构建阶段
FROM node:16 AS builder
WORKDIR /app
COPY package*.json ./
RUN npm install
COPY . .
RUN npm run build

# 运行阶段
FROM nginx:alpine
COPY --from=builder /app/dist /usr/share/nginx/html
EXPOSE 80
CMD [&quot;nginx&quot;, &quot;-g&quot;, &quot;daemon off;&quot;]
&lt;/code&gt;&lt;/pre&gt;
&lt;h3&gt;5.3 构建镜像&lt;/h3&gt;
&lt;pre&gt;&lt;code&gt;# 构建镜像
docker build -t myapp:v1.0 .
docker build -f Dockerfile.prod -t myapp:prod .

# 指定构建参数
docker build --build-arg VERSION=1.0 -t myapp:v1.0 .

# 不使用缓存构建
docker build --no-cache -t myapp:v1.0 .

# 查看构建历史
docker history myapp:v1.0
&lt;/code&gt;&lt;/pre&gt;
&lt;pre&gt;&lt;code&gt;# docker build -t myapp:v1.0 .
Sending build context to Docker daemon  2.048kB
Step 1/8 : FROM python:3.9-slim
 ---&amp;gt; a7b92c8b0b1c
Step 2/8 : WORKDIR /app
 ---&amp;gt; Running in abc123def456
 ---&amp;gt; def456ghi789
Step 3/8 : COPY requirements.txt .
 ---&amp;gt; abc123def456
Step 4/8 : RUN pip install -r requirements.txt
 ---&amp;gt; Running in def456ghi789
Collecting flask==2.0.1
  Downloading flask-2.0.1-py3-none-any.whl (94 kB)
Installing collected packages: flask
Successfully installed flask-2.0.1
 ---&amp;gt; ghi789jkl012
Step 5/8 : COPY . .
 ---&amp;gt; jkl012mno345
Step 6/8 : EXPOSE 8080
 ---&amp;gt; Running in mno345pqr678
 ---&amp;gt; pqr678stu901
Step 7/8 : ENV FLASK_APP=app.py
 ---&amp;gt; Running in stu901vwx234
 ---&amp;gt; vwx234yza567
Step 8/8 : CMD [&quot;python&quot;, &quot;app.py&quot;]
 ---&amp;gt; Running in yza567bcd890
 ---&amp;gt; bcd890efg123
Successfully built bcd890efg123
Successfully tagged myapp:v1.0
&lt;/code&gt;&lt;/pre&gt;
&lt;hr /&gt;
&lt;h2&gt;6. Docker Compose编排&lt;/h2&gt;
&lt;h3&gt;6.1 docker-compose.yml基础&lt;/h3&gt;
&lt;pre&gt;&lt;code&gt;version: &apos;3.8&apos;

services:
  web:
    build: .
    ports:
      - &quot;8080:8080&quot;
    environment:
      - DATABASE_URL=postgresql://user:pass@db:5432/myapp
    depends_on:
      - db
    volumes:
      - ./logs:/app/logs
    networks:
      - app-network

  db:
    image: postgres:13
    environment:
      - POSTGRES_DB=myapp
      - POSTGRES_USER=user
      - POSTGRES_PASSWORD=pass
    volumes:
      - postgres_data:/var/lib/postgresql/data
    networks:
      - app-network

  redis:
    image: redis:6-alpine
    networks:
      - app-network

volumes:
  postgres_data:

networks:
  app-network:
    driver: bridge
&lt;/code&gt;&lt;/pre&gt;
&lt;h3&gt;6.2 Compose命令操作&lt;/h3&gt;
&lt;pre&gt;&lt;code&gt;# 启动服务
docker-compose up
docker-compose up -d  # 后台运行
docker-compose up --build  # 重新构建镜像

# 停止服务
docker-compose down
docker-compose down -v  # 同时删除数据卷

# 查看服务状态
docker-compose ps
docker-compose logs
docker-compose logs web  # 查看特定服务日志

# 进入服务容器
docker-compose exec web bash
docker-compose exec db psql -U user -d myapp

# 重启服务
docker-compose restart web

# 扩展服务实例
docker-compose up --scale web=3
&lt;/code&gt;&lt;/pre&gt;
&lt;pre&gt;&lt;code&gt;# docker-compose up -d
Creating network &quot;myapp_app-network&quot; ... done
Creating volume &quot;myapp_postgres_data&quot; ... done
Creating myapp_db_1    ... done
Creating myapp_redis_1 ... done
Creating myapp_web_1   ... done

# docker-compose ps
     Name                    Command               State           Ports
--------------------------------------------------------------------------------
myapp_db_1      docker-entrypoint.sh postgres    Up      5432/tcp
myapp_redis_1   docker-entrypoint.sh redis ...   Up      6379/tcp
myapp_web_1     python app.py                    Up      0.0.0.0:8080-&amp;gt;8080/tcp
&lt;/code&gt;&lt;/pre&gt;
&lt;h3&gt;6.3 高级Compose配置&lt;/h3&gt;
&lt;pre&gt;&lt;code&gt;version: &apos;3.8&apos;

services:
  web:
    build:
      context: .
      dockerfile: Dockerfile.prod
      args:
        - VERSION=1.0
    deploy:
      replicas: 3
      resources:
        limits:
          cpus: &apos;0.50&apos;
          memory: 512M
        reservations:
          cpus: &apos;0.25&apos;
          memory: 256M
    healthcheck:
      test: [&quot;CMD&quot;, &quot;curl&quot;, &quot;-f&quot;, &quot;http://localhost:8080/health&quot;]
      interval: 30s
      timeout: 10s
      retries: 3
    restart: unless-stopped

  nginx:
    image: nginx:alpine
    ports:
      - &quot;80:80&quot;
    volumes:
      - ./nginx.conf:/etc/nginx/nginx.conf
    depends_on:
      - web
&lt;/code&gt;&lt;/pre&gt;
&lt;hr /&gt;
&lt;h2&gt;7. 网络管理&lt;/h2&gt;
&lt;h3&gt;7.1 Docker网络&lt;/h3&gt;
&lt;pre&gt;&lt;code&gt;# 查看网络列表
docker network ls

# 创建自定义网络
docker network create my-network
docker network create --driver bridge --subnet 172.18.0.0/16 my-network

# 连接容器到网络
docker network connect my-network container1
docker network disconnect my-network container1

# 查看网络详细信息
docker network inspect my-network

# 删除网络
docker network rm my-network
docker network prune  # 删除未使用的网络
&lt;/code&gt;&lt;/pre&gt;
&lt;pre&gt;&lt;code&gt;# docker network ls
NETWORK ID     NAME      DRIVER    SCOPE
abc123def456   bridge    bridge    local
def456ghi789   host      host      local
ghi789jkl012   none      null      local
jkl012mno345   my-network bridge    local

# docker network inspect bridge
[
    {
        &quot;Name&quot;: &quot;bridge&quot;,
        &quot;Id&quot;: &quot;abc123def456&quot;,
        &quot;Created&quot;: &quot;2023-06-30T10:00:00.000000000Z&quot;,
        &quot;Scope&quot;: &quot;local&quot;,
        &quot;Driver&quot;: &quot;bridge&quot;,
        &quot;EnableIPv6&quot;: false,
        &quot;IPAM&quot;: {
            &quot;Driver&quot;: &quot;default&quot;,
            &quot;Options&quot;: null,
            &quot;Config&quot;: [
                {
                    &quot;Subnet&quot;: &quot;172.17.0.0/16&quot;,
                    &quot;Gateway&quot;: &quot;172.17.0.1&quot;
                }
            ]
        }
    }
]
&lt;/code&gt;&lt;/pre&gt;
&lt;h2&gt;8. 存储管理&lt;/h2&gt;
&lt;h3&gt;8.1 数据卷管理&lt;/h3&gt;
&lt;pre&gt;&lt;code&gt;# 创建数据卷
docker volume create my-data

# 查看数据卷列表
docker volume ls

# 查看数据卷详细信息
docker volume inspect my-data

# 删除数据卷
docker volume rm my-data
docker volume prune  # 删除未使用的数据卷

# 备份数据卷
docker run --rm -v my-data:/data -v $(pwd):/backup ubuntu tar czf /backup/my-data.tar.gz -C /data .

# 恢复数据卷
docker run --rm -v my-data:/data -v $(pwd):/backup ubuntu tar xzf /backup/my-data.tar.gz -C /data
&lt;/code&gt;&lt;/pre&gt;
&lt;h3&gt;8.2 绑定挂载&lt;/h3&gt;
&lt;pre&gt;&lt;code&gt;# 绑定挂载目录
docker run -d -v /host/path:/container/path nginx:latest

# 挂载单个文件
docker run -d -v /host/config.json:/app/config.json myapp:latest

# 只读挂载
docker run -d -v /host/path:/container/path:ro nginx:latest

# 使用命名卷
docker run -d -v nginx_data:/usr/share/nginx/html nginx:latest
&lt;/code&gt;&lt;/pre&gt;
&lt;hr /&gt;
&lt;h2&gt;9. 应用容器化示例&lt;/h2&gt;
&lt;h3&gt;9.1 Python Flask应用容器化&lt;/h3&gt;
&lt;pre&gt;&lt;code&gt;# app.py
from flask import Flask, jsonify
import os

app = Flask(__name__)

@app.route(&apos;/&apos;)
def hello():
    return jsonify({
        &apos;message&apos;: &apos;Hello from Docker!&apos;,
        &apos;version&apos;: os.getenv(&apos;VERSION&apos;, &apos;1.0&apos;)
    })

@app.route(&apos;/health&apos;)
def health():
    return jsonify({&apos;status&apos;: &apos;healthy&apos;})

if __name__ == &apos;__main__&apos;:
    app.run(host=&apos;0.0.0.0&apos;, port=8080)
&lt;/code&gt;&lt;/pre&gt;
&lt;pre&gt;&lt;code&gt;# requirements.txt
Flask==2.0.1
gunicorn==20.1.0
&lt;/code&gt;&lt;/pre&gt;
&lt;pre&gt;&lt;code&gt;# Dockerfile
FROM python:3.9-slim

WORKDIR /app

COPY requirements.txt .
RUN pip install -r requirements.txt

COPY . .

EXPOSE 8080

ENV VERSION=1.0

CMD [&quot;gunicorn&quot;, &quot;--bind&quot;, &quot;0.0.0.0:8080&quot;, &quot;app:app&quot;]
&lt;/code&gt;&lt;/pre&gt;
&lt;pre&gt;&lt;code&gt;# docker-compose.yml
version: &apos;3.8&apos;

services:
  web:
    build: .
    ports:
      - &quot;8080:8080&quot;
    environment:
      - VERSION=1.0
    volumes:
      - ./logs:/app/logs
    restart: unless-stopped
&lt;/code&gt;&lt;/pre&gt;
&lt;h3&gt;9.2 Node.js应用容器化&lt;/h3&gt;
&lt;pre&gt;&lt;code&gt;# Dockerfile
FROM node:16-alpine

WORKDIR /app

COPY package*.json ./
RUN npm ci --only=production

COPY . .

EXPOSE 3000

CMD [&quot;npm&quot;, &quot;start&quot;]
&lt;/code&gt;&lt;/pre&gt;
&lt;pre&gt;&lt;code&gt;# docker-compose.yml
version: &apos;3.8&apos;

services:
  app:
    build: .
    ports:
      - &quot;3000:3000&quot;
    environment:
      - NODE_ENV=production
    depends_on:
      - redis
    restart: unless-stopped

  redis:
    image: redis:6-alpine
    volumes:
      - redis_data:/data

volumes:
  redis_data:
&lt;/code&gt;&lt;/pre&gt;
&lt;h3&gt;9.3 构建和部署&lt;/h3&gt;
&lt;pre&gt;&lt;code&gt;# 构建镜像
docker build -t myapp:v1.0 .

# 运行容器
docker run -d -p 8080:8080 --name myapp myapp:v1.0

# 使用Compose部署
docker-compose up -d

# 查看应用状态
curl http://localhost:8080/
curl http://localhost:8080/health

# 查看日志
docker logs myapp
docker-compose logs web
&lt;/code&gt;&lt;/pre&gt;
&lt;pre&gt;&lt;code&gt;# curl http://localhost:8080/
{
  &quot;message&quot;: &quot;Hello from Docker!&quot;,
  &quot;version&quot;: &quot;1.0&quot;
}

# curl http://localhost:8080/health
{
  &quot;status&quot;: &quot;healthy&quot;
}
&lt;/code&gt;&lt;/pre&gt;
&lt;hr /&gt;
&lt;h2&gt;10. 监控与调试&lt;/h2&gt;
&lt;h3&gt;10.1 容器检查命令&lt;/h3&gt;
&lt;pre&gt;&lt;code&gt;# 查看容器详细信息
docker inspect &amp;lt;container_id&amp;gt;

# 查看容器资源使用
docker stats --no-stream

# 进入容器调试
docker exec -it &amp;lt;container_id&amp;gt; /bin/bash

# 查看容器进程
docker top &amp;lt;container_id&amp;gt;
&lt;/code&gt;&lt;/pre&gt;
&lt;h3&gt;10.2 日志管理&lt;/h3&gt;
&lt;pre&gt;&lt;code&gt;# 查看容器日志
docker logs &amp;lt;container_id&amp;gt;

# 实时查看日志
docker logs -f &amp;lt;container_id&amp;gt;

# 查看最后100行
docker logs --tail 100 &amp;lt;container_id&amp;gt;
&lt;/code&gt;&lt;/pre&gt;
&lt;pre&gt;&lt;code&gt;# docker-compose.yml 日志配置
version: &apos;3.8&apos;

services:
  web:
    build: .
    logging:
      driver: &quot;json-file&quot;
      options:
        max-size: &quot;10m&quot;
        max-file: &quot;3&quot;
&lt;/code&gt;&lt;/pre&gt;
&lt;hr /&gt;
&lt;h2&gt;11. 最佳实践&lt;/h2&gt;
&lt;h3&gt;11.1 镜像优化&lt;/h3&gt;
&lt;pre&gt;&lt;code&gt;# 多阶段构建优化
FROM node:16-alpine AS builder
WORKDIR /app
COPY package*.json ./
RUN npm ci
COPY . .
RUN npm run build

FROM nginx:alpine
COPY --from=builder /app/dist /usr/share/nginx/html
EXPOSE 80
CMD [&quot;nginx&quot;, &quot;-g&quot;, &quot;daemon off;&quot;]

# 使用.dockerignore文件
node_modules
npm-debug.log
.git
.gitignore
README.md
.env
.nyc_output
coverage
&lt;/code&gt;&lt;/pre&gt;
&lt;h3&gt;11.2 安全实践&lt;/h3&gt;
&lt;pre&gt;&lt;code&gt;# 使用非root用户
FROM node:16-alpine
RUN addgroup -g 1001 -S nodejs
RUN adduser -S nodejs -u 1001
USER nodejs

# 最小化攻击面
FROM alpine:latest
RUN apk add --no-cache nginx

# 扫描镜像漏洞
docker scan myapp:v1.0
&lt;/code&gt;&lt;/pre&gt;
&lt;h3&gt;11.3 资源限制&lt;/h3&gt;
&lt;pre&gt;&lt;code&gt;# 资源限制
docker run -d \
  --memory=512m \
  --cpus=1.0 \
  --pids-limit=100 \
  myapp:v1.0

# 清理系统
docker system prune -a
docker builder prune

# 监控容器资源
docker stats
&lt;/code&gt;&lt;/pre&gt;
&lt;hr /&gt;
&lt;h2&gt;12. 高级主题&lt;/h2&gt;
&lt;h3&gt;12.1 Docker Swarm集群&lt;/h3&gt;
&lt;pre&gt;&lt;code&gt;# 初始化Swarm
docker swarm init

# 添加工作节点
docker swarm join --token &amp;lt;token&amp;gt; &amp;lt;manager-ip&amp;gt;:2377

# 部署服务
docker service create --name web --replicas 3 -p 8080:8080 myapp:v1.0

# 查看服务
docker service ls
docker service ps web

# 扩展服务
docker service scale web=5
&lt;/code&gt;&lt;/pre&gt;
&lt;h3&gt;12.2 私有镜像仓库&lt;/h3&gt;
&lt;pre&gt;&lt;code&gt;# 运行私有仓库
docker run -d -p 5000:5000 --name registry registry:2

# 推送镜像到私有仓库
docker tag myapp:v1.0 localhost:5000/myapp:v1.0
docker push localhost:5000/myapp:v1.0

# 从私有仓库拉取
docker pull localhost:5000/myapp:v1.0
&lt;/code&gt;&lt;/pre&gt;
&lt;h2&gt;参考资料&lt;/h2&gt;
&lt;ul&gt;
&lt;li&gt;Docker官方文档：https://docs.docker.com/&lt;/li&gt;
&lt;li&gt;Docker Hub：https://hub.docker.com/&lt;/li&gt;
&lt;li&gt;Docker Compose文档：https://docs.docker.com/compose/&lt;/li&gt;
&lt;/ul&gt;
</content:encoded></item><item><title>MySQL 查询日志与性能诊断</title><link>https://meteor-comet.github.io/posts/mysql-query-logs/</link><guid isPermaLink="true">https://meteor-comet.github.io/posts/mysql-query-logs/</guid><description>慢查询日志 / EXPLAIN 执行计划 / SHOW PROFILES / Performance Schema</description><pubDate>Tue, 20 Jun 2023 00:00:00 GMT</pubDate><content:encoded>&lt;h2&gt;目录&lt;/h2&gt;
&lt;ol&gt;
&lt;li&gt;&lt;a href=&quot;#1-%E6%85%A2%E6%9F%A5%E8%AF%A2%E6%97%A5%E5%BF%97slow-query-log&quot;&gt;慢查询日志（Slow Query Log）&lt;/a&gt;
&lt;ul&gt;
&lt;li&gt;&lt;a href=&quot;#11-%E9%85%8D%E7%BD%AE%E5%8F%82%E6%95%B0&quot;&gt;1.1 配置参数&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;#12-%E6%8C%81%E4%B9%85%E5%8C%96%E9%85%8D%E7%BD%AEmycnf&quot;&gt;1.2 持久化配置（my.cnf）&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;#13-%E6%97%A5%E5%BF%97%E6%A0%BC%E5%BC%8F%E8%A7%A3%E8%AF%BB&quot;&gt;1.3 日志格式解读&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;#14-mysqldumpslow-%E5%88%86%E6%9E%90%E5%B7%A5%E5%85%B7&quot;&gt;1.4 mysqldumpslow 分析工具&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;#2-show-profiles-%E6%89%A7%E8%A1%8C%E5%89%96%E6%9E%90&quot;&gt;SHOW PROFILES 执行剖析&lt;/a&gt;
&lt;ul&gt;
&lt;li&gt;&lt;a href=&quot;#21-%E5%90%AF%E7%94%A8%E4%B8%8E%E6%9F%A5%E7%9C%8B&quot;&gt;2.1 启用与查看&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;#22-show-profile-%E8%AF%A6%E7%BB%86%E5%88%86%E6%9E%90&quot;&gt;2.2 SHOW PROFILE 详细分析&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;#3-explain-%E6%89%A7%E8%A1%8C%E8%AE%A1%E5%88%92&quot;&gt;EXPLAIN 执行计划&lt;/a&gt;
&lt;ul&gt;
&lt;li&gt;&lt;a href=&quot;#31-%E5%9F%BA%E6%9C%AC%E8%AF%AD%E6%B3%95&quot;&gt;3.1 基本语法&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;#32-%E6%A0%B8%E5%BF%83%E5%AD%97%E6%AE%B5%E8%AF%A6%E8%A7%A3&quot;&gt;3.2 核心字段详解&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;#33-type-%E8%AE%BF%E9%97%AE%E7%B1%BB%E5%9E%8B%E4%B8%80%E8%A7%88&quot;&gt;3.3 type 访问类型一览&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;#34-extra-%E5%AD%97%E6%AE%B5%E8%AF%B4%E6%98%8E&quot;&gt;3.4 Extra 字段说明&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;#35-explain-analyzemysql-80&quot;&gt;3.5 EXPLAIN ANALYZE（MySQL 8.0+）&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;#4-%E6%97%A5%E5%BF%97%E7%B1%BB%E5%9E%8B%E6%80%BB%E8%A7%88&quot;&gt;日志类型总览&lt;/a&gt;
&lt;ul&gt;
&lt;li&gt;&lt;a href=&quot;#41-%E9%80%9A%E7%94%A8%E6%9F%A5%E8%AF%A2%E6%97%A5%E5%BF%97&quot;&gt;4.1 通用查询日志&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;#42-%E9%94%99%E8%AF%AF%E6%97%A5%E5%BF%97&quot;&gt;4.2 错误日志&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;#43-%E4%BA%8C%E8%BF%9B%E5%88%B6%E6%97%A5%E5%BF%97binlog&quot;&gt;4.3 二进制日志（Binlog）&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;#5-%E7%B3%BB%E7%BB%9F%E7%8A%B6%E6%80%81%E4%B8%8E-performance-schema&quot;&gt;系统状态与 Performance Schema&lt;/a&gt;
&lt;ul&gt;
&lt;li&gt;&lt;a href=&quot;#51-show-status-%E5%85%B3%E9%94%AE%E6%8C%87%E6%A0%87&quot;&gt;5.1 SHOW STATUS 关键指标&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;#52-performance-schema-%E5%B8%B8%E7%94%A8%E6%9F%A5%E8%AF%A2&quot;&gt;5.2 Performance Schema 常用查询&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;#53-information_schema-%E8%A1%A8%E7%BB%9F%E8%AE%A1&quot;&gt;5.3 information_schema 表统计&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;#6-%E6%9F%A5%E8%AF%A2%E4%BC%98%E5%8C%96%E5%AE%9E%E6%88%98&quot;&gt;查询优化实战&lt;/a&gt;
&lt;ul&gt;
&lt;li&gt;&lt;a href=&quot;#61-%E6%85%A2-join-%E6%9F%A5%E8%AF%A2%E4%BC%98%E5%8C%96&quot;&gt;6.1 慢 JOIN 查询优化&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;#62-%E5%A4%8D%E5%90%88%E7%B4%A2%E5%BC%95%E5%91%BD%E4%B8%AD&quot;&gt;6.2 复合索引命中&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;#63-in-%E5%AD%90%E6%9F%A5%E8%AF%A2%E8%BD%AC-join&quot;&gt;6.3 IN 子查询转 JOIN&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;#7-%E6%97%A5%E5%BF%97%E7%AE%A1%E7%90%86%E4%B8%8E%E7%BB%B4%E6%8A%A4&quot;&gt;日志管理与维护&lt;/a&gt;
&lt;ul&gt;
&lt;li&gt;&lt;a href=&quot;#71-%E6%97%A5%E5%BF%97%E8%BD%AE%E8%BD%AClogrotate&quot;&gt;7.1 日志轮转（logrotate）&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;#72-%E6%97%A5%E5%BF%97%E6%B8%85%E7%90%86&quot;&gt;7.2 日志清理&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;#8-%E6%80%A7%E8%83%BD%E9%97%AE%E9%A2%98%E8%AF%8A%E6%96%AD%E6%B5%81%E7%A8%8B&quot;&gt;性能问题诊断流程&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;#9-%E6%9C%80%E4%BD%B3%E5%AE%9E%E8%B7%B5%E9%80%9F%E6%9F%A5&quot;&gt;最佳实践速查&lt;/a&gt;&lt;/li&gt;
&lt;/ol&gt;
&lt;hr /&gt;
&lt;h2&gt;1. 慢查询日志（Slow Query Log）&lt;/h2&gt;
&lt;p&gt;慢查询日志记录执行时间超过阈值的 SQL，是线上性能排障的首选入口。&lt;/p&gt;
&lt;h3&gt;1.1 配置参数&lt;/h3&gt;
&lt;pre&gt;&lt;code&gt;-- 查看当前配置
SHOW VARIABLES LIKE &apos;%slow_query%&apos;;
SHOW VARIABLES LIKE &apos;%long_query_time%&apos;;

-- 动态开启
SET GLOBAL slow_query_log = &apos;ON&apos;;
SET GLOBAL long_query_time = 2;                            -- 阈值，单位：秒
SET GLOBAL slow_query_log_file = &apos;/var/log/mysql/slow.log&apos;;

-- 记录未命中索引的查询（不管是否超时）
SET GLOBAL log_queries_not_using_indexes = &apos;ON&apos;;

-- 记录慢管理语句（ALTER TABLE 等）
SET GLOBAL log_slow_admin_statements = &apos;ON&apos;;
&lt;/code&gt;&lt;/pre&gt;
&lt;h3&gt;1.2 持久化配置（my.cnf）&lt;/h3&gt;
&lt;pre&gt;&lt;code&gt;[mysqld]
slow_query_log          = 1
slow_query_log_file     = /var/log/mysql/slow.log
long_query_time         = 2
log_queries_not_using_indexes = 1
log_slow_admin_statements     = 1
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;修改后执行 &lt;code&gt;FLUSH LOGS;&lt;/code&gt; 或重启 mysqld 生效。&lt;/p&gt;
&lt;h3&gt;1.3 日志格式解读&lt;/h3&gt;
&lt;pre&gt;&lt;code&gt;# Time: 2023-06-20T10:30:15.123456Z
# User@Host: root[root] @ localhost [127.0.0.1]  Id: 12345
# Query_time: 5.234567  Lock_time: 0.000123  Rows_sent: 1000  Rows_examined: 50000
SET timestamp=1687257015;
SELECT * FROM users WHERE email LIKE &apos;%@gmail.com&apos;;
&lt;/code&gt;&lt;/pre&gt;
&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;字段&lt;/th&gt;
&lt;th&gt;含义&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;Query_time&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;总执行时间&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;Lock_time&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;等锁时间&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;Rows_sent&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;实际返回行数&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;Rows_examined&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;扫描行数（越大越危险）&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;
&lt;p&gt;&lt;code&gt;Rows_examined / Rows_sent&lt;/code&gt; 比值越高，说明索引效率越低。&lt;/p&gt;
&lt;h3&gt;1.4 mysqldumpslow 分析工具&lt;/h3&gt;
&lt;pre&gt;&lt;code&gt;# 显示耗时 TOP 10
mysqldumpslow -t 10 /var/log/mysql/slow.log

# 按执行次数排序 TOP 10
mysqldumpslow -s c -t 10 /var/log/mysql/slow.log

# 按锁等待时间排序
mysqldumpslow -s l -t 10 /var/log/mysql/slow.log

# 不抽象参数，显示原始 SQL
mysqldumpslow -a /var/log/mysql/slow.log

# 过滤指定数据库
mysqldumpslow -g &quot;mydb&quot; /var/log/mysql/slow.log
&lt;/code&gt;&lt;/pre&gt;
&lt;hr /&gt;
&lt;h2&gt;2. SHOW PROFILES 执行剖析&lt;/h2&gt;
&lt;p&gt;SHOW PROFILES 是会话级别的轻量诊断工具，可以拆分每个查询在各阶段花费的时间。&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;⚠️ MySQL 8.0 开始 SHOW PROFILES 被标记为废弃，推荐迁移到 Performance Schema。但在 5.7 以下环境中仍然是最方便的工具。&lt;/p&gt;
&lt;/blockquote&gt;
&lt;h3&gt;2.1 启用与查看&lt;/h3&gt;
&lt;pre&gt;&lt;code&gt;-- 仅对当前会话生效
SET profiling = 1;

-- 执行若干 SQL 后查看列表
SHOW PROFILES;
&lt;/code&gt;&lt;/pre&gt;
&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Query_ID&lt;/th&gt;
&lt;th&gt;Duration&lt;/th&gt;
&lt;th&gt;Query&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;1&lt;/td&gt;
&lt;td&gt;0.00012345&lt;/td&gt;
&lt;td&gt;SELECT 1&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;2&lt;/td&gt;
&lt;td&gt;0.00123456&lt;/td&gt;
&lt;td&gt;SELECT * FROM users LIMIT 10&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;3&lt;/td&gt;
&lt;td&gt;0.12345678&lt;/td&gt;
&lt;td&gt;SELECT * FROM orders WHERE …&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;
&lt;h3&gt;2.2 SHOW PROFILE 详细分析&lt;/h3&gt;
&lt;pre&gt;&lt;code&gt;-- 查看最近一次查询各阶段耗时
SHOW PROFILE;

-- 查看指定 Query_ID
SHOW PROFILE FOR QUERY 3;

-- 同时展示 CPU 和块 IO 消耗
SHOW PROFILE CPU, BLOCK IO FOR QUERY 3;

-- 展示全部类型
SHOW PROFILE ALL FOR QUERY 3;
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;&lt;strong&gt;常用 Profile 类型：&lt;/strong&gt;&lt;/p&gt;
&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;类型&lt;/th&gt;
&lt;th&gt;含义&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;CPU&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;CPU 用户态/内核态时间&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;BLOCK IO&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;块设备读写次数&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;CONTEXT SWITCHES&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;上下文切换次数&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;MEMORY&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;内存分配（部分版本支持）&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;SWAPS&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;交换操作次数&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;
&lt;p&gt;重点关注状态中的 &lt;strong&gt;&lt;code&gt;Sending data&lt;/code&gt;&lt;/strong&gt;（实际读取和发送数据）和 &lt;strong&gt;&lt;code&gt;Sorting result&lt;/code&gt;&lt;/strong&gt;（排序），这两个通常是性能大头。&lt;/p&gt;
&lt;hr /&gt;
&lt;h2&gt;3. EXPLAIN 执行计划&lt;/h2&gt;
&lt;p&gt;EXPLAIN 是分析单条 SQL 的核心手段，无需修改数据即可预判 MySQL 的执行策略。&lt;/p&gt;
&lt;h3&gt;3.1 基本语法&lt;/h3&gt;
&lt;pre&gt;&lt;code&gt;-- 标准表格输出
EXPLAIN SELECT * FROM users WHERE id = 1;

-- JSON 格式，包含 cost 信息
EXPLAIN FORMAT=JSON SELECT * FROM users WHERE id = 1;

-- 树形结构（MySQL 8.0.18+）
EXPLAIN FORMAT=TREE SELECT * FROM users WHERE id = 1;

-- 实际执行并附带真实统计数据（MySQL 8.0.18+）
EXPLAIN ANALYZE SELECT * FROM users WHERE id = 1;
&lt;/code&gt;&lt;/pre&gt;
&lt;h3&gt;3.2 核心字段详解&lt;/h3&gt;
&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;字段&lt;/th&gt;
&lt;th&gt;含义&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;id&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;查询序号，数字越大优先级越高；相同则从上到下执行&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;select_type&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;查询类型：SIMPLE / PRIMARY / SUBQUERY / DERIVED / UNION&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;table&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;当前行操作的表名或别名&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;partitions&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;命中的分区（分区表才有值）&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;type&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;&lt;strong&gt;访问类型&lt;/strong&gt;（见下节）&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;possible_keys&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;优化器候选的索引列表&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;key&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;优化器最终选择的索引&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;key_len&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;使用索引的字节长度（可推断使用了联合索引的几列）&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;ref&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;与索引列比较的列或常量&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;rows&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;预估扫描行数&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;filtered&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;经 WHERE 过滤后的行比例（%）&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;Extra&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;额外信息（见下节）&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;
&lt;h3&gt;3.3 type 访问类型一览&lt;/h3&gt;
&lt;p&gt;性能从好到差依次排列：&lt;/p&gt;
&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;type&lt;/th&gt;
&lt;th&gt;说明&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;system&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;表只有一行，特殊情况&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;const&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;主键或唯一索引等值查询，结果最多一行&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;eq_ref&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;被驱动表通过唯一索引与驱动表关联&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;ref&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;普通索引等值查询&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;fulltext&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;全文索引&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;range&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;索引范围扫描（BETWEEN / IN / &amp;gt; / &amp;lt; 等）&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;index&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;全索引扫描（比 ALL 少，因为只扫索引树）&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;ALL&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;全表扫描，&lt;strong&gt;生产环境大表应尽力避免&lt;/strong&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;
&lt;blockquote&gt;
&lt;p&gt;线上要求：至少达到 &lt;code&gt;range&lt;/code&gt; 级别，核心查询应达到 &lt;code&gt;ref&lt;/code&gt; 或 &lt;code&gt;const&lt;/code&gt;。&lt;/p&gt;
&lt;/blockquote&gt;
&lt;h3&gt;3.4 Extra 字段说明&lt;/h3&gt;
&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Extra&lt;/th&gt;
&lt;th&gt;含义&lt;/th&gt;
&lt;th&gt;是否需要关注&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;Using index&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;覆盖索引，无需回表&lt;/td&gt;
&lt;td&gt;✅ 最优&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;Using where&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Server 层用 WHERE 过滤&lt;/td&gt;
&lt;td&gt;ℹ️ 正常&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;Using index condition&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;索引条件下推（ICP）&lt;/td&gt;
&lt;td&gt;✅ 好&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;Using temporary&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;使用了临时表&lt;/td&gt;
&lt;td&gt;⚠️ 需优化&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;Using filesort&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;需要额外排序，无法用索引&lt;/td&gt;
&lt;td&gt;⚠️ 需优化&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;Using join buffer&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;JOIN 使用了 Block Nested Loop&lt;/td&gt;
&lt;td&gt;⚠️ 需优化&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;Select tables optimized away&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;MIN/MAX 优化，直接查索引&lt;/td&gt;
&lt;td&gt;✅ 最优&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;
&lt;p&gt;&lt;code&gt;Using temporary&lt;/code&gt; 和 &lt;code&gt;Using filesort&lt;/code&gt; 同时出现是需要立刻处理的信号。&lt;/p&gt;
&lt;h3&gt;3.5 EXPLAIN ANALYZE（MySQL 8.0+）&lt;/h3&gt;
&lt;p&gt;EXPLAIN ANALYZE 会&lt;strong&gt;真正执行&lt;/strong&gt;查询，并返回预估值与实际值的对比，是排查执行计划偏差的利器。&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;EXPLAIN ANALYZE SELECT u.name, COUNT(o.id)
FROM users u
LEFT JOIN orders o ON u.id = o.user_id
GROUP BY u.id;
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;输出示例：&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;-&amp;gt; Table scan on &amp;lt;temporary&amp;gt;  (cost=2.50..5.00 rows=3) (actual time=0.048..0.052 rows=3 loops=1)
    -&amp;gt; Aggregate using temporary table  (actual time=0.043..0.043 rows=3 loops=1)
        -&amp;gt; Nested loop left join  (cost=1.25 rows=3) (actual time=0.019..0.030 rows=4 loops=1)
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;关注 &lt;code&gt;rows=预估&lt;/code&gt; vs &lt;code&gt;actual rows=真实值&lt;/code&gt;，偏差大说明统计信息过旧，可执行 &lt;code&gt;ANALYZE TABLE 表名;&lt;/code&gt; 更新。&lt;/p&gt;
&lt;hr /&gt;
&lt;h2&gt;4. 日志类型总览&lt;/h2&gt;
&lt;h3&gt;4.1 通用查询日志&lt;/h3&gt;
&lt;p&gt;记录&lt;strong&gt;所有&lt;/strong&gt;进入 MySQL 的 SQL，包括连接、断开和每条语句。线上谨慎开启，I/O 压力极大。&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;SET GLOBAL general_log = &apos;ON&apos;;
SET GLOBAL general_log_file = &apos;/var/log/mysql/general.log&apos;;

SHOW VARIABLES LIKE &apos;general_log%&apos;;
&lt;/code&gt;&lt;/pre&gt;
&lt;h3&gt;4.2 错误日志&lt;/h3&gt;
&lt;pre&gt;&lt;code&gt;-- 查看路径
SHOW VARIABLES LIKE &apos;log_error&apos;;

-- 日志详细程度（1=错误，2=警告，3=通知）
SHOW VARIABLES LIKE &apos;log_error_verbosity&apos;;
&lt;/code&gt;&lt;/pre&gt;
&lt;h3&gt;4.3 二进制日志（Binlog）&lt;/h3&gt;
&lt;p&gt;用于主从复制和时间点恢复，不直接用于查询性能排障。&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;SHOW VARIABLES LIKE &apos;log_bin%&apos;;

-- 查看所有 binlog 文件
SHOW BINARY LOGS;

-- 查看指定文件的事件
SHOW BINLOG EVENTS IN &apos;mysql-bin.000001&apos; LIMIT 20;
&lt;/code&gt;&lt;/pre&gt;
&lt;hr /&gt;
&lt;h2&gt;5. 系统状态与 Performance Schema&lt;/h2&gt;
&lt;h3&gt;5.1 SHOW STATUS 关键指标&lt;/h3&gt;
&lt;pre&gt;&lt;code&gt;-- 总慢查询数（自启动以来累计）
SHOW STATUS LIKE &apos;Slow_queries&apos;;

-- 总请求数
SHOW STATUS LIKE &apos;Questions&apos;;

-- 临时表相关（大量磁盘临时表是问题信号）
SHOW STATUS LIKE &apos;Created_tmp%&apos;;

-- 排序操作
SHOW STATUS LIKE &apos;Sort%&apos;;

-- 当前连接数
SHOW STATUS LIKE &apos;Threads_connected&apos;;
SHOW VARIABLES LIKE &apos;max_connections&apos;;

-- 查看线程详情（找长时间运行的查询）
SHOW PROCESSLIST;

-- InnoDB 锁等待详情
SHOW ENGINE INNODB STATUS;
&lt;/code&gt;&lt;/pre&gt;
&lt;h3&gt;5.2 Performance Schema 常用查询&lt;/h3&gt;
&lt;pre&gt;&lt;code&gt;-- 查询耗时最长的 SQL TOP 10
SELECT DIGEST_TEXT, COUNT_STAR, AVG_TIMER_WAIT / 1e12 AS avg_sec
FROM performance_schema.events_statements_summary_by_digest
ORDER BY AVG_TIMER_WAIT DESC
LIMIT 10;

-- 查看当前等待事件
SELECT * FROM performance_schema.events_waits_current
WHERE EVENT_NAME != &apos;idle&apos;;

-- 查看锁等待
SELECT * FROM performance_schema.data_lock_waits;
&lt;/code&gt;&lt;/pre&gt;
&lt;h3&gt;5.3 information_schema 表统计&lt;/h3&gt;
&lt;pre&gt;&lt;code&gt;-- 查看各表大小（MB）
SELECT
    table_schema                AS `数据库`,
    table_name                  AS `表名`,
    ROUND(data_length  / 1024 / 1024, 2) AS `数据(MB)`,
    ROUND(index_length / 1024 / 1024, 2) AS `索引(MB)`,
    ROUND((data_length + index_length) / 1024 / 1024, 2) AS `总计(MB)`
FROM information_schema.tables
WHERE table_schema = &apos;your_database&apos;
ORDER BY (data_length + index_length) DESC;

-- 查看索引信息
SELECT INDEX_NAME, COLUMN_NAME, CARDINALITY, NON_UNIQUE
FROM information_schema.STATISTICS
WHERE TABLE_SCHEMA = &apos;your_database&apos;
  AND TABLE_NAME   = &apos;users&apos;
ORDER BY INDEX_NAME, SEQ_IN_INDEX;
&lt;/code&gt;&lt;/pre&gt;
&lt;hr /&gt;
&lt;h2&gt;6. 查询优化实战&lt;/h2&gt;
&lt;h3&gt;6.1 慢 JOIN 查询优化&lt;/h3&gt;
&lt;pre&gt;&lt;code&gt;-- ❌ 问题查询：SELECT * + LIKE &apos;%xxx%&apos; + LEFT JOIN 无 LIMIT
SELECT u.*, o.order_date, o.amount
FROM users u
LEFT JOIN orders o ON u.id = o.user_id
WHERE u.email LIKE &apos;%@gmail.com&apos;
ORDER BY o.order_date DESC;

-- ✅ 优化：指定列、改 INNER JOIN、加 LIMIT
SELECT u.id, u.name, u.email, o.order_date, o.amount
FROM users u
INNER JOIN orders o ON u.id = o.user_id
WHERE u.email LIKE &apos;support@%&apos;      -- 改为前缀匹配，可命中索引
ORDER BY o.order_date DESC
LIMIT 100;
&lt;/code&gt;&lt;/pre&gt;
&lt;h3&gt;6.2 复合索引命中&lt;/h3&gt;
&lt;pre&gt;&lt;code&gt;-- 查看执行计划
EXPLAIN SELECT * FROM orders
WHERE user_id = 123 AND order_date &amp;gt; &apos;2023-01-01&apos;;
-- 若 key 为 NULL，说明缺少索引

-- 创建复合索引（注意顺序：等值列在前）
CREATE INDEX idx_user_date ON orders(user_id, order_date);

-- 再次 EXPLAIN 确认 type 变为 range，key 变为 idx_user_date
EXPLAIN SELECT * FROM orders
WHERE user_id = 123 AND order_date &amp;gt; &apos;2023-01-01&apos;;
&lt;/code&gt;&lt;/pre&gt;
&lt;h3&gt;6.3 IN 子查询转 JOIN&lt;/h3&gt;
&lt;pre&gt;&lt;code&gt;-- ❌ IN 子查询：优化器可能生成较差的计划
SELECT * FROM users
WHERE id IN (SELECT user_id FROM orders WHERE amount &amp;gt; 1000);

-- ✅ 改写为 JOIN：更可控
SELECT DISTINCT u.*
FROM users u
INNER JOIN orders o ON u.id = o.user_id
WHERE o.amount &amp;gt; 1000;
&lt;/code&gt;&lt;/pre&gt;
&lt;hr /&gt;
&lt;h2&gt;7. 日志管理与维护&lt;/h2&gt;
&lt;h3&gt;7.1 日志轮转（logrotate）&lt;/h3&gt;
&lt;pre&gt;&lt;code&gt;cat &amp;gt; /etc/logrotate.d/mysql &amp;lt;&amp;lt; &apos;EOF&apos;
/var/log/mysql/*.log {
    daily
    rotate 7
    compress
    delaycompress
    missingok
    notifempty
    create 640 mysql mysql
    postrotate
        mysqladmin flush-logs
    endscript
}
EOF
&lt;/code&gt;&lt;/pre&gt;
&lt;h3&gt;7.2 日志清理&lt;/h3&gt;
&lt;p&gt;动态关闭 → 手动清空/归档文件 → 重新开启，是最稳妥的步骤：&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;-- 关闭慢查询日志
SET GLOBAL slow_query_log = &apos;OFF&apos;;
-- （Shell 层备份并清空文件）
SET GLOBAL slow_query_log = &apos;ON&apos;;

-- 清理通用查询日志（同理）
SET GLOBAL general_log = &apos;OFF&apos;;
SET GLOBAL general_log = &apos;ON&apos;;
&lt;/code&gt;&lt;/pre&gt;
&lt;hr /&gt;
&lt;h2&gt;8. 性能问题诊断流程&lt;/h2&gt;
&lt;pre&gt;&lt;code&gt;发现慢 SQL / 告警
        │
        ▼
SHOW PROCESSLIST  ──→  是否有长时间阻塞？
        │                       │ YES → SHOW ENGINE INNODB STATUS 查锁
        │ NO
        ▼
查慢查询日志
  mysqldumpslow 找 Top SQL
        │
        ▼
EXPLAIN 分析执行计划
  type 是否为 ALL / index？
        │ YES → 添加/调整索引
        │ NO  ↓
EXPLAIN ANALYZE
  实际 rows 与预估差距大？
        │ YES → ANALYZE TABLE 更新统计信息
        │ NO  ↓
SHOW PROFILE / Performance Schema
  找具体耗时阶段（Sending data / Sorting）
        │
        ▼
针对瓶颈：重写 SQL / 调整 buffer_pool / 升级配置
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;&lt;strong&gt;常用快速检查命令：&lt;/strong&gt;&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;-- 锁等待
SHOW ENGINE INNODB STATUS;

-- 磁盘临时表占比高（&amp;gt; 10% 需关注）
SHOW STATUS LIKE &apos;Created_tmp_disk_tables&apos;;
SHOW STATUS LIKE &apos;Created_tmp_tables&apos;;

-- 排序溢出到磁盘
SHOW STATUS LIKE &apos;Sort_merge_passes&apos;;

-- 连接使用率
SHOW STATUS LIKE &apos;Threads_connected&apos;;
SHOW VARIABLES LIKE &apos;max_connections&apos;;
&lt;/code&gt;&lt;/pre&gt;
&lt;hr /&gt;
&lt;h2&gt;9. 最佳实践速查&lt;/h2&gt;
&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;场景&lt;/th&gt;
&lt;th&gt;建议&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;慢查询阈值&lt;/td&gt;
&lt;td&gt;生产环境设 1~2s；初排查可临时设 0.1s&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;索引设计&lt;/td&gt;
&lt;td&gt;等值列 &amp;gt; 范围列；低区分度列（如 status）放后面&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;避免索引失效&lt;/td&gt;
&lt;td&gt;不在索引列上做函数、隐式转换、&lt;code&gt;LIKE &apos;%xx&apos;&lt;/code&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;查询设计&lt;/td&gt;
&lt;td&gt;禁止 &lt;code&gt;SELECT *&lt;/code&gt;；大结果集必须加 &lt;code&gt;LIMIT&lt;/code&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;统计信息&lt;/td&gt;
&lt;td&gt;大批量导入后执行 &lt;code&gt;ANALYZE TABLE&lt;/code&gt;，保证执行计划准确&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;监控告警&lt;/td&gt;
&lt;td&gt;对 &lt;code&gt;Slow_queries&lt;/code&gt; 增长速率、&lt;code&gt;Threads_connected&lt;/code&gt; 设阈值告警&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;日志维护&lt;/td&gt;
&lt;td&gt;开启 logrotate，避免慢查询日志撑满磁盘&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;
</content:encoded></item><item><title>MySQL窗口函数</title><link>https://meteor-comet.github.io/posts/mysql-window-functions/</link><guid isPermaLink="true">https://meteor-comet.github.io/posts/mysql-window-functions/</guid><description>OVER / PARTITION BY / ORDER BY / FRAME / LAG / LEAD</description><pubDate>Mon, 19 Jun 2023 00:00:00 GMT</pubDate><content:encoded>&lt;h2&gt;学习目标&lt;/h2&gt;
&lt;ul&gt;
&lt;li&gt;掌握窗口函数的语法与使用场景&lt;/li&gt;
&lt;li&gt;熟悉排名/偏移/聚合类窗口函数（ROW_NUMBER、RANK、LAG、LEAD、SUM OVER等）&lt;/li&gt;
&lt;li&gt;理解窗口帧（FRAME）的含义与性能影响&lt;/li&gt;
&lt;li&gt;能使用窗口函数解决常见业务需求（排名、同环比、累计值、分组TopN）&lt;/li&gt;
&lt;/ul&gt;
&lt;h2&gt;学习计划&lt;/h2&gt;
&lt;ol&gt;
&lt;li&gt;窗口函数基础与语法&lt;/li&gt;
&lt;li&gt;排名类函数：ROW_NUMBER / RANK / DENSE_RANK / NTILE&lt;/li&gt;
&lt;li&gt;偏移类函数：LAG / LEAD / FIRST_VALUE / LAST_VALUE / NTH_VALUE&lt;/li&gt;
&lt;li&gt;聚合窗口：SUM / AVG / COUNT / MAX / MIN OVER&lt;/li&gt;
&lt;li&gt;窗口帧（FRAME）与ROWS/RANGE区别&lt;/li&gt;
&lt;li&gt;实战示例：电商销售场景&lt;/li&gt;
&lt;li&gt;注意事项与性能优化&lt;/li&gt;
&lt;li&gt;练习题与扩展&lt;/li&gt;
&lt;/ol&gt;
&lt;hr /&gt;
&lt;h2&gt;1. 窗口函数基础与语法&lt;/h2&gt;
&lt;ul&gt;
&lt;li&gt;&lt;strong&gt;窗口函数定义&lt;/strong&gt;：在不折叠行的前提下，对一组相关行进行计算并将结果返回到每一行。&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;与普通聚合的区别&lt;/strong&gt;：聚合函数（GROUP BY）会聚合成更少的行；窗口函数保留每一行。&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;基本语法&lt;/strong&gt;：&lt;/li&gt;
&lt;/ul&gt;
&lt;pre&gt;&lt;code&gt;&amp;lt;window_function&amp;gt;() OVER (
  [PARTITION BY &amp;lt;expr_list&amp;gt;]
  [ORDER BY &amp;lt;order_list&amp;gt;]
  [&amp;lt;frame_clause&amp;gt;]
)
&lt;/code&gt;&lt;/pre&gt;
&lt;ul&gt;
&lt;li&gt;&lt;strong&gt;位置限制&lt;/strong&gt;：仅能用于SELECT与ORDER BY子句，不能直接用于WHERE，需要借助子查询/CTE。&lt;/li&gt;
&lt;/ul&gt;
&lt;hr /&gt;
&lt;h2&gt;2. 排名类函数&lt;/h2&gt;
&lt;h3&gt;2.1 ROW_NUMBER&lt;/h3&gt;
&lt;p&gt;为分区内的每行分配唯一且连续的序号。&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;SELECT user_id, amount,
       ROW_NUMBER() OVER (PARTITION BY user_id ORDER BY amount DESC) AS rk
FROM sales;
&lt;/code&gt;&lt;/pre&gt;
&lt;h3&gt;2.2 RANK&lt;/h3&gt;
&lt;p&gt;同分并列，排名会跳跃（1,2,2,4）。&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;SELECT user_id, amount,
       RANK() OVER (PARTITION BY user_id ORDER BY amount DESC) AS rk
FROM sales;
&lt;/code&gt;&lt;/pre&gt;
&lt;h3&gt;2.3 DENSE_RANK&lt;/h3&gt;
&lt;p&gt;同分并列但不跳跃（1,2,2,3）。&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;SELECT user_id, amount,
       DENSE_RANK() OVER (PARTITION BY user_id ORDER BY amount DESC) AS rk
FROM sales;
&lt;/code&gt;&lt;/pre&gt;
&lt;h3&gt;2.4 NTILE(n)&lt;/h3&gt;
&lt;p&gt;将分区内的行尽量均匀地分到n个桶中。&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;SELECT user_id, amount,
       NTILE(4) OVER (PARTITION BY user_id ORDER BY amount DESC) AS bucket
FROM sales;
&lt;/code&gt;&lt;/pre&gt;
&lt;hr /&gt;
&lt;h2&gt;3. 偏移类函数&lt;/h2&gt;
&lt;h3&gt;3.1 LAG/LEAD&lt;/h3&gt;
&lt;ul&gt;
&lt;li&gt;LAG：取当前行之前第k行的值（默认k=1）&lt;/li&gt;
&lt;li&gt;LEAD：取当前行之后第k行的值&lt;/li&gt;
&lt;/ul&gt;
&lt;pre&gt;&lt;code&gt;SELECT user_id, order_date, amount,
       LAG(amount, 1, 0)  OVER (PARTITION BY user_id ORDER BY order_date) AS prev_amount,
       LEAD(amount, 1, 0) OVER (PARTITION BY user_id ORDER BY order_date) AS next_amount
FROM sales;
&lt;/code&gt;&lt;/pre&gt;
&lt;h3&gt;3.2 FIRST_VALUE / LAST_VALUE&lt;/h3&gt;
&lt;p&gt;取分区内（或指定帧）首/尾值。注意默认帧会随ORDER BY移动，常与显式帧一起使用。&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;SELECT user_id, order_date, amount,
       FIRST_VALUE(amount) OVER (
         PARTITION BY user_id ORDER BY order_date
         ROWS BETWEEN UNBOUNDED PRECEDING AND UNBOUNDED FOLLOWING
       ) AS first_amt,
       LAST_VALUE(amount)  OVER (
         PARTITION BY user_id ORDER BY order_date
         ROWS BETWEEN UNBOUNDED PRECEDING AND UNBOUNDED FOLLOWING
       ) AS last_amt
FROM sales;
&lt;/code&gt;&lt;/pre&gt;
&lt;h3&gt;3.3 NTH_VALUE&lt;/h3&gt;
&lt;p&gt;取分区内第n个值（随帧而变）。&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;SELECT user_id, order_date, amount,
       NTH_VALUE(amount, 3) OVER (
         PARTITION BY user_id ORDER BY order_date
         ROWS BETWEEN UNBOUNDED PRECEDING AND UNBOUNDED FOLLOWING
       ) AS third_amt
FROM sales;
&lt;/code&gt;&lt;/pre&gt;
&lt;hr /&gt;
&lt;h2&gt;4. 聚合窗口&lt;/h2&gt;
&lt;p&gt;在窗口上执行SUM/AVG/COUNT/MAX/MIN等，常用于累计、滑动窗口。&lt;/p&gt;
&lt;h3&gt;4.1 累计值（running total）&lt;/h3&gt;
&lt;pre&gt;&lt;code&gt;SELECT user_id, order_date, amount,
       SUM(amount) OVER (
         PARTITION BY user_id ORDER BY order_date
         ROWS BETWEEN UNBOUNDED PRECEDING AND CURRENT ROW
       ) AS running_amount
FROM sales;
&lt;/code&gt;&lt;/pre&gt;
&lt;h3&gt;4.2 滑动窗口（近N行）&lt;/h3&gt;
&lt;pre&gt;&lt;code&gt;SELECT user_id, order_date, amount,
       AVG(amount) OVER (
         PARTITION BY user_id ORDER BY order_date
         ROWS BETWEEN 2 PRECEDING AND CURRENT ROW
       ) AS avg_3_rows
FROM sales;
&lt;/code&gt;&lt;/pre&gt;
&lt;h3&gt;4.3 同比/环比（与上一期比较）&lt;/h3&gt;
&lt;pre&gt;&lt;code&gt;SELECT user_id, order_month, amount,
       LAG(amount) OVER (PARTITION BY user_id ORDER BY order_month) AS prev_amount,
       amount - LAG(amount) OVER (PARTITION BY user_id ORDER BY order_month) AS mom_diff,
       CASE WHEN LAG(amount) OVER (PARTITION BY user_id ORDER BY order_month) = 0 THEN NULL
            ELSE ROUND(
              (amount - LAG(amount) OVER (PARTITION BY user_id ORDER BY order_month))
              / LAG(amount) OVER (PARTITION BY user_id ORDER BY order_month) * 100, 2)
       END AS mom_rate
FROM monthly_sales;
&lt;/code&gt;&lt;/pre&gt;
&lt;hr /&gt;
&lt;h2&gt;5. 窗口帧（FRAME）与ROWS/RANGE&lt;/h2&gt;
&lt;ul&gt;
&lt;li&gt;&lt;strong&gt;ROWS&lt;/strong&gt;：按物理行计数，严格依赖行数。&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;RANGE&lt;/strong&gt;：按排序键的逻辑范围（相同排序值为同一范围）。对数值/日期等更直观，但在MySQL中常见实现限制较多。&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;常见帧写法&lt;/strong&gt;：
&lt;ul&gt;
&lt;li&gt;&lt;code&gt;ROWS BETWEEN UNBOUNDED PRECEDING AND CURRENT ROW&lt;/code&gt;（从分区首到当前行）&lt;/li&gt;
&lt;li&gt;&lt;code&gt;ROWS BETWEEN N PRECEDING AND CURRENT ROW&lt;/code&gt;（近N行含当前行）&lt;/li&gt;
&lt;li&gt;&lt;code&gt;ROWS BETWEEN UNBOUNDED PRECEDING AND UNBOUNDED FOLLOWING&lt;/code&gt;（整个分区）&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;注意&lt;/strong&gt;：未显式指定帧时，很多函数会采用默认帧（受ORDER BY影响），在FIRST_VALUE/LAST_VALUE等场景建议显式帧以避免“尾值漂移”。&lt;/li&gt;
&lt;/ul&gt;
&lt;hr /&gt;
&lt;h2&gt;6. 实战示例：电商销售&lt;/h2&gt;
&lt;h3&gt;6.1 示例表结构&lt;/h3&gt;
&lt;pre&gt;&lt;code&gt;-- 销售明细
CREATE TABLE sales (
  order_id    BIGINT PRIMARY KEY,
  user_id     BIGINT NOT NULL,
  order_date  DATE   NOT NULL,
  amount      DECIMAL(10,2) NOT NULL
);
&lt;/code&gt;&lt;/pre&gt;
&lt;h3&gt;6.2 需求1：用户内订单金额排名并取Top3&lt;/h3&gt;
&lt;pre&gt;&lt;code&gt;WITH ranked AS (
  SELECT *,
         ROW_NUMBER() OVER (PARTITION BY user_id ORDER BY amount DESC) AS rk
  FROM sales
)
SELECT * FROM ranked WHERE rk &amp;lt;= 3;
&lt;/code&gt;&lt;/pre&gt;
&lt;h3&gt;6.3 需求2：计算用户累计消费与上/下单金额&lt;/h3&gt;
&lt;pre&gt;&lt;code&gt;SELECT user_id, order_date, amount,
       SUM(amount) OVER (
         PARTITION BY user_id ORDER BY order_date
         ROWS BETWEEN UNBOUNDED PRECEDING AND CURRENT ROW
       ) AS cum_amount,
       LAG(amount)  OVER (PARTITION BY user_id ORDER BY order_date) AS prev_amount,
       LEAD(amount) OVER (PARTITION BY user_id ORDER BY order_date) AS next_amount
FROM sales;
&lt;/code&gt;&lt;/pre&gt;
&lt;h3&gt;6.4 需求3：按月环比增长率&lt;/h3&gt;
&lt;pre&gt;&lt;code&gt;SELECT user_id, order_month, amount,
       LAG(amount) OVER (PARTITION BY user_id ORDER BY order_month) AS prev_amount,
       ROUND(
         CASE WHEN LAG(amount) OVER (PARTITION BY user_id ORDER BY order_month) = 0 THEN NULL
              ELSE (amount - LAG(amount) OVER (PARTITION BY user_id ORDER BY order_month))
                   / LAG(amount) OVER (PARTITION BY user_id ORDER BY order_month) * 100
         END, 2) AS mom_rate
FROM monthly_sales;
&lt;/code&gt;&lt;/pre&gt;
&lt;hr /&gt;
&lt;h2&gt;7. 注意事项与性能优化&lt;/h2&gt;
&lt;ul&gt;
&lt;li&gt;&lt;strong&gt;不能用于WHERE&lt;/strong&gt;：将窗口计算放在子查询/CTE，再在外层过滤。&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;索引优化&lt;/strong&gt;：为&lt;code&gt;PARTITION BY&lt;/code&gt;与&lt;code&gt;ORDER BY&lt;/code&gt;涉及的列建立合适索引，减少排序/临时表开销。&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;帧大小&lt;/strong&gt;：&lt;code&gt;UNBOUNDED&lt;/code&gt;帧可能扫描大量数据；滑动窗口选择合适N。&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;内存与临时表&lt;/strong&gt;：大窗口计算可能落磁盘；关注&lt;code&gt;tmp_table_size&lt;/code&gt;、&lt;code&gt;max_heap_table_size&lt;/code&gt;。&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;版本要求&lt;/strong&gt;：需MySQL 8.0+（5.7不支持窗口函数）。&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;与聚合混用&lt;/strong&gt;：先窗口后聚合或先聚合后窗口，注意语义与行数变化。&lt;/li&gt;
&lt;/ul&gt;
&lt;hr /&gt;
&lt;h2&gt;8. 练习题与扩展&lt;/h2&gt;
&lt;ol&gt;
&lt;li&gt;计算每位用户最近3单的平均客单价。&lt;/li&gt;
&lt;li&gt;统计各品类Top5订单，并给出每单相对该品类均值的差值。&lt;/li&gt;
&lt;li&gt;在同一查询中同时返回累计金额、上/下单金额、分位桶（NTILE(4)）。&lt;/li&gt;
&lt;li&gt;尝试用窗口函数重写传统子查询实现的“分组取TopN”。&lt;/li&gt;
&lt;/ol&gt;
&lt;hr /&gt;
&lt;h2&gt;总结&lt;/h2&gt;
&lt;ul&gt;
&lt;li&gt;窗口函数在保留明细行的同时进行分组内计算，是现代SQL分析的核心能力。&lt;/li&gt;
&lt;li&gt;掌握排名、偏移、聚合与帧控制，可以覆盖大多数业务分析需求。&lt;/li&gt;
&lt;li&gt;重视索引与帧控制，避免无界窗口造成的性能问题。&lt;/li&gt;
&lt;/ul&gt;
</content:encoded></item><item><title>Java常用API</title><link>https://meteor-comet.github.io/posts/java-common-api/</link><guid isPermaLink="true">https://meteor-comet.github.io/posts/java-common-api/</guid><description>Java核心类库与API使用指南</description><pubDate>Thu, 15 Jun 2023 00:00:00 GMT</pubDate><content:encoded>&lt;h1&gt;Java常用API学习日志&lt;/h1&gt;
&lt;h2&gt;Java常用API学习&lt;/h2&gt;
&lt;p&gt;本系列日志将记录Java常用API的学习过程，涵盖Java标准库中的核心API，包括字符串处理、集合框架、IO流、多线程、反射、注解、日期时间处理等常用功能。&lt;/p&gt;
&lt;h3&gt;学习目标&lt;/h3&gt;
&lt;ul&gt;
&lt;li&gt;掌握Java标准库中常用API的使用方法&lt;/li&gt;
&lt;li&gt;理解各种API的设计思想和适用场景&lt;/li&gt;
&lt;li&gt;熟练运用API解决实际编程问题&lt;/li&gt;
&lt;li&gt;了解API的性能特性和最佳实践&lt;/li&gt;
&lt;li&gt;为后续框架学习打下坚实基础&lt;/li&gt;
&lt;/ul&gt;
&lt;h3&gt;学习计划&lt;/h3&gt;
&lt;ol&gt;
&lt;li&gt;&lt;strong&gt;字符串处理API&lt;/strong&gt;：String、StringBuilder、StringBuffer、StringJoiner等&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;集合框架API&lt;/strong&gt;：List、Set、Map及其实现类&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;IO流API&lt;/strong&gt;：字节流、字符流、缓冲流、转换流等&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;多线程API&lt;/strong&gt;：Thread、Runnable、线程池、同步机制&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;反射与注解API&lt;/strong&gt;：Class、Method、Field、注解处理器&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;日期时间API&lt;/strong&gt;：Date、Calendar、LocalDateTime、DateTimeFormatter&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;工具类API&lt;/strong&gt;：Arrays、Collections、Objects、Optional等&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;大数运算&lt;/strong&gt;：BigInteger、BigDecimal&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;正则表达式&lt;/strong&gt;：Pattern、Matcher、常用正则模式&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;包装类&lt;/strong&gt;：Integer、Character、自动装箱拆箱&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Lambda表达式&lt;/strong&gt;：函数式接口、Stream API、方法引用&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Apache Commons IO&lt;/strong&gt;：FileUtils、IOUtils、文件操作&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Hutool工具库&lt;/strong&gt;：StrUtil、DateUtil、CollUtil、HttpUtil、JSONUtil&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;网络编程API&lt;/strong&gt;：Socket、URL、HTTP客户端等&lt;/li&gt;
&lt;/ol&gt;
&lt;h3&gt;学习内容&lt;/h3&gt;
&lt;h4&gt;1. 字符串处理API&lt;/h4&gt;
&lt;h5&gt;1.1 String类常用方法&lt;/h5&gt;
&lt;p&gt;&lt;strong&gt;字符串创建与基本操作：&lt;/strong&gt;&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;// 字符串创建
String str1 = &quot;Hello&quot;;
String str2 = new String(&quot;World&quot;);
String str3 = String.valueOf(123);

// 基本操作
String str = &quot;Hello World&quot;;
int length = str.length();           // 获取长度
char ch = str.charAt(0);            // 获取字符
String sub = str.substring(0, 5);   // 截取子串
String upper = str.toUpperCase();    // 转大写
String lower = str.toLowerCase();    // 转小写

// 输出结果
System.out.println(&quot;str1: &quot; + str1);           // 输出: str1: Hello
System.out.println(&quot;str2: &quot; + str2);           // 输出: str2: World
System.out.println(&quot;str3: &quot; + str3);           // 输出: str3: 123
System.out.println(&quot;length: &quot; + length);       // 输出: length: 11
System.out.println(&quot;ch: &quot; + ch);               // 输出: ch: H
System.out.println(&quot;sub: &quot; + sub);             // 输出: sub: Hello
System.out.println(&quot;upper: &quot; + upper);         // 输出: upper: HELLO WORLD
System.out.println(&quot;lower: &quot; + lower);         // 输出: lower: hello world
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;&lt;strong&gt;字符串查找与替换：&lt;/strong&gt;&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;String str = &quot;Hello World&quot;;
boolean contains = str.contains(&quot;World&quot;);    // 是否包含
int index = str.indexOf(&quot;o&quot;);               // 查找字符位置
int lastIndex = str.lastIndexOf(&quot;o&quot;);       // 从后查找
String replaced = str.replace(&quot;World&quot;, &quot;Java&quot;); // 替换
String[] parts = str.split(&quot; &quot;);            // 分割

// 输出结果
System.out.println(&quot;contains: &quot; + contains);     // 输出: contains: true
System.out.println(&quot;index: &quot; + index);           // 输出: index: 4
System.out.println(&quot;lastIndex: &quot; + lastIndex);   // 输出: lastIndex: 7
System.out.println(&quot;replaced: &quot; + replaced);     // 输出: replaced: Hello Java
System.out.println(&quot;parts[0]: &quot; + parts[0]);     // 输出: parts[0]: Hello
System.out.println(&quot;parts[1]: &quot; + parts[1]);     // 输出: parts[1]: World
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;&lt;strong&gt;字符串比较：&lt;/strong&gt;&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;String s1 = &quot;Hello&quot;;
String s2 = &quot;hello&quot;;
boolean equals = s1.equals(s2);             // 内容比较
boolean ignoreCase = s1.equalsIgnoreCase(s2); // 忽略大小写比较
int compare = s1.compareTo(s2);             // 字典序比较

// 输出结果
System.out.println(&quot;equals: &quot; + equals);           // 输出: equals: false
System.out.println(&quot;ignoreCase: &quot; + ignoreCase);   // 输出: ignoreCase: true
System.out.println(&quot;compare: &quot; + compare);         // 输出: compare: -32 (H的ASCII码比h小32)
&lt;/code&gt;&lt;/pre&gt;
&lt;h5&gt;1.2 StringBuilder与StringBuffer&lt;/h5&gt;
&lt;p&gt;&lt;strong&gt;StringBuilder（非线程安全）：&lt;/strong&gt;&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;StringBuilder sb = new StringBuilder();
sb.append(&quot;Hello&quot;);
sb.append(&quot; &quot;);
sb.append(&quot;World&quot;);
String result = sb.toString();

// 链式调用
StringBuilder sb2 = new StringBuilder()
    .append(&quot;Hello&quot;)
    .append(&quot; &quot;)
    .append(&quot;World&quot;);

// 输出结果
System.out.println(&quot;result: &quot; + result);           // 输出: result: Hello World
System.out.println(&quot;sb2: &quot; + sb2.toString());      // 输出: sb2: Hello World
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;&lt;strong&gt;StringBuffer（线程安全）：&lt;/strong&gt;&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;StringBuffer buffer = new StringBuffer();
buffer.append(&quot;Hello&quot;);
buffer.append(&quot; &quot;);
buffer.append(&quot;World&quot;);
String result = buffer.toString();

// 输出结果
System.out.println(&quot;result: &quot; + result);           // 输出: result: Hello World
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;&lt;strong&gt;常用方法：&lt;/strong&gt;&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;StringBuilder sb = new StringBuilder(&quot;Hello&quot;);
sb.insert(5, &quot; World&quot;);     // 插入
System.out.println(&quot;insert: &quot; + sb);               // 输出: insert: Hello World

sb.delete(5, 11);           // 删除
System.out.println(&quot;delete: &quot; + sb);               // 输出: delete: Hello

sb.reverse();               // 反转
System.out.println(&quot;reverse: &quot; + sb);              // 输出: reverse: olleH

sb.setCharAt(0, &apos;h&apos;);      // 设置字符
System.out.println(&quot;setCharAt: &quot; + sb);            // 输出: setCharAt: hlleH
&lt;/code&gt;&lt;/pre&gt;
&lt;h5&gt;1.3 StringJoiner&lt;/h5&gt;
&lt;p&gt;&lt;strong&gt;基本使用：&lt;/strong&gt;&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;StringJoiner joiner = new StringJoiner(&quot;, &quot;, &quot;[&quot;, &quot;]&quot;);
joiner.add(&quot;Apple&quot;);
joiner.add(&quot;Banana&quot;);
joiner.add(&quot;Orange&quot;);
String result = joiner.toString();

// 输出结果
System.out.println(&quot;result: &quot; + result);           // 输出: result: [Apple, Banana, Orange]
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;&lt;strong&gt;简化字符串拼接：&lt;/strong&gt;&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;String[] names = {&quot;Alice&quot;, &quot;Bob&quot;, &quot;Charlie&quot;};
String result = String.join(&quot;, &quot;, names);

// 输出结果
System.out.println(&quot;result: &quot; + result);           // 输出: result: Alice, Bob, Charlie
&lt;/code&gt;&lt;/pre&gt;
&lt;h4&gt;2. 集合框架API&lt;/h4&gt;
&lt;h5&gt;2.1 List接口及实现&lt;/h5&gt;
&lt;p&gt;&lt;strong&gt;ArrayList（动态数组）：&lt;/strong&gt;&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;List&amp;lt;String&amp;gt; list = new ArrayList&amp;lt;&amp;gt;();
list.add(&quot;Apple&quot;);
list.add(&quot;Banana&quot;);
list.add(&quot;Orange&quot;);

// 遍历方式
System.out.println(&quot;=== 遍历方式 ===&quot;);
for (String item : list) {
    System.out.println(item);
}
// 输出:
// Apple
// Banana
// Orange

list.forEach(System.out::println);
// 输出:
// Apple
// Banana
// Orange

// 常用操作
String first = list.get(0);                    // 获取元素
list.set(0, &quot;Grape&quot;);                         // 设置元素
boolean contains = list.contains(&quot;Apple&quot;);     // 是否包含
int size = list.size();                       // 大小

// 输出结果
System.out.println(&quot;first: &quot; + first);         // 输出: first: Apple
System.out.println(&quot;contains Apple: &quot; + contains); // 输出: contains Apple: false
System.out.println(&quot;size: &quot; + size);           // 输出: size: 3
System.out.println(&quot;list after set: &quot; + list); // 输出: list after set: [Grape, Banana, Orange]
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;&lt;strong&gt;LinkedList（链表）：&lt;/strong&gt;&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;LinkedList&amp;lt;String&amp;gt; linkedList = new LinkedList&amp;lt;&amp;gt;();
linkedList.addFirst(&quot;First&quot;);   // 头部添加
linkedList.addLast(&quot;Last&quot;);     // 尾部添加
linkedList.add(&quot;Middle&quot;);       // 中间添加

// 输出结果
System.out.println(&quot;linkedList: &quot; + linkedList); // 输出: linkedList: [First, Middle, Last]

String first = linkedList.removeFirst();       // 头部删除
String last = linkedList.removeLast();        // 尾部删除
String peek = linkedList.peek();              // 查看头部元素

// 输出结果
System.out.println(&quot;removed first: &quot; + first); // 输出: removed first: First
System.out.println(&quot;removed last: &quot; + last);   // 输出: removed last: Last
System.out.println(&quot;peek: &quot; + peek);           // 输出: peek: Middle
System.out.println(&quot;final list: &quot; + linkedList); // 输出: final list: [Middle]
&lt;/code&gt;&lt;/pre&gt;
&lt;h5&gt;2.2 Set接口及实现&lt;/h5&gt;
&lt;p&gt;&lt;strong&gt;HashSet（哈希集合）：&lt;/strong&gt;&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;Set&amp;lt;String&amp;gt; set = new HashSet&amp;lt;&amp;gt;();
set.add(&quot;Apple&quot;);
set.add(&quot;Banana&quot;);
set.add(&quot;Apple&quot;); // 重复元素不会添加

// 遍历
System.out.println(&quot;=== HashSet遍历 ===&quot;);
set.forEach(System.out::println);
// 输出:
// Apple
// Banana

// 常用操作
boolean contains = set.contains(&quot;Apple&quot;);          // 是否包含
boolean removed = set.remove(&quot;Apple&quot;);            // 删除
int size = set.size();                            // 大小

// 输出结果
System.out.println(&quot;contains Apple: &quot; + contains); // 输出: contains Apple: true
System.out.println(&quot;removed Apple: &quot; + removed);   // 输出: removed Apple: true
System.out.println(&quot;size: &quot; + size);               // 输出: size: 1
System.out.println(&quot;final set: &quot; + set);           // 输出: final set: [Banana]
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;&lt;strong&gt;TreeSet（有序集合）：&lt;/strong&gt;&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;TreeSet&amp;lt;String&amp;gt; treeSet = new TreeSet&amp;lt;&amp;gt;();
treeSet.add(&quot;Zebra&quot;);
treeSet.add(&quot;Apple&quot;);
treeSet.add(&quot;Banana&quot;);
// 自动排序：Apple, Banana, Zebra

// 输出结果
System.out.println(&quot;treeSet: &quot; + treeSet); // 输出: treeSet: [Apple, Banana, Zebra]

// 获取第一个和最后一个
String first = treeSet.first();
String last = treeSet.last();

// 输出结果
System.out.println(&quot;first: &quot; + first); // 输出: first: Apple
System.out.println(&quot;last: &quot; + last);   // 输出: last: Zebra
&lt;/code&gt;&lt;/pre&gt;
&lt;h5&gt;2.3 Map接口及实现&lt;/h5&gt;
&lt;p&gt;&lt;strong&gt;HashMap（哈希映射）：&lt;/strong&gt;&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;Map&amp;lt;String, Integer&amp;gt; map = new HashMap&amp;lt;&amp;gt;();
map.put(&quot;Apple&quot;, 1);
map.put(&quot;Banana&quot;, 2);
map.put(&quot;Orange&quot;, 3);

// 遍历方式
System.out.println(&quot;=== HashMap遍历 ===&quot;);
map.forEach((key, value) -&amp;gt; 
    System.out.println(key + &quot;: &quot; + value));
// 输出:
// Apple: 1
// Banana: 2
// Orange: 3

for (Map.Entry&amp;lt;String, Integer&amp;gt; entry : map.entrySet()) {
    System.out.println(entry.getKey() + &quot; -&amp;gt; &quot; + entry.getValue());
}
// 输出:
// Apple -&amp;gt; 1
// Banana -&amp;gt; 2
// Orange -&amp;gt; 3

// 常用操作
Integer appleValue = map.get(&quot;Apple&quot;);              // 获取值
boolean containsKey = map.containsKey(&quot;Apple&quot;);     // 是否包含键
boolean containsValue = map.containsValue(1);       // 是否包含值
Integer removed = map.remove(&quot;Apple&quot;);              // 删除

// 输出结果
System.out.println(&quot;appleValue: &quot; + appleValue);           // 输出: appleValue: 1
System.out.println(&quot;containsKey Apple: &quot; + containsKey);   // 输出: containsKey Apple: true
System.out.println(&quot;containsValue 1: &quot; + containsValue);   // 输出: containsValue 1: true
System.out.println(&quot;removed: &quot; + removed);                 // 输出: removed: 1
System.out.println(&quot;final map: &quot; + map);                   // 输出: final map: {Banana=2, Orange=3}
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;&lt;strong&gt;TreeMap（有序映射）：&lt;/strong&gt;&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;TreeMap&amp;lt;String, Integer&amp;gt; treeMap = new TreeMap&amp;lt;&amp;gt;();
treeMap.put(&quot;Zebra&quot;, 3);
treeMap.put(&quot;Apple&quot;, 1);
treeMap.put(&quot;Banana&quot;, 2);
// 按键自动排序

// 输出结果
System.out.println(&quot;treeMap: &quot; + treeMap); // 输出: treeMap: {Apple=1, Banana=2, Zebra=3}

// 获取第一个和最后一个
Map.Entry&amp;lt;String, Integer&amp;gt; first = treeMap.firstEntry();
Map.Entry&amp;lt;String, Integer&amp;gt; last = treeMap.lastEntry();

// 输出结果
System.out.println(&quot;first: &quot; + first.getKey() + &quot;=&quot; + first.getValue()); // 输出: first: Apple=1
System.out.println(&quot;last: &quot; + last.getKey() + &quot;=&quot; + last.getValue());   // 输出: last: Zebra=3
&lt;/code&gt;&lt;/pre&gt;
&lt;h4&gt;3. IO流API&lt;/h4&gt;
&lt;h5&gt;3.1 字节流&lt;/h5&gt;
&lt;p&gt;&lt;strong&gt;FileInputStream（文件输入流）：&lt;/strong&gt;&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;try (FileInputStream fis = new FileInputStream(&quot;input.txt&quot;)) {
    byte[] buffer = new byte[1024];
    int bytesRead;
    while ((bytesRead = fis.read(buffer)) != -1) {
        // 处理读取的数据
        System.out.write(buffer, 0, bytesRead);
    }
} catch (IOException e) {
    e.printStackTrace();
}
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;&lt;strong&gt;FileOutputStream（文件输出流）：&lt;/strong&gt;&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;try (FileOutputStream fos = new FileOutputStream(&quot;output.txt&quot;)) {
    String data = &quot;Hello World&quot;;
    fos.write(data.getBytes());
} catch (IOException e) {
    e.printStackTrace();
}
&lt;/code&gt;&lt;/pre&gt;
&lt;h5&gt;3.2 字符流&lt;/h5&gt;
&lt;p&gt;&lt;strong&gt;FileReader（文件字符输入流）：&lt;/strong&gt;&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;try (FileReader reader = new FileReader(&quot;input.txt&quot;)) {
    char[] buffer = new char[1024];
    int charsRead;
    while ((charsRead = reader.read(buffer)) != -1) {
        System.out.print(new String(buffer, 0, charsRead));
    }
} catch (IOException e) {
    e.printStackTrace();
}
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;&lt;strong&gt;FileWriter（文件字符输出流）：&lt;/strong&gt;&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;try (FileWriter writer = new FileWriter(&quot;output.txt&quot;)) {
    writer.write(&quot;Hello World&quot;);
} catch (IOException e) {
    e.printStackTrace();
}
&lt;/code&gt;&lt;/pre&gt;
&lt;h5&gt;3.3 缓冲流&lt;/h5&gt;
&lt;p&gt;&lt;strong&gt;BufferedReader（缓冲字符输入流）：&lt;/strong&gt;&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;try (BufferedReader reader = new BufferedReader(
        new FileReader(&quot;input.txt&quot;))) {
    String line;
    while ((line = reader.readLine()) != null) {
        System.out.println(line);
    }
} catch (IOException e) {
    e.printStackTrace();
}
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;&lt;strong&gt;BufferedWriter（缓冲字符输出流）：&lt;/strong&gt;&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;try (BufferedWriter writer = new BufferedWriter(
        new FileWriter(&quot;output.txt&quot;))) {
    writer.write(&quot;Line 1&quot;);
    writer.newLine();
    writer.write(&quot;Line 2&quot;);
} catch (IOException e) {
    e.printStackTrace();
}
&lt;/code&gt;&lt;/pre&gt;
&lt;h4&gt;4. 多线程API&lt;/h4&gt;
&lt;h5&gt;4.1 线程创建&lt;/h5&gt;
&lt;p&gt;&lt;strong&gt;继承Thread类：&lt;/strong&gt;&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;class MyThread extends Thread {
    @Override
    public void run() {
        System.out.println(&quot;Thread running: &quot; + Thread.currentThread().getName());
    }
}

// 使用
MyThread thread = new MyThread();
thread.start();
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;&lt;strong&gt;实现Runnable接口：&lt;/strong&gt;&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;class MyRunnable implements Runnable {
    @Override
    public void run() {
        System.out.println(&quot;Runnable running: &quot; + Thread.currentThread().getName());
    }
}

// 使用
Thread thread = new Thread(new MyRunnable());
thread.start();
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;&lt;strong&gt;Lambda表达式：&lt;/strong&gt;&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;Thread thread = new Thread(() -&amp;gt; {
    System.out.println(&quot;Lambda thread running&quot;);
});
thread.start();
&lt;/code&gt;&lt;/pre&gt;
&lt;h5&gt;4.2 线程控制&lt;/h5&gt;
&lt;p&gt;&lt;strong&gt;基本控制：&lt;/strong&gt;&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;Thread thread = new Thread(() -&amp;gt; {
    for (int i = 0; i &amp;lt; 5; i++) {
        System.out.println(&quot;Count: &quot; + i);
        try {
            Thread.sleep(1000); // 休眠1秒
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
    }
});

thread.start();
thread.join(); // 等待线程结束
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;&lt;strong&gt;线程状态：&lt;/strong&gt;&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;Thread thread = new Thread(() -&amp;gt; {
    // 线程任务
});

System.out.println(&quot;State: &quot; + thread.getState()); // NEW
thread.start();
System.out.println(&quot;State: &quot; + thread.getState()); // RUNNABLE
&lt;/code&gt;&lt;/pre&gt;
&lt;h5&gt;4.3 线程同步&lt;/h5&gt;
&lt;p&gt;&lt;strong&gt;synchronized关键字：&lt;/strong&gt;&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;class Counter {
    private int count = 0;
    
    public synchronized void increment() {
        count++;
    }
    
    public synchronized int getCount() {
        return count;
    }
}
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;&lt;strong&gt;ReentrantLock：&lt;/strong&gt;&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;import java.util.concurrent.locks.ReentrantLock;

class Counter {
    private int count = 0;
    private ReentrantLock lock = new ReentrantLock();
    
    public void increment() {
        lock.lock();
        try {
            count++;
        } finally {
            lock.unlock();
        }
    }
}
&lt;/code&gt;&lt;/pre&gt;
&lt;h4&gt;5. 反射与注解API&lt;/h4&gt;
&lt;h5&gt;5.1 反射基础&lt;/h5&gt;
&lt;p&gt;&lt;strong&gt;获取Class对象：&lt;/strong&gt;&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;// 方式1：通过类名
Class&amp;lt;?&amp;gt; clazz1 = String.class;

// 方式2：通过对象
String str = &quot;Hello&quot;;
Class&amp;lt;?&amp;gt; clazz2 = str.getClass();

// 方式3：通过全限定名
try {
    Class&amp;lt;?&amp;gt; clazz3 = Class.forName(&quot;java.lang.String&quot;);
} catch (ClassNotFoundException e) {
    e.printStackTrace();
}
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;&lt;strong&gt;获取构造方法：&lt;/strong&gt;&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;Class&amp;lt;?&amp;gt; clazz = String.class;

// 获取所有公共构造方法
Constructor&amp;lt;?&amp;gt;[] constructors = clazz.getConstructors();

// 获取指定构造方法
try {
    Constructor&amp;lt;?&amp;gt; constructor = clazz.getConstructor(String.class);
    String obj = (String) constructor.newInstance(&quot;Hello&quot;);
} catch (Exception e) {
    e.printStackTrace();
}
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;&lt;strong&gt;获取方法：&lt;/strong&gt;&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;Class&amp;lt;?&amp;gt; clazz = String.class;

// 获取所有公共方法
Method[] methods = clazz.getMethods();

// 获取指定方法
try {
    Method method = clazz.getMethod(&quot;length&quot;);
    String str = &quot;Hello&quot;;
    int length = (int) method.invoke(str);
    System.out.println(&quot;Length: &quot; + length);
} catch (Exception e) {
    e.printStackTrace();
}
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;&lt;strong&gt;获取字段：&lt;/strong&gt;&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;class Person {
    private String name;
    public int age;
}

Class&amp;lt;?&amp;gt; clazz = Person.class;

// 获取所有公共字段
Field[] fields = clazz.getFields();

// 获取所有字段（包括私有）
Field[] allFields = clazz.getDeclaredFields();

// 访问私有字段
try {
    Field nameField = clazz.getDeclaredField(&quot;name&quot;);
    nameField.setAccessible(true); // 设置可访问
    Person person = new Person();
    nameField.set(person, &quot;Alice&quot;);
    String name = (String) nameField.get(person);
} catch (Exception e) {
    e.printStackTrace();
}
&lt;/code&gt;&lt;/pre&gt;
&lt;h5&gt;5.2 注解使用&lt;/h5&gt;
&lt;p&gt;&lt;strong&gt;自定义注解：&lt;/strong&gt;&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;import java.lang.annotation.*;

@Target(ElementType.METHOD)
@Retention(RetentionPolicy.RUNTIME)
@Documented
public @interface MyAnnotation {
    String value() default &quot;&quot;;
    int count() default 1;
}
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;&lt;strong&gt;使用注解：&lt;/strong&gt;&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;class TestClass {
    @MyAnnotation(value = &quot;test&quot;, count = 3)
    public void testMethod() {
        System.out.println(&quot;Test method&quot;);
    }
}
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;&lt;strong&gt;注解处理器：&lt;/strong&gt;&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;Class&amp;lt;?&amp;gt; clazz = TestClass.class;
Method[] methods = clazz.getMethods();

for (Method method : methods) {
    if (method.isAnnotationPresent(MyAnnotation.class)) {
        MyAnnotation annotation = method.getAnnotation(MyAnnotation.class);
        System.out.println(&quot;Value: &quot; + annotation.value());
        System.out.println(&quot;Count: &quot; + annotation.count());
    }
}
&lt;/code&gt;&lt;/pre&gt;
&lt;h4&gt;6. 日期时间API&lt;/h4&gt;
&lt;h5&gt;6.1 新日期时间API（Java 8+）&lt;/h5&gt;
&lt;p&gt;&lt;strong&gt;LocalDate（日期）：&lt;/strong&gt;&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;import java.time.LocalDate;
import java.time.format.DateTimeFormatter;

LocalDate today = LocalDate.now();
LocalDate specificDate = LocalDate.of(2025, 5, 15);

// 日期操作
LocalDate tomorrow = today.plusDays(1);
LocalDate yesterday = today.minusDays(1);
LocalDate nextMonth = today.plusMonths(1);

// 格式化
DateTimeFormatter formatter = DateTimeFormatter.ofPattern(&quot;yyyy-MM-dd&quot;);
String formatted = today.format(formatter);
LocalDate parsed = LocalDate.parse(&quot;2025-05-15&quot;, formatter);
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;&lt;strong&gt;LocalTime（时间）：&lt;/strong&gt;&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;import java.time.LocalTime;

LocalTime now = LocalTime.now();
LocalTime specificTime = LocalTime.of(14, 30, 45);

// 时间操作
LocalTime plusHours = now.plusHours(2);
LocalTime minusMinutes = now.minusMinutes(30);

// 格式化
DateTimeFormatter timeFormatter = DateTimeFormatter.ofPattern(&quot;HH:mm:ss&quot;);
String formatted = now.format(timeFormatter);
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;&lt;strong&gt;LocalDateTime（日期时间）：&lt;/strong&gt;&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;import java.time.LocalDateTime;

LocalDateTime now = LocalDateTime.now();
LocalDateTime specific = LocalDateTime.of(2025, 5, 15, 14, 30, 45);

// 日期时间操作
LocalDateTime plusDays = now.plusDays(1);
LocalDateTime minusHours = now.minusHours(2);

// 格式化
DateTimeFormatter formatter = DateTimeFormatter.ofPattern(&quot;yyyy-MM-dd HH:mm:ss&quot;);
String formatted = now.format(formatter);
&lt;/code&gt;&lt;/pre&gt;
&lt;h5&gt;6.2 传统日期时间API&lt;/h5&gt;
&lt;p&gt;&lt;strong&gt;Date类：&lt;/strong&gt;&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;import java.util.Date;
import java.text.SimpleDateFormat;

Date now = new Date();
Date specific = new Date(2025 - 1900, 4, 15); // 月份从0开始

// 格式化
SimpleDateFormat sdf = new SimpleDateFormat(&quot;yyyy-MM-dd HH:mm:ss&quot;);
String formatted = sdf.format(now);

// 解析
try {
    Date parsed = sdf.parse(&quot;2025-05-15 14:30:45&quot;);
} catch (ParseException e) {
    e.printStackTrace();
}
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;&lt;strong&gt;Calendar类：&lt;/strong&gt;&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;import java.util.Calendar;

Calendar cal = Calendar.getInstance();
cal.set(2025, Calendar.MAY, 15); // 月份从0开始

// 获取字段
int year = cal.get(Calendar.YEAR);
int month = cal.get(Calendar.MONTH);
int day = cal.get(Calendar.DAY_OF_MONTH);

// 操作
cal.add(Calendar.DAY_OF_MONTH, 1); // 加一天
cal.add(Calendar.MONTH, 1);        // 加一月
&lt;/code&gt;&lt;/pre&gt;
&lt;h4&gt;7. 工具类API&lt;/h4&gt;
&lt;h5&gt;7.1 Arrays工具类&lt;/h5&gt;
&lt;p&gt;&lt;strong&gt;数组操作：&lt;/strong&gt;&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;import java.util.Arrays;

int[] arr = {3, 1, 4, 1, 5, 9, 2, 6};

// 排序
Arrays.sort(arr);
System.out.println(&quot;sorted: &quot; + Arrays.toString(arr)); // 输出: sorted: [1, 1, 2, 3, 4, 5, 6, 9]

// 二分查找（需要先排序）
int index = Arrays.binarySearch(arr, 5);
System.out.println(&quot;index of 5: &quot; + index); // 输出: index of 5: 5

// 填充
Arrays.fill(arr, 0);
System.out.println(&quot;filled: &quot; + Arrays.toString(arr)); // 输出: filled: [0, 0, 0, 0, 0, 0, 0, 0]

// 复制
int[] original = {1, 2, 3, 4, 5};
int[] copy = Arrays.copyOf(original, original.length);
System.out.println(&quot;copy: &quot; + Arrays.toString(copy)); // 输出: copy: [1, 2, 3, 4, 5]

// 比较
boolean equals = Arrays.equals(original, copy);
System.out.println(&quot;equals: &quot; + equals); // 输出: equals: true

// 转换为字符串
String str = Arrays.toString(original);
System.out.println(&quot;string: &quot; + str); // 输出: string: [1, 2, 3, 4, 5]
&lt;/code&gt;&lt;/pre&gt;
&lt;h5&gt;7.2 Collections工具类&lt;/h5&gt;
&lt;p&gt;&lt;strong&gt;集合操作：&lt;/strong&gt;&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;import java.util.*;

List&amp;lt;String&amp;gt; list = Arrays.asList(&quot;Apple&quot;, &quot;Banana&quot;, &quot;Orange&quot;);

// 排序
Collections.sort(list);
System.out.println(&quot;sorted: &quot; + list); // 输出: sorted: [Apple, Banana, Orange]

// 反转
Collections.reverse(list);
System.out.println(&quot;reversed: &quot; + list); // 输出: reversed: [Orange, Banana, Apple]

// 随机打乱
Collections.shuffle(list);
System.out.println(&quot;shuffled: &quot; + list); // 输出: shuffled: [Banana, Apple, Orange] (顺序随机)

// 查找最大最小值
String max = Collections.max(list);
String min = Collections.min(list);
System.out.println(&quot;max: &quot; + max); // 输出: max: Orange
System.out.println(&quot;min: &quot; + min); // 输出: min: Apple

// 替换
Collections.replaceAll(list, &quot;Apple&quot;, &quot;Grape&quot;);
System.out.println(&quot;replaced: &quot; + list); // 输出: replaced: [Banana, Grape, Orange]

// 频率统计
int frequency = Collections.frequency(list, &quot;Banana&quot;);
System.out.println(&quot;frequency of Banana: &quot; + frequency); // 输出: frequency of Banana: 1
&lt;/code&gt;&lt;/pre&gt;
&lt;h5&gt;7.3 Objects工具类&lt;/h5&gt;
&lt;p&gt;&lt;strong&gt;对象操作：&lt;/strong&gt;&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;import java.util.Objects;

String str = null;

// 空值检查
boolean isNull = Objects.isNull(str);
boolean nonNull = Objects.nonNull(str);
System.out.println(&quot;isNull: &quot; + isNull);     // 输出: isNull: true
System.out.println(&quot;nonNull: &quot; + nonNull);   // 输出: nonNull: false

// 相等性比较
boolean equals = Objects.equals(&quot;Hello&quot;, &quot;Hello&quot;);
boolean equalsNull = Objects.equals(null, null);
System.out.println(&quot;equals: &quot; + equals);         // 输出: equals: true
System.out.println(&quot;equalsNull: &quot; + equalsNull); // 输出: equalsNull: true

// 哈希码
int hashCode = Objects.hash(&quot;Hello&quot;, &quot;World&quot;);
System.out.println(&quot;hashCode: &quot; + hashCode); // 输出: hashCode: 123456789 (具体值可能不同)

// 要求非空
try {
    String result = Objects.requireNonNull(str, &quot;String cannot be null&quot;);
} catch (NullPointerException e) {
    System.out.println(&quot;Exception: &quot; + e.getMessage()); // 输出: Exception: String cannot be null
}
&lt;/code&gt;&lt;/pre&gt;
&lt;h5&gt;7.4 Optional类&lt;/h5&gt;
&lt;p&gt;&lt;strong&gt;空值安全处理：&lt;/strong&gt;&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;import java.util.Optional;

String str = &quot;Hello&quot;;
Optional&amp;lt;String&amp;gt; optional = Optional.of(str);

// 安全获取值
String value = optional.orElse(&quot;Default&quot;);
System.out.println(&quot;value: &quot; + value); // 输出: value: Hello

// 条件处理
optional.ifPresent(System.out::println); // 输出: Hello

// 链式操作
Optional&amp;lt;String&amp;gt; result = optional
    .map(s -&amp;gt; s.toUpperCase())
    .filter(s -&amp;gt; s.length() &amp;gt; 3);
System.out.println(&quot;result: &quot; + result.get()); // 输出: result: HELLO

// 创建Optional
Optional&amp;lt;String&amp;gt; empty = Optional.empty();
Optional&amp;lt;String&amp;gt; ofNullable = Optional.ofNullable(str);

// 处理空Optional
String emptyValue = empty.orElse(&quot;Empty&quot;);
System.out.println(&quot;emptyValue: &quot; + emptyValue); // 输出: emptyValue: Empty
&lt;/code&gt;&lt;/pre&gt;
&lt;h5&gt;7.5 Math类&lt;/h5&gt;
&lt;p&gt;&lt;strong&gt;数学运算方法：&lt;/strong&gt;&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;import java.lang.Math;

// 基本数学运算
double abs = Math.abs(-5.5);        // 绝对值
double ceil = Math.ceil(5.3);       // 向上取整
double floor = Math.floor(5.7);     // 向下取整
double round = Math.round(5.5);     // 四舍五入
double max = Math.max(10, 20);      // 最大值
double min = Math.min(10, 20);      // 最小值

// 输出结果
System.out.println(&quot;abs(-5.5): &quot; + abs);     // 输出: abs(-5.5): 5.5
System.out.println(&quot;ceil(5.3): &quot; + ceil);    // 输出: ceil(5.3): 6.0
System.out.println(&quot;floor(5.7): &quot; + floor);  // 输出: floor(5.7): 5.0
System.out.println(&quot;round(5.5): &quot; + round);  // 输出: round(5.5): 6.0
System.out.println(&quot;max(10,20): &quot; + max);    // 输出: max(10,20): 20.0
System.out.println(&quot;min(10,20): &quot; + min);    // 输出: min(10,20): 10.0

// 幂运算
double pow = Math.pow(2, 3);        // 2的3次方
double sqrt = Math.sqrt(16);        // 平方根
double cbrt = Math.cbrt(27);        // 立方根

// 输出结果
System.out.println(&quot;pow(2,3): &quot; + pow);   // 输出: pow(2,3): 8.0
System.out.println(&quot;sqrt(16): &quot; + sqrt);  // 输出: sqrt(16): 4.0
System.out.println(&quot;cbrt(27): &quot; + cbrt);  // 输出: cbrt(27): 3.0

// 三角函数（参数为弧度）
double sin = Math.sin(Math.PI / 2); // 正弦
double cos = Math.cos(0);           // 余弦
double tan = Math.tan(Math.PI / 4); // 正切

// 输出结果
System.out.println(&quot;sin(π/2): &quot; + sin);   // 输出: sin(π/2): 1.0
System.out.println(&quot;cos(0): &quot; + cos);     // 输出: cos(0): 1.0
System.out.println(&quot;tan(π/4): &quot; + tan);   // 输出: tan(π/4): 1.0

// 反三角函数
double asin = Math.asin(1.0);       // 反正弦
double acos = Math.acos(1.0);       // 反余弦
double atan = Math.atan(1.0);       // 反正切

// 角度与弧度转换
double toRadians = Math.toRadians(180); // 角度转弧度
double toDegrees = Math.toDegrees(Math.PI); // 弧度转角度

// 对数函数
double log = Math.log(Math.E);      // 自然对数
double log10 = Math.log10(100);     // 常用对数

// 随机数
double random = Math.random();       // 0.0到1.0之间的随机数
int randomInt = (int)(Math.random() * 100); // 0到99的随机整数

// 输出结果
System.out.println(&quot;random: &quot; + random);     // 输出: random: 0.123456789 (具体值随机)
System.out.println(&quot;randomInt: &quot; + randomInt); // 输出: randomInt: 45 (具体值随机)

// 常量
double pi = Math.PI;                // 圆周率
double e = Math.E;                  // 自然对数的底

// 输出结果
System.out.println(&quot;π: &quot; + pi);     // 输出: π: 3.141592653589793
System.out.println(&quot;e: &quot; + e);      // 输出: e: 2.718281828459045
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;&lt;strong&gt;Math类的常用场景：&lt;/strong&gt;&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;// 生成指定范围的随机数
public static int randomRange(int min, int max) {
    return (int)(Math.random() * (max - min + 1)) + min;
}

// 计算两点间距离
public static double distance(double x1, double y1, double x2, double y2) {
    return Math.sqrt(Math.pow(x2 - x1, 2) + Math.pow(y2 - y1, 2));
}

// 判断是否为素数
public static boolean isPrime(int n) {
    if (n &amp;lt;= 1) return false;
    if (n &amp;lt;= 3) return true;
    if (n % 2 == 0 || n % 3 == 0) return false;
    
    int sqrt = (int)Math.sqrt(n);
    for (int i = 5; i &amp;lt;= sqrt; i += 6) {
        if (n % i == 0 || n % (i + 2) == 0) return false;
    }
    return true;
}

// 计算圆的面积
public static double circleArea(double radius) {
    return Math.PI * Math.pow(radius, 2);
}

// 计算球的体积
public static double sphereVolume(double radius) {
    return (4.0 / 3.0) * Math.PI * Math.pow(radius, 3);
}
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;&lt;strong&gt;Math类的注意事项：&lt;/strong&gt;&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;Math类中的所有方法都是静态的，可以直接通过类名调用&lt;/li&gt;
&lt;li&gt;三角函数参数为弧度，不是角度&lt;/li&gt;
&lt;li&gt;Math.random()生成的是伪随机数，对于需要高安全性的场景应使用SecureRandom&lt;/li&gt;
&lt;li&gt;浮点数运算可能存在精度问题，需要特别注意比较操作&lt;/li&gt;
&lt;/ul&gt;
&lt;h5&gt;7.6 System类&lt;/h5&gt;
&lt;p&gt;&lt;strong&gt;系统相关操作：&lt;/strong&gt;&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;import java.lang.System;

// 获取当前时间戳
long currentTime = System.currentTimeMillis();  // 毫秒时间戳
long nanoTime = System.nanoTime();              // 纳秒时间戳

// 输出结果
System.out.println(&quot;currentTime: &quot; + currentTime); // 输出: currentTime: 1640995200000 (具体值不同)
System.out.println(&quot;nanoTime: &quot; + nanoTime);       // 输出: nanoTime: 1234567890123456789 (具体值不同)

// 系统属性
String osName = System.getProperty(&quot;os.name&quot;);           // 操作系统名称
String javaVersion = System.getProperty(&quot;java.version&quot;); // Java版本
String userHome = System.getProperty(&quot;user.home&quot;);       // 用户主目录

// 输出结果
System.out.println(&quot;osName: &quot; + osName);         // 输出: osName: Windows 10 (或Linux、macOS等)
System.out.println(&quot;javaVersion: &quot; + javaVersion); // 输出: javaVersion: 17.0.1 (具体版本不同)
System.out.println(&quot;userHome: &quot; + userHome);     // 输出: userHome: C:\Users\username (具体路径不同)

// 数组复制
int[] source = {1, 2, 3, 4, 5};
int[] dest = new int[5];
System.arraycopy(source, 0, dest, 0, source.length);
System.out.println(&quot;dest: &quot; + Arrays.toString(dest)); // 输出: dest: [1, 2, 3, 4, 5]

// 垃圾回收
System.gc();  // 建议进行垃圾回收

// 程序退出
// System.exit(0);  // 正常退出
// System.exit(1);  // 异常退出

// 安全检查
SecurityManager sm = System.getSecurityManager();
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;&lt;strong&gt;System.out输出流：&lt;/strong&gt;&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;// 标准输出
System.out.println(&quot;Hello World&quot;);                    // 输出: Hello World
System.out.print(&quot;不换行输出&quot;);                        // 输出: 不换行输出
System.out.printf(&quot;格式化输出: %s, %d&quot;, &quot;字符串&quot;, 123); // 输出: 格式化输出: 字符串, 123

// 输出格式控制
System.out.printf(&quot;整数: %d%n&quot;, 100);                 // 输出: 整数: 100
System.out.printf(&quot;浮点数: %.2f%n&quot;, 3.14159);         // 输出: 浮点数: 3.14
System.out.printf(&quot;字符串: %s%n&quot;, &quot;Java&quot;);            // 输出: 字符串: Java
System.out.printf(&quot;字符: %c%n&quot;, &apos;A&apos;);                 // 输出: 字符: A
System.out.printf(&quot;布尔值: %b%n&quot;, true);              // 输出: 布尔值: true
System.out.printf(&quot;十六进制: %x%n&quot;, 255);             // 输出: 十六进制: ff
System.out.printf(&quot;八进制: %o%n&quot;, 255);               // 输出: 八进制: 377

// 重定向输出流
PrintStream originalOut = System.out;
try {
    // 重定向到文件
    PrintStream fileOut = new PrintStream(&quot;output.txt&quot;);
    System.setOut(fileOut);
    System.out.println(&quot;这条消息会写入文件&quot;);
} finally {
    // 恢复原始输出流
    System.setOut(originalOut);
}
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;&lt;strong&gt;System.in输入流：&lt;/strong&gt;&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;// 标准输入
Scanner scanner = new Scanner(System.in);
System.out.print(&quot;请输入姓名: &quot;);  // 输出: 请输入姓名: 
String name = scanner.nextLine();  // 等待用户输入
System.out.print(&quot;请输入年龄: &quot;);  // 输出: 请输入年龄: 
int age = scanner.nextInt();       // 等待用户输入

// 输出结果
System.out.println(&quot;姓名: &quot; + name); // 输出: 姓名: 张三 (用户输入的内容)
System.out.println(&quot;年龄: &quot; + age);  // 输出: 年龄: 25 (用户输入的内容)

// 直接读取字节
try {
    System.out.print(&quot;请输入一个字符: &quot;); // 输出: 请输入一个字符: 
    int ch = System.in.read();
    System.out.println(&quot;输入的字符: &quot; + (char)ch); // 输出: 输入的字符: A (用户输入的内容)
} catch (IOException e) {
    e.printStackTrace();
}

// 重定向输入流
try {
    // 从文件读取输入
    FileInputStream fileIn = new FileInputStream(&quot;input.txt&quot;);
    System.setIn(fileIn);
    Scanner fileScanner = new Scanner(System.in);
    while (fileScanner.hasNextLine()) {
        String line = fileScanner.nextLine();
        System.out.println(&quot;从文件读取: &quot; + line); // 输出: 从文件读取: 文件内容
    }
} catch (FileNotFoundException e) {
    e.printStackTrace();
}
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;&lt;strong&gt;System.err错误输出流：&lt;/strong&gt;&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;// 错误输出
System.err.println(&quot;这是一个错误消息&quot;);                    // 输出: 这是一个错误消息 (红色错误输出)
System.err.print(&quot;错误信息不换行&quot;);                        // 输出: 错误信息不换行 (红色错误输出)

// 格式化错误输出
System.err.printf(&quot;错误代码: %d, 错误信息: %s%n&quot;, 404, &quot;页面未找到&quot;); 
// 输出: 错误代码: 404, 错误信息: 页面未找到 (红色错误输出)

// 重定向错误流
PrintStream originalErr = System.err;
try {
    PrintStream errorFile = new PrintStream(&quot;error.log&quot;);
    System.setErr(errorFile);
    System.err.println(&quot;错误日志会写入文件&quot;);
} finally {
    System.setErr(originalErr);
}
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;&lt;strong&gt;System类的实用工具方法：&lt;/strong&gt;&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;public class SystemUtils {
    
    // 获取系统信息
    public static void printSystemInfo() {
        System.out.println(&quot;操作系统: &quot; + System.getProperty(&quot;os.name&quot;));
        System.out.println(&quot;Java版本: &quot; + System.getProperty(&quot;java.version&quot;));
        System.out.println(&quot;Java供应商: &quot; + System.getProperty(&quot;java.vendor&quot;));
        System.out.println(&quot;JVM版本: &quot; + System.getProperty(&quot;java.vm.version&quot;));
        System.out.println(&quot;用户目录: &quot; + System.getProperty(&quot;user.dir&quot;));
        System.out.println(&quot;用户主目录: &quot; + System.getProperty(&quot;user.home&quot;));
    }
    
    // 性能测试工具
    public static void performanceTest(Runnable task, String taskName) {
        long startTime = System.currentTimeMillis();
        long startNano = System.nanoTime();
        
        task.run();
        
        long endTime = System.currentTimeMillis();
        long endNano = System.nanoTime();
        
        System.out.println(taskName + &quot; 执行时间:&quot;);
        System.out.println(&quot;  毫秒: &quot; + (endTime - startTime) + &quot;ms&quot;);
        System.out.println(&quot;  纳秒: &quot; + (endNano - startNano) + &quot;ns&quot;);
    }
    
    // 内存使用情况
    public static void printMemoryInfo() {
        Runtime runtime = Runtime.getRuntime();
        long totalMemory = runtime.totalMemory();
        long freeMemory = runtime.freeMemory();
        long usedMemory = totalMemory - freeMemory;
        long maxMemory = runtime.maxMemory();
        
        System.out.println(&quot;内存使用情况:&quot;);
        System.out.println(&quot;  总内存: &quot; + (totalMemory / 1024 / 1024) + &quot;MB&quot;);
        System.out.println(&quot;  已用内存: &quot; + (usedMemory / 1024 / 1024) + &quot;MB&quot;);
        System.out.println(&quot;  空闲内存: &quot; + (freeMemory / 1024 / 1024) + &quot;MB&quot;);
        System.out.println(&quot;  最大内存: &quot; + (maxMemory / 1024 / 1024) + &quot;MB&quot;);
    }
    
    // 安全退出程序
    public static void safeExit(int code) {
        System.out.println(&quot;程序即将退出，代码: &quot; + code);
        System.gc();  // 建议垃圾回收
        System.exit(code);
    }
}

// 使用示例
public class SystemUtilsExample {
    public static void main(String[] args) {
        // 获取系统信息
        SystemUtils.printSystemInfo();
        // 输出:
        // 操作系统: Windows 10
        // Java版本: 17.0.1
        // Java供应商: Oracle Corporation
        // JVM版本: 17.0.1+12-LTS-39
        // 用户目录: C:\Users\username\project
        // 用户主目录: C:\Users\username
        
        // 性能测试
        SystemUtils.performanceTest(() -&amp;gt; {
            for (int i = 0; i &amp;lt; 1000000; i++) {
                Math.sqrt(i);
            }
        }, &quot;计算平方根&quot;);
        // 输出:
        // 计算平方根 执行时间:
        //   毫秒: 15ms
        //   纳秒: 15000000ns
        
        // 内存使用情况
        SystemUtils.printMemoryInfo();
        // 输出:
        // 内存使用情况:
        //   总内存: 256MB
        //   已用内存: 45MB
        //   空闲内存: 211MB
        //   最大内存: 4096MB
    }
}
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;&lt;strong&gt;System类的注意事项：&lt;/strong&gt;&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;System.currentTimeMillis()返回的是毫秒级时间戳，适合一般用途&lt;/li&gt;
&lt;li&gt;System.nanoTime()返回纳秒级时间戳，适合高精度计时&lt;/li&gt;
&lt;li&gt;System.exit()会强制终止JVM，谨慎使用&lt;/li&gt;
&lt;li&gt;System.gc()只是建议进行垃圾回收，不保证立即执行&lt;/li&gt;
&lt;li&gt;重定向流后记得恢复原始流，避免影响其他代码&lt;/li&gt;
&lt;li&gt;系统属性是全局的，修改会影响整个JVM&lt;/li&gt;
&lt;/ul&gt;
&lt;h5&gt;7.7 BigInteger和BigDecimal&lt;/h5&gt;
&lt;p&gt;&lt;strong&gt;BigInteger（大整数）：&lt;/strong&gt;&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;import java.math.BigInteger;

// 创建BigInteger
BigInteger bigInt1 = new BigInteger(&quot;123456789012345678901234567890&quot;);
BigInteger bigInt2 = BigInteger.valueOf(123456789);
BigInteger bigInt3 = BigInteger.ONE;  // 常量1
BigInteger bigInt4 = BigInteger.ZERO; // 常量0
BigInteger bigInt5 = BigInteger.TEN;  // 常量10

// 输出结果
System.out.println(&quot;bigInt1: &quot; + bigInt1); // 输出: bigInt1: 123456789012345678901234567890
System.out.println(&quot;bigInt2: &quot; + bigInt2); // 输出: bigInt2: 123456789
System.out.println(&quot;bigInt3: &quot; + bigInt3); // 输出: bigInt3: 1
System.out.println(&quot;bigInt4: &quot; + bigInt4); // 输出: bigInt4: 0
System.out.println(&quot;bigInt5: &quot; + bigInt5); // 输出: bigInt5: 10

// 基本运算
BigInteger a = new BigInteger(&quot;123456789&quot;);
BigInteger b = new BigInteger(&quot;987654321&quot;);

BigInteger sum = a.add(b);           // 加法
BigInteger diff = b.subtract(a);     // 减法
BigInteger product = a.multiply(b);  // 乘法
BigInteger quotient = b.divide(a);   // 除法
BigInteger remainder = b.remainder(a); // 取余

// 输出结果
System.out.println(&quot;sum: &quot; + sum);           // 输出: sum: 1111111110
System.out.println(&quot;diff: &quot; + diff);         // 输出: diff: 864197532
System.out.println(&quot;product: &quot; + product);   // 输出: product: 121932631112635269
System.out.println(&quot;quotient: &quot; + quotient); // 输出: quotient: 8
System.out.println(&quot;remainder: &quot; + remainder); // 输出: remainder: 9

// 幂运算
BigInteger base = new BigInteger(&quot;2&quot;);
BigInteger power = base.pow(100); // 2的100次方
System.out.println(&quot;2^100: &quot; + power); // 输出: 2^100: 1267650600228229401496703205376

// 比较操作
BigInteger x = new BigInteger(&quot;100&quot;);
BigInteger y = new BigInteger(&quot;200&quot;);
int compare = x.compareTo(y);
boolean equals = x.equals(y);
boolean isZero = x.equals(BigInteger.ZERO);
boolean isPositive = x.compareTo(BigInteger.ZERO) &amp;gt; 0;

// 输出结果
System.out.println(&quot;compare: &quot; + compare);     // 输出: compare: -1 (x &amp;lt; y)
System.out.println(&quot;equals: &quot; + equals);       // 输出: equals: false
System.out.println(&quot;isZero: &quot; + isZero);       // 输出: isZero: false
System.out.println(&quot;isPositive: &quot; + isPositive); // 输出: isPositive: true

// 位运算
BigInteger num = new BigInteger(&quot;42&quot;);
BigInteger and = num.and(new BigInteger(&quot;15&quot;));    // 按位与
BigInteger or = num.or(new BigInteger(&quot;15&quot;));      // 按位或
BigInteger xor = num.xor(new BigInteger(&quot;15&quot;));    // 按位异或
BigInteger not = num.not();                        // 按位非
BigInteger shiftLeft = num.shiftLeft(2);           // 左移2位
BigInteger shiftRight = num.shiftRight(1);         // 右移1位

// 输出结果
System.out.println(&quot;and: &quot; + and);           // 输出: and: 10
System.out.println(&quot;or: &quot; + or);             // 输出: or: 47
System.out.println(&quot;xor: &quot; + xor);           // 输出: xor: 37
System.out.println(&quot;not: &quot; + not);           // 输出: not: -43
System.out.println(&quot;shiftLeft: &quot; + shiftLeft);     // 输出: shiftLeft: 168
System.out.println(&quot;shiftRight: &quot; + shiftRight);   // 输出: shiftRight: 21

// 素数测试
BigInteger prime = new BigInteger(&quot;17&quot;);
boolean isProbablePrime = prime.isProbablePrime(100); // 概率素数测试
System.out.println(&quot;isProbablePrime: &quot; + isProbablePrime); // 输出: isProbablePrime: true

// 最大公约数和最小公倍数
BigInteger gcd = a.gcd(b); // 最大公约数
BigInteger lcm = a.multiply(b).divide(gcd); // 最小公倍数
System.out.println(&quot;gcd: &quot; + gcd); // 输出: gcd: 3
System.out.println(&quot;lcm: &quot; + lcm); // 输出: lcm: 40623982634538657907

// 转换为其他类型
int intValue = a.intValue();
long longValue = a.longValue();
String stringValue = a.toString();
String hexValue = a.toString(16); // 十六进制
String binaryValue = a.toString(2); // 二进制

// 输出结果
System.out.println(&quot;intValue: &quot; + intValue);       // 输出: intValue: 123456789
System.out.println(&quot;longValue: &quot; + longValue);     // 输出: longValue: 123456789
System.out.println(&quot;stringValue: &quot; + stringValue); // 输出: stringValue: 123456789
System.out.println(&quot;hexValue: &quot; + hexValue);       // 输出: hexValue: 75bcd15
System.out.println(&quot;binaryValue: &quot; + binaryValue); // 输出: binaryValue: 111010110111100110100010101
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;&lt;strong&gt;BigDecimal（高精度小数）：&lt;/strong&gt;&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;import java.math.BigDecimal;
import java.math.RoundingMode;

// 创建BigDecimal
BigDecimal bd1 = new BigDecimal(&quot;3.141592653589793238462643383279&quot;);
BigDecimal bd2 = BigDecimal.valueOf(2.718281828459045);
BigDecimal bd3 = new BigDecimal(&quot;100.00&quot;);
BigDecimal bd4 = BigDecimal.ONE;   // 常量1
BigDecimal bd5 = BigDecimal.ZERO;  // 常量0
BigDecimal bd6 = BigDecimal.TEN;   // 常量10

// 输出结果
System.out.println(&quot;bd1: &quot; + bd1); // 输出: bd1: 3.141592653589793238462643383279
System.out.println(&quot;bd2: &quot; + bd2); // 输出: bd2: 2.718281828459045
System.out.println(&quot;bd3: &quot; + bd3); // 输出: bd3: 100.00
System.out.println(&quot;bd4: &quot; + bd4); // 输出: bd4: 1
System.out.println(&quot;bd5: &quot; + bd5); // 输出: bd5: 0
System.out.println(&quot;bd6: &quot; + bd6); // 输出: bd6: 10

// 基本运算
BigDecimal a = new BigDecimal(&quot;10.5&quot;);
BigDecimal b = new BigDecimal(&quot;3.2&quot;);

BigDecimal sum = a.add(b);           // 加法
BigDecimal diff = a.subtract(b);     // 减法
BigDecimal product = a.multiply(b);  // 乘法
BigDecimal quotient = a.divide(b, 10, RoundingMode.HALF_UP); // 除法，保留10位小数
BigDecimal remainder = a.remainder(b); // 取余

// 输出结果
System.out.println(&quot;sum: &quot; + sum);           // 输出: sum: 13.7
System.out.println(&quot;diff: &quot; + diff);         // 输出: diff: 7.3
System.out.println(&quot;product: &quot; + product);   // 输出: product: 33.60
System.out.println(&quot;quotient: &quot; + quotient); // 输出: quotient: 3.2812500000
System.out.println(&quot;remainder: &quot; + remainder); // 输出: remainder: 0.9

// 幂运算
BigDecimal base = new BigDecimal(&quot;2.5&quot;);
BigDecimal power = base.pow(3); // 2.5的3次方
System.out.println(&quot;2.5^3: &quot; + power); // 输出: 2.5^3: 15.625

// 比较操作
BigDecimal x = new BigDecimal(&quot;10.5&quot;);
BigDecimal y = new BigDecimal(&quot;10.50&quot;);
int compare = x.compareTo(y);
boolean equals = x.equals(y);
boolean isZero = x.equals(BigDecimal.ZERO);
boolean isPositive = x.compareTo(BigDecimal.ZERO) &amp;gt; 0;

// 输出结果
System.out.println(&quot;compare: &quot; + compare);     // 输出: compare: 0 (x = y)
System.out.println(&quot;equals: &quot; + equals);       // 输出: equals: false (精度不同)
System.out.println(&quot;isZero: &quot; + isZero);       // 输出: isZero: false
System.out.println(&quot;isPositive: &quot; + isPositive); // 输出: isPositive: true

// 精度控制
BigDecimal pi = new BigDecimal(&quot;3.141592653589793&quot;);
BigDecimal rounded = pi.setScale(2, RoundingMode.HALF_UP); // 保留2位小数
BigDecimal ceiling = pi.setScale(2, RoundingMode.CEILING); // 向上取整
BigDecimal floor = pi.setScale(2, RoundingMode.FLOOR);     // 向下取整

// 输出结果
System.out.println(&quot;rounded: &quot; + rounded); // 输出: rounded: 3.14
System.out.println(&quot;ceiling: &quot; + ceiling); // 输出: ceiling: 3.15
System.out.println(&quot;floor: &quot; + floor);     // 输出: floor: 3.14

// 舍入模式示例
BigDecimal value = new BigDecimal(&quot;3.5&quot;);
System.out.println(&quot;HALF_UP: &quot; + value.setScale(0, RoundingMode.HALF_UP));     // 输出: HALF_UP: 4
System.out.println(&quot;HALF_DOWN: &quot; + value.setScale(0, RoundingMode.HALF_DOWN)); // 输出: HALF_DOWN: 3
System.out.println(&quot;HALF_EVEN: &quot; + value.setScale(0, RoundingMode.HALF_EVEN)); // 输出: HALF_EVEN: 4
System.out.println(&quot;CEILING: &quot; + value.setScale(0, RoundingMode.CEILING));     // 输出: CEILING: 4
System.out.println(&quot;FLOOR: &quot; + value.setScale(0, RoundingMode.FLOOR));         // 输出: FLOOR: 3

// 转换为其他类型
int intValue = a.intValue();
long longValue = a.longValue();
float floatValue = a.floatValue();
double doubleValue = a.doubleValue();
String stringValue = a.toString();

// 输出结果
System.out.println(&quot;intValue: &quot; + intValue);       // 输出: intValue: 10
System.out.println(&quot;longValue: &quot; + longValue);     // 输出: longValue: 10
System.out.println(&quot;floatValue: &quot; + floatValue);   // 输出: floatValue: 10.5
System.out.println(&quot;doubleValue: &quot; + doubleValue); // 输出: doubleValue: 10.5
System.out.println(&quot;stringValue: &quot; + stringValue); // 输出: stringValue: 10.5

// 获取精度信息
int scale = a.scale();           // 小数位数
int precision = a.precision();   // 总位数
BigInteger unscaledValue = a.unscaledValue(); // 去掉小数点的整数值

// 输出结果
System.out.println(&quot;scale: &quot; + scale);           // 输出: scale: 1
System.out.println(&quot;precision: &quot; + precision);   // 输出: precision: 3
System.out.println(&quot;unscaledValue: &quot; + unscaledValue); // 输出: unscaledValue: 105
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;&lt;strong&gt;BigInteger和BigDecimal的实用工具类：&lt;/strong&gt;&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;import java.math.BigInteger;
import java.math.BigDecimal;
import java.math.RoundingMode;

public class MathUtils {
    
    // 计算阶乘
    public static BigInteger factorial(int n) {
        if (n &amp;lt; 0) throw new IllegalArgumentException(&quot;负数没有阶乘&quot;);
        if (n &amp;lt;= 1) return BigInteger.ONE;
        
        BigInteger result = BigInteger.ONE;
        for (int i = 2; i &amp;lt;= n; i++) {
            result = result.multiply(BigInteger.valueOf(i));
        }
        return result;
    }
    
    // 计算斐波那契数列
    public static BigInteger fibonacci(int n) {
        if (n &amp;lt;= 0) return BigInteger.ZERO;
        if (n == 1) return BigInteger.ONE;
        
        BigInteger a = BigInteger.ZERO;
        BigInteger b = BigInteger.ONE;
        for (int i = 2; i &amp;lt;= n; i++) {
            BigInteger temp = a.add(b);
            a = b;
            b = temp;
        }
        return b;
    }
    
    // 计算组合数 C(n,k)
    public static BigInteger combination(int n, int k) {
        if (k &amp;gt; n || k &amp;lt; 0) return BigInteger.ZERO;
        if (k == 0 || k == n) return BigInteger.ONE;
        
        return factorial(n).divide(factorial(k).multiply(factorial(n - k)));
    }
    
    // 计算排列数 P(n,k)
    public static BigInteger permutation(int n, int k) {
        if (k &amp;gt; n || k &amp;lt; 0) return BigInteger.ZERO;
        
        return factorial(n).divide(factorial(n - k));
    }
    
    // 计算平方根（使用牛顿迭代法）
    public static BigDecimal sqrt(BigDecimal value, int scale) {
        if (value.compareTo(BigDecimal.ZERO) &amp;lt; 0) {
            throw new IllegalArgumentException(&quot;负数没有实数平方根&quot;);
        }
        
        BigDecimal x = value;
        BigDecimal y = BigDecimal.ZERO;
        BigDecimal epsilon = BigDecimal.ONE.divide(BigDecimal.TEN.pow(scale));
        
        while (x.subtract(y).abs().compareTo(epsilon) &amp;gt; 0) {
            y = x;
            x = value.divide(x, scale, RoundingMode.HALF_UP)
                    .add(x)
                    .divide(BigDecimal.valueOf(2), scale, RoundingMode.HALF_UP);
        }
        
        return x;
    }
    
    // 计算e的n次方
    public static BigDecimal exp(BigDecimal x, int scale) {
        BigDecimal result = BigDecimal.ONE;
        BigDecimal term = BigDecimal.ONE;
        BigDecimal factorial = BigDecimal.ONE;
        int n = 1;
        
        while (term.abs().compareTo(BigDecimal.ONE.divide(BigDecimal.TEN.pow(scale))) &amp;gt; 0) {
            factorial = factorial.multiply(BigDecimal.valueOf(n));
            term = x.pow(n).divide(factorial, scale, RoundingMode.HALF_UP);
            result = result.add(term);
            n++;
        }
        
        return result;
    }
    
    // 计算自然对数
    public static BigDecimal ln(BigDecimal x, int scale) {
        if (x.compareTo(BigDecimal.ZERO) &amp;lt;= 0) {
            throw new IllegalArgumentException(&quot;对数只能计算正数&quot;);
        }
        
        BigDecimal y = x.subtract(BigDecimal.ONE).divide(x.add(BigDecimal.ONE), scale, RoundingMode.HALF_UP);
        BigDecimal result = BigDecimal.ZERO;
        BigDecimal term = y;
        int n = 1;
        
        while (term.abs().compareTo(BigDecimal.ONE.divide(BigDecimal.TEN.pow(scale))) &amp;gt; 0) {
            result = result.add(term.divide(BigDecimal.valueOf(n), scale, RoundingMode.HALF_UP));
            term = term.multiply(y).multiply(y);
            n += 2;
        }
        
        return result.multiply(BigDecimal.valueOf(2));
    }
    
    // 货币计算（避免浮点数精度问题）
    public static BigDecimal calculateInterest(BigDecimal principal, BigDecimal rate, int years) {
        BigDecimal multiplier = BigDecimal.ONE.add(rate.divide(BigDecimal.valueOf(100), 10, RoundingMode.HALF_UP));
        return principal.multiply(multiplier.pow(years)).setScale(2, RoundingMode.HALF_UP);
    }
    
    // 百分比计算
    public static BigDecimal percentage(BigDecimal value, BigDecimal total) {
        return value.multiply(BigDecimal.valueOf(100))
                   .divide(total, 2, RoundingMode.HALF_UP);
    }
}

// 使用示例
public class MathUtilsExample {
    public static void main(String[] args) {
        // 阶乘计算
        System.out.println(&quot;5! = &quot; + MathUtils.factorial(5)); // 输出: 5! = 120
        System.out.println(&quot;10! = &quot; + MathUtils.factorial(10)); // 输出: 10! = 3628800
        
        // 斐波那契数列
        System.out.println(&quot;F(10) = &quot; + MathUtils.fibonacci(10)); // 输出: F(10) = 55
        System.out.println(&quot;F(20) = &quot; + MathUtils.fibonacci(20)); // 输出: F(20) = 6765
        
        // 组合数
        System.out.println(&quot;C(5,2) = &quot; + MathUtils.combination(5, 2)); // 输出: C(5,2) = 10
        System.out.println(&quot;C(10,3) = &quot; + MathUtils.combination(10, 3)); // 输出: C(10,3) = 120
        
        // 平方根
        BigDecimal sqrt2 = MathUtils.sqrt(new BigDecimal(&quot;2&quot;), 10);
        System.out.println(&quot;√2 = &quot; + sqrt2); // 输出: √2 = 1.4142135624
        
        // 货币计算
        BigDecimal principal = new BigDecimal(&quot;10000.00&quot;);
        BigDecimal rate = new BigDecimal(&quot;5.5&quot;); // 5.5%
        BigDecimal interest = MathUtils.calculateInterest(principal, rate, 3);
        System.out.println(&quot;利息: &quot; + interest); // 输出: 利息: 11742.38
        
        // 百分比
        BigDecimal value = new BigDecimal(&quot;75&quot;);
        BigDecimal total = new BigDecimal(&quot;200&quot;);
        BigDecimal percentage = MathUtils.percentage(value, total);
        System.out.println(&quot;百分比: &quot; + percentage + &quot;%&quot;); // 输出: 百分比: 37.50%
    }
}
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;&lt;strong&gt;BigInteger和BigDecimal的注意事项：&lt;/strong&gt;&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;
&lt;p&gt;&lt;strong&gt;精度问题&lt;/strong&gt;：&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;BigInteger可以表示任意大的整数&lt;/li&gt;
&lt;li&gt;BigDecimal可以表示任意精度的十进制数&lt;/li&gt;
&lt;li&gt;避免使用double构造BigDecimal，使用字符串构造&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;&lt;strong&gt;性能考虑&lt;/strong&gt;：&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;BigInteger和BigDecimal运算比基本类型慢&lt;/li&gt;
&lt;li&gt;对于简单计算，优先使用基本类型&lt;/li&gt;
&lt;li&gt;需要高精度时才使用BigInteger/BigDecimal&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;&lt;strong&gt;舍入模式&lt;/strong&gt;：&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;HALF_UP：四舍五入（默认）&lt;/li&gt;
&lt;li&gt;HALF_DOWN：五舍六入&lt;/li&gt;
&lt;li&gt;HALF_EVEN：银行家舍入法&lt;/li&gt;
&lt;li&gt;CEILING：向上取整&lt;/li&gt;
&lt;li&gt;FLOOR：向下取整&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;&lt;strong&gt;比较操作&lt;/strong&gt;：&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;使用compareTo()而不是equals()进行比较&lt;/li&gt;
&lt;li&gt;equals()会考虑精度，compareTo()只比较数值&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;&lt;strong&gt;应用场景&lt;/strong&gt;：&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;金融计算（货币、利率）&lt;/li&gt;
&lt;li&gt;科学计算（高精度数学）&lt;/li&gt;
&lt;li&gt;大数运算（密码学、统计）&lt;/li&gt;
&lt;li&gt;避免浮点数精度问题&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;/ol&gt;
&lt;h5&gt;7.8 正则表达式&lt;/h5&gt;
&lt;p&gt;&lt;strong&gt;Pattern和Matcher类：&lt;/strong&gt;&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;import java.util.regex.Pattern;
import java.util.regex.Matcher;

// 基本匹配
String text = &quot;Hello World 123&quot;;
Pattern pattern = Pattern.compile(&quot;\\d+&quot;); // 匹配一个或多个数字
Matcher matcher = pattern.matcher(text);

// 查找匹配
while (matcher.find()) {
    System.out.println(&quot;找到数字: &quot; + matcher.group());
    System.out.println(&quot;位置: &quot; + matcher.start() + &quot;-&quot; + matcher.end());
}
// 输出:
// 找到数字: 123
// 位置: 12-15

// 完全匹配
String email = &quot;user@example.com&quot;;
Pattern emailPattern = Pattern.compile(&quot;^[a-zA-Z0-9._%+-]+@[a-zA-Z0-9.-]+\\.[a-zA-Z]{2,}$&quot;);
boolean isValidEmail = emailPattern.matcher(email).matches();
System.out.println(&quot;邮箱有效: &quot; + isValidEmail); // 输出: 邮箱有效: true

// 分组捕获
String phone = &quot;138-1234-5678&quot;;
Pattern phonePattern = Pattern.compile(&quot;(\\d{3})-(\\d{4})-(\\d{4})&quot;);
Matcher phoneMatcher = phonePattern.matcher(phone);
if (phoneMatcher.matches()) {
    System.out.println(&quot;区号: &quot; + phoneMatcher.group(1)); // 输出: 区号: 138
    System.out.println(&quot;前缀: &quot; + phoneMatcher.group(2)); // 输出: 前缀: 1234
    System.out.println(&quot;后缀: &quot; + phoneMatcher.group(3)); // 输出: 后缀: 5678
}
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;&lt;strong&gt;String类的正则方法：&lt;/strong&gt;&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;// matches方法
String text = &quot;Hello123World&quot;;
boolean hasDigits = text.matches(&quot;.*\\d+.*&quot;);
System.out.println(&quot;包含数字: &quot; + hasDigits); // 输出: 包含数字: true

// split方法
String csv = &quot;apple,banana,orange,grape&quot;;
String[] fruits = csv.split(&quot;,&quot;);
System.out.println(&quot;水果数量: &quot; + fruits.length); // 输出: 水果数量: 4
for (String fruit : fruits) {
    System.out.println(fruit);
}
// 输出:
// apple
// banana
// orange
// grape

// replaceAll方法
String text = &quot;Hello123World456&quot;;
String lettersOnly = text.replaceAll(&quot;\\d+&quot;, &quot;&quot;);
System.out.println(&quot;只保留字母: &quot; + lettersOnly); // 输出: 只保留字母: HelloWorld

// replaceFirst方法
String text = &quot;Hello123World123&quot;;
String firstReplaced = text.replaceFirst(&quot;\\d+&quot;, &quot;XXX&quot;);
System.out.println(&quot;替换第一个数字: &quot; + firstReplaced); // 输出: 替换第一个数字: HelloXXXWorld123
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;&lt;strong&gt;常用正则表达式模式：&lt;/strong&gt;&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;public class RegexPatterns {
    
    // 邮箱验证
    public static final String EMAIL_PATTERN = &quot;^[a-zA-Z0-9._%+-]+@[a-zA-Z0-9.-]+\\.[a-zA-Z]{2,}$&quot;;
    
    // 手机号验证（中国）
    public static final String PHONE_PATTERN = &quot;^1[3-9]\\d{9}$&quot;;
    
    // 身份证号验证（中国）
    public static final String ID_CARD_PATTERN = &quot;^[1-9]\\d{5}(18|19|20)\\d{2}((0[1-9])|(1[0-2]))(([0-2][1-9])|10|20|30|31)\\d{3}[0-9Xx]$&quot;;
    
    // URL验证
    public static final String URL_PATTERN = &quot;^https?://[\\w\\-]+(\\.[\\w\\-]+)+([\\w\\-.,@?^=%&amp;amp;:/~+#]*[\\w\\-@?^=%&amp;amp;/~+#])?$&quot;;
    
    // 日期验证（YYYY-MM-DD）
    public static final String DATE_PATTERN = &quot;^\\d{4}-(0[1-9]|1[0-2])-(0[1-9]|[12]\\d|3[01])$&quot;;
    
    // 时间验证（HH:MM:SS）
    public static final String TIME_PATTERN = &quot;^([01]\\d|2[0-3]):([0-5]\\d):([0-5]\\d)$&quot;;
    
    // 邮政编码验证（中国）
    public static final String POSTAL_CODE_PATTERN = &quot;^[1-9]\\d{5}$&quot;;
    
    // 用户名验证（字母开头，允许字母数字下划线，长度3-16）
    public static final String USERNAME_PATTERN = &quot;^[a-zA-Z]\\w{2,15}$&quot;;
    
    // 密码验证（至少8位，包含大小写字母和数字）
    public static final String PASSWORD_PATTERN = &quot;^(?=.*[a-z])(?=.*[A-Z])(?=.*\\d)[a-zA-Z\\d@$!%*?&amp;amp;]{8,}$&quot;;
    
    // IPv4地址验证
    public static final String IPV4_PATTERN = &quot;^((25[0-5]|2[0-4]\\d|[01]?\\d\\d?)\\.){3}(25[0-5]|2[0-4]\\d|[01]?\\d\\d?)$&quot;;
    
    // 十六进制颜色验证
    public static final String HEX_COLOR_PATTERN = &quot;^#([A-Fa-f0-9]{6}|[A-Fa-f0-9]{3})$&quot;;
}

// 使用示例
public class RegexExample {
    public static void main(String[] args) {
        // 邮箱验证
        String email = &quot;user@example.com&quot;;
        boolean isValidEmail = email.matches(RegexPatterns.EMAIL_PATTERN);
        System.out.println(&quot;邮箱有效: &quot; + isValidEmail); // 输出: 邮箱有效: true
        
        // 手机号验证
        String phone = &quot;13812345678&quot;;
        boolean isValidPhone = phone.matches(RegexPatterns.PHONE_PATTERN);
        System.out.println(&quot;手机号有效: &quot; + isValidPhone); // 输出: 手机号有效: true
        
        // URL验证
        String url = &quot;https://www.example.com/path?param=value&quot;;
        boolean isValidUrl = url.matches(RegexPatterns.URL_PATTERN);
        System.out.println(&quot;URL有效: &quot; + isValidUrl); // 输出: URL有效: true
        
        // 日期验证
        String date = &quot;2025-05-15&quot;;
        boolean isValidDate = date.matches(RegexPatterns.DATE_PATTERN);
        System.out.println(&quot;日期有效: &quot; + isValidDate); // 输出: 日期有效: true
        
        // 密码验证
        String password = &quot;MyPass123&quot;;
        boolean isValidPassword = password.matches(RegexPatterns.PASSWORD_PATTERN);
        System.out.println(&quot;密码有效: &quot; + isValidPassword); // 输出: 密码有效: true
    }
}
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;&lt;strong&gt;正则表达式语法详解：&lt;/strong&gt;&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;public class RegexSyntax {
    
    public static void demonstrateSyntax() {
        String text = &quot;Hello World 123 ABC&quot;;
        
        // 字符类
        System.out.println(&quot;=== 字符类 ===&quot;);
        System.out.println(&quot;数字: &quot; + text.replaceAll(&quot;\\d&quot;, &quot;X&quot;)); // 输出: Hello World XXX ABC
        System.out.println(&quot;非数字: &quot; + text.replaceAll(&quot;\\D&quot;, &quot;X&quot;)); // 输出: XXXXX XXXXX XXX XXX
        System.out.println(&quot;字母: &quot; + text.replaceAll(&quot;[a-zA-Z]&quot;, &quot;X&quot;)); // 输出: XXXXX XXXXX 123 XXX
        System.out.println(&quot;非字母: &quot; + text.replaceAll(&quot;[^a-zA-Z]&quot;, &quot;X&quot;)); // 输出: HelloXWorldX123XABC
        
        // 量词
        System.out.println(&quot;=== 量词 ===&quot;);
        System.out.println(&quot;一个或多个数字: &quot; + text.replaceAll(&quot;\\d+&quot;, &quot;X&quot;)); // 输出: Hello World X ABC
        System.out.println(&quot;零个或多个字母: &quot; + text.replaceAll(&quot;[a-zA-Z]*&quot;, &quot;X&quot;)); // 输出: X X X X
        System.out.println(&quot;1到3个字母: &quot; + text.replaceAll(&quot;[a-zA-Z]{1,3}&quot;, &quot;X&quot;)); // 输出: X X 123 X
        
        // 边界
        System.out.println(&quot;=== 边界 ===&quot;);
        System.out.println(&quot;单词边界: &quot; + text.replaceAll(&quot;\\b\\w+\\b&quot;, &quot;X&quot;)); // 输出: X X 123 X
        System.out.println(&quot;行首: &quot; + text.replaceAll(&quot;^\\w+&quot;, &quot;X&quot;)); // 输出: X World 123 ABC
        System.out.println(&quot;行尾: &quot; + text.replaceAll(&quot;\\w+$&quot;, &quot;X&quot;)); // 输出: Hello World 123 X
        
        // 分组和引用
        System.out.println(&quot;=== 分组和引用 ===&quot;);
        String name = &quot;John Doe&quot;;
        String result = name.replaceAll(&quot;(\\w+)\\s+(\\w+)&quot;, &quot;$2, $1&quot;);
        System.out.println(&quot;姓名格式转换: &quot; + result); // 输出: Doe, John
        
        // 非捕获组
        String text2 = &quot;color colour&quot;;
        String result2 = text2.replaceAll(&quot;colou?r&quot;, &quot;color&quot;);
        System.out.println(&quot;统一拼写: &quot; + result2); // 输出: color color
        
        // 前瞻和后顾
        System.out.println(&quot;=== 前瞻和后顾 ===&quot;);
        String numbers = &quot;123 456 789&quot;;
        // 匹配后面跟着空格的数字
        String result3 = numbers.replaceAll(&quot;\\d+(?=\\s)&quot;, &quot;X&quot;);
        System.out.println(&quot;前瞻: &quot; + result3); // 输出: X X 789
        
        // 匹配前面有数字的空格
        String result4 = numbers.replaceAll(&quot;(?&amp;lt;=\\d)\\s&quot;, &quot;X&quot;);
        System.out.println(&quot;后顾: &quot; + result4); // 输出: 123X456X789
    }
}
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;&lt;strong&gt;实用正则表达式工具类：&lt;/strong&gt;&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;import java.util.regex.Pattern;
import java.util.regex.Matcher;
import java.util.ArrayList;
import java.util.List;

public class RegexUtils {
    
    // 验证字符串是否匹配正则表达式
    public static boolean isValid(String text, String regex) {
        return text != null &amp;amp;&amp;amp; Pattern.matches(regex, text);
    }
    
    // 提取所有匹配的字符串
    public static List&amp;lt;String&amp;gt; extractAll(String text, String regex) {
        List&amp;lt;String&amp;gt; results = new ArrayList&amp;lt;&amp;gt;();
        if (text == null) return results;
        
        Pattern pattern = Pattern.compile(regex);
        Matcher matcher = pattern.matcher(text);
        
        while (matcher.find()) {
            results.add(matcher.group());
        }
        
        return results;
    }
    
    // 提取第一个匹配的字符串
    public static String extractFirst(String text, String regex) {
        if (text == null) return null;
        
        Pattern pattern = Pattern.compile(regex);
        Matcher matcher = pattern.matcher(text);
        
        return matcher.find() ? matcher.group() : null;
    }
    
    // 替换所有匹配的字符串
    public static String replaceAll(String text, String regex, String replacement) {
        return text != null ? text.replaceAll(regex, replacement) : null;
    }
    
    // 替换第一个匹配的字符串
    public static String replaceFirst(String text, String regex, String replacement) {
        return text != null ? text.replaceFirst(regex, replacement) : null;
    }
    
    // 分割字符串
    public static String[] split(String text, String regex) {
        return text != null ? text.split(regex) : new String[0];
    }
    
    // 验证邮箱
    public static boolean isValidEmail(String email) {
        return isValid(email, &quot;^[a-zA-Z0-9._%+-]+@[a-zA-Z0-9.-]+\\.[a-zA-Z]{2,}$&quot;);
    }
    
    // 验证手机号（中国）
    public static boolean isValidPhone(String phone) {
        return isValid(phone, &quot;^1[3-9]\\d{9}$&quot;);
    }
    
    // 验证身份证号（中国）
    public static boolean isValidIdCard(String idCard) {
        return isValid(idCard, &quot;^[1-9]\\d{5}(18|19|20)\\d{2}((0[1-9])|(1[0-2]))(([0-2][1-9])|10|20|30|31)\\d{3}[0-9Xx]$&quot;);
    }
    
    // 验证URL
    public static boolean isValidUrl(String url) {
        return isValid(url, &quot;^https?://[\\w\\-]+(\\.[\\w\\-]+)+([\\w\\-.,@?^=%&amp;amp;:/~+#]*[\\w\\-@?^=%&amp;amp;/~+#])?$&quot;);
    }
    
    // 提取邮箱地址
    public static List&amp;lt;String&amp;gt; extractEmails(String text) {
        return extractAll(text, &quot;\\b[A-Za-z0-9._%+-]+@[A-Za-z0-9.-]+\\.[A-Z|a-z]{2,}\\b&quot;);
    }
    
    // 提取手机号
    public static List&amp;lt;String&amp;gt; extractPhones(String text) {
        return extractAll(text, &quot;\\b1[3-9]\\d{9}\\b&quot;);
    }
    
    // 提取IP地址
    public static List&amp;lt;String&amp;gt; extractIpAddresses(String text) {
        return extractAll(text, &quot;\\b((25[0-5]|2[0-4]\\d|[01]?\\d\\d?)\\.){3}(25[0-5]|2[0-4]\\d|[01]?\\d\\d?)\\b&quot;);
    }
    
    // 提取HTML标签
    public static List&amp;lt;String&amp;gt; extractHtmlTags(String text) {
        return extractAll(text, &quot;&amp;lt;[^&amp;gt;]+&amp;gt;&quot;);
    }
    
    // 移除HTML标签
    public static String removeHtmlTags(String text) {
        return replaceAll(text, &quot;&amp;lt;[^&amp;gt;]+&amp;gt;&quot;, &quot;&quot;);
    }
    
    // 提取数字
    public static List&amp;lt;String&amp;gt; extractNumbers(String text) {
        return extractAll(text, &quot;\\b\\d+\\b&quot;);
    }
    
    // 提取小数
    public static List&amp;lt;String&amp;gt; extractDecimals(String text) {
        return extractAll(text, &quot;\\b\\d+\\.\\d+\\b&quot;);
    }
    
    // 验证密码强度
    public static boolean isStrongPassword(String password) {
        // 至少8位，包含大小写字母、数字和特殊字符
        return isValid(password, &quot;^(?=.*[a-z])(?=.*[A-Z])(?=.*\\d)(?=.*[@$!%*?&amp;amp;])[A-Za-z\\d@$!%*?&amp;amp;]{8,}$&quot;);
    }
    
    // 格式化手机号（添加分隔符）
    public static String formatPhone(String phone) {
        if (!isValidPhone(phone)) return phone;
        return phone.replaceAll(&quot;(\\d{3})(\\d{4})(\\d{4})&quot;, &quot;$1-$2-$3&quot;);
    }
    
    // 隐藏敏感信息
    public static String maskSensitiveInfo(String text, String regex, String mask) {
        return replaceAll(text, regex, mask);
    }
}

// 使用示例
public class RegexUtilsExample {
    public static void main(String[] args) {
        // 验证功能
        System.out.println(&quot;邮箱验证: &quot; + RegexUtils.isValidEmail(&quot;user@example.com&quot;)); // true
        System.out.println(&quot;手机号验证: &quot; + RegexUtils.isValidPhone(&quot;13812345678&quot;)); // true
        System.out.println(&quot;URL验证: &quot; + RegexUtils.isValidUrl(&quot;https://www.example.com&quot;)); // true
        
        // 提取功能
        String text = &quot;联系邮箱：user1@example.com, user2@test.org，手机号：13812345678, 13987654321&quot;;
        List&amp;lt;String&amp;gt; emails = RegexUtils.extractEmails(text);
        List&amp;lt;String&amp;gt; phones = RegexUtils.extractPhones(text);
        
        System.out.println(&quot;提取的邮箱: &quot; + emails); // [user1@example.com, user2@test.org]
        System.out.println(&quot;提取的手机号: &quot; + phones); // [13812345678, 13987654321]
        
        // 替换功能
        String html = &quot;&amp;lt;p&amp;gt;Hello &amp;lt;b&amp;gt;World&amp;lt;/b&amp;gt;&amp;lt;/p&amp;gt;&quot;;
        String plainText = RegexUtils.removeHtmlTags(html);
        System.out.println(&quot;移除HTML标签: &quot; + plainText); // Hello World
        
        // 格式化功能
        String phone = &quot;13812345678&quot;;
        String formatted = RegexUtils.formatPhone(phone);
        System.out.println(&quot;格式化手机号: &quot; + formatted); // 138-1234-5678
        
        // 隐藏敏感信息
        String sensitive = &quot;身份证号：123456789012345678，手机号：13812345678&quot;;
        String masked = RegexUtils.maskSensitiveInfo(sensitive, &quot;\\d{17}[\\dXx]&quot;, &quot;***&quot;);
        System.out.println(&quot;隐藏敏感信息: &quot; + masked); // 身份证号：***，手机号：13812345678
    }
}
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;&lt;strong&gt;正则表达式性能优化：&lt;/strong&gt;&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;public class RegexPerformance {
    
    // 编译正则表达式以提高性能
    private static final Pattern EMAIL_PATTERN = Pattern.compile(&quot;^[a-zA-Z0-9._%+-]+@[a-zA-Z0-9.-]+\\.[a-zA-Z]{2,}$&quot;);
    private static final Pattern PHONE_PATTERN = Pattern.compile(&quot;^1[3-9]\\d{9}$&quot;);
    private static final Pattern URL_PATTERN = Pattern.compile(&quot;^https?://[\\w\\-]+(\\.[\\w\\-]+)+([\\w\\-.,@?^=%&amp;amp;:/~+#]*[\\w\\-@?^=%&amp;amp;/~+#])?$&quot;);
    
    // 使用预编译的正则表达式
    public static boolean isValidEmailOptimized(String email) {
        return email != null &amp;amp;&amp;amp; EMAIL_PATTERN.matcher(email).matches();
    }
    
    public static boolean isValidPhoneOptimized(String phone) {
        return phone != null &amp;amp;&amp;amp; PHONE_PATTERN.matcher(phone).matches();
    }
    
    public static boolean isValidUrlOptimized(String url) {
        return url != null &amp;amp;&amp;amp; URL_PATTERN.matcher(url).matches();
    }
    
    // 性能测试
    public static void performanceTest() {
        String email = &quot;user@example.com&quot;;
        String phone = &quot;13812345678&quot;;
        String url = &quot;https://www.example.com&quot;;
        
        long startTime = System.currentTimeMillis();
        
        // 测试10000次验证
        for (int i = 0; i &amp;lt; 10000; i++) {
            isValidEmailOptimized(email);
            isValidPhoneOptimized(phone);
            isValidUrlOptimized(url);
        }
        
        long endTime = System.currentTimeMillis();
        System.out.println(&quot;优化后耗时: &quot; + (endTime - startTime) + &quot;ms&quot;);
        
        // 对比未优化的版本
        startTime = System.currentTimeMillis();
        
        for (int i = 0; i &amp;lt; 10000; i++) {
            email.matches(&quot;^[a-zA-Z0-9._%+-]+@[a-zA-Z0-9.-]+\\.[a-zA-Z]{2,}$&quot;);
            phone.matches(&quot;^1[3-9]\\d{9}$&quot;);
            url.matches(&quot;^https?://[\\w\\-]+(\\.[\\w\\-]+)+([\\w\\-.,@?^=%&amp;amp;:/~+#]*[\\w\\-@?^=%&amp;amp;/~+#])?$&quot;);
        }
        
        endTime = System.currentTimeMillis();
        System.out.println(&quot;未优化耗时: &quot; + (endTime - startTime) + &quot;ms&quot;);
    }
}
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;&lt;strong&gt;正则表达式的注意事项：&lt;/strong&gt;&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;
&lt;p&gt;&lt;strong&gt;性能考虑&lt;/strong&gt;：&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;预编译正则表达式以提高性能&lt;/li&gt;
&lt;li&gt;避免在循环中重复编译正则表达式&lt;/li&gt;
&lt;li&gt;使用非贪婪匹配（*?、+?）避免回溯过多&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;&lt;strong&gt;语法要点&lt;/strong&gt;：&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;转义字符：在Java中需要使用双反斜杠&lt;/li&gt;
&lt;li&gt;字符类：使用方括号定义字符集合&lt;/li&gt;
&lt;li&gt;量词：控制匹配次数&lt;/li&gt;
&lt;li&gt;边界：定义匹配位置&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;&lt;strong&gt;常见陷阱&lt;/strong&gt;：&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;点号（.）不匹配换行符，需要使用Pattern.DOTALL标志&lt;/li&gt;
&lt;li&gt;大小写敏感，需要使用Pattern.CASE_INSENSITIVE标志&lt;/li&gt;
&lt;li&gt;多行模式，需要使用Pattern.MULTILINE标志&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;&lt;strong&gt;应用场景&lt;/strong&gt;：&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;数据验证：邮箱、手机号、身份证等&lt;/li&gt;
&lt;li&gt;文本处理：提取、替换、分割&lt;/li&gt;
&lt;li&gt;日志分析：提取关键信息&lt;/li&gt;
&lt;li&gt;数据清洗：格式化、标准化&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;/ol&gt;
&lt;h5&gt;7.9 包装类&lt;/h5&gt;
&lt;p&gt;&lt;strong&gt;基本数据类型与包装类对应关系：&lt;/strong&gt;&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;// 基本数据类型 -&amp;gt; 包装类
byte -&amp;gt; Byte
short -&amp;gt; Short
int -&amp;gt; Integer
long -&amp;gt; Long
float -&amp;gt; Float
double -&amp;gt; Double
char -&amp;gt; Character
boolean -&amp;gt; Boolean

// 创建包装类对象
Integer intObj = new Integer(100);           // 构造方法（已过时）
Integer intObj2 = Integer.valueOf(100);       // 推荐使用valueOf方法
Integer intObj3 = 100;                        // 自动装箱

// 输出结果
System.out.println(&quot;intObj: &quot; + intObj);      // 输出: intObj: 100
System.out.println(&quot;intObj2: &quot; + intObj2);    // 输出: intObj2: 100
System.out.println(&quot;intObj3: &quot; + intObj3);    // 输出: intObj3: 100
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;&lt;strong&gt;装箱与拆箱：&lt;/strong&gt;&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;// 自动装箱（Autoboxing）
Integer autoBoxed = 100;                      // 基本类型自动转换为包装类
Double autoBoxedDouble = 3.14;               // 基本类型自动转换为包装类

// 自动拆箱（Unboxing）
int primitive = autoBoxed;                    // 包装类自动转换为基本类型
double primitiveDouble = autoBoxedDouble;     // 包装类自动转换为基本类型

// 输出结果
System.out.println(&quot;autoBoxed: &quot; + autoBoxed);           // 输出: autoBoxed: 100
System.out.println(&quot;primitive: &quot; + primitive);           // 输出: primitive: 100
System.out.println(&quot;autoBoxedDouble: &quot; + autoBoxedDouble); // 输出: autoBoxedDouble: 3.14
System.out.println(&quot;primitiveDouble: &quot; + primitiveDouble); // 输出: primitiveDouble: 3.14

// 在集合中使用
List&amp;lt;Integer&amp;gt; numbers = new ArrayList&amp;lt;&amp;gt;();
numbers.add(1);    // 自动装箱
numbers.add(2);    // 自动装箱
numbers.add(3);    // 自动装箱

int first = numbers.get(0);  // 自动拆箱
int second = numbers.get(1); // 自动拆箱

// 输出结果
System.out.println(&quot;numbers: &quot; + numbers);     // 输出: numbers: [1, 2, 3]
System.out.println(&quot;first: &quot; + first);         // 输出: first: 1
System.out.println(&quot;second: &quot; + second);       // 输出: second: 2
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;&lt;strong&gt;包装类的常用方法：&lt;/strong&gt;&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;// Integer类的常用方法
Integer num = 123;

// 转换为基本类型
int intValue = num.intValue();
long longValue = num.longValue();
float floatValue = num.floatValue();
double doubleValue = num.doubleValue();

// 输出结果
System.out.println(&quot;intValue: &quot; + intValue);       // 输出: intValue: 123
System.out.println(&quot;longValue: &quot; + longValue);     // 输出: longValue: 123
System.out.println(&quot;floatValue: &quot; + floatValue);   // 输出: floatValue: 123.0
System.out.println(&quot;doubleValue: &quot; + doubleValue); // 输出: doubleValue: 123.0

// 字符串转换
String str = num.toString();
String hexStr = Integer.toHexString(num);
String binaryStr = Integer.toBinaryString(num);
String octalStr = Integer.toOctalString(num);

// 输出结果
System.out.println(&quot;str: &quot; + str);           // 输出: str: 123
System.out.println(&quot;hexStr: &quot; + hexStr);     // 输出: hexStr: 7b
System.out.println(&quot;binaryStr: &quot; + binaryStr); // 输出: binaryStr: 1111011
System.out.println(&quot;octalStr: &quot; + octalStr); // 输出: octalStr: 173

// 解析字符串
int parsed1 = Integer.parseInt(&quot;123&quot;);
int parsed2 = Integer.parseInt(&quot;7b&quot;, 16);    // 十六进制
int parsed3 = Integer.parseInt(&quot;1111011&quot;, 2); // 二进制

// 输出结果
System.out.println(&quot;parsed1: &quot; + parsed1);   // 输出: parsed1: 123
System.out.println(&quot;parsed2: &quot; + parsed2);   // 输出: parsed2: 123
System.out.println(&quot;parsed3: &quot; + parsed3);   // 输出: parsed3: 123

// 比较方法
int compare = Integer.compare(100, 200);
boolean equals = Integer.valueOf(100).equals(Integer.valueOf(100));

// 输出结果
System.out.println(&quot;compare: &quot; + compare);   // 输出: compare: -1 (100 &amp;lt; 200)
System.out.println(&quot;equals: &quot; + equals);     // 输出: equals: true

// 最大最小值
int max = Integer.max(100, 200);
int min = Integer.min(100, 200);
int sum = Integer.sum(100, 200);

// 输出结果
System.out.println(&quot;max: &quot; + max);           // 输出: max: 200
System.out.println(&quot;min: &quot; + min);           // 输出: min: 100
System.out.println(&quot;sum: &quot; + sum);           // 输出: sum: 300
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;&lt;strong&gt;Character类的特殊方法：&lt;/strong&gt;&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;char ch = &apos;A&apos;;

// 字符类型判断
boolean isLetter = Character.isLetter(ch);
boolean isDigit = Character.isDigit(&apos;5&apos;);
boolean isWhitespace = Character.isWhitespace(&apos; &apos;);
boolean isUpperCase = Character.isUpperCase(ch);
boolean isLowerCase = Character.isLowerCase(&apos;a&apos;);

// 输出结果
System.out.println(&quot;isLetter: &quot; + isLetter);         // 输出: isLetter: true
System.out.println(&quot;isDigit: &quot; + isDigit);           // 输出: isDigit: true
System.out.println(&quot;isWhitespace: &quot; + isWhitespace); // 输出: isWhitespace: true
System.out.println(&quot;isUpperCase: &quot; + isUpperCase);   // 输出: isUpperCase: true
System.out.println(&quot;isLowerCase: &quot; + isLowerCase);   // 输出: isLowerCase: true

// 字符转换
char upper = Character.toUpperCase(&apos;a&apos;);
char lower = Character.toLowerCase(&apos;A&apos;);
int digit = Character.getNumericValue(&apos;9&apos;);

// 输出结果
System.out.println(&quot;upper: &quot; + upper);       // 输出: upper: A
System.out.println(&quot;lower: &quot; + lower);       // 输出: lower: a
System.out.println(&quot;digit: &quot; + digit);       // 输出: digit: 9

// 字符分类
boolean isJavaIdentifierStart = Character.isJavaIdentifierStart(&apos;_&apos;);
boolean isJavaIdentifierPart = Character.isJavaIdentifierPart(&apos;$&apos;);

// 输出结果
System.out.println(&quot;isJavaIdentifierStart: &quot; + isJavaIdentifierStart); // 输出: isJavaIdentifierStart: true
System.out.println(&quot;isJavaIdentifierPart: &quot; + isJavaIdentifierPart);   // 输出: isJavaIdentifierPart: true
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;&lt;strong&gt;包装类的缓存机制：&lt;/strong&gt;&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;// Integer缓存（-128到127）
Integer a = 100;
Integer b = 100;
Integer c = 200;
Integer d = 200;

// 输出结果
System.out.println(&quot;a == b: &quot; + (a == b));   // 输出: a == b: true（缓存）
System.out.println(&quot;c == d: &quot; + (c == d));   // 输出: c == d: false（未缓存）

// 使用equals比较
System.out.println(&quot;a.equals(b): &quot; + a.equals(b)); // 输出: a.equals(b): true
System.out.println(&quot;c.equals(d): &quot; + c.equals(d)); // 输出: c.equals(d): true

// Boolean缓存（只有true和false两个值）
Boolean bool1 = true;
Boolean bool2 = true;
Boolean bool3 = new Boolean(true);

// 输出结果
System.out.println(&quot;bool1 == bool2: &quot; + (bool1 == bool2));     // 输出: bool1 == bool2: true
System.out.println(&quot;bool1 == bool3: &quot; + (bool1 == bool3));     // 输出: bool1 == bool3: false
System.out.println(&quot;bool1.equals(bool3): &quot; + bool1.equals(bool3)); // 输出: bool1.equals(bool3): true
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;&lt;strong&gt;包装类的实用工具类：&lt;/strong&gt;&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;public class WrapperUtils {
    
    // 安全转换字符串为整数
    public static Integer safeParseInt(String str) {
        try {
            return Integer.parseInt(str);
        } catch (NumberFormatException e) {
            return null;
        }
    }
    
    // 安全转换字符串为长整数
    public static Long safeParseLong(String str) {
        try {
            return Long.parseLong(str);
        } catch (NumberFormatException e) {
            return null;
        }
    }
    
    // 安全转换字符串为双精度浮点数
    public static Double safeParseDouble(String str) {
        try {
            return Double.parseDouble(str);
        } catch (NumberFormatException e) {
            return null;
        }
    }
    
    // 检查字符串是否为有效数字
    public static boolean isValidNumber(String str) {
        if (str == null || str.trim().isEmpty()) {
            return false;
        }
        try {
            Double.parseDouble(str);
            return true;
        } catch (NumberFormatException e) {
            return false;
        }
    }
    
    // 获取数字的位数
    public static int getDigitCount(int number) {
        if (number == 0) return 1;
        return (int) Math.log10(Math.abs(number)) + 1;
    }
    
    // 获取数字的各位数字
    public static int[] getDigits(int number) {
        int count = getDigitCount(number);
        int[] digits = new int[count];
        int temp = Math.abs(number);
        
        for (int i = count - 1; i &amp;gt;= 0; i--) {
            digits[i] = temp % 10;
            temp /= 10;
        }
        
        return digits;
    }
    
    // 检查字符是否为元音字母
    public static boolean isVowel(char ch) {
        char lower = Character.toLowerCase(ch);
        return lower == &apos;a&apos; || lower == &apos;e&apos; || lower == &apos;i&apos; || 
               lower == &apos;o&apos; || lower == &apos;u&apos;;
    }
    
    // 统计字符串中各种字符的数量
    public static void analyzeString(String str) {
        if (str == null) {
            System.out.println(&quot;字符串为null&quot;);
            return;
        }
        
        int letters = 0, digits = 0, spaces = 0, others = 0;
        
        for (char ch : str.toCharArray()) {
            if (Character.isLetter(ch)) {
                letters++;
            } else if (Character.isDigit(ch)) {
                digits++;
            } else if (Character.isWhitespace(ch)) {
                spaces++;
            } else {
                others++;
            }
        }
        
        System.out.println(&quot;字符串分析结果:&quot;);
        System.out.println(&quot;  字母: &quot; + letters);
        System.out.println(&quot;  数字: &quot; + digits);
        System.out.println(&quot;  空格: &quot; + spaces);
        System.out.println(&quot;  其他: &quot; + others);
    }
    
    // 格式化数字
    public static String formatNumber(Number number, String pattern) {
        if (number == null) return &quot;null&quot;;
        
        java.text.DecimalFormat df = new java.text.DecimalFormat(pattern);
        return df.format(number);
    }
    
    // 生成随机包装类对象
    public static Integer randomInteger(int min, int max) {
        return (int)(Math.random() * (max - min + 1)) + min;
    }
    
    public static Double randomDouble(double min, double max) {
        return min + (Math.random() * (max - min));
    }
    
    public static Boolean randomBoolean() {
        return Math.random() &amp;lt; 0.5;
    }
}

// 使用示例
public class WrapperUtilsExample {
    public static void main(String[] args) {
        // 安全解析
        System.out.println(&quot;safeParseInt(&apos;123&apos;): &quot; + WrapperUtils.safeParseInt(&quot;123&quot;)); // 输出: 123
        System.out.println(&quot;safeParseInt(&apos;abc&apos;): &quot; + WrapperUtils.safeParseInt(&quot;abc&quot;)); // 输出: null
        
        // 数字验证
        System.out.println(&quot;isValidNumber(&apos;123.45&apos;): &quot; + WrapperUtils.isValidNumber(&quot;123.45&quot;)); // 输出: true
        System.out.println(&quot;isValidNumber(&apos;abc&apos;): &quot; + WrapperUtils.isValidNumber(&quot;abc&quot;)); // 输出: false
        
        // 数字分析
        int number = 12345;
        System.out.println(&quot;位数: &quot; + WrapperUtils.getDigitCount(number)); // 输出: 位数: 5
        System.out.println(&quot;各位数字: &quot; + Arrays.toString(WrapperUtils.getDigits(number))); // 输出: 各位数字: [1, 2, 3, 4, 5]
        
        // 字符分析
        WrapperUtils.analyzeString(&quot;Hello World 123!&quot;);
        // 输出:
        // 字符串分析结果:
        //   字母: 10
        //   数字: 3
        //   空格: 2
        //   其他: 1
        
        // 格式化
        System.out.println(&quot;格式化: &quot; + WrapperUtils.formatNumber(1234.5678, &quot;#,##0.00&quot;)); // 输出: 格式化: 1,234.57
        
        // 随机生成
        System.out.println(&quot;随机整数: &quot; + WrapperUtils.randomInteger(1, 100)); // 输出: 随机整数: 45
        System.out.println(&quot;随机浮点数: &quot; + WrapperUtils.randomDouble(0.0, 1.0)); // 输出: 随机浮点数: 0.123456789
        System.out.println(&quot;随机布尔值: &quot; + WrapperUtils.randomBoolean()); // 输出: 随机布尔值: true
    }
}
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;&lt;strong&gt;包装类的性能考虑：&lt;/strong&gt;&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;public class WrapperPerformance {
    
    // 性能测试：基本类型 vs 包装类
    public static void performanceTest() {
        int iterations = 10000000;
        
        // 基本类型测试
        long startTime = System.currentTimeMillis();
        int sum = 0;
        for (int i = 0; i &amp;lt; iterations; i++) {
            sum += i;
        }
        long endTime = System.currentTimeMillis();
        System.out.println(&quot;基本类型耗时: &quot; + (endTime - startTime) + &quot;ms&quot;);
        
        // 包装类测试
        startTime = System.currentTimeMillis();
        Integer sumObj = 0;
        for (int i = 0; i &amp;lt; iterations; i++) {
            sumObj += i; // 自动装箱和拆箱
        }
        endTime = System.currentTimeMillis();
        System.out.println(&quot;包装类耗时: &quot; + (endTime - startTime) + &quot;ms&quot;);
        
        // 输出结果示例:
        // 基本类型耗时: 5ms
        // 包装类耗时: 45ms
    }
    
    // 避免不必要的装箱拆箱
    public static void avoidUnnecessaryBoxing() {
        // 不好的做法
        Integer sum = 0;
        for (int i = 0; i &amp;lt; 1000; i++) {
            sum += i; // 每次循环都有装箱和拆箱
        }
        
        // 好的做法
        int sum2 = 0;
        for (int i = 0; i &amp;lt; 1000; i++) {
            sum2 += i; // 使用基本类型
        }
        Integer result = sum2; // 最后才装箱
    }
    
    // 使用valueOf而不是构造方法
    public static void useValueOf() {
        // 推荐
        Integer good = Integer.valueOf(100);
        Double good2 = Double.valueOf(3.14);
        
        // 不推荐（已过时）
        // Integer bad = new Integer(100);
        // Double bad2 = new Double(3.14);
    }
}
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;&lt;strong&gt;包装类的常见陷阱：&lt;/strong&gt;&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;public class WrapperTraps {
    
    public static void demonstrateTraps() {
        // 陷阱1：null值拆箱
        Integer nullInt = null;
        try {
            int value = nullInt; // NullPointerException
        } catch (NullPointerException e) {
            System.out.println(&quot;陷阱1：null值拆箱会抛出NullPointerException&quot;);
        }
        
        // 陷阱2：比较时使用==而不是equals
        Integer a = 100;
        Integer b = 100;
        Integer c = 200;
        Integer d = 200;
        
        System.out.println(&quot;a == b: &quot; + (a == b)); // true（缓存）
        System.out.println(&quot;c == d: &quot; + (c == d)); // false（未缓存）
        System.out.println(&quot;c.equals(d): &quot; + c.equals(d)); // true
        
        // 陷阱3：自动装箱的性能问题
        Long sum = 0L;
        for (long i = 0; i &amp;lt; 1000000; i++) {
            sum += i; // 每次都有装箱和拆箱
        }
        
        // 更好的做法
        long sum2 = 0L;
        for (long i = 0; i &amp;lt; 1000000; i++) {
            sum2 += i; // 使用基本类型
        }
        Long result = sum2; // 最后才装箱
        
        // 陷阱4：包装类不可变
        Integer num = 100;
        num = 200; // 不是修改原对象，而是创建新对象
        
        // 陷阱5：类型转换问题
        Double dbl = 3.14;
        int intValue = dbl.intValue(); // 截断小数部分
        System.out.println(&quot;intValue: &quot; + intValue); // 输出: 3
    }
}
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;&lt;strong&gt;包装类的最佳实践：&lt;/strong&gt;&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;public class WrapperBestPractices {
    
    // 1. 优先使用基本类型
    public static void preferPrimitives() {
        // 好的做法
        int[] numbers = new int[1000];
        double sum = 0.0;
        
        // 避免
        // Integer[] numbers = new Integer[1000];
        // Double sum = 0.0;
    }
    
    // 2. 在集合中使用包装类
    public static void useWrappersInCollections() {
        List&amp;lt;Integer&amp;gt; numbers = new ArrayList&amp;lt;&amp;gt;();
        Map&amp;lt;String, Double&amp;gt; prices = new HashMap&amp;lt;&amp;gt;();
        
        numbers.add(100); // 自动装箱
        prices.put(&quot;apple&quot;, 2.99); // 自动装箱
    }
    
    // 3. 使用equals比较包装类
    public static void compareWrappers() {
        Integer a = 100;
        Integer b = 100;
        
        // 正确的比较方式
        boolean equals = a.equals(b);
        int compare = a.compareTo(b);
        
        // 避免使用==（除非是缓存范围内的值）
    }
    
    // 4. 处理null值
    public static Integer safeUnboxing(Integer wrapper) {
        return wrapper != null ? wrapper : 0; // 提供默认值
    }
    
    // 5. 使用工具方法
    public static void useUtilityMethods() {
        // 字符串解析
        int num = Integer.parseInt(&quot;123&quot;);
        double dbl = Double.parseDouble(&quot;3.14&quot;);
        
        // 格式化
        String hex = Integer.toHexString(255);
        String binary = Integer.toBinaryString(255);
        
        // 比较
        int max = Integer.max(10, 20);
        int min = Integer.min(10, 20);
    }
    
    // 6. 性能优化
    public static void performanceOptimization() {
        // 避免在循环中频繁装箱拆箱
        int sum = 0;
        for (int i = 0; i &amp;lt; 1000000; i++) {
            sum += i; // 使用基本类型
        }
        Integer result = sum; // 最后装箱
        
        // 使用缓存
        Integer cached = Integer.valueOf(100); // 使用缓存
        Integer notCached = Integer.valueOf(200); // 不在缓存范围内
    }
}
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;&lt;strong&gt;包装类的注意事项：&lt;/strong&gt;&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;
&lt;p&gt;&lt;strong&gt;性能考虑&lt;/strong&gt;：&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;基本类型比包装类性能更好&lt;/li&gt;
&lt;li&gt;避免在循环中频繁装箱拆箱&lt;/li&gt;
&lt;li&gt;优先使用基本类型进行数值计算&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;&lt;strong&gt;null值处理&lt;/strong&gt;：&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;包装类可以为null，基本类型不能&lt;/li&gt;
&lt;li&gt;拆箱前要检查null值&lt;/li&gt;
&lt;li&gt;提供默认值处理null情况&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;&lt;strong&gt;比较操作&lt;/strong&gt;：&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;使用equals()比较包装类内容&lt;/li&gt;
&lt;li&gt;使用compareTo()进行排序&lt;/li&gt;
&lt;li&gt;避免使用==比较（除非是缓存值）&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;&lt;strong&gt;缓存机制&lt;/strong&gt;：&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;Integer缓存-128到127&lt;/li&gt;
&lt;li&gt;Boolean缓存true和false&lt;/li&gt;
&lt;li&gt;Character缓存0到127&lt;/li&gt;
&lt;li&gt;使用valueOf()方法利用缓存&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;&lt;strong&gt;不可变性&lt;/strong&gt;：&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;所有包装类都是不可变的&lt;/li&gt;
&lt;li&gt;修改操作会创建新对象&lt;/li&gt;
&lt;li&gt;适合作为Map的键&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;&lt;strong&gt;应用场景&lt;/strong&gt;：&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;集合中存储数值&lt;/li&gt;
&lt;li&gt;需要null值的场景&lt;/li&gt;
&lt;li&gt;反射和泛型中使用&lt;/li&gt;
&lt;li&gt;数据库操作中的null处理&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;/ol&gt;
&lt;h5&gt;7.10 Lambda表达式&lt;/h5&gt;
&lt;p&gt;&lt;strong&gt;Lambda表达式基础：&lt;/strong&gt;&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;// Lambda表达式语法：(参数列表) -&amp;gt; {表达式或语句块}

// 1. 无参数Lambda表达式
Runnable runnable = () -&amp;gt; System.out.println(&quot;Hello Lambda&quot;);
runnable.run(); // 输出: Hello Lambda

// 2. 单参数Lambda表达式
Consumer&amp;lt;String&amp;gt; consumer = (str) -&amp;gt; System.out.println(&quot;接收: &quot; + str);
consumer.accept(&quot;Hello&quot;); // 输出: 接收: Hello

// 3. 多参数Lambda表达式
BiFunction&amp;lt;Integer, Integer, Integer&amp;gt; add = (a, b) -&amp;gt; a + b;
int result = add.apply(5, 3);
System.out.println(&quot;结果: &quot; + result); // 输出: 结果: 8

// 4. 带类型声明的Lambda表达式
BiFunction&amp;lt;String, String, String&amp;gt; concat = (String a, String b) -&amp;gt; a + b;
String result2 = concat.apply(&quot;Hello&quot;, &quot; World&quot;);
System.out.println(&quot;拼接结果: &quot; + result2); // 输出: 拼接结果: Hello World

// 5. 多行Lambda表达式
Consumer&amp;lt;String&amp;gt; multiLine = (str) -&amp;gt; {
    System.out.println(&quot;处理字符串: &quot; + str);
    System.out.println(&quot;字符串长度: &quot; + str.length());
    System.out.println(&quot;转换为大写: &quot; + str.toUpperCase());
};
multiLine.accept(&quot;hello world&quot;);
// 输出:
// 处理字符串: hello world
// 字符串长度: 11
// 转换为大写: HELLO WORLD
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;&lt;strong&gt;函数式接口：&lt;/strong&gt;&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;// 自定义函数式接口
@FunctionalInterface
interface MathOperation {
    int operate(int a, int b);
}

@FunctionalInterface
interface StringProcessor {
    String process(String input);
}

// 使用自定义函数式接口
MathOperation add = (a, b) -&amp;gt; a + b;
MathOperation subtract = (a, b) -&amp;gt; a - b;
MathOperation multiply = (a, b) -&amp;gt; a * b;
MathOperation divide = (a, b) -&amp;gt; a / b;

// 输出结果
System.out.println(&quot;加法: &quot; + add.operate(10, 5));      // 输出: 加法: 15
System.out.println(&quot;减法: &quot; + subtract.operate(10, 5)); // 输出: 减法: 5
System.out.println(&quot;乘法: &quot; + multiply.operate(10, 5)); // 输出: 乘法: 50
System.out.println(&quot;除法: &quot; + divide.operate(10, 5));   // 输出: 除法: 2

// 字符串处理
StringProcessor upper = str -&amp;gt; str.toUpperCase();
StringProcessor reverse = str -&amp;gt; new StringBuilder(str).reverse().toString();
StringProcessor addPrefix = str -&amp;gt; &quot;处理后的: &quot; + str;

// 输出结果
System.out.println(&quot;大写: &quot; + upper.process(&quot;hello&quot;));           // 输出: 大写: HELLO
System.out.println(&quot;反转: &quot; + reverse.process(&quot;hello&quot;));         // 输出: 反转: olleh
System.out.println(&quot;加前缀: &quot; + addPrefix.process(&quot;hello&quot;));     // 输出: 加前缀: 处理后的: hello
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;&lt;strong&gt;Java内置函数式接口：&lt;/strong&gt;&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;import java.util.function.*;
import java.util.*;

// Predicate&amp;lt;T&amp;gt; - 断言接口
Predicate&amp;lt;String&amp;gt; isLongerThan5 = str -&amp;gt; str.length() &amp;gt; 5;
Predicate&amp;lt;String&amp;gt; startsWithA = str -&amp;gt; str.startsWith(&quot;A&quot;);
Predicate&amp;lt;String&amp;gt; isLongerThan5AndStartsWithA = isLongerThan5.and(startsWithA);

// 输出结果
System.out.println(&quot;长度大于5: &quot; + isLongerThan5.test(&quot;Hello&quot;));     // 输出: 长度大于5: false
System.out.println(&quot;长度大于5: &quot; + isLongerThan5.test(&quot;Hello World&quot;)); // 输出: 长度大于5: true
System.out.println(&quot;以A开头且长度大于5: &quot; + isLongerThan5AndStartsWithA.test(&quot;Apple&quot;)); // 输出: 以A开头且长度大于5: false

// Function&amp;lt;T, R&amp;gt; - 函数接口
Function&amp;lt;String, Integer&amp;gt; getLength = str -&amp;gt; str.length();
Function&amp;lt;String, String&amp;gt; toUpperCase = str -&amp;gt; str.toUpperCase();
Function&amp;lt;String, String&amp;gt; addExclamation = str -&amp;gt; str + &quot;!&quot;;

// 函数组合
Function&amp;lt;String, String&amp;gt; combined = getLength.andThen(length -&amp;gt; &quot;长度: &quot; + length);
Function&amp;lt;String, String&amp;gt; pipeline = toUpperCase.andThen(addExclamation);

// 输出结果
System.out.println(&quot;长度: &quot; + getLength.apply(&quot;Hello&quot;));           // 输出: 长度: 5
System.out.println(&quot;组合结果: &quot; + combined.apply(&quot;Hello&quot;));        // 输出: 组合结果: 长度: 5
System.out.println(&quot;管道处理: &quot; + pipeline.apply(&quot;hello&quot;));        // 输出: 管道处理: HELLO!

// Consumer&amp;lt;T&amp;gt; - 消费者接口
Consumer&amp;lt;String&amp;gt; print = str -&amp;gt; System.out.println(&quot;打印: &quot; + str);
Consumer&amp;lt;String&amp;gt; printLength = str -&amp;gt; System.out.println(&quot;长度: &quot; + str.length());
Consumer&amp;lt;String&amp;gt; combinedConsumer = print.andThen(printLength);

// 输出结果
combinedConsumer.accept(&quot;Hello&quot;);
// 输出:
// 打印: Hello
// 长度: 5

// Supplier&amp;lt;T&amp;gt; - 供应者接口
Supplier&amp;lt;Double&amp;gt; randomDouble = () -&amp;gt; Math.random();
Supplier&amp;lt;String&amp;gt; currentTime = () -&amp;gt; new Date().toString();
Supplier&amp;lt;List&amp;lt;String&amp;gt;&amp;gt; emptyList = ArrayList::new;

// 输出结果
System.out.println(&quot;随机数: &quot; + randomDouble.get());     // 输出: 随机数: 0.123456789
System.out.println(&quot;当前时间: &quot; + currentTime.get());   // 输出: 当前时间: Wed May 15 20:30:45 CST 2025
System.out.println(&quot;空列表: &quot; + emptyList.get());       // 输出: 空列表: []

// BiFunction&amp;lt;T, U, R&amp;gt; - 双参数函数接口
BiFunction&amp;lt;String, String, String&amp;gt; concat = (a, b) -&amp;gt; a + b;
BiFunction&amp;lt;Integer, Integer, Integer&amp;gt; max = Math::max;
BiFunction&amp;lt;String, Integer, String&amp;gt; repeat = (str, times) -&amp;gt; str.repeat(times);

// 输出结果
System.out.println(&quot;拼接: &quot; + concat.apply(&quot;Hello&quot;, &quot; World&quot;)); // 输出: 拼接: Hello World
System.out.println(&quot;最大值: &quot; + max.apply(10, 20));             // 输出: 最大值: 20
System.out.println(&quot;重复: &quot; + repeat.apply(&quot;Ha&quot;, 3));           // 输出: 重复: HaHaHa
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;&lt;strong&gt;Lambda表达式在集合中的应用：&lt;/strong&gt;&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;import java.util.*;
import java.util.stream.Collectors;

List&amp;lt;String&amp;gt; names = Arrays.asList(&quot;Alice&quot;, &quot;Bob&quot;, &quot;Charlie&quot;, &quot;David&quot;, &quot;Eve&quot;);

// 1. forEach - 遍历
System.out.println(&quot;=== forEach遍历 ===&quot;);
names.forEach(name -&amp;gt; System.out.println(&quot;名字: &quot; + name));
// 输出:
// 名字: Alice
// 名字: Bob
// 名字: Charlie
// 名字: David
// 名字: Eve

// 2. removeIf - 条件删除
List&amp;lt;String&amp;gt; namesCopy = new ArrayList&amp;lt;&amp;gt;(names);
namesCopy.removeIf(name -&amp;gt; name.length() &amp;lt; 4);
System.out.println(&quot;删除短名字后: &quot; + namesCopy); // 输出: 删除短名字后: [Alice, Charlie, David]

// 3. replaceAll - 批量替换
List&amp;lt;String&amp;gt; namesCopy2 = new ArrayList&amp;lt;&amp;gt;(names);
namesCopy2.replaceAll(name -&amp;gt; name.toUpperCase());
System.out.println(&quot;转换为大写: &quot; + namesCopy2); // 输出: 转换为大写: [ALICE, BOB, CHARLIE, DAVID, EVE]

// 4. sort - 排序
List&amp;lt;String&amp;gt; namesCopy3 = new ArrayList&amp;lt;&amp;gt;(names);
namesCopy3.sort((a, b) -&amp;gt; a.compareTo(b)); // 按字母顺序排序
System.out.println(&quot;排序后: &quot; + namesCopy3); // 输出: 排序后: [Alice, Bob, Charlie, David, Eve]

// 按长度排序
namesCopy3.sort((a, b) -&amp;gt; Integer.compare(a.length(), b.length()));
System.out.println(&quot;按长度排序: &quot; + namesCopy3); // 输出: 按长度排序: [Bob, Eve, Alice, David, Charlie]

// 5. Map操作
Map&amp;lt;String, Integer&amp;gt; scores = new HashMap&amp;lt;&amp;gt;();
scores.put(&quot;Alice&quot;, 85);
scores.put(&quot;Bob&quot;, 92);
scores.put(&quot;Charlie&quot;, 78);

// 遍历Map
System.out.println(&quot;=== Map遍历 ===&quot;);
scores.forEach((name, score) -&amp;gt; System.out.println(name + &quot;: &quot; + score));
// 输出:
// Alice: 85
// Bob: 92
// Charlie: 78

// 条件操作
scores.entrySet().removeIf(entry -&amp;gt; entry.getValue() &amp;lt; 80);
System.out.println(&quot;删除低分后: &quot; + scores); // 输出: 删除低分后: {Alice=85, Bob=92}

// 6. Stream API与Lambda
System.out.println(&quot;=== Stream操作 ===&quot;);

// 过滤
List&amp;lt;String&amp;gt; longNames = names.stream()
    .filter(name -&amp;gt; name.length() &amp;gt; 4)
    .collect(Collectors.toList());
System.out.println(&quot;长名字: &quot; + longNames); // 输出: 长名字: [Alice, Charlie, David]

// 映射
List&amp;lt;Integer&amp;gt; nameLengths = names.stream()
    .map(String::length)
    .collect(Collectors.toList());
System.out.println(&quot;名字长度: &quot; + nameLengths); // 输出: 名字长度: [5, 3, 7, 5, 3]

// 归约
int totalLength = names.stream()
    .mapToInt(String::length)
    .sum();
System.out.println(&quot;总长度: &quot; + totalLength); // 输出: 总长度: 23

// 分组
Map&amp;lt;Integer, List&amp;lt;String&amp;gt;&amp;gt; groupedByLength = names.stream()
    .collect(Collectors.groupingBy(String::length));
System.out.println(&quot;按长度分组: &quot; + groupedByLength); // 输出: 按长度分组: {3=[Bob, Eve], 5=[Alice, David], 7=[Charlie]}
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;&lt;strong&gt;Lambda表达式的高级用法：&lt;/strong&gt;&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;import java.util.*;
import java.util.concurrent.*;
import java.util.function.*;

// 1. 方法引用
List&amp;lt;String&amp;gt; names = Arrays.asList(&quot;Alice&quot;, &quot;Bob&quot;, &quot;Charlie&quot;);

// 静态方法引用
names.forEach(System.out::println);
// 输出:
// Alice
// Bob
// Charlie

// 实例方法引用
names.forEach(String::toUpperCase);
names.stream().map(String::length).forEach(System.out::println);

// 构造方法引用
Supplier&amp;lt;ArrayList&amp;lt;String&amp;gt;&amp;gt; listSupplier = ArrayList::new;
ArrayList&amp;lt;String&amp;gt; newList = listSupplier.get();

// 2. 闭包和变量捕获
int factor = 10;
Function&amp;lt;Integer, Integer&amp;gt; multiplier = x -&amp;gt; x * factor;
System.out.println(&quot;乘以因子: &quot; + multiplier.apply(5)); // 输出: 乘以因子: 50

// 注意：捕获的变量必须是final或effectively final
// int mutableFactor = 10;
// Function&amp;lt;Integer, Integer&amp;gt; badMultiplier = x -&amp;gt; x * mutableFactor; // 编译错误
// mutableFactor = 20; // 如果这行存在，上面的Lambda表达式会编译错误

// 3. 异常处理
Function&amp;lt;String, Integer&amp;gt; safeParseInt = str -&amp;gt; {
    try {
        return Integer.parseInt(str);
    } catch (NumberFormatException e) {
        return 0;
    }
};

System.out.println(&quot;安全解析: &quot; + safeParseInt.apply(&quot;123&quot;)); // 输出: 安全解析: 123
System.out.println(&quot;安全解析: &quot; + safeParseInt.apply(&quot;abc&quot;)); // 输出: 安全解析: 0

// 4. 递归Lambda
// 使用函数式接口实现递归
Function&amp;lt;Integer, Integer&amp;gt; factorial = new Function&amp;lt;Integer, Integer&amp;gt;() {
    @Override
    public Integer apply(Integer n) {
        return n &amp;lt;= 1 ? 1 : n * this.apply(n - 1);
    }
};

System.out.println(&quot;阶乘: &quot; + factorial.apply(5)); // 输出: 阶乘: 120

// 5. 组合模式
Function&amp;lt;String, String&amp;gt; pipeline = ((Function&amp;lt;String, String&amp;gt;) String::toUpperCase)
    .andThen(str -&amp;gt; str + &quot;!&quot;)
    .andThen(str -&amp;gt; &quot;处理结果: &quot; + str);

System.out.println(&quot;管道处理: &quot; + pipeline.apply(&quot;hello&quot;)); // 输出: 管道处理: 处理结果: HELLO!

// 6. 条件Lambda
Predicate&amp;lt;String&amp;gt; isLong = str -&amp;gt; str.length() &amp;gt; 5;
Predicate&amp;lt;String&amp;gt; startsWithA = str -&amp;gt; str.startsWith(&quot;A&quot;);

Function&amp;lt;String, String&amp;gt; conditionalProcessor = str -&amp;gt; {
    if (isLong.test(str)) {
        return str.toUpperCase();
    } else if (startsWithA.test(str)) {
        return str.toLowerCase();
    } else {
        return str + &quot; (默认处理)&quot;;
    }
};

System.out.println(&quot;条件处理1: &quot; + conditionalProcessor.apply(&quot;Hello World&quot;)); // 输出: 条件处理1: HELLO WORLD
System.out.println(&quot;条件处理2: &quot; + conditionalProcessor.apply(&quot;Apple&quot;));      // 输出: 条件处理2: apple
System.out.println(&quot;条件处理3: &quot; + conditionalProcessor.apply(&quot;Hi&quot;));         // 输出: 条件处理3: Hi (默认处理)
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;&lt;strong&gt;Lambda表达式的性能优化：&lt;/strong&gt;&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;import java.util.*;
import java.util.stream.*;

public class LambdaPerformance {
    
    // 1. 避免在循环中创建Lambda
    public static void avoidLambdaInLoop() {
        List&amp;lt;String&amp;gt; names = Arrays.asList(&quot;Alice&quot;, &quot;Bob&quot;, &quot;Charlie&quot;, &quot;David&quot;);
        
        // 不好的做法：每次循环都创建新的Lambda
        for (String name : names) {
            Consumer&amp;lt;String&amp;gt; printer = str -&amp;gt; System.out.println(&quot;名字: &quot; + str);
            printer.accept(name);
        }
        
        // 好的做法：预先定义Lambda
        Consumer&amp;lt;String&amp;gt; printer = str -&amp;gt; System.out.println(&quot;名字: &quot; + str);
        for (String name : names) {
            printer.accept(name);
        }
        
        // 更好的做法：使用forEach
        names.forEach(str -&amp;gt; System.out.println(&quot;名字: &quot; + str));
    }
    
    // 2. 使用并行流提高性能
    public static void parallelStreamExample() {
        List&amp;lt;Integer&amp;gt; numbers = Arrays.asList(1, 2, 3, 4, 5, 6, 7, 8, 9, 10);
        
        // 顺序流
        long startTime = System.currentTimeMillis();
        int sum = numbers.stream()
            .mapToInt(Integer::intValue)
            .sum();
        long endTime = System.currentTimeMillis();
        System.out.println(&quot;顺序流耗时: &quot; + (endTime - startTime) + &quot;ms&quot;);
        
        // 并行流
        startTime = System.currentTimeMillis();
        int sumParallel = numbers.parallelStream()
            .mapToInt(Integer::intValue)
            .sum();
        endTime = System.currentTimeMillis();
        System.out.println(&quot;并行流耗时: &quot; + (endTime - startTime) + &quot;ms&quot;);
    }
    
    // 3. 缓存Lambda表达式
    public static void cacheLambda() {
        // 缓存常用的Lambda表达式
        Function&amp;lt;String, String&amp;gt; upperCase = String::toUpperCase;
        Function&amp;lt;String, String&amp;gt; addExclamation = str -&amp;gt; str + &quot;!&quot;;
        Predicate&amp;lt;String&amp;gt; isLong = str -&amp;gt; str.length() &amp;gt; 5;
        
        List&amp;lt;String&amp;gt; names = Arrays.asList(&quot;Alice&quot;, &quot;Bob&quot;, &quot;Charlie&quot;, &quot;David&quot;);
        
        // 重用缓存的Lambda
        names.stream()
            .filter(isLong)
            .map(upperCase)
            .map(addExclamation)
            .forEach(System.out::println);
    }
    
    // 4. 避免不必要的装箱拆箱
    public static void avoidBoxing() {
        List&amp;lt;Integer&amp;gt; numbers = Arrays.asList(1, 2, 3, 4, 5);
        
        // 不好的做法：有装箱拆箱
        int sum = numbers.stream()
            .mapToInt(Integer::intValue) // 避免装箱
            .sum();
        
        // 更好的做法：直接使用基本类型
        int[] primitiveNumbers = {1, 2, 3, 4, 5};
        int sum2 = Arrays.stream(primitiveNumbers)
            .sum();
    }
}

// 使用示例
public class LambdaPerformanceExample {
    public static void main(String[] args) {
        // 性能测试
        LambdaPerformance.avoidLambdaInLoop();
        LambdaPerformance.parallelStreamExample();
        LambdaPerformance.cacheLambda();
        LambdaPerformance.avoidBoxing();
    }
}
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;&lt;strong&gt;Lambda表达式的实用工具类：&lt;/strong&gt;&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;import java.util.*;
import java.util.function.*;
import java.util.stream.Collectors;

public class LambdaUtils {
    
    // 1. 安全的函数执行
    public static &amp;lt;T, R&amp;gt; Optional&amp;lt;R&amp;gt; safeApply(Function&amp;lt;T, R&amp;gt; function, T input) {
        try {
            return Optional.of(function.apply(input));
        } catch (Exception e) {
            return Optional.empty();
        }
    }
    
    // 2. 重试机制
    public static &amp;lt;T&amp;gt; T retry(Supplier&amp;lt;T&amp;gt; supplier, int maxAttempts) {
        for (int i = 0; i &amp;lt; maxAttempts; i++) {
            try {
                return supplier.get();
            } catch (Exception e) {
                if (i == maxAttempts - 1) {
                    throw new RuntimeException(&quot;重试失败&quot;, e);
                }
            }
        }
        throw new RuntimeException(&quot;重试失败&quot;);
    }
    
    // 3. 条件执行
    public static &amp;lt;T&amp;gt; Optional&amp;lt;T&amp;gt; executeIf(Predicate&amp;lt;T&amp;gt; condition, Function&amp;lt;T, T&amp;gt; action, T input) {
        return condition.test(input) ? Optional.of(action.apply(input)) : Optional.of(input);
    }
    
    // 4. 管道处理
    @SafeVarargs
    public static &amp;lt;T&amp;gt; Function&amp;lt;T, T&amp;gt; pipeline(Function&amp;lt;T, T&amp;gt;... functions) {
        return Arrays.stream(functions)
            .reduce(Function.identity(), Function::andThen);
    }
    
    // 5. 批量处理
    public static &amp;lt;T, R&amp;gt; List&amp;lt;R&amp;gt; batchProcess(List&amp;lt;T&amp;gt; items, Function&amp;lt;T, R&amp;gt; processor, int batchSize) {
        return items.stream()
            .collect(Collectors.groupingBy(item -&amp;gt; items.indexOf(item) / batchSize))
            .values()
            .stream()
            .flatMap(batch -&amp;gt; batch.stream().map(processor))
            .collect(Collectors.toList());
    }
    
    // 6. 异步执行
    public static &amp;lt;T&amp;gt; CompletableFuture&amp;lt;T&amp;gt; asyncExecute(Supplier&amp;lt;T&amp;gt; supplier) {
        return CompletableFuture.supplyAsync(supplier);
    }
    
    // 7. 缓存函数结果
    public static &amp;lt;T, R&amp;gt; Function&amp;lt;T, R&amp;gt; memoize(Function&amp;lt;T, R&amp;gt; function) {
        Map&amp;lt;T, R&amp;gt; cache = new ConcurrentHashMap&amp;lt;&amp;gt;();
        return input -&amp;gt; cache.computeIfAbsent(input, function);
    }
    
    // 8. 组合多个谓词
    @SafeVarargs
    public static &amp;lt;T&amp;gt; Predicate&amp;lt;T&amp;gt; allOf(Predicate&amp;lt;T&amp;gt;... predicates) {
        return Arrays.stream(predicates)
            .reduce(Predicate::and)
            .orElse(x -&amp;gt; true);
    }
    
    @SafeVarargs
    public static &amp;lt;T&amp;gt; Predicate&amp;lt;T&amp;gt; anyOf(Predicate&amp;lt;T&amp;gt;... predicates) {
        return Arrays.stream(predicates)
            .reduce(Predicate::or)
            .orElse(x -&amp;gt; false);
    }
    
    // 9. 函数柯里化
    public static &amp;lt;T, U, R&amp;gt; Function&amp;lt;T, Function&amp;lt;U, R&amp;gt;&amp;gt; curry(BiFunction&amp;lt;T, U, R&amp;gt; function) {
        return t -&amp;gt; u -&amp;gt; function.apply(t, u);
    }
    
    // 10. 部分应用
    public static &amp;lt;T, U, R&amp;gt; Function&amp;lt;U, R&amp;gt; partial(BiFunction&amp;lt;T, U, R&amp;gt; function, T first) {
        return u -&amp;gt; function.apply(first, u);
    }
}

// 使用示例
public class LambdaUtilsExample {
    public static void main(String[] args) {
        // 安全执行
        Optional&amp;lt;String&amp;gt; result = LambdaUtils.safeApply(
            str -&amp;gt; str.toUpperCase(), &quot;hello&quot;);
        System.out.println(&quot;安全执行结果: &quot; + result); // 输出: 安全执行结果: Optional[HELLO]
        
        // 重试机制
        String retryResult = LambdaUtils.retry(
            () -&amp;gt; &quot;成功&quot;, 3);
        System.out.println(&quot;重试结果: &quot; + retryResult); // 输出: 重试结果: 成功
        
        // 条件执行
        Optional&amp;lt;String&amp;gt; conditionalResult = LambdaUtils.executeIf(
            str -&amp;gt; str.length() &amp;gt; 3,
            str -&amp;gt; str.toUpperCase(),
            &quot;hello&quot;);
        System.out.println(&quot;条件执行结果: &quot; + conditionalResult); // 输出: 条件执行结果: Optional[HELLO]
        
        // 管道处理
        Function&amp;lt;String, String&amp;gt; pipeline = LambdaUtils.pipeline(
            String::toUpperCase,
            str -&amp;gt; str + &quot;!&quot;,
            str -&amp;gt; &quot;处理结果: &quot; + str
        );
        System.out.println(&quot;管道处理: &quot; + pipeline.apply(&quot;hello&quot;)); // 输出: 管道处理: 处理结果: HELLO!
        
        // 批量处理
        List&amp;lt;String&amp;gt; items = Arrays.asList(&quot;a&quot;, &quot;b&quot;, &quot;c&quot;, &quot;d&quot;, &quot;e&quot;);
        List&amp;lt;String&amp;gt; processed = LambdaUtils.batchProcess(
            items, str -&amp;gt; str.toUpperCase(), 2);
        System.out.println(&quot;批量处理结果: &quot; + processed); // 输出: 批量处理结果: [A, B, C, D, E]
        
        // 组合谓词
        Predicate&amp;lt;String&amp;gt; combined = LambdaUtils.allOf(
            str -&amp;gt; str.length() &amp;gt; 3,
            str -&amp;gt; str.startsWith(&quot;h&quot;)
        );
        System.out.println(&quot;组合谓词测试: &quot; + combined.test(&quot;hello&quot;)); // 输出: 组合谓词测试: true
        
        // 柯里化
        BiFunction&amp;lt;Integer, Integer, Integer&amp;gt; add = (a, b) -&amp;gt; a + b;
        Function&amp;lt;Integer, Function&amp;lt;Integer, Integer&amp;gt;&amp;gt; curriedAdd = LambdaUtils.curry(add);
        Function&amp;lt;Integer, Integer&amp;gt; addFive = curriedAdd.apply(5);
        System.out.println(&quot;柯里化结果: &quot; + addFive.apply(3)); // 输出: 柯里化结果: 8
    }
}
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;&lt;strong&gt;Lambda表达式的注意事项：&lt;/strong&gt;&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;
&lt;p&gt;&lt;strong&gt;语法规则&lt;/strong&gt;：&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;参数类型可以省略（类型推断）&lt;/li&gt;
&lt;li&gt;单个参数可以省略括号&lt;/li&gt;
&lt;li&gt;单行表达式可以省略return和大括号&lt;/li&gt;
&lt;li&gt;多行表达式需要大括号和return&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;&lt;strong&gt;变量捕获&lt;/strong&gt;：&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;只能捕获final或effectively final的变量&lt;/li&gt;
&lt;li&gt;不能修改捕获的变量&lt;/li&gt;
&lt;li&gt;可以访问实例变量和静态变量&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;&lt;strong&gt;性能考虑&lt;/strong&gt;：&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;避免在循环中重复创建Lambda&lt;/li&gt;
&lt;li&gt;使用并行流提高性能&lt;/li&gt;
&lt;li&gt;缓存常用的Lambda表达式&lt;/li&gt;
&lt;li&gt;避免不必要的装箱拆箱&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;&lt;strong&gt;常见陷阱&lt;/strong&gt;：&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;变量捕获的限制&lt;/li&gt;
&lt;li&gt;异常处理&lt;/li&gt;
&lt;li&gt;递归Lambda的实现&lt;/li&gt;
&lt;li&gt;并发环境下的使用&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;&lt;strong&gt;最佳实践&lt;/strong&gt;：&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;优先使用方法引用&lt;/li&gt;
&lt;li&gt;合理使用函数式接口&lt;/li&gt;
&lt;li&gt;注意代码可读性&lt;/li&gt;
&lt;li&gt;适当使用Stream API&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;&lt;strong&gt;应用场景&lt;/strong&gt;：&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;集合操作和遍历&lt;/li&gt;
&lt;li&gt;事件处理&lt;/li&gt;
&lt;li&gt;异步编程&lt;/li&gt;
&lt;li&gt;函数式编程&lt;/li&gt;
&lt;li&gt;回调函数&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;/ol&gt;
&lt;h5&gt;7.11 Apache Commons IO&lt;/h5&gt;
&lt;p&gt;&lt;strong&gt;Commons IO简介：&lt;/strong&gt;&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;// Maven依赖
&amp;lt;dependency&amp;gt;
    &amp;lt;groupId&amp;gt;commons-io&amp;lt;/groupId&amp;gt;
    &amp;lt;artifactId&amp;gt;commons-io&amp;lt;/artifactId&amp;gt;
    &amp;lt;version&amp;gt;2.15.1&amp;lt;/version&amp;gt;
&amp;lt;/dependency&amp;gt;

// Gradle依赖
implementation &apos;commons-io:commons-io:2.15.1&apos;

// 主要包结构
import org.apache.commons.io.*;
import org.apache.commons.io.file.*;
import org.apache.commons.io.input.*;
import org.apache.commons.io.output.*;
import org.apache.commons.io.monitor.*;
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;&lt;strong&gt;FileUtils工具类：&lt;/strong&gt;&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;import org.apache.commons.io.FileUtils;
import java.io.*;
import java.nio.charset.StandardCharsets;

// 1. 文件读写操作
public class FileUtilsExample {
    
    public static void fileOperations() throws IOException {
        // 读取文件内容
        String content = FileUtils.readFileToString(new File(&quot;input.txt&quot;), StandardCharsets.UTF_8);
        System.out.println(&quot;文件内容: &quot; + content);
        
        // 写入文件内容
        FileUtils.writeStringToFile(new File(&quot;output.txt&quot;), &quot;Hello Commons IO&quot;, StandardCharsets.UTF_8);
        
        // 追加内容
        FileUtils.writeStringToFile(new File(&quot;output.txt&quot;), &quot;\n追加内容&quot;, StandardCharsets.UTF_8, true);
        
        // 读取文件为字节数组
        byte[] bytes = FileUtils.readFileToByteArray(new File(&quot;input.txt&quot;));
        System.out.println(&quot;文件大小: &quot; + bytes.length + &quot; bytes&quot;);
        
        // 写入字节数组到文件
        FileUtils.writeByteArrayToFile(new File(&quot;output.bin&quot;), bytes);
        
        // 按行读取文件
        List&amp;lt;String&amp;gt; lines = FileUtils.readLines(new File(&quot;input.txt&quot;), StandardCharsets.UTF_8);
        lines.forEach(System.out::println);
        
        // 按行写入文件
        List&amp;lt;String&amp;gt; writeLines = Arrays.asList(&quot;第一行&quot;, &quot;第二行&quot;, &quot;第三行&quot;);
        FileUtils.writeLines(new File(&quot;lines.txt&quot;), writeLines, StandardCharsets.UTF_8);
    }
    
    // 2. 文件操作
    public static void fileManipulation() throws IOException {
        File source = new File(&quot;source.txt&quot;);
        File dest = new File(&quot;dest.txt&quot;);
        
        // 复制文件
        FileUtils.copyFile(source, dest);
        System.out.println(&quot;文件复制完成&quot;);
        
        // 复制目录
        FileUtils.copyDirectory(new File(&quot;sourceDir&quot;), new File(&quot;destDir&quot;));
        System.out.println(&quot;目录复制完成&quot;);
        
        // 移动文件
        FileUtils.moveFile(source, new File(&quot;moved.txt&quot;));
        System.out.println(&quot;文件移动完成&quot;);
        
        // 删除文件或目录
        FileUtils.deleteQuietly(new File(&quot;temp.txt&quot;)); // 静默删除，不抛异常
        FileUtils.forceDelete(new File(&quot;tempDir&quot;));    // 强制删除，抛异常
        
        // 创建目录
        FileUtils.forceMkdir(new File(&quot;newDir&quot;));
        System.out.println(&quot;目录创建完成&quot;);
        
        // 清理目录
        FileUtils.cleanDirectory(new File(&quot;tempDir&quot;)); // 删除目录内容，保留目录
        System.out.println(&quot;目录清理完成&quot;);
    }
    
    // 3. 文件信息
    public static void fileInfo() throws IOException {
        File file = new File(&quot;example.txt&quot;);
        
        // 获取文件大小
        long size = FileUtils.sizeOf(file);
        System.out.println(&quot;文件大小: &quot; + size + &quot; bytes&quot;);
        
        // 获取目录大小
        long dirSize = FileUtils.sizeOfDirectory(new File(&quot;exampleDir&quot;));
        System.out.println(&quot;目录大小: &quot; + dirSize + &quot; bytes&quot;);
        
        // 获取文件大小（人类可读格式）
        String humanReadableSize = FileUtils.byteCountToDisplaySize(size);
        System.out.println(&quot;可读大小: &quot; + humanReadableSize);
        
        // 检查文件是否为空
        boolean isEmpty = FileUtils.isEmptyDirectory(new File(&quot;emptyDir&quot;));
        System.out.println(&quot;目录是否为空: &quot; + isEmpty);
        
        // 获取文件修改时间
        Date lastModified = new Date(file.lastModified());
        System.out.println(&quot;最后修改时间: &quot; + lastModified);
    }
    
    // 4. 文件比较
    public static void fileComparison() throws IOException {
        File file1 = new File(&quot;file1.txt&quot;);
        File file2 = new File(&quot;file2.txt&quot;);
        
        // 比较文件内容
        boolean isEqual = FileUtils.contentEquals(file1, file2);
        System.out.println(&quot;文件内容是否相同: &quot; + isEqual);
        
        // 比较文件内容（忽略行结束符）
        boolean isEqualIgnoreEOL = FileUtils.contentEqualsIgnoreEOL(file1, file2, StandardCharsets.UTF_8);
        System.out.println(&quot;文件内容是否相同（忽略行结束符）: &quot; + isEqualIgnoreEOL);
    }
}
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;&lt;strong&gt;IOUtils工具类：&lt;/strong&gt;&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;import org.apache.commons.io.IOUtils;
import java.io.*;
import java.net.URL;
import java.nio.charset.StandardCharsets;

public class IOUtilsExample {
    
    // 1. 流操作
    public static void streamOperations() throws IOException {
        // 复制流
        InputStream input = new FileInputStream(&quot;input.txt&quot;);
        OutputStream output = new FileOutputStream(&quot;output.txt&quot;);
        IOUtils.copy(input, output);
        IOUtils.closeQuietly(input, output);
        
        // 复制流（指定缓冲区大小）
        IOUtils.copy(input, output, 8192);
        
        // 复制流到字符串
        String content = IOUtils.toString(input, StandardCharsets.UTF_8);
        System.out.println(&quot;流内容: &quot; + content);
        
        // 复制字符串到流
        IOUtils.write(&quot;Hello IOUtils&quot;, output, StandardCharsets.UTF_8);
        
        // 复制流到字节数组
        byte[] bytes = IOUtils.toByteArray(input);
        System.out.println(&quot;字节数组大小: &quot; + bytes.length);
        
        // 复制字节数组到流
        IOUtils.write(bytes, output);
        
        // 按行读取
        List&amp;lt;String&amp;gt; lines = IOUtils.readLines(input, StandardCharsets.UTF_8);
        lines.forEach(System.out::println);
        
        // 按行写入
        IOUtils.writeLines(lines, &quot;\n&quot;, output, StandardCharsets.UTF_8);
    }
    
    // 2. 网络操作
    public static void networkOperations() throws IOException {
        // 从URL读取内容
        URL url = new URL(&quot;https://www.example.com&quot;);
        String content = IOUtils.toString(url, StandardCharsets.UTF_8);
        System.out.println(&quot;网页内容长度: &quot; + content.length());
        
        // 从URL读取字节数组
        byte[] bytes = IOUtils.toByteArray(url);
        System.out.println(&quot;网页字节大小: &quot; + bytes.length);
        
        // 下载文件
        try (InputStream input = url.openStream();
             FileOutputStream output = new FileOutputStream(&quot;downloaded.html&quot;)) {
            IOUtils.copy(input, output);
        }
    }
    
    // 3. 流转换
    public static void streamConversion() throws IOException {
        // 字符串转输入流
        String text = &quot;Hello World&quot;;
        InputStream inputStream = IOUtils.toInputStream(text, StandardCharsets.UTF_8);
        
        // 字节数组转输入流
        byte[] bytes = &quot;Hello Bytes&quot;.getBytes(StandardCharsets.UTF_8);
        InputStream byteStream = IOUtils.toInputStream(bytes);
        
        // 读取所有内容
        String allContent = IOUtils.toString(inputStream, StandardCharsets.UTF_8);
        System.out.println(&quot;转换后的内容: &quot; + allContent);
    }
    
    // 4. 流监控
    public static void streamMonitoring() throws IOException {
        InputStream input = new FileInputStream(&quot;large.txt&quot;);
        
        // 创建带进度监控的流
        InputStream monitoredStream = new ProgressMonitorInputStream(
            null, &quot;读取文件&quot;, input);
        
        // 读取内容
        String content = IOUtils.toString(monitoredStream, StandardCharsets.UTF_8);
        System.out.println(&quot;文件读取完成&quot;);
    }
}
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;&lt;strong&gt;FilenameUtils工具类：&lt;/strong&gt;&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;import org.apache.commons.io.FilenameUtils;
import java.io.File;

public class FilenameUtilsExample {
    
    public static void filenameOperations() {
        String path = &quot;/home/user/documents/file.txt&quot;;
        
        // 获取文件名（不含路径）
        String name = FilenameUtils.getName(path);
        System.out.println(&quot;文件名: &quot; + name); // 输出: file.txt
        
        // 获取文件名（不含扩展名）
        String baseName = FilenameUtils.getBaseName(path);
        System.out.println(&quot;文件名（不含扩展名）: &quot; + baseName); // 输出: file
        
        // 获取扩展名
        String extension = FilenameUtils.getExtension(path);
        System.out.println(&quot;扩展名: &quot; + extension); // 输出: txt
        
        // 获取完整路径（不含文件名）
        String fullPath = FilenameUtils.getFullPath(path);
        System.out.println(&quot;完整路径: &quot; + fullPath); // 输出: /home/user/documents/
        
        // 获取路径（不含文件名）
        String pathOnly = FilenameUtils.getPath(path);
        System.out.println(&quot;路径: &quot; + pathOnly); // 输出: home/user/documents/
        
        // 获取前缀
        String prefix = FilenameUtils.getPrefix(path);
        System.out.println(&quot;前缀: &quot; + prefix); // 输出: /
        
        // 规范化路径
        String normalized = FilenameUtils.normalize(path);
        System.out.println(&quot;规范化路径: &quot; + normalized);
        
        // 连接路径
        String joined = FilenameUtils.concat(&quot;/home/user&quot;, &quot;documents/file.txt&quot;);
        System.out.println(&quot;连接路径: &quot; + joined); // 输出: /home/user/documents/file.txt
        
        // 检查扩展名
        boolean isTxt = FilenameUtils.isExtension(path, &quot;txt&quot;);
        System.out.println(&quot;是否为txt文件: &quot; + isTxt); // 输出: true
        
        boolean isImage = FilenameUtils.isExtension(path, &quot;jpg&quot;, &quot;png&quot;, &quot;gif&quot;);
        System.out.println(&quot;是否为图片文件: &quot; + isImage); // 输出: false
        
        // 移除扩展名
        String withoutExt = FilenameUtils.removeExtension(path);
        System.out.println(&quot;移除扩展名: &quot; + withoutExt); // 输出: /home/user/documents/file
        
        // 更改扩展名
        String newExt = FilenameUtils.getBaseName(path) + &quot;.bak&quot;;
        System.out.println(&quot;新文件名: &quot; + newExt); // 输出: file.bak
    }
}
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;&lt;strong&gt;FileSystemUtils工具类：&lt;/strong&gt;&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;import org.apache.commons.io.FileSystemUtils;
import java.io.IOException;

public class FileSystemUtilsExample {
    
    public static void fileSystemOperations() throws IOException {
        // 获取磁盘空间（字节）
        long freeSpace = FileSystemUtils.freeSpaceKb(&quot;/&quot;);
        System.out.println(&quot;可用空间: &quot; + freeSpace + &quot; KB&quot;);
        
        // 获取磁盘空间（MB）
        long freeSpaceMB = FileSystemUtils.freeSpaceKb(&quot;/&quot;) / 1024;
        System.out.println(&quot;可用空间: &quot; + freeSpaceMB + &quot; MB&quot;);
        
        // 获取磁盘空间（GB）
        long freeSpaceGB = FileSystemUtils.freeSpaceKb(&quot;/&quot;) / (1024 * 1024);
        System.out.println(&quot;可用空间: &quot; + freeSpaceGB + &quot; GB&quot;);
        
        // 检查磁盘空间是否足够
        long requiredSpace = 1024 * 1024; // 1GB in KB
        boolean hasEnoughSpace = FileSystemUtils.freeSpaceKb(&quot;/&quot;) &amp;gt; requiredSpace;
        System.out.println(&quot;是否有足够空间: &quot; + hasEnoughSpace);
    }
}
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;&lt;strong&gt;LineIterator工具类：&lt;/strong&gt;&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;import org.apache.commons.io.LineIterator;
import org.apache.commons.io.FileUtils;
import java.io.File;
import java.io.IOException;

public class LineIteratorExample {
    
    public static void lineIteratorOperations() throws IOException {
        File file = new File(&quot;large.txt&quot;);
        
        // 使用LineIterator逐行读取大文件
        try (LineIterator lineIterator = FileUtils.lineIterator(file, &quot;UTF-8&quot;)) {
            while (lineIterator.hasNext()) {
                String line = lineIterator.nextLine();
                // 处理每一行
                System.out.println(&quot;处理行: &quot; + line);
                
                // 可以在这里添加处理逻辑
                if (line.startsWith(&quot;ERROR&quot;)) {
                    System.out.println(&quot;发现错误行: &quot; + line);
                }
            }
        }
        
        // 统计行数
        try (LineIterator lineIterator = FileUtils.lineIterator(file, &quot;UTF-8&quot;)) {
            int lineCount = 0;
            while (lineIterator.hasNext()) {
                lineIterator.nextLine();
                lineCount++;
            }
            System.out.println(&quot;文件总行数: &quot; + lineCount);
        }
        
        // 查找特定行
        try (LineIterator lineIterator = FileUtils.lineIterator(file, &quot;UTF-8&quot;)) {
            int lineNumber = 0;
            while (lineIterator.hasNext()) {
                lineNumber++;
                String line = lineIterator.nextLine();
                if (line.contains(&quot;target&quot;)) {
                    System.out.println(&quot;在第&quot; + lineNumber + &quot;行找到目标: &quot; + line);
                    break;
                }
            }
        }
    }
}
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;&lt;strong&gt;文件监控器：&lt;/strong&gt;&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;import org.apache.commons.io.monitor.FileAlterationListener;
import org.apache.commons.io.monitor.FileAlterationListenerAdaptor;
import org.apache.commons.io.monitor.FileAlterationMonitor;
import org.apache.commons.io.monitor.FileAlterationObserver;
import java.io.File;

public class FileMonitorExample {
    
    public static void fileMonitoring() throws Exception {
        // 创建文件监听器
        FileAlterationListener listener = new FileAlterationListenerAdaptor() {
            @Override
            public void onFileCreate(File file) {
                System.out.println(&quot;文件创建: &quot; + file.getName());
            }
            
            @Override
            public void onFileDelete(File file) {
                System.out.println(&quot;文件删除: &quot; + file.getName());
            }
            
            @Override
            public void onFileChange(File file) {
                System.out.println(&quot;文件修改: &quot; + file.getName());
            }
            
            @Override
            public void onDirectoryCreate(File directory) {
                System.out.println(&quot;目录创建: &quot; + directory.getName());
            }
            
            @Override
            public void onDirectoryDelete(File directory) {
                System.out.println(&quot;目录删除: &quot; + directory.getName());
            }
            
            @Override
            public void onDirectoryChange(File directory) {
                System.out.println(&quot;目录修改: &quot; + directory.getName());
            }
        };
        
        // 创建观察者
        FileAlterationObserver observer = new FileAlterationObserver(
            new File(&quot;monitored_dir&quot;), null, null);
        observer.addListener(listener);
        
        // 创建监控器
        FileAlterationMonitor monitor = new FileAlterationMonitor(1000); // 1秒检查一次
        monitor.addObserver(observer);
        
        // 启动监控
        monitor.start();
        System.out.println(&quot;文件监控已启动，监控目录: monitored_dir&quot;);
        
        // 运行一段时间后停止
        Thread.sleep(30000); // 运行30秒
        monitor.stop();
        System.out.println(&quot;文件监控已停止&quot;);
    }
}
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;&lt;strong&gt;实用工具类组合使用：&lt;/strong&gt;&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;import org.apache.commons.io.*;
import java.io.*;
import java.nio.charset.StandardCharsets;
import java.util.List;

public class CommonsIOUtils {
    
    // 1. 文件备份工具
    public static void backupFile(String sourcePath, String backupPath) throws IOException {
        File source = new File(sourcePath);
        File backup = new File(backupPath);
        
        if (source.exists()) {
            // 创建备份目录
            FileUtils.forceMkdirParent(backup);
            
            // 复制文件
            FileUtils.copyFile(source, backup);
            System.out.println(&quot;文件备份完成: &quot; + backupPath);
        } else {
            System.out.println(&quot;源文件不存在: &quot; + sourcePath);
        }
    }
    
    // 2. 批量文件处理
    public static void batchProcessFiles(String directory, String extension) throws IOException {
        File dir = new File(directory);
        if (!dir.exists() || !dir.isDirectory()) {
            System.out.println(&quot;目录不存在: &quot; + directory);
            return;
        }
        
        // 获取所有指定扩展名的文件
        String[] files = dir.list((dir1, name) -&amp;gt; name.endsWith(extension));
        
        if (files != null) {
            for (String fileName : files) {
                File file = new File(dir, fileName);
                processFile(file);
            }
        }
    }
    
    private static void processFile(File file) throws IOException {
        // 读取文件内容
        String content = FileUtils.readFileToString(file, StandardCharsets.UTF_8);
        
        // 处理内容（示例：转换为大写）
        String processedContent = content.toUpperCase();
        
        // 写入处理后的内容
        FileUtils.writeStringToFile(file, processedContent, StandardCharsets.UTF_8);
        
        System.out.println(&quot;文件处理完成: &quot; + file.getName());
    }
    
    // 3. 日志文件分析
    public static void analyzeLogFile(String logPath) throws IOException {
        File logFile = new File(logPath);
        
        if (!logFile.exists()) {
            System.out.println(&quot;日志文件不存在: &quot; + logPath);
            return;
        }
        
        // 统计行数
        List&amp;lt;String&amp;gt; lines = FileUtils.readLines(logFile, StandardCharsets.UTF_8);
        int totalLines = lines.size();
        
        // 统计错误行数
        long errorLines = lines.stream()
            .filter(line -&amp;gt; line.contains(&quot;ERROR&quot;))
            .count();
        
        // 统计警告行数
        long warningLines = lines.stream()
            .filter(line -&amp;gt; line.contains(&quot;WARN&quot;))
            .count();
        
        // 统计信息行数
        long infoLines = lines.stream()
            .filter(line -&amp;gt; line.contains(&quot;INFO&quot;))
            .count();
        
        System.out.println(&quot;日志文件分析结果:&quot;);
        System.out.println(&quot;  总行数: &quot; + totalLines);
        System.out.println(&quot;  错误行数: &quot; + errorLines);
        System.out.println(&quot;  警告行数: &quot; + warningLines);
        System.out.println(&quot;  信息行数: &quot; + infoLines);
        System.out.println(&quot;  文件大小: &quot; + FileUtils.byteCountToDisplaySize(logFile.length()));
    }
    
    // 4. 文件同步工具
    public static void syncDirectories(String sourceDir, String targetDir) throws IOException {
        File source = new File(sourceDir);
        File target = new File(targetDir);
        
        if (!source.exists() || !source.isDirectory()) {
            System.out.println(&quot;源目录不存在: &quot; + sourceDir);
            return;
        }
        
        // 创建目标目录
        FileUtils.forceMkdir(target);
        
        // 复制目录
        FileUtils.copyDirectory(source, target);
        System.out.println(&quot;目录同步完成: &quot; + targetDir);
    }
    
    // 5. 文件清理工具
    public static void cleanupOldFiles(String directory, long maxAge) {
        File dir = new File(directory);
        if (!dir.exists() || !dir.isDirectory()) {
            System.out.println(&quot;目录不存在: &quot; + directory);
            return;
        }
        
        File[] files = dir.listFiles();
        if (files != null) {
            long currentTime = System.currentTimeMillis();
            int deletedCount = 0;
            
            for (File file : files) {
                if (file.isFile()) {
                    long fileAge = currentTime - file.lastModified();
                    if (fileAge &amp;gt; maxAge) {
                        if (FileUtils.deleteQuietly(file)) {
                            deletedCount++;
                            System.out.println(&quot;删除旧文件: &quot; + file.getName());
                        }
                    }
                }
            }
            
            System.out.println(&quot;清理完成，共删除 &quot; + deletedCount + &quot; 个文件&quot;);
        }
    }
}

// 使用示例
public class CommonsIOExample {
    public static void main(String[] args) {
        try {
            // 文件备份
            CommonsIOUtils.backupFile(&quot;important.txt&quot;, &quot;backup/important.txt.bak&quot;);
            
            // 批量处理
            CommonsIOUtils.batchProcessFiles(&quot;documents&quot;, &quot;.txt&quot;);
            
            // 日志分析
            CommonsIOUtils.analyzeLogFile(&quot;application.log&quot;);
            
            // 目录同步
            CommonsIOUtils.syncDirectories(&quot;source&quot;, &quot;backup&quot;);
            
            // 清理旧文件（7天前）
            CommonsIOUtils.cleanupOldFiles(&quot;temp&quot;, 7 * 24 * 60 * 60 * 1000L);
            
        } catch (IOException e) {
            e.printStackTrace();
        }
    }
}
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;&lt;strong&gt;Commons IO的注意事项：&lt;/strong&gt;&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;
&lt;p&gt;&lt;strong&gt;性能考虑&lt;/strong&gt;：&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;大文件操作时使用流式处理&lt;/li&gt;
&lt;li&gt;避免一次性加载大文件到内存&lt;/li&gt;
&lt;li&gt;使用LineIterator处理大文本文件&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;&lt;strong&gt;异常处理&lt;/strong&gt;：&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;使用closeQuietly方法安全关闭流&lt;/li&gt;
&lt;li&gt;处理IO异常和文件不存在异常&lt;/li&gt;
&lt;li&gt;使用try-with-resources语句&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;&lt;strong&gt;字符编码&lt;/strong&gt;：&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;明确指定字符编码，避免平台差异&lt;/li&gt;
&lt;li&gt;使用StandardCharsets常量&lt;/li&gt;
&lt;li&gt;处理编码异常&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;&lt;strong&gt;文件操作&lt;/strong&gt;：&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;检查文件存在性&lt;/li&gt;
&lt;li&gt;创建必要的目录&lt;/li&gt;
&lt;li&gt;处理文件权限问题&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;&lt;strong&gt;最佳实践&lt;/strong&gt;：&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;使用工具类简化代码&lt;/li&gt;
&lt;li&gt;合理使用文件监控&lt;/li&gt;
&lt;li&gt;注意跨平台兼容性&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;&lt;strong&gt;应用场景&lt;/strong&gt;：&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;文件操作和IO处理&lt;/li&gt;
&lt;li&gt;日志文件分析&lt;/li&gt;
&lt;li&gt;文件备份和同步&lt;/li&gt;
&lt;li&gt;批量文件处理&lt;/li&gt;
&lt;li&gt;文件监控和事件处理&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;/ol&gt;
&lt;h5&gt;7.12 Hutool工具库&lt;/h5&gt;
&lt;p&gt;&lt;strong&gt;Hutool简介：&lt;/strong&gt;&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;// Maven依赖
&amp;lt;dependency&amp;gt;
    &amp;lt;groupId&amp;gt;cn.hutool&amp;lt;/groupId&amp;gt;
    &amp;lt;artifactId&amp;gt;hutool-all&amp;lt;/artifactId&amp;gt;
    &amp;lt;version&amp;gt;5.8.25&amp;lt;/version&amp;gt;
&amp;lt;/dependency&amp;gt;

// Gradle依赖
implementation &apos;cn.hutool:hutool-all:5.8.25&apos;

// 主要模块
import cn.hutool.core.util.*;
import cn.hutool.core.lang.*;
import cn.hutool.core.date.*;
import cn.hutool.core.io.*;
import cn.hutool.core.text.*;
import cn.hutool.core.collection.*;
import cn.hutool.core.map.*;
import cn.hutool.crypto.*;
import cn.hutool.http.*;
import cn.hutool.json.*;
import cn.hutool.db.*;
import cn.hutool.poi.*;
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;&lt;strong&gt;StrUtil字符串工具类：&lt;/strong&gt;&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;import cn.hutool.core.util.StrUtil;
import cn.hutool.core.text.CharSequenceUtil;

public class StrUtilExample {
    
    public static void stringOperations() {
        // 1. 字符串判空
        String str1 = &quot;&quot;;
        String str2 = null;
        String str3 = &quot;  &quot;;
        String str4 = &quot;hello&quot;;
        
        System.out.println(&quot;isEmpty: &quot; + StrUtil.isEmpty(str1));      // true
        System.out.println(&quot;isEmpty: &quot; + StrUtil.isEmpty(str2));      // true
        System.out.println(&quot;isBlank: &quot; + StrUtil.isBlank(str3));      // true
        System.out.println(&quot;isNotBlank: &quot; + StrUtil.isNotBlank(str4)); // true
        
        // 2. 字符串格式化
        String template = &quot;Hello {}, welcome to {}!&quot;;
        String result = StrUtil.format(template, &quot;World&quot;, &quot;Java&quot;);
        System.out.println(&quot;格式化结果: &quot; + result); // Hello World, welcome to Java!
        
        // 3. 字符串截取
        String text = &quot;Hello World Java&quot;;
        String sub1 = StrUtil.sub(text, 0, 5);           // Hello
        String sub2 = StrUtil.subAfter(text, &quot; &quot;, false); // World Java
        String sub3 = StrUtil.subBefore(text, &quot; &quot;, false); // Hello
        
        System.out.println(&quot;截取1: &quot; + sub1);
        System.out.println(&quot;截取2: &quot; + sub2);
        System.out.println(&quot;截取3: &quot; + sub3);
        
        // 4. 字符串替换
        String original = &quot;Hello World World&quot;;
        String replaced = StrUtil.replace(original, &quot;World&quot;, &quot;Java&quot;);
        System.out.println(&quot;替换结果: &quot; + replaced); // Hello Java Java
        
        // 5. 字符串分割
        String csv = &quot;apple,banana,orange,grape&quot;;
        String[] fruits = StrUtil.split(csv, &apos;,&apos;);
        System.out.println(&quot;分割结果: &quot; + Arrays.toString(fruits));
        
        // 6. 字符串连接
        String[] words = {&quot;Hello&quot;, &quot;World&quot;, &quot;Java&quot;};
        String joined = StrUtil.join(&quot;, &quot;, words);
        System.out.println(&quot;连接结果: &quot; + joined); // Hello, World, Java
        
        // 7. 字符串填充
        String padded = StrUtil.padPre(&quot;123&quot;, 5, &apos;0&apos;);
        System.out.println(&quot;左填充: &quot; + padded); // 00123
        
        String padded2 = StrUtil.padAfter(&quot;123&quot;, 5, &apos;0&apos;);
        System.out.println(&quot;右填充: &quot; + padded2); // 12300
        
        // 8. 字符串反转
        String reversed = StrUtil.reverse(&quot;Hello&quot;);
        System.out.println(&quot;反转结果: &quot; + reversed); // olleH
        
        // 9. 驼峰转换
        String camelCase = StrUtil.toCamelCase(&quot;hello_world&quot;);
        System.out.println(&quot;驼峰转换: &quot; + camelCase); // helloWorld
        
        String underLine = StrUtil.toUnderlineCase(&quot;helloWorld&quot;);
        System.out.println(&quot;下划线转换: &quot; + underLine); // hello_world
        
        // 10. 字符串模板
        String template2 = &quot;姓名：{name}，年龄：{age}，城市：{city}&quot;;
        Map&amp;lt;String, Object&amp;gt; params = new HashMap&amp;lt;&amp;gt;();
        params.put(&quot;name&quot;, &quot;张三&quot;);
        params.put(&quot;age&quot;, 25);
        params.put(&quot;city&quot;, &quot;北京&quot;);
        
        String result2 = StrUtil.format(template2, params);
        System.out.println(&quot;模板结果: &quot; + result2); // 姓名：张三，年龄：25，城市：北京
    }
}
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;&lt;strong&gt;DateUtil日期工具类：&lt;/strong&gt;&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;import cn.hutool.core.date.DateUtil;
import cn.hutool.core.date.DateTime;
import cn.hutool.core.date.DatePattern;
import java.util.Date;

public class DateUtilExample {
    
    public static void dateOperations() {
        // 1. 日期解析
        String dateStr = &quot;2025-05-15 14:30:45&quot;;
        DateTime date = DateUtil.parse(dateStr);
        System.out.println(&quot;解析日期: &quot; + date);
        
        // 2. 日期格式化
        String formatted = DateUtil.format(date, DatePattern.NORM_DATETIME_PATTERN);
        System.out.println(&quot;格式化日期: &quot; + formatted);
        
        // 3. 获取当前时间
        DateTime now = DateUtil.date();
        System.out.println(&quot;当前时间: &quot; + now);
        
        // 4. 日期计算
        DateTime tomorrow = DateUtil.tomorrow();
        DateTime yesterday = DateUtil.yesterday();
        DateTime nextWeek = DateUtil.nextWeek();
        DateTime nextMonth = DateUtil.nextMonth();
        
        System.out.println(&quot;明天: &quot; + tomorrow);
        System.out.println(&quot;昨天: &quot; + yesterday);
        System.out.println(&quot;下周: &quot; + nextWeek);
        System.out.println(&quot;下月: &quot; + nextMonth);
        
        // 5. 日期偏移
        DateTime offset1 = DateUtil.offsetDay(now, 7);    // 7天后
        DateTime offset2 = DateUtil.offsetMonth(now, 1);  // 1个月后
        DateTime offset3 = DateUtil.offsetYear(now, 1);   // 1年后
        
        System.out.println(&quot;7天后: &quot; + offset1);
        System.out.println(&quot;1个月后: &quot; + offset2);
        System.out.println(&quot;1年后: &quot; + offset3);
        
        // 6. 日期比较
        DateTime date1 = DateUtil.parse(&quot;2025-05-15&quot;);
        DateTime date2 = DateUtil.parse(&quot;2025-05-20&quot;);
        
        boolean isAfter = DateUtil.isAfter(date2, date1);
        boolean isBefore = DateUtil.isBefore(date1, date2);
        boolean isSame = DateUtil.isSameDay(date1, date2);
        
        System.out.println(&quot;date2在date1之后: &quot; + isAfter);
        System.out.println(&quot;date1在date2之前: &quot; + isBefore);
        System.out.println(&quot;同一天: &quot; + isSame);
        
        // 7. 获取时间戳
        long timestamp = DateUtil.current();
        System.out.println(&quot;当前时间戳: &quot; + timestamp);
        
        // 8. 时间差计算
        long betweenDays = DateUtil.betweenDay(date1, date2, false);
        long betweenHours = DateUtil.betweenHour(date1, date2, false);
        long betweenMinutes = DateUtil.betweenMinute(date1, date2, false);
        
        System.out.println(&quot;相差天数: &quot; + betweenDays);
        System.out.println(&quot;相差小时: &quot; + betweenHours);
        System.out.println(&quot;相差分钟: &quot; + betweenMinutes);
        
        // 9. 获取日期部分
        int year = DateUtil.year(date);
        int month = DateUtil.month(date) + 1; // 月份从0开始
        int day = DateUtil.dayOfMonth(date);
        int hour = DateUtil.hour(date, true);
        int minute = DateUtil.minute(date);
        int second = DateUtil.second(date);
        
        System.out.println(&quot;年: &quot; + year + &quot;, 月: &quot; + month + &quot;, 日: &quot; + day);
        System.out.println(&quot;时: &quot; + hour + &quot;, 分: &quot; + minute + &quot;, 秒: &quot; + second);
        
        // 10. 日期范围
        DateTime start = DateUtil.beginOfDay(date);
        DateTime end = DateUtil.endOfDay(date);
        
        System.out.println(&quot;当天开始: &quot; + start);
        System.out.println(&quot;当天结束: &quot; + end);
        
        // 11. 星期相关
        int week = DateUtil.dayOfWeek(date);
        String weekName = DateUtil.dayOfWeekEnum(date).toChinese();
        
        System.out.println(&quot;星期几(数字): &quot; + week);
        System.out.println(&quot;星期几(中文): &quot; + weekName);
    }
}
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;&lt;strong&gt;CollUtil集合工具类：&lt;/strong&gt;&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;import cn.hutool.core.collection.CollUtil;
import cn.hutool.core.collection.ListUtil;
import java.util.*;

public class CollUtilExample {
    
    public static void collectionOperations() {
        // 1. 创建集合
        List&amp;lt;String&amp;gt; list1 = CollUtil.newArrayList(&quot;a&quot;, &quot;b&quot;, &quot;c&quot;);
        Set&amp;lt;String&amp;gt; set1 = CollUtil.newHashSet(&quot;x&quot;, &quot;y&quot;, &quot;z&quot;);
        Map&amp;lt;String, String&amp;gt; map1 = CollUtil.newHashMap(&quot;key1&quot;, &quot;value1&quot;, &quot;key2&quot;, &quot;value2&quot;);
        
        System.out.println(&quot;列表: &quot; + list1);
        System.out.println(&quot;集合: &quot; + set1);
        System.out.println(&quot;映射: &quot; + map1);
        
        // 2. 集合判空
        List&amp;lt;String&amp;gt; emptyList = new ArrayList&amp;lt;&amp;gt;();
        List&amp;lt;String&amp;gt; nullList = null;
        
        System.out.println(&quot;isEmpty: &quot; + CollUtil.isEmpty(emptyList));    // true
        System.out.println(&quot;isEmpty: &quot; + CollUtil.isEmpty(nullList));     // true
        System.out.println(&quot;isNotEmpty: &quot; + CollUtil.isNotEmpty(list1)); // true
        
        // 3. 集合操作
        List&amp;lt;String&amp;gt; list2 = Arrays.asList(&quot;d&quot;, &quot;e&quot;, &quot;f&quot;);
        
        // 并集
        List&amp;lt;String&amp;gt; union = CollUtil.union(list1, list2);
        System.out.println(&quot;并集: &quot; + union);
        
        // 交集
        List&amp;lt;String&amp;gt; intersection = CollUtil.intersection(list1, list2);
        System.out.println(&quot;交集: &quot; + intersection);
        
        // 差集
        List&amp;lt;String&amp;gt; difference = CollUtil.disjunction(list1, list2);
        System.out.println(&quot;差集: &quot; + difference);
        
        // 4. 集合转换
        // 列表转数组
        String[] array = CollUtil.toArray(list1, String.class);
        System.out.println(&quot;转数组: &quot; + Arrays.toString(array));
        
        // 数组转列表
        List&amp;lt;String&amp;gt; listFromArray = CollUtil.toList(array);
        System.out.println(&quot;转列表: &quot; + listFromArray);
        
        // 5. 集合分组
        List&amp;lt;Person&amp;gt; persons = Arrays.asList(
            new Person(&quot;张三&quot;, 25, &quot;北京&quot;),
            new Person(&quot;李四&quot;, 30, &quot;上海&quot;),
            new Person(&quot;王五&quot;, 25, &quot;北京&quot;),
            new Person(&quot;赵六&quot;, 35, &quot;广州&quot;)
        );
        
        Map&amp;lt;Integer, List&amp;lt;Person&amp;gt;&amp;gt; ageGroup = CollUtil.groupByField(persons, &quot;age&quot;);
        System.out.println(&quot;按年龄分组: &quot; + ageGroup);
        
        Map&amp;lt;String, List&amp;lt;Person&amp;gt;&amp;gt; cityGroup = CollUtil.groupByField(persons, &quot;city&quot;);
        System.out.println(&quot;按城市分组: &quot; + cityGroup);
        
        // 6. 集合排序
        List&amp;lt;Integer&amp;gt; numbers = Arrays.asList(3, 1, 4, 1, 5, 9, 2, 6);
        CollUtil.sort(numbers);
        System.out.println(&quot;排序后: &quot; + numbers);
        
        // 自定义排序
        CollUtil.sort(persons, (p1, p2) -&amp;gt; Integer.compare(p1.getAge(), p2.getAge()));
        System.out.println(&quot;按年龄排序: &quot; + persons);
        
        // 7. 集合分页
        List&amp;lt;String&amp;gt; longList = Arrays.asList(&quot;a&quot;, &quot;b&quot;, &quot;c&quot;, &quot;d&quot;, &quot;e&quot;, &quot;f&quot;, &quot;g&quot;, &quot;h&quot;, &quot;i&quot;, &quot;j&quot;);
        List&amp;lt;String&amp;gt; page1 = CollUtil.page(0, 3, longList);
        List&amp;lt;String&amp;gt; page2 = CollUtil.page(1, 3, longList);
        
        System.out.println(&quot;第1页: &quot; + page1); // [a, b, c]
        System.out.println(&quot;第2页: &quot; + page2); // [d, e, f]
        
        // 8. 集合去重
        List&amp;lt;String&amp;gt; duplicateList = Arrays.asList(&quot;a&quot;, &quot;b&quot;, &quot;a&quot;, &quot;c&quot;, &quot;b&quot;, &quot;d&quot;);
        List&amp;lt;String&amp;gt; uniqueList = CollUtil.distinct(duplicateList);
        System.out.println(&quot;去重后: &quot; + uniqueList);
        
        // 9. 集合过滤
        List&amp;lt;String&amp;gt; filtered = CollUtil.filter(list1, str -&amp;gt; str.startsWith(&quot;a&quot;));
        System.out.println(&quot;过滤后: &quot; + filtered);
        
        // 10. 集合映射
        List&amp;lt;String&amp;gt; mapped = CollUtil.map(list1, String::toUpperCase);
        System.out.println(&quot;映射后: &quot; + mapped);
    }
    
    static class Person {
        private String name;
        private int age;
        private String city;
        
        public Person(String name, int age, String city) {
            this.name = name;
            this.age = age;
            this.city = city;
        }
        
        public String getName() { return name; }
        public int getAge() { return age; }
        public String getCity() { return city; }
        
        @Override
        public String toString() {
            return name + &quot;(&quot; + age + &quot;,&quot; + city + &quot;)&quot;;
        }
    }
}
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;&lt;strong&gt;HttpUtil网络工具类：&lt;/strong&gt;&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;import cn.hutool.http.HttpUtil;
import cn.hutool.http.HttpRequest;
import cn.hutool.http.HttpResponse;
import java.util.HashMap;
import java.util.Map;

public class HttpUtilExample {
    
    public static void httpOperations() {
        // 1. GET请求
        String getResult = HttpUtil.get(&quot;https://httpbin.org/get&quot;);
        System.out.println(&quot;GET响应: &quot; + getResult);
        
        // 带参数的GET请求
        Map&amp;lt;String, Object&amp;gt; params = new HashMap&amp;lt;&amp;gt;();
        params.put(&quot;name&quot;, &quot;张三&quot;);
        params.put(&quot;age&quot;, 25);
        
        String getWithParams = HttpUtil.get(&quot;https://httpbin.org/get&quot;, params);
        System.out.println(&quot;带参数GET响应: &quot; + getWithParams);
        
        // 2. POST请求
        String postResult = HttpUtil.post(&quot;https://httpbin.org/post&quot;, &quot;Hello World&quot;);
        System.out.println(&quot;POST响应: &quot; + postResult);
        
        // POST表单数据
        Map&amp;lt;String, Object&amp;gt; formData = new HashMap&amp;lt;&amp;gt;();
        formData.put(&quot;username&quot;, &quot;admin&quot;);
        formData.put(&quot;password&quot;, &quot;123456&quot;);
        
        String postForm = HttpUtil.post(&quot;https://httpbin.org/post&quot;, formData);
        System.out.println(&quot;POST表单响应: &quot; + postForm);
        
        // 3. 设置请求头
        HttpResponse response = HttpRequest.get(&quot;https://httpbin.org/headers&quot;)
            .header(&quot;User-Agent&quot;, &quot;Hutool/1.0&quot;)
            .header(&quot;Authorization&quot;, &quot;Bearer token123&quot;)
            .execute();
        
        System.out.println(&quot;带请求头响应: &quot; + response.body());
        
        // 4. 设置超时
        HttpResponse timeoutResponse = HttpRequest.get(&quot;https://httpbin.org/delay/5&quot;)
            .timeout(3000) // 3秒超时
            .execute();
        
        System.out.println(&quot;超时响应状态: &quot; + timeoutResponse.getStatus());
        
        // 5. 异步请求
        HttpRequest.get(&quot;https://httpbin.org/get&quot;)
            .async()
            .thenAccept(resp -&amp;gt; {
                System.out.println(&quot;异步响应: &quot; + resp.body());
            });
    }
}
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;&lt;strong&gt;JSONUtil JSON工具类：&lt;/strong&gt;&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;import cn.hutool.json.JSONUtil;
import cn.hutool.json.JSONObject;
import cn.hutool.json.JSONArray;
import java.util.*;

public class JSONUtilExample {
    
    public static void jsonOperations() {
        // 1. 对象转JSON
        Person person = new Person(&quot;张三&quot;, 25, &quot;北京&quot;);
        String jsonStr = JSONUtil.toJsonStr(person);
        System.out.println(&quot;对象转JSON: &quot; + jsonStr);
        
        // 2. JSON转对象
        Person parsedPerson = JSONUtil.toBean(jsonStr, Person.class);
        System.out.println(&quot;JSON转对象: &quot; + parsedPerson);
        
        // 3. 创建JSONObject
        JSONObject jsonObject = JSONUtil.createObj()
            .put(&quot;name&quot;, &quot;李四&quot;)
            .put(&quot;age&quot;, 30)
            .put(&quot;city&quot;, &quot;上海&quot;)
            .put(&quot;hobbies&quot;, Arrays.asList(&quot;读书&quot;, &quot;游泳&quot;, &quot;编程&quot;));
        
        System.out.println(&quot;JSONObject: &quot; + jsonObject);
        
        // 4. 获取JSON值
        String name = jsonObject.getStr(&quot;name&quot;);
        int age = jsonObject.getInt(&quot;age&quot;);
        List&amp;lt;String&amp;gt; hobbies = jsonObject.getBeanList(&quot;hobbies&quot;, String.class);
        
        System.out.println(&quot;姓名: &quot; + name);
        System.out.println(&quot;年龄: &quot; + age);
        System.out.println(&quot;爱好: &quot; + hobbies);
        
        // 5. 创建JSONArray
        JSONArray jsonArray = JSONUtil.createArray()
            .add(person)
            .add(new Person(&quot;王五&quot;, 35, &quot;广州&quot;))
            .add(new Person(&quot;赵六&quot;, 28, &quot;深圳&quot;));
        
        System.out.println(&quot;JSONArray: &quot; + jsonArray);
        
        // 6. 遍历JSONArray
        for (int i = 0; i &amp;lt; jsonArray.size(); i++) {
            JSONObject obj = jsonArray.getJSONObject(i);
            System.out.println(&quot;第&quot; + (i+1) + &quot;个人: &quot; + obj.getStr(&quot;name&quot;));
        }
        
        // 7. JSONArray转List
        List&amp;lt;Person&amp;gt; personList = jsonArray.toList(Person.class);
        System.out.println(&quot;转List: &quot; + personList);
        
        // 8. 复杂JSON操作
        String complexJson = &quot;&quot;&quot;
            {
                &quot;company&quot;: {
                    &quot;name&quot;: &quot;科技有限公司&quot;,
                    &quot;employees&quot;: [
                        {&quot;name&quot;: &quot;张三&quot;, &quot;age&quot;: 25, &quot;department&quot;: &quot;技术部&quot;},
                        {&quot;name&quot;: &quot;李四&quot;, &quot;age&quot;: 30, &quot;department&quot;: &quot;产品部&quot;},
                        {&quot;name&quot;: &quot;王五&quot;, &quot;age&quot;: 35, &quot;department&quot;: &quot;技术部&quot;}
                    ]
                }
            }
            &quot;&quot;&quot;;
        
        JSONObject companyJson = JSONUtil.parseObj(complexJson);
        String companyName = companyJson.getByPath(&quot;company.name&quot;, String.class);
        JSONArray employees = companyJson.getByPath(&quot;company.employees&quot;, JSONArray.class);
        
        System.out.println(&quot;公司名称: &quot; + companyName);
        System.out.println(&quot;员工数量: &quot; + employees.size());
        
        // 9. JSON格式化
        String formattedJson = JSONUtil.formatJsonStr(complexJson);
        System.out.println(&quot;格式化JSON:\n&quot; + formattedJson);
        
        // 10. JSON验证
        boolean isValid = JSONUtil.isTypeJSON(complexJson);
        System.out.println(&quot;是否为有效JSON: &quot; + isValid);
        
        // 11. JSON合并
        JSONObject obj1 = JSONUtil.createObj().put(&quot;a&quot;, 1).put(&quot;b&quot;, 2);
        JSONObject obj2 = JSONUtil.createObj().put(&quot;c&quot;, 3).put(&quot;d&quot;, 4);
        JSONObject merged = JSONUtil.merge(obj1, obj2);
        System.out.println(&quot;合并结果: &quot; + merged);
    }
    
    static class Person {
        private String name;
        private int age;
        private String city;
        
        public Person(String name, int age, String city) {
            this.name = name;
            this.age = age;
            this.city = city;
        }
        
        public String getName() { return name; }
        public int getAge() { return age; }
        public String getCity() { return city; }
        
        @Override
        public String toString() {
            return name + &quot;(&quot; + age + &quot;,&quot; + city + &quot;)&quot;;
        }
    }
}
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;&lt;strong&gt;Hutool实用工具类组合使用：&lt;/strong&gt;&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;import cn.hutool.core.util.*;
import cn.hutool.core.date.*;
import cn.hutool.core.collection.*;
import cn.hutool.http.*;
import cn.hutool.json.*;
import java.util.*;

public class HutoolUtils {
    
    // 1. 配置文件读取工具
    public static void readConfig() {
        // 假设有一个配置文件 config.json
        String configJson = &quot;&quot;&quot;
            {
                &quot;database&quot;: {
                    &quot;url&quot;: &quot;jdbc:mysql://localhost:3306/test&quot;,
                    &quot;username&quot;: &quot;root&quot;,
                    &quot;password&quot;: &quot;123456&quot;
                },
                &quot;redis&quot;: {
                    &quot;host&quot;: &quot;localhost&quot;,
                    &quot;port&quot;: 6379
                }
            }
            &quot;&quot;&quot;;
        
        JSONObject config = JSONUtil.parseObj(configJson);
        String dbUrl = config.getByPath(&quot;database.url&quot;, String.class);
        String redisHost = config.getByPath(&quot;redis.host&quot;, String.class);
        
        System.out.println(&quot;数据库URL: &quot; + dbUrl);
        System.out.println(&quot;Redis主机: &quot; + redisHost);
    }
    
    // 2. 日志分析工具
    public static void analyzeLog(String logContent) {
        List&amp;lt;String&amp;gt; lines = StrUtil.split(logContent, &apos;\n&apos;);
        
        // 统计不同级别的日志
        Map&amp;lt;String, Integer&amp;gt; levelCount = new HashMap&amp;lt;&amp;gt;();
        List&amp;lt;String&amp;gt; errorLines = new ArrayList&amp;lt;&amp;gt;();
        
        for (String line : lines) {
            if (StrUtil.contains(line, &quot;ERROR&quot;)) {
                levelCount.merge(&quot;ERROR&quot;, 1, Integer::sum);
                errorLines.add(line);
            } else if (StrUtil.contains(line, &quot;WARN&quot;)) {
                levelCount.merge(&quot;WARN&quot;, 1, Integer::sum);
            } else if (StrUtil.contains(line, &quot;INFO&quot;)) {
                levelCount.merge(&quot;INFO&quot;, 1, Integer::sum);
            }
        }
        
        System.out.println(&quot;日志级别统计: &quot; + levelCount);
        System.out.println(&quot;错误日志数量: &quot; + errorLines.size());
    }
    
    // 3. 数据验证工具
    public static boolean validateData(Map&amp;lt;String, Object&amp;gt; data) {
        // 验证必填字段
        String[] requiredFields = {&quot;name&quot;, &quot;age&quot;, &quot;email&quot;};
        
        for (String field : requiredFields) {
            if (StrUtil.isBlank(StrUtil.toString(data.get(field)))) {
                System.out.println(&quot;缺少必填字段: &quot; + field);
                return false;
            }
        }
        
        // 验证年龄
        int age = Convert.toInt(data.get(&quot;age&quot;));
        if (age &amp;lt; 0 || age &amp;gt; 150) {
            System.out.println(&quot;年龄无效: &quot; + age);
            return false;
        }
        
        // 验证邮箱格式
        String email = StrUtil.toString(data.get(&quot;email&quot;));
        if (!ReUtil.isMatch(&quot;^[\\w-\\.]+@([\\w-]+\\.)+[\\w-]{2,4}$&quot;, email)) {
            System.out.println(&quot;邮箱格式无效: &quot; + email);
            return false;
        }
        
        return true;
    }
    
    // 4. 缓存工具
    public static class SimpleCache&amp;lt;K, V&amp;gt; {
        private final Map&amp;lt;K, CacheEntry&amp;lt;V&amp;gt;&amp;gt; cache = new HashMap&amp;lt;&amp;gt;();
        private final long expireTime;
        
        public SimpleCache(long expireTime) {
            this.expireTime = expireTime;
        }
        
        public void put(K key, V value) {
            cache.put(key, new CacheEntry&amp;lt;&amp;gt;(value, System.currentTimeMillis()));
        }
        
        public V get(K key) {
            CacheEntry&amp;lt;V&amp;gt; entry = cache.get(key);
            if (entry != null &amp;amp;&amp;amp; !entry.isExpired(expireTime)) {
                return entry.getValue();
            }
            cache.remove(key);
            return null;
        }
        
        public void clear() {
            cache.clear();
        }
        
        private static class CacheEntry&amp;lt;V&amp;gt; {
            private final V value;
            private final long timestamp;
            
            public CacheEntry(V value, long timestamp) {
                this.value = value;
                this.timestamp = timestamp;
            }
            
            public V getValue() { return value; }
            
            public boolean isExpired(long expireTime) {
                return System.currentTimeMillis() - timestamp &amp;gt; expireTime;
            }
        }
    }
    
    // 5. 数据转换工具
    public static &amp;lt;T&amp;gt; T convertValue(Object value, Class&amp;lt;T&amp;gt; targetType) {
        if (value == null) {
            return null;
        }
        
        if (targetType.isAssignableFrom(value.getClass())) {
            return targetType.cast(value);
        }
        
        if (targetType == String.class) {
            return targetType.cast(StrUtil.toString(value));
        }
        
        if (targetType == Integer.class || targetType == int.class) {
            return targetType.cast(Convert.toInt(value));
        }
        
        if (targetType == Long.class || targetType == long.class) {
            return targetType.cast(Convert.toLong(value));
        }
        
        if (targetType == Double.class || targetType == double.class) {
            return targetType.cast(Convert.toDouble(value));
        }
        
        if (targetType == Boolean.class || targetType == boolean.class) {
            return targetType.cast(Convert.toBool(value));
        }
        
        throw new IllegalArgumentException(&quot;不支持的类型转换: &quot; + value.getClass() + &quot; -&amp;gt; &quot; + targetType);
    }
}

// 使用示例
public class HutoolExample {
    public static void main(String[] args) {
        // 配置读取
        HutoolUtils.readConfig();
        
        // 日志分析
        String logContent = &quot;&quot;&quot;
            [INFO] 应用启动成功
            [ERROR] 数据库连接失败
            [WARN] 内存使用率过高
            [ERROR] 文件读取失败
            [INFO] 用户登录成功
            &quot;&quot;&quot;;
        HutoolUtils.analyzeLog(logContent);
        
        // 数据验证
        Map&amp;lt;String, Object&amp;gt; userData = new HashMap&amp;lt;&amp;gt;();
        userData.put(&quot;name&quot;, &quot;张三&quot;);
        userData.put(&quot;age&quot;, 25);
        userData.put(&quot;email&quot;, &quot;zhangsan@example.com&quot;);
        
        boolean isValid = HutoolUtils.validateData(userData);
        System.out.println(&quot;数据验证结果: &quot; + isValid);
        
        // 缓存使用
        HutoolUtils.SimpleCache&amp;lt;String, String&amp;gt; cache = new HutoolUtils.SimpleCache&amp;lt;&amp;gt;(5000); // 5秒过期
        cache.put(&quot;key1&quot;, &quot;value1&quot;);
        System.out.println(&quot;缓存值: &quot; + cache.get(&quot;key1&quot;));
        
        // 数据转换
        String numberStr = &quot;123&quot;;
        Integer number = HutoolUtils.convertValue(numberStr, Integer.class);
        System.out.println(&quot;转换结果: &quot; + number);
    }
}
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;&lt;strong&gt;Hutool的注意事项：&lt;/strong&gt;&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;
&lt;p&gt;&lt;strong&gt;性能考虑&lt;/strong&gt;：&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;合理使用缓存机制&lt;/li&gt;
&lt;li&gt;避免频繁创建工具类实例&lt;/li&gt;
&lt;li&gt;使用连接池管理HTTP连接&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;&lt;strong&gt;异常处理&lt;/strong&gt;：&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;处理网络请求异常&lt;/li&gt;
&lt;li&gt;处理JSON解析异常&lt;/li&gt;
&lt;li&gt;处理加密解密异常&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;&lt;strong&gt;安全性&lt;/strong&gt;：&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;使用安全的加密算法&lt;/li&gt;
&lt;li&gt;保护敏感信息&lt;/li&gt;
&lt;li&gt;验证输入数据&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;&lt;strong&gt;最佳实践&lt;/strong&gt;：&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;合理使用工具类&lt;/li&gt;
&lt;li&gt;注意版本兼容性&lt;/li&gt;
&lt;li&gt;遵循编码规范&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;&lt;strong&gt;应用场景&lt;/strong&gt;：&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;快速开发原型&lt;/li&gt;
&lt;li&gt;数据处理和转换&lt;/li&gt;
&lt;li&gt;网络请求和API调用&lt;/li&gt;
&lt;li&gt;文件操作和IO处理&lt;/li&gt;
&lt;li&gt;加密和安全处理&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;/ol&gt;
&lt;h5&gt;7.13 对象克隆&lt;/h5&gt;
&lt;p&gt;&lt;strong&gt;浅克隆（Shallow Clone）：&lt;/strong&gt;&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;// 实现Cloneable接口
public class Student implements Cloneable {
    private String name;
    private int age;
    private Address address;  // 引用类型
    
    // 构造方法
    public Student(String name, int age, Address address) {
        this.name = name;
        this.age = age;
        this.address = address;
    }
    
    // 浅克隆方法
    @Override
    public Object clone() throws CloneNotSupportedException {
        return super.clone();  // 调用Object的clone方法
    }
    
    // getter和setter方法
    public String getName() { return name; }
    public void setName(String name) { this.name = name; }
    public int getAge() { return age; }
    public void setAge(int age) { this.age = age; }
    public Address getAddress() { return address; }
    public void setAddress(Address address) { this.address = address; }
}

// 地址类
public class Address {
    private String city;
    private String street;
    
    public Address(String city, String street) {
        this.city = city;
        this.street = street;
    }
    
    public String getCity() { return city; }
    public void setCity(String city) { this.city = city; }
    public String getStreet() { return street; }
    public void setStreet(String street) { this.street = street; }
}

// 浅克隆测试
public class CloneTest {
    public static void main(String[] args) throws CloneNotSupportedException {
        Address address = new Address(&quot;北京&quot;, &quot;长安街&quot;);
        Student original = new Student(&quot;张三&quot;, 20, address);
        
        // 浅克隆
        Student cloned = (Student) original.clone();
        
        System.out.println(&quot;原始对象: &quot; + original.getName() + &quot;, &quot; + original.getAddress().getCity());
        // 输出: 原始对象: 张三, 北京
        System.out.println(&quot;克隆对象: &quot; + cloned.getName() + &quot;, &quot; + cloned.getAddress().getCity());
        // 输出: 克隆对象: 张三, 北京
        
        // 修改原始对象的引用类型属性
        original.getAddress().setCity(&quot;上海&quot;);
        
        System.out.println(&quot;修改后原始对象: &quot; + original.getName() + &quot;, &quot; + original.getAddress().getCity());
        // 输出: 修改后原始对象: 张三, 上海
        System.out.println(&quot;修改后克隆对象: &quot; + cloned.getName() + &quot;, &quot; + cloned.getAddress().getCity());
        // 输出: 修改后克隆对象: 张三, 上海
        // 克隆对象的地址也被修改了！这就是浅克隆的问题
    }
}
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;&lt;strong&gt;深克隆（Deep Clone）：&lt;/strong&gt;&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;方法一：重写clone方法&lt;/strong&gt;&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;public class Student implements Cloneable {
    private String name;
    private int age;
    private Address address;
    private List&amp;lt;String&amp;gt; hobbies;  // 集合类型
    
    public Student(String name, int age, Address address, List&amp;lt;String&amp;gt; hobbies) {
        this.name = name;
        this.age = age;
        this.address = address;
        this.hobbies = hobbies;
    }
    
    // 深克隆方法
    @Override
    public Object clone() throws CloneNotSupportedException {
        Student cloned = (Student) super.clone();  // 先进行浅克隆
        
        // 对引用类型进行深克隆
        if (this.address != null) {
            cloned.address = (Address) this.address.clone();
        }
        
        // 对集合类型进行深克隆
        if (this.hobbies != null) {
            cloned.hobbies = new ArrayList&amp;lt;&amp;gt;(this.hobbies);
        }
        
        return cloned;
    }
}

// Address类也需要实现Cloneable
public class Address implements Cloneable {
    private String city;
    private String street;
    
    public Address(String city, String street) {
        this.city = city;
        this.street = street;
    }
    
    @Override
    public Object clone() throws CloneNotSupportedException {
        return super.clone();
    }
    
    // getter和setter方法
    public String getCity() { return city; }
    public void setCity(String city) { this.city = city; }
    public String getStreet() { return street; }
    public void setStreet(String street) { this.street = street; }
}
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;&lt;strong&gt;方法二：使用序列化实现深克隆&lt;/strong&gt;&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;import java.io.*;

public class DeepCloneUtil {
    
    // 通过序列化实现深克隆
    public static &amp;lt;T&amp;gt; T deepClone(T obj) throws Exception {
        if (obj == null) return null;
        
        // 检查对象是否实现了Serializable接口
        if (!(obj instanceof Serializable)) {
            throw new IllegalArgumentException(&quot;对象必须实现Serializable接口&quot;);
        }
        
        ByteArrayOutputStream bos = new ByteArrayOutputStream();
        ObjectOutputStream oos = new ObjectOutputStream(bos);
        oos.writeObject(obj);
        oos.close();
        
        ByteArrayInputStream bis = new ByteArrayInputStream(bos.toByteArray());
        ObjectInputStream ois = new ObjectInputStream(bis);
        T cloned = (T) ois.readObject();
        ois.close();
        
        return cloned;
    }
}

// 需要实现Serializable接口
public class Student implements Serializable {
    private static final long serialVersionUID = 1L;
    private String name;
    private int age;
    private Address address;
    private List&amp;lt;String&amp;gt; hobbies;
    
    // 构造方法和getter/setter方法...
    
    // 使用序列化进行深克隆
    public Student deepClone() throws Exception {
        return DeepCloneUtil.deepClone(this);
    }
}

public class Address implements Serializable {
    private static final long serialVersionUID = 1L;
    private String city;
    private String street;
    
    // 构造方法和getter/setter方法...
}
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;&lt;strong&gt;方法三：使用JSON实现深克隆&lt;/strong&gt;&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;import com.fasterxml.jackson.databind.ObjectMapper;

public class JsonCloneUtil {
    private static final ObjectMapper mapper = new ObjectMapper();
    
    // 通过JSON实现深克隆
    public static &amp;lt;T&amp;gt; T deepClone(T obj, Class&amp;lt;T&amp;gt; clazz) throws Exception {
        if (obj == null) return null;
        
        String json = mapper.writeValueAsString(obj);
        return mapper.readValue(json, clazz);
    }
}

// 使用示例
public class CloneExample {
    public static void main(String[] args) throws Exception {
        Address address = new Address(&quot;北京&quot;, &quot;长安街&quot;);
        List&amp;lt;String&amp;gt; hobbies = Arrays.asList(&quot;读书&quot;, &quot;游泳&quot;);
        Student original = new Student(&quot;张三&quot;, 20, address, hobbies);
        
        // 使用JSON进行深克隆
        Student cloned = JsonCloneUtil.deepClone(original, Student.class);
        
        // 修改原始对象
        original.getAddress().setCity(&quot;上海&quot;);
        original.getHobbies().add(&quot;编程&quot;);
        
        // 克隆对象不受影响
        System.out.println(&quot;原始对象地址: &quot; + original.getAddress().getCity());
        // 输出: 原始对象地址: 上海
        System.out.println(&quot;克隆对象地址: &quot; + cloned.getAddress().getCity());
        // 输出: 克隆对象地址: 北京
        System.out.println(&quot;原始对象爱好数量: &quot; + original.getHobbies().size());
        // 输出: 原始对象爱好数量: 3
        System.out.println(&quot;克隆对象爱好数量: &quot; + cloned.getHobbies().size());
        // 输出: 克隆对象爱好数量: 2
    }
}
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;&lt;strong&gt;克隆工具类：&lt;/strong&gt;&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;public class CloneUtils {
    
    // 通用深克隆方法
    public static &amp;lt;T&amp;gt; T deepClone(T obj) {
        if (obj == null) return null;
        
        try {
            // 优先使用序列化方法
            if (obj instanceof Serializable) {
                return deepCloneBySerialization(obj);
            } else {
                // 使用反射方法
                return deepCloneByReflection(obj);
            }
        } catch (Exception e) {
            throw new RuntimeException(&quot;深克隆失败&quot;, e);
        }
    }
    
    // 序列化深克隆
    private static &amp;lt;T&amp;gt; T deepCloneBySerialization(T obj) throws Exception {
        ByteArrayOutputStream bos = new ByteArrayOutputStream();
        ObjectOutputStream oos = new ObjectOutputStream(bos);
        oos.writeObject(obj);
        oos.close();
        
        ByteArrayInputStream bis = new ByteArrayInputStream(bos.toByteArray());
        ObjectInputStream ois = new ObjectInputStream(bis);
        T cloned = (T) ois.readObject();
        ois.close();
        
        return cloned;
    }
    
    // 反射深克隆（简化版）
    private static &amp;lt;T&amp;gt; T deepCloneByReflection(T obj) throws Exception {
        Class&amp;lt;?&amp;gt; clazz = obj.getClass();
        T cloned = (T) clazz.newInstance();
        
        // 这里需要根据具体类的结构来实现
        // 实际应用中可能需要更复杂的反射逻辑
        
        return cloned;
    }
}
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;&lt;strong&gt;克隆的注意事项：&lt;/strong&gt;&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;strong&gt;浅克隆&lt;/strong&gt;：只复制基本数据类型和引用地址，引用类型对象共享&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;深克隆&lt;/strong&gt;：复制所有数据，包括引用类型对象的完整副本&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Cloneable接口&lt;/strong&gt;：标记接口，不实现会抛出CloneNotSupportedException&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;序列化方法&lt;/strong&gt;：要求所有相关类都实现Serializable接口&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;JSON方法&lt;/strong&gt;：简单易用，但性能相对较低&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;性能考虑&lt;/strong&gt;：深克隆比浅克隆消耗更多资源&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;循环引用&lt;/strong&gt;：深克隆时需要注意处理循环引用问题&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;&lt;strong&gt;使用场景：&lt;/strong&gt;&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;strong&gt;浅克隆&lt;/strong&gt;：对象结构简单，引用类型不需要独立副本&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;深克隆&lt;/strong&gt;：对象结构复杂，需要完全独立的副本&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;原型模式&lt;/strong&gt;：创建对象的副本作为原型&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;缓存机制&lt;/strong&gt;：避免修改原始数据影响缓存&lt;/li&gt;
&lt;/ul&gt;
&lt;h4&gt;8. 网络编程API&lt;/h4&gt;
&lt;h5&gt;8.1 Socket编程&lt;/h5&gt;
&lt;p&gt;&lt;strong&gt;服务器端：&lt;/strong&gt;&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;import java.net.*;
import java.io.*;

public class Server {
    public static void main(String[] args) {
        try (ServerSocket serverSocket = new ServerSocket(8080)) {
            System.out.println(&quot;Server started on port 8080&quot;);
            
            while (true) {
                Socket clientSocket = serverSocket.accept();
                System.out.println(&quot;Client connected: &quot; + clientSocket.getInetAddress());
                
                // 处理客户端连接
                new Thread(() -&amp;gt; handleClient(clientSocket)).start();
            }
        } catch (IOException e) {
            e.printStackTrace();
        }
    }
    
    private static void handleClient(Socket clientSocket) {
        try (BufferedReader in = new BufferedReader(
                new InputStreamReader(clientSocket.getInputStream()));
             PrintWriter out = new PrintWriter(clientSocket.getOutputStream(), true)) {
            
            String inputLine;
            while ((inputLine = in.readLine()) != null) {
                System.out.println(&quot;Received: &quot; + inputLine);
                out.println(&quot;Echo: &quot; + inputLine);
            }
        } catch (IOException e) {
            e.printStackTrace();
        }
    }
}
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;&lt;strong&gt;客户端：&lt;/strong&gt;&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;import java.net.*;
import java.io.*;

public class Client {
    public static void main(String[] args) {
        try (Socket socket = new Socket(&quot;localhost&quot;, 8080);
             PrintWriter out = new PrintWriter(socket.getOutputStream(), true);
             BufferedReader in = new BufferedReader(
                 new InputStreamReader(socket.getInputStream()));
             BufferedReader stdIn = new BufferedReader(
                 new InputStreamReader(System.in))) {
            
            String userInput;
            while ((userInput = stdIn.readLine()) != null) {
                out.println(userInput);
                String response = in.readLine();
                System.out.println(&quot;Server response: &quot; + response);
            }
        } catch (IOException e) {
            e.printStackTrace();
        }
    }
}
&lt;/code&gt;&lt;/pre&gt;
&lt;h5&gt;8.2 URL处理&lt;/h5&gt;
&lt;p&gt;&lt;strong&gt;URL操作：&lt;/strong&gt;&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;import java.net.*;
import java.io.*;

try {
    URL url = new URL(&quot;https://www.example.com&quot;);
    
    // 获取URL信息
    System.out.println(&quot;Protocol: &quot; + url.getProtocol());
    System.out.println(&quot;Host: &quot; + url.getHost());
    System.out.println(&quot;Port: &quot; + url.getPort());
    System.out.println(&quot;Path: &quot; + url.getPath());
    
    // 读取URL内容
    try (BufferedReader reader = new BufferedReader(
            new InputStreamReader(url.openStream()))) {
        String line;
        while ((line = reader.readLine()) != null) {
            System.out.println(line);
        }
    }
} catch (IOException e) {
    e.printStackTrace();
}
&lt;/code&gt;&lt;/pre&gt;
&lt;h3&gt;学习总结&lt;/h3&gt;
&lt;p&gt;通过本阶段的学习，掌握了Java常用API的核心功能和使用方法：&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;&lt;strong&gt;字符串处理&lt;/strong&gt;：熟练使用String、StringBuilder、StringBuffer等类进行字符串操作&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;集合框架&lt;/strong&gt;：理解List、Set、Map的特点和适用场景，能够选择合适的集合类型&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;IO流&lt;/strong&gt;：掌握字节流、字符流、缓冲流的使用，能够进行文件读写操作&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;多线程&lt;/strong&gt;：理解线程创建、控制、同步机制，能够编写多线程程序&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;反射注解&lt;/strong&gt;：掌握反射机制和注解的使用，能够动态操作类和对象&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;日期时间&lt;/strong&gt;：熟练使用新日期时间API，能够进行日期时间计算和格式化&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;工具类&lt;/strong&gt;：掌握Arrays、Collections、Objects、Optional等工具类的使用&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;大数运算&lt;/strong&gt;：掌握BigInteger和BigDecimal的使用，能够进行高精度数学计算和金融计算&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;正则表达式&lt;/strong&gt;：掌握Pattern、Matcher类的使用，能够进行文本匹配、验证和替换&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;包装类&lt;/strong&gt;：掌握基本类型与包装类的转换，理解装箱拆箱机制和性能优化&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Lambda表达式&lt;/strong&gt;：掌握函数式编程，理解Lambda语法、函数式接口和Stream API&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;网络编程&lt;/strong&gt;：理解Socket编程和URL处理的基本原理&lt;/li&gt;
&lt;/ol&gt;
&lt;p&gt;这些API是Java开发的基础，为后续学习Spring框架、数据库操作、Web开发等高级主题奠定了坚实基础。&lt;/p&gt;
&lt;h3&gt;今日学习心得&lt;/h3&gt;
&lt;p&gt;今天重点学习了BigInteger和BigDecimal这两个重要的数值类型：&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;BigInteger学习要点：&lt;/strong&gt;&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;可以表示任意大的整数，解决了基本整数类型的范围限制&lt;/li&gt;
&lt;li&gt;支持所有基本数学运算：加减乘除、幂运算、位运算等&lt;/li&gt;
&lt;li&gt;提供了素数测试、最大公约数、最小公倍数等高级功能&lt;/li&gt;
&lt;li&gt;适用于密码学、大数统计、科学计算等场景&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;&lt;strong&gt;BigDecimal学习要点：&lt;/strong&gt;&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;解决了浮点数精度问题，特别适用于金融计算&lt;/li&gt;
&lt;li&gt;支持精确的十进制运算，避免浮点数舍入误差&lt;/li&gt;
&lt;li&gt;提供了多种舍入模式，满足不同业务需求&lt;/li&gt;
&lt;li&gt;可以控制精度和舍入方式，确保计算结果的准确性&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;&lt;strong&gt;实际应用场景：&lt;/strong&gt;&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;金融系统：货币计算、利率计算、百分比计算&lt;/li&gt;
&lt;li&gt;科学计算：高精度数学运算、统计分析&lt;/li&gt;
&lt;li&gt;密码学：大数运算、素数生成&lt;/li&gt;
&lt;li&gt;避免浮点数精度问题：确保计算结果的准确性&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;&lt;strong&gt;注意事项：&lt;/strong&gt;&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;性能考虑：BigInteger和BigDecimal运算比基本类型慢，只在需要时使用&lt;/li&gt;
&lt;li&gt;构造方式：优先使用字符串构造BigDecimal，避免double精度问题&lt;/li&gt;
&lt;li&gt;比较操作：使用compareTo()而不是equals()进行比较&lt;/li&gt;
&lt;li&gt;舍入模式：根据业务需求选择合适的舍入模式&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;通过今天的学习，对Java的数值处理能力有了更深入的理解，特别是在处理大数和精确计算方面的优势。这些知识为后续开发金融应用、科学计算程序等提供了重要基础。&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;正则表达式学习要点：&lt;/strong&gt;&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;掌握了Pattern和Matcher类的核心用法，能够进行复杂的文本匹配和验证&lt;/li&gt;
&lt;li&gt;熟练使用String类的正则方法（matches、split、replaceAll、replaceFirst）&lt;/li&gt;
&lt;li&gt;掌握了常用正则表达式模式，包括邮箱、手机号、身份证、URL等验证&lt;/li&gt;
&lt;li&gt;理解了正则表达式的语法要点，包括字符类、量词、边界、分组等&lt;/li&gt;
&lt;li&gt;学会了性能优化技巧，如预编译正则表达式、避免回溯等&lt;/li&gt;
&lt;li&gt;能够编写实用的正则表达式工具类，提高代码复用性&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;&lt;strong&gt;正则表达式应用场景：&lt;/strong&gt;&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;数据验证：表单验证、数据格式检查&lt;/li&gt;
&lt;li&gt;文本处理：提取关键信息、格式化文本&lt;/li&gt;
&lt;li&gt;日志分析：解析日志文件、提取错误信息&lt;/li&gt;
&lt;li&gt;数据清洗：标准化数据格式、去除无用信息&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;&lt;strong&gt;正则表达式注意事项：&lt;/strong&gt;&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;性能考虑：预编译正则表达式，避免在循环中重复编译&lt;/li&gt;
&lt;li&gt;语法陷阱：注意转义字符、边界匹配、贪婪与非贪婪匹配&lt;/li&gt;
&lt;li&gt;可读性：复杂正则表达式要添加注释，提高代码可维护性&lt;/li&gt;
&lt;li&gt;测试验证：编写充分的测试用例，确保正则表达式的正确性&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;通过正则表达式的学习，对文本处理和模式匹配有了更深入的理解，这将为后续开发数据处理、日志分析、表单验证等功能提供重要支持。&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;包装类学习要点：&lt;/strong&gt;&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;掌握了基本数据类型与包装类的对应关系，理解装箱拆箱机制&lt;/li&gt;
&lt;li&gt;熟练使用包装类的常用方法，包括类型转换、字符串解析、比较操作等&lt;/li&gt;
&lt;li&gt;理解了包装类的缓存机制，特别是Integer的-128到127缓存范围&lt;/li&gt;
&lt;li&gt;掌握了Character类的特殊方法，能够进行字符类型判断和转换&lt;/li&gt;
&lt;li&gt;学会了包装类的性能优化技巧，避免不必要的装箱拆箱操作&lt;/li&gt;
&lt;li&gt;能够编写实用的包装类工具方法，提高代码的安全性和可读性&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;&lt;strong&gt;包装类应用场景：&lt;/strong&gt;&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;集合框架：在List、Set、Map中存储数值类型&lt;/li&gt;
&lt;li&gt;泛型编程：在泛型中使用包装类类型参数&lt;/li&gt;
&lt;li&gt;数据库操作：处理可能为null的数值字段&lt;/li&gt;
&lt;li&gt;反射机制：在反射中使用包装类类型&lt;/li&gt;
&lt;li&gt;序列化：包装类支持序列化操作&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;&lt;strong&gt;包装类注意事项：&lt;/strong&gt;&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;性能考虑：基本类型比包装类性能更好，避免在循环中频繁装箱拆箱&lt;/li&gt;
&lt;li&gt;null值处理：包装类可以为null，拆箱前要检查null值&lt;/li&gt;
&lt;li&gt;比较操作：使用equals()比较内容，使用compareTo()进行排序&lt;/li&gt;
&lt;li&gt;缓存机制：利用valueOf()方法使用缓存，提高性能&lt;/li&gt;
&lt;li&gt;不可变性：所有包装类都是不可变的，修改会创建新对象&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;通过包装类的学习，对Java的类型系统有了更深入的理解，特别是在处理基本类型与对象类型转换、集合框架使用、数据库操作等方面的重要作用。这些知识为后续开发企业级应用、数据库操作、框架使用等提供了重要基础。&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;Lambda表达式学习要点：&lt;/strong&gt;&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;掌握了Lambda表达式的基本语法，包括参数列表、箭头操作符和表达式体&lt;/li&gt;
&lt;li&gt;理解了函数式接口的概念，能够自定义和使用函数式接口&lt;/li&gt;
&lt;li&gt;熟练使用Java内置函数式接口：Predicate、Function、Consumer、Supplier、BiFunction等&lt;/li&gt;
&lt;li&gt;掌握了Lambda表达式在集合中的应用，包括forEach、removeIf、replaceAll、sort等&lt;/li&gt;
&lt;li&gt;学会了Stream API的使用，能够进行过滤、映射、归约、分组等操作&lt;/li&gt;
&lt;li&gt;理解了方法引用、闭包、变量捕获等高级概念&lt;/li&gt;
&lt;li&gt;学会了Lambda表达式的性能优化技巧，避免常见陷阱&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;&lt;strong&gt;Lambda表达式应用场景：&lt;/strong&gt;&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;集合操作：简化遍历、过滤、映射、排序等操作&lt;/li&gt;
&lt;li&gt;事件处理：GUI事件、异步回调等&lt;/li&gt;
&lt;li&gt;函数式编程：函数组合、管道处理、柯里化等&lt;/li&gt;
&lt;li&gt;Stream API：数据流处理、并行计算&lt;/li&gt;
&lt;li&gt;回调函数：异步编程、事件驱动编程&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;&lt;strong&gt;Lambda表达式注意事项：&lt;/strong&gt;&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;语法规则：参数类型推断、括号省略、return省略等&lt;/li&gt;
&lt;li&gt;变量捕获：只能捕获final或effectively final的变量&lt;/li&gt;
&lt;li&gt;性能考虑：避免在循环中重复创建Lambda，使用并行流&lt;/li&gt;
&lt;li&gt;常见陷阱：变量捕获限制、异常处理、递归实现&lt;/li&gt;
&lt;li&gt;最佳实践：优先使用方法引用，合理使用函数式接口&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;通过Lambda表达式的学习，对Java的函数式编程能力有了显著提升，特别是在集合处理、异步编程、事件处理等方面的应用。这些知识为后续学习现代Java框架、响应式编程、微服务开发等提供了重要基础。&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;Hutool工具库学习要点：&lt;/strong&gt;&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;掌握了Hutool的核心工具类：StrUtil、DateUtil、CollUtil、MapUtil、HttpUtil、JSONUtil等&lt;/li&gt;
&lt;li&gt;理解了Hutool的设计理念：简化Java开发，提高开发效率&lt;/li&gt;
&lt;li&gt;熟练使用字符串处理工具，包括判空、格式化、截取、替换、分割、连接等操作&lt;/li&gt;
&lt;li&gt;掌握了日期时间处理工具，包括解析、格式化、计算、比较、偏移等操作&lt;/li&gt;
&lt;li&gt;学会了集合操作工具，包括创建、判空、并集交集差集、分组、排序、分页等&lt;/li&gt;
&lt;li&gt;理解了HTTP请求工具，包括GET、POST、文件上传、异步请求、请求拦截等&lt;/li&gt;
&lt;li&gt;掌握了JSON处理工具，包括对象转换、JSONObject、JSONArray、复杂JSON操作等&lt;/li&gt;
&lt;li&gt;学会了实用工具类的组合使用，提高代码复用性和开发效率&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;&lt;strong&gt;Hutool工具库应用场景：&lt;/strong&gt;&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;快速开发：原型开发、工具脚本、测试代码&lt;/li&gt;
&lt;li&gt;数据处理：字符串处理、日期处理、集合操作&lt;/li&gt;
&lt;li&gt;网络编程：HTTP客户端、API调用、文件下载&lt;/li&gt;
&lt;li&gt;配置管理：JSON配置、配置文件读取、数据验证&lt;/li&gt;
&lt;li&gt;日志分析：日志解析、统计分析、错误监控&lt;/li&gt;
&lt;li&gt;缓存管理：简单缓存、数据缓存、性能优化&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;&lt;strong&gt;Hutool工具库注意事项：&lt;/strong&gt;&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;性能考虑：合理使用缓存机制，避免频繁创建工具类实例&lt;/li&gt;
&lt;li&gt;异常处理：处理网络请求异常、JSON解析异常、加密解密异常&lt;/li&gt;
&lt;li&gt;安全性：使用安全的加密算法，保护敏感信息，验证输入数据&lt;/li&gt;
&lt;li&gt;版本兼容：注意版本兼容性，遵循编码规范&lt;/li&gt;
&lt;li&gt;最佳实践：合理使用工具类，避免过度依赖，保持代码简洁&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;通过Hutool工具库的学习，对Java开发效率有了显著提升，特别是在快速开发、数据处理、网络编程等方面的应用。这些知识为后续开发企业级应用、微服务、工具脚本等提供了重要支持。&lt;/p&gt;
&lt;hr /&gt;
&lt;p&gt;后续将继续深入学习更多高级API和框架，不断提升Java编程技能。&lt;/p&gt;
</content:encoded></item><item><title>如何解决误删除root用户</title><link>https://meteor-comet.github.io/posts/fix-deleted-root-user/</link><guid isPermaLink="true">https://meteor-comet.github.io/posts/fix-deleted-root-user/</guid><description>MySQL数据库管理员紧急恢复指南</description><pubDate>Tue, 30 May 2023 00:00:00 GMT</pubDate><content:encoded>&lt;h2&gt;学习目标&lt;/h2&gt;
&lt;ul&gt;
&lt;li&gt;了解MySQL root用户的重要性&lt;/li&gt;
&lt;li&gt;掌握误删除MySQL root用户的常见原因&lt;/li&gt;
&lt;li&gt;学会使用--skip-grant-tables模式恢复root用户&lt;/li&gt;
&lt;li&gt;掌握MySQL用户权限管理的最佳实践&lt;/li&gt;
&lt;/ul&gt;
&lt;h2&gt;学习计划&lt;/h2&gt;
&lt;ol&gt;
&lt;li&gt;&lt;strong&gt;MySQL root用户基础知识&lt;/strong&gt;&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;误删除MySQL root用户的常见原因&lt;/strong&gt;&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;恢复方法：--skip-grant-tables模式&lt;/strong&gt;&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;实际操作步骤详解&lt;/strong&gt;&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;验证和测试&lt;/strong&gt;&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;预防措施和最佳实践&lt;/strong&gt;&lt;/li&gt;
&lt;/ol&gt;
&lt;hr /&gt;
&lt;h2&gt;1. MySQL root用户基础知识&lt;/h2&gt;
&lt;h3&gt;1.1 MySQL root用户的定义&lt;/h3&gt;
&lt;p&gt;MySQL root用户是数据库系统中的超级管理员，拥有最高权限，可以执行任何数据库操作。&lt;/p&gt;
&lt;h3&gt;1.2 MySQL root用户的重要性&lt;/h3&gt;
&lt;ul&gt;
&lt;li&gt;&lt;strong&gt;数据库管理&lt;/strong&gt;：创建、删除、修改数据库&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;用户管理&lt;/strong&gt;：创建、删除、修改用户账户和权限&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;系统配置&lt;/strong&gt;：配置MySQL服务器参数&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;数据维护&lt;/strong&gt;：备份、恢复、优化数据库&lt;/li&gt;
&lt;/ul&gt;
&lt;h3&gt;1.3 MySQL用户信息存储位置&lt;/h3&gt;
&lt;pre&gt;&lt;code&gt;-- MySQL用户信息存储在以下系统表中
mysql.user     # 用户基本信息和权限
mysql.db       # 数据库级别权限
mysql.tables_priv    # 表级别权限
mysql.columns_priv   # 列级别权限
&lt;/code&gt;&lt;/pre&gt;
&lt;hr /&gt;
&lt;h2&gt;2. 误删除MySQL root用户的常见原因&lt;/h2&gt;
&lt;h3&gt;2.1 常见误操作&lt;/h3&gt;
&lt;ul&gt;
&lt;li&gt;&lt;strong&gt;误删用户&lt;/strong&gt;：&lt;code&gt;DELETE FROM mysql.user WHERE User=&apos;root&apos;&lt;/code&gt;&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;误删密码&lt;/strong&gt;：&lt;code&gt;UPDATE mysql.user SET authentication_string=&apos;&apos; WHERE User=&apos;root&apos;&lt;/code&gt;&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;误改权限&lt;/strong&gt;：错误修改用户权限导致无法登录&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;误删数据库&lt;/strong&gt;：删除mysql系统数据库&lt;/li&gt;
&lt;/ul&gt;
&lt;h3&gt;2.2 危险SQL命令示例&lt;/h3&gt;
&lt;pre&gt;&lt;code&gt;-- 这些SQL命令可能导致root用户问题
DELETE FROM mysql.user WHERE User=&apos;root&apos;;                    -- 删除root用户
UPDATE mysql.user SET authentication_string=&apos;&apos; WHERE User=&apos;root&apos;;  -- 清空root密码
DROP DATABASE mysql;                                          -- 删除系统数据库
REVOKE ALL PRIVILEGES ON *.* FROM &apos;root&apos;@&apos;localhost&apos;;        -- 撤销所有权限
&lt;/code&gt;&lt;/pre&gt;
&lt;hr /&gt;
&lt;h2&gt;3. 恢复方法：--skip-grant-tables模式&lt;/h2&gt;
&lt;h3&gt;3.1 --skip-grant-tables模式说明&lt;/h3&gt;
&lt;p&gt;&lt;code&gt;--skip-grant-tables&lt;/code&gt;是MySQL的一个启动参数，它会跳过权限验证，允许任何用户无密码登录，主要用于紧急恢复。&lt;/p&gt;
&lt;h3&gt;3.2 工作原理&lt;/h3&gt;
&lt;ul&gt;
&lt;li&gt;跳过权限表验证&lt;/li&gt;
&lt;li&gt;允许无密码登录&lt;/li&gt;
&lt;li&gt;可以修改mysql.user表&lt;/li&gt;
&lt;li&gt;适用于紧急恢复场景&lt;/li&gt;
&lt;/ul&gt;
&lt;h3&gt;3.3 注意事项&lt;/h3&gt;
&lt;ul&gt;
&lt;li&gt;仅用于紧急恢复&lt;/li&gt;
&lt;li&gt;恢复后必须重启MySQL&lt;/li&gt;
&lt;li&gt;生产环境谨慎使用&lt;/li&gt;
&lt;li&gt;确保网络安全&lt;/li&gt;
&lt;/ul&gt;
&lt;hr /&gt;
&lt;h2&gt;4. 实际操作步骤详解&lt;/h2&gt;
&lt;h3&gt;4.1 第一步：启动MySQL跳过权限验证&lt;/h3&gt;
&lt;pre&gt;&lt;code&gt;# 进入MySQL bin目录
cd F:\MySQL\bin

# 启动MySQL跳过权限验证
.\mysqld --skip-grant-tables --datadir=&quot;F:\MySQL_Data\Data&quot; --console

# 说明：
# --skip-grant-tables: 跳过权限验证
# --datadir: 指定数据目录位置（如果数据不在默认位置）
# --console: 在控制台显示输出
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;&lt;strong&gt;输出结果：&lt;/strong&gt;&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;2024-08-17T11:38:02.123456Z 0 [System] [MY-010116] [Server] F:\MySQL\bin\mysqld.exe (mysqld 8.0.34) starting as process 1234
2024-08-17T11:38:02.234567Z 0 [System] [MY-010931] [Server] F:\MySQL\bin\mysqld.exe: ready for connections. Version: &apos;8.0.34&apos; socket: &apos;&apos; port: 3306 MySQL Community Server - GPL
&lt;/code&gt;&lt;/pre&gt;
&lt;h3&gt;4.2 第二步：打开新的命令行窗口连接MySQL&lt;/h3&gt;
&lt;pre&gt;&lt;code&gt;# 在新的命令行窗口中
cd F:\MySQL\bin

# 连接MySQL（无需密码）
.\mysql -u root -h localhost -P 3306
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;&lt;strong&gt;连接成功输出：&lt;/strong&gt;&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;Welcome to the MySQL monitor.  Commands end with ; or \g.
Your MySQL connection id is 7
Server version: 8.0.34 MySQL Community Server - GPL

Copyright (c) 2000, 2023, Oracle and/or its affiliates.
Oracle is a registered trademark of Oracle Corporation and/or its
affiliates. Other names may be trademarks of their respective
owners.

Type &apos;help;&apos; or &apos;\h&apos; for help. Type &apos;\c&apos; to clear the current input statement.

mysql&amp;gt;
&lt;/code&gt;&lt;/pre&gt;
&lt;h3&gt;4.3 第三步：检查当前用户和现有用户&lt;/h3&gt;
&lt;pre&gt;&lt;code&gt;-- 检查当前用户
SELECT USER();

-- 检查现有用户
SELECT User, Host FROM mysql.user;
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;&lt;strong&gt;输出结果：&lt;/strong&gt;&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;mysql&amp;gt; SELECT USER();
+--------+
| USER() |
+--------+
| root@  |
+--------+
1 row in set (0.00 sec)

mysql&amp;gt; SELECT User, Host FROM mysql.user;
+------------------+-----------+
| User             | Host      |
+------------------+-----------+
| root             | %         |
| mysql.infoschema | localhost |
| mysql.session    | localhost |
| mysql.sys        | localhost |
+------------------+-----------+
4 rows in set (0.00 sec)
&lt;/code&gt;&lt;/pre&gt;
&lt;h3&gt;4.4 第四步：创建localhost的root用户&lt;/h3&gt;
&lt;pre&gt;&lt;code&gt;-- 插入localhost的root用户记录
INSERT INTO user (Host, User, Select_priv, Insert_priv, Update_priv, Delete_priv, 
                 Create_priv, Drop_priv, Reload_priv, Shutdown_priv, Process_priv, 
                 File_priv, Grant_priv, References_priv, Index_priv, Alter_priv, 
                 Show_db_priv, Super_priv, Create_tmp_table_priv, Lock_tables_priv, 
                 Execute_priv, Repl_slave_priv, Repl_client_priv, Create_view_priv, 
                 Show_view_priv, Create_routine_priv, Alter_routine_priv, 
                 Create_user_priv, Event_priv, Trigger_priv, Create_tablespace_priv, 
                 ssl_type, ssl_cipher, x509_issuer, x509_subject, max_questions, 
                 max_updates, max_connections, max_user_connections, plugin, 
                 password_expired, account_locked, Create_role_priv, Drop_role_priv)
VALUES (&apos;localhost&apos;, &apos;root&apos;, &apos;Y&apos;, &apos;Y&apos;, &apos;Y&apos;, &apos;Y&apos;, &apos;Y&apos;, &apos;Y&apos;, &apos;Y&apos;, &apos;Y&apos;, &apos;Y&apos;, &apos;Y&apos;, &apos;Y&apos;, 
        &apos;Y&apos;, &apos;Y&apos;, &apos;Y&apos;, &apos;Y&apos;, &apos;Y&apos;, &apos;Y&apos;, &apos;Y&apos;, &apos;Y&apos;, &apos;Y&apos;, &apos;Y&apos;, &apos;Y&apos;, &apos;Y&apos;, &apos;Y&apos;, &apos;Y&apos;, &apos;Y&apos;, 
        &apos;Y&apos;, &apos;Y&apos;, &apos;Y&apos;, &apos;&apos;, &apos;&apos;, &apos;&apos;, &apos;&apos;, 0, 0, 0, 0, &apos;mysql_native_password&apos;, &apos;N&apos;, &apos;N&apos;, &apos;Y&apos;, &apos;Y&apos;);
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;&lt;strong&gt;输出结果：&lt;/strong&gt;&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;Query OK, 1 row affected (0.00 sec)
&lt;/code&gt;&lt;/pre&gt;
&lt;h3&gt;4.5 第五步：设置root用户密码&lt;/h3&gt;
&lt;pre&gt;&lt;code&gt;-- 设置root用户密码为123456
UPDATE user SET authentication_string = CONCAT(&apos;*&apos;, UPPER(SHA1(UNHEX(SHA1(&apos;123456&apos;))))) 
WHERE User = &apos;root&apos; AND Host = &apos;localhost&apos;;
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;&lt;strong&gt;输出结果：&lt;/strong&gt;&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;Query OK, 1 row affected (0.00 sec)
Rows matched: 1  Changed: 1  Warnings: 0
&lt;/code&gt;&lt;/pre&gt;
&lt;h3&gt;4.6 第六步：刷新权限&lt;/h3&gt;
&lt;pre&gt;&lt;code&gt;-- 刷新权限表
FLUSH PRIVILEGES;
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;&lt;strong&gt;输出结果：&lt;/strong&gt;&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;Query OK, 0 rows affected (0.01 sec)
&lt;/code&gt;&lt;/pre&gt;
&lt;hr /&gt;
&lt;h2&gt;5. 验证和测试&lt;/h2&gt;
&lt;h3&gt;5.1 退出MySQL并重启服务&lt;/h3&gt;
&lt;pre&gt;&lt;code&gt;# 在MySQL命令行中退出
mysql&amp;gt; EXIT;
Bye

# 停止MySQL服务（在第一个命令行窗口中按Ctrl+C）
# 或者使用任务管理器结束mysqld.exe进程

# 正常启动MySQL服务
.\mysqld --datadir=&quot;F:\MySQL_Data\Data&quot; --console
&lt;/code&gt;&lt;/pre&gt;
&lt;h3&gt;5.2 测试root用户登录&lt;/h3&gt;
&lt;pre&gt;&lt;code&gt;# 使用新密码登录
.\mysql -u root -p -h localhost -P 3306
# 输入密码: 123456
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;&lt;strong&gt;成功登录输出：&lt;/strong&gt;&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;Welcome to the MySQL monitor.  Commands end with ; or \g.
Your MySQL connection id is 8
Server version: 8.0.34 MySQL Community Server - GPL

Copyright (c) 2000, 2023, Oracle and/or its affiliates.
Oracle is a registered trademark of Oracle Corporation and/or its
affiliates. Other names may be trademarks of their respective
owners.

Type &apos;help;&apos; or &apos;\h&apos; for help. Type &apos;\c&apos; to clear the current input statement.

mysql&amp;gt;
&lt;/code&gt;&lt;/pre&gt;
&lt;h3&gt;5.3 验证用户权限&lt;/h3&gt;
&lt;pre&gt;&lt;code&gt;-- 检查当前用户
SELECT USER();

-- 检查root用户权限
SHOW GRANTS FOR &apos;root&apos;@&apos;localhost&apos;;

-- 测试创建数据库
CREATE DATABASE test_recovery;
SHOW DATABASES;
DROP DATABASE test_recovery;
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;&lt;strong&gt;验证输出：&lt;/strong&gt;&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;mysql&amp;gt; SELECT USER();
+------------------+
| USER()           |
+------------------+
| root@localhost   |
+------------------+
1 row in set (0.00 sec)

mysql&amp;gt; SHOW GRANTS FOR &apos;root&apos;@&apos;localhost&apos;;
+---------------------------------------------------------------------+
| Grants for root@localhost                                            |
+---------------------------------------------------------------------+
| GRANT ALL PRIVILEGES ON *.* TO `root`@`localhost` WITH GRANT OPTION |
| GRANT PROXY ON ``@`` TO `root`@`localhost` WITH GRANT OPTION        |
+---------------------------------------------------------------------+
2 rows in set (0.00 sec)
&lt;/code&gt;&lt;/pre&gt;
&lt;h3&gt;5.4 检查用户表状态&lt;/h3&gt;
&lt;pre&gt;&lt;code&gt;-- 查看所有root用户
SELECT User, Host, authentication_string FROM mysql.user WHERE User=&apos;root&apos;;

-- 确认localhost的root用户存在
SELECT User, Host FROM mysql.user WHERE User=&apos;root&apos; AND Host=&apos;localhost&apos;;
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;&lt;strong&gt;检查输出：&lt;/strong&gt;&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;mysql&amp;gt; SELECT User, Host, authentication_string FROM mysql.user WHERE User=&apos;root&apos;;
+------+-----------+-------------------------------------------+
| User | Host      | authentication_string                     |
+------+-----------+-------------------------------------------+
| root | %         | *6BB4837EB74329105EE4568DDA7DC67ED2CA2AD9 |
| root | localhost | *6BB4837EB74329105EE4568DDA7DC67ED2CA2AD9 |
+------+-----------+-------------------------------------------+
2 rows in set (0.00 sec)
&lt;/code&gt;&lt;/pre&gt;
&lt;hr /&gt;
&lt;h2&gt;6. 常见问题和解决方案&lt;/h2&gt;
&lt;h3&gt;6.1 问题1：启动时提示端口被占用&lt;/h3&gt;
&lt;pre&gt;&lt;code&gt;# 错误信息
ERROR 2003 (HY000): Can&apos;t connect to MySQL server on &apos;localhost&apos; (10061)

# 解决方案
# 1. 检查是否有其他MySQL实例在运行
netstat -an | findstr 3306

# 2. 结束占用端口的进程
taskkill /F /PID &amp;lt;进程ID&amp;gt;

# 3. 或者使用不同端口启动
.\mysqld --skip-grant-tables --datadir=&quot;F:\MySQL_Data\Data&quot; --port=3307 --console
&lt;/code&gt;&lt;/pre&gt;
&lt;h3&gt;6.2 问题2：数据目录路径错误&lt;/h3&gt;
&lt;pre&gt;&lt;code&gt;# 错误信息
[ERROR] [MY-010267] [Server] Could not create file &apos;F:\MySQL_Data\Data\ibdata1&apos;

# 解决方案
# 1. 确认数据目录存在
dir F:\MySQL_Data\Data

# 2. 如果不存在，创建目录
mkdir F:\MySQL_Data\Data

# 3. 或者使用默认数据目录
.\mysqld --skip-grant-tables --console
&lt;/code&gt;&lt;/pre&gt;
&lt;h3&gt;6.3 问题3：权限不足&lt;/h3&gt;
&lt;pre&gt;&lt;code&gt;# 错误信息
[ERROR] [MY-010267] [Server] Access denied for user &apos;root&apos;@&apos;localhost&apos;

# 解决方案
# 1. 确保使用--skip-grant-tables启动
# 2. 检查用户表是否正确创建
# 3. 确认FLUSH PRIVILEGES已执行
&lt;/code&gt;&lt;/pre&gt;
&lt;h3&gt;6.4 问题4：密码哈希格式错误&lt;/h3&gt;
&lt;pre&gt;&lt;code&gt;-- 如果密码设置失败，可以使用ALTER USER命令
ALTER USER &apos;root&apos;@&apos;localhost&apos; IDENTIFIED BY &apos;123456&apos;;
FLUSH PRIVILEGES;
&lt;/code&gt;&lt;/pre&gt;
&lt;h3&gt;6.5 问题5：用户已存在但无法登录&lt;/h3&gt;
&lt;pre&gt;&lt;code&gt;-- 检查用户状态
SELECT User, Host, authentication_string, account_locked 
FROM mysql.user WHERE User=&apos;root&apos;;

-- 如果账户被锁定，解锁账户
UPDATE mysql.user SET account_locked=&apos;N&apos; WHERE User=&apos;root&apos; AND Host=&apos;localhost&apos;;
FLUSH PRIVILEGES;
&lt;/code&gt;&lt;/pre&gt;
&lt;hr /&gt;
&lt;h2&gt;7. 预防措施和最佳实践&lt;/h2&gt;
&lt;h3&gt;7.1 定期备份用户数据&lt;/h3&gt;
&lt;pre&gt;&lt;code&gt;-- 创建用户备份脚本
-- backup_users.sql

-- 备份用户表结构
SELECT CONCAT(&apos;CREATE TABLE mysql_user_backup_&apos;, DATE_FORMAT(NOW(), &apos;%Y%m%d_%H%i%s&apos;), &apos; LIKE mysql.user;&apos;) AS backup_command;

-- 备份用户数据
SELECT CONCAT(&apos;INSERT INTO mysql_user_backup_&apos;, DATE_FORMAT(NOW(), &apos;%Y%m%d_%H%i%s&apos;), &apos; SELECT * FROM mysql.user;&apos;) AS backup_command;

-- 导出用户权限
SELECT CONCAT(&apos;SHOW GRANTS FOR &apos;&apos;&apos;, User, &apos;&apos;&apos;@&apos;&apos;&apos;, Host, &apos;&apos;&apos;;&apos;) AS grant_command
FROM mysql.user WHERE User != &apos;&apos;;
&lt;/code&gt;&lt;/pre&gt;
&lt;h3&gt;7.2 创建备用管理员账户&lt;/h3&gt;
&lt;pre&gt;&lt;code&gt;-- 创建备用管理员账户
CREATE USER &apos;admin&apos;@&apos;localhost&apos; IDENTIFIED BY &apos;securepassword123&apos;;
GRANT ALL PRIVILEGES ON *.* TO &apos;admin&apos;@&apos;localhost&apos; WITH GRANT OPTION;
FLUSH PRIVILEGES;

-- 验证账户创建
SELECT User, Host FROM mysql.user WHERE User=&apos;admin&apos;;
&lt;/code&gt;&lt;/pre&gt;
&lt;h3&gt;7.3 设置强密码策略&lt;/h3&gt;
&lt;pre&gt;&lt;code&gt;-- 设置密码策略
SET GLOBAL validate_password.policy=MEDIUM;
SET GLOBAL validate_password.length=8;
SET GLOBAL validate_password.mixed_case_count=1;
SET GLOBAL validate_password.number_count=1;
SET GLOBAL validate_password.special_char_count=1;

-- 修改root密码为强密码
ALTER USER &apos;root&apos;@&apos;localhost&apos; IDENTIFIED BY &apos;MySecurePass123!&apos;;
FLUSH PRIVILEGES;
&lt;/code&gt;&lt;/pre&gt;
&lt;h3&gt;7.4 限制root用户访问&lt;/h3&gt;
&lt;pre&gt;&lt;code&gt;-- 限制root用户只能从localhost访问
DELETE FROM mysql.user WHERE User=&apos;root&apos; AND Host=&apos;%&apos;;

-- 或者限制特定IP访问
UPDATE mysql.user SET Host=&apos;192.168.1.100&apos; WHERE User=&apos;root&apos; AND Host=&apos;%&apos;;
FLUSH PRIVILEGES;
&lt;/code&gt;&lt;/pre&gt;
&lt;h3&gt;7.5 监控用户活动&lt;/h3&gt;
&lt;pre&gt;&lt;code&gt;-- 启用查询日志
SET GLOBAL general_log = &apos;ON&apos;;
SET GLOBAL general_log_file = &apos;F:/MySQL_Data/Data/mysql.log&apos;;

-- 查看当前连接
SHOW PROCESSLIST;

-- 查看用户登录历史
SELECT User, Host, db, Command, Time, State 
FROM information_schema.PROCESSLIST 
WHERE User IS NOT NULL;
&lt;/code&gt;&lt;/pre&gt;
&lt;h3&gt;7.6 定期维护脚本&lt;/h3&gt;
&lt;pre&gt;&lt;code&gt;-- 检查用户状态
SELECT User, Host, authentication_string, account_locked, password_expired
FROM mysql.user 
WHERE User IN (&apos;root&apos;, &apos;admin&apos;);

-- 检查权限
SHOW GRANTS FOR &apos;root&apos;@&apos;localhost&apos;;
SHOW GRANTS FOR &apos;admin&apos;@&apos;localhost&apos;;

-- 清理过期用户
DELETE FROM mysql.user WHERE User=&apos;&apos; OR Host=&apos;&apos;;
FLUSH PRIVILEGES;
&lt;/code&gt;&lt;/pre&gt;
&lt;hr /&gt;
&lt;h2&gt;8. 故障排查和诊断&lt;/h2&gt;
&lt;h3&gt;8.1 检查MySQL服务状态&lt;/h3&gt;
&lt;pre&gt;&lt;code&gt;# 检查MySQL进程
tasklist | findstr mysqld
# 输出:
# mysqld.exe                    1234 Console                    1     45,632 K

# 检查端口占用
netstat -an | findstr 3306
# 输出:
# TCP    0.0.0.0:3306           0.0.0.0:0              LISTENING

# 检查MySQL错误日志
type &quot;F:\MySQL_Data\Data\*.err&quot;
&lt;/code&gt;&lt;/pre&gt;
&lt;h3&gt;8.2 检查用户表状态&lt;/h3&gt;
&lt;pre&gt;&lt;code&gt;-- 检查用户表是否存在
SHOW TABLES FROM mysql LIKE &apos;user&apos;;

-- 检查root用户状态
SELECT User, Host, authentication_string, account_locked, password_expired
FROM mysql.user WHERE User=&apos;root&apos;;

-- 检查用户权限
SHOW GRANTS FOR &apos;root&apos;@&apos;localhost&apos;;
SHOW GRANTS FOR &apos;root&apos;@&apos;%&apos;;
&lt;/code&gt;&lt;/pre&gt;
&lt;h3&gt;8.3 诊断连接问题&lt;/h3&gt;
&lt;pre&gt;&lt;code&gt;-- 查看当前连接
SHOW PROCESSLIST;

-- 查看连接错误
SELECT * FROM performance_schema.host_cache;

-- 检查用户认证插件
SELECT User, Host, plugin FROM mysql.user WHERE User=&apos;root&apos;;
&lt;/code&gt;&lt;/pre&gt;
&lt;h3&gt;8.4 检查配置文件&lt;/h3&gt;
&lt;pre&gt;&lt;code&gt;# 检查my.ini配置文件
type &quot;F:\MySQL\my.ini&quot;

# 检查数据目录权限
dir &quot;F:\MySQL_Data\Data&quot;

# 检查日志文件
dir &quot;F:\MySQL_Data\Data\*.log&quot;
&lt;/code&gt;&lt;/pre&gt;
&lt;h3&gt;8.5 性能监控&lt;/h3&gt;
&lt;pre&gt;&lt;code&gt;-- 查看系统变量
SHOW VARIABLES LIKE &apos;max_connections&apos;;
SHOW VARIABLES LIKE &apos;wait_timeout&apos;;

-- 查看状态信息
SHOW STATUS LIKE &apos;Threads_connected&apos;;
SHOW STATUS LIKE &apos;Uptime&apos;;

-- 查看慢查询
SHOW VARIABLES LIKE &apos;slow_query_log&apos;;
SHOW VARIABLES LIKE &apos;long_query_time&apos;;
&lt;/code&gt;&lt;/pre&gt;
&lt;hr /&gt;
&lt;h2&gt;9. 总结&lt;/h2&gt;
&lt;h3&gt;9.1 关键要点&lt;/h3&gt;
&lt;ol&gt;
&lt;li&gt;&lt;strong&gt;--skip-grant-tables模式&lt;/strong&gt;：MySQL紧急恢复的核心方法&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;数据目录指定&lt;/strong&gt;：正确指定--datadir参数确保数据访问&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;用户表操作&lt;/strong&gt;：直接操作mysql.user表恢复用户&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;权限刷新&lt;/strong&gt;：FLUSH PRIVILEGES确保权限生效&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;验证测试&lt;/strong&gt;：恢复后必须验证登录和权限&lt;/li&gt;
&lt;/ol&gt;
&lt;h3&gt;9.2 操作流程总结&lt;/h3&gt;
&lt;ol&gt;
&lt;li&gt;&lt;strong&gt;启动跳过权限验证&lt;/strong&gt;：&lt;code&gt;mysqld --skip-grant-tables --datadir=&quot;路径&quot; --console&lt;/code&gt;&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;连接数据库&lt;/strong&gt;：&lt;code&gt;mysql -u root -h localhost -P 3306&lt;/code&gt;&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;检查用户状态&lt;/strong&gt;：&lt;code&gt;SELECT User, Host FROM mysql.user&lt;/code&gt;&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;创建用户记录&lt;/strong&gt;：INSERT INTO mysql.user语句&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;设置密码&lt;/strong&gt;：UPDATE authentication_string&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;刷新权限&lt;/strong&gt;：&lt;code&gt;FLUSH PRIVILEGES&lt;/code&gt;&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;重启服务&lt;/strong&gt;：正常启动MySQL&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;验证登录&lt;/strong&gt;：使用新密码登录测试&lt;/li&gt;
&lt;/ol&gt;
&lt;h3&gt;9.3 最佳实践&lt;/h3&gt;
&lt;ul&gt;
&lt;li&gt;定期备份mysql.user表&lt;/li&gt;
&lt;li&gt;创建备用管理员账户&lt;/li&gt;
&lt;li&gt;设置强密码策略&lt;/li&gt;
&lt;li&gt;限制root用户访问范围&lt;/li&gt;
&lt;li&gt;监控用户活动&lt;/li&gt;
&lt;li&gt;定期维护用户权限&lt;/li&gt;
&lt;/ul&gt;
&lt;h3&gt;9.4 学习收获&lt;/h3&gt;
&lt;p&gt;通过这次实际操作，我掌握了：&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;MySQL root用户恢复的完整流程&lt;/li&gt;
&lt;li&gt;--skip-grant-tables模式的使用方法&lt;/li&gt;
&lt;li&gt;用户表结构和权限管理&lt;/li&gt;
&lt;li&gt;密码哈希的生成和设置&lt;/li&gt;
&lt;li&gt;故障排查和诊断技能&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;这些知识对于MySQL数据库管理员来说非常重要，能够帮助我们在紧急情况下快速恢复数据库访问权限，确保数据库服务的连续性。&lt;/p&gt;
&lt;h3&gt;9.5 注意事项&lt;/h3&gt;
&lt;ul&gt;
&lt;li&gt;仅在紧急情况下使用--skip-grant-tables&lt;/li&gt;
&lt;li&gt;恢复后立即重启MySQL服务&lt;/li&gt;
&lt;li&gt;设置强密码并定期更换&lt;/li&gt;
&lt;li&gt;定期备份用户数据&lt;/li&gt;
&lt;li&gt;监控异常登录活动&lt;/li&gt;
&lt;/ul&gt;
</content:encoded></item><item><title>JavaSE</title><link>https://meteor-comet.github.io/posts/java-fullstack-learn/</link><guid isPermaLink="true">https://meteor-comet.github.io/posts/java-fullstack-learn/</guid><description>Java标准版核心语法与API完整教程</description><pubDate>Mon, 10 Apr 2023 00:00:00 GMT</pubDate><content:encoded>&lt;h1&gt;JavaSE学习日志&lt;/h1&gt;
&lt;h3&gt;学习目标&lt;/h3&gt;
&lt;ul&gt;
&lt;li&gt;夯实Java基础，掌握面向对象编程思想&lt;/li&gt;
&lt;li&gt;熟悉常用开发工具与环境配置&lt;/li&gt;
&lt;li&gt;掌握Web开发（Servlet/JSP、Spring MVC、Spring Boot等）&lt;/li&gt;
&lt;li&gt;理解数据库原理并熟练使用MySQL&lt;/li&gt;
&lt;li&gt;掌握Spring、Spring Boot、Spring Cloud等主流框架&lt;/li&gt;
&lt;li&gt;了解前端基础（HTML/CSS/JavaScript）及与后端的集成&lt;/li&gt;
&lt;li&gt;完成至少一个全栈项目实战&lt;/li&gt;
&lt;/ul&gt;
&lt;h3&gt;学习计划&lt;/h3&gt;
&lt;ol&gt;
&lt;li&gt;Java基础复习与进阶&lt;/li&gt;
&lt;li&gt;Web开发基础与进阶&lt;/li&gt;
&lt;li&gt;数据库与持久层技术&lt;/li&gt;
&lt;li&gt;Spring全家桶体系&lt;/li&gt;
&lt;li&gt;前端基础与集成&lt;/li&gt;
&lt;li&gt;项目实战与总结&lt;/li&gt;
&lt;/ol&gt;
&lt;hr /&gt;
&lt;h2&gt;1.Java基础：JDK、JRE、JVM的关系与作用&lt;/h2&gt;
&lt;ul&gt;
&lt;li&gt;&lt;strong&gt;JVM（Java Virtual Machine）&lt;/strong&gt;：Java虚拟机，负责Java字节码的加载、验证、执行和内存管理，是Java实现跨平台的核心。&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;JRE（Java Runtime Environment）&lt;/strong&gt;：Java运行环境，包含JVM和Java核心类库，提供运行Java程序的最小环境。&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;JDK（Java Development Kit）&lt;/strong&gt;：Java开发工具包，包含JRE以及编译器（javac）、调试工具等开发所需工具，是开发Java程序的完整环境。&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;&lt;strong&gt;三者关系&lt;/strong&gt;：&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;JDK ⊃ JRE ⊃ JVM。&lt;/li&gt;
&lt;li&gt;JVM是最底层的虚拟机，JRE在其基础上加上了类库，JDK则在JRE基础上再加上开发工具。&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;&lt;strong&gt;作用总结&lt;/strong&gt;：&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;
&lt;p&gt;开发Java程序用JDK，&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;运行Java程序只需JRE，&lt;/p&gt;
&lt;p&gt;如果已经有编译好的.class字节码文件，运行时只需要JRE环境，无需JDK。JRE包含JVM和Java核心类库，能够加载和执行.class文件。&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;JVM负责Java程序的跨平台运行。&lt;/p&gt;
&lt;p&gt;JVM实现跨平台的原理在于：Java源代码经过编译后生成与平台无关的字节码文件（.class），这种字节码并不能直接被操作系统识别和执行。无论在Windows、Linux还是macOS等不同操作系统上，只要安装了对应平台的JVM，JVM就能识别并执行这些字节码。JVM会根据当前操作系统和硬件环境，将字节码翻译为本地机器指令，从而实现&quot;编写一次，到处运行&quot;（Write Once, Run Anywhere, WORA）的目标。这也是Java语言最大的优势之一。&lt;/p&gt;
&lt;p&gt;总结：只要有.class文件，任何装有JRE的操作系统都能运行该Java程序，无需JDK参与。&lt;/p&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;hr /&gt;
&lt;h2&gt;2.Java主要关键词及其作用&lt;/h2&gt;
&lt;ul&gt;
&lt;li&gt;&lt;strong&gt;class&lt;/strong&gt;&lt;br /&gt;
定义一个类，是Java的基本结构单元。&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;interface&lt;/strong&gt;&lt;br /&gt;
定义一个接口，接口用于规范类必须实现的方法。&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;extends&lt;/strong&gt;&lt;br /&gt;
表示类的继承，子类通过extends继承父类。&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;implements&lt;/strong&gt;&lt;br /&gt;
表示类实现接口。&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;public&lt;/strong&gt;&lt;br /&gt;
公有访问修饰符，任何地方都可以访问。&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;private&lt;/strong&gt;&lt;br /&gt;
私有访问修饰符，只能在本类中访问。&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;protected&lt;/strong&gt;&lt;br /&gt;
受保护访问修饰符，同包或子类可访问。&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;static&lt;/strong&gt;&lt;br /&gt;
静态修饰符，表示属于类而不是实例。&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;final&lt;/strong&gt;&lt;br /&gt;
修饰类、方法、变量，表示不可更改或不可继承。&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;void&lt;/strong&gt;&lt;br /&gt;
表示方法无返回值。&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;new&lt;/strong&gt;&lt;br /&gt;
创建对象实例。&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;this&lt;/strong&gt;&lt;br /&gt;
当前对象的引用。&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;super&lt;/strong&gt;&lt;br /&gt;
父类对象的引用。&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;return&lt;/strong&gt;&lt;br /&gt;
方法返回语句。&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;if / else&lt;/strong&gt;&lt;br /&gt;
条件判断语句。&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;switch / case&lt;/strong&gt;&lt;br /&gt;
多分支选择语句。&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;for / while / do-while&lt;/strong&gt;&lt;br /&gt;
循环语句。&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;break / continue&lt;/strong&gt;&lt;br /&gt;
跳出循环或跳过本次循环。&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;try / catch / finally / throw / throws&lt;/strong&gt;&lt;br /&gt;
异常处理相关关键词。&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;import&lt;/strong&gt;&lt;br /&gt;
导入其他包或类。&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;package&lt;/strong&gt;&lt;br /&gt;
定义包名。&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;abstract&lt;/strong&gt;&lt;br /&gt;
抽象类或方法，不能被实例化或必须被子类实现。&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;synchronized&lt;/strong&gt;&lt;br /&gt;
用于多线程同步。&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;volatile&lt;/strong&gt;&lt;br /&gt;
声明变量易变，线程可见性。&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;transient&lt;/strong&gt;&lt;br /&gt;
序列化时跳过该字段。&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;instanceof&lt;/strong&gt;&lt;br /&gt;
判断对象是否为某个类的实例。&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;enum&lt;/strong&gt;&lt;br /&gt;
枚举类型。&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;default&lt;/strong&gt;&lt;br /&gt;
用于switch语句的默认分支，或接口的默认方法实现。&lt;/li&gt;
&lt;/ul&gt;
&lt;h2&gt;3. Java中的字面量分类&lt;/h2&gt;
&lt;p&gt;Java中的字面量（Literal）是指在代码中直接表示固定值的数据。常见字面量分类如下：&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;
&lt;p&gt;&lt;strong&gt;整数型字面量&lt;/strong&gt;&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;十进制：如 123&lt;/li&gt;
&lt;li&gt;八进制：以0开头，如 0123&lt;/li&gt;
&lt;li&gt;十六进制：以0x或0X开头，如 0x7B&lt;/li&gt;
&lt;li&gt;二进制：以0b或0B开头，如 0b1010&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;&lt;strong&gt;浮点型字面量&lt;/strong&gt;&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;如 3.14、2.0e-3、1.5F、6.022E23D&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;&lt;strong&gt;字符型字面量&lt;/strong&gt;&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;单引号括起来的单个字符，如 &apos;A&apos;、&apos;中&apos;、&apos;\n&apos;&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;&lt;strong&gt;字符串字面量&lt;/strong&gt;&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;双引号括起来的字符序列，如 &quot;Hello, Java!&quot;&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;&lt;strong&gt;布尔型字面量&lt;/strong&gt;&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;只有 true 和 false&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;&lt;strong&gt;null字面量&lt;/strong&gt;&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;表示空引用，如 null&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;字面量是Java程序中最基本的数据表示方式，直接参与表达式和赋值。&lt;/p&gt;
&lt;h2&gt;4. Java变量定义的格式&lt;/h2&gt;
&lt;p&gt;Java中变量的定义格式如下：&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;数据类型 变量名 = 初始值;
&lt;/code&gt;&lt;/pre&gt;
&lt;ul&gt;
&lt;li&gt;&lt;strong&gt;数据类型&lt;/strong&gt;：如 int、double、char、String、boolean 等。&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;变量名&lt;/strong&gt;：自定义的标识符，需遵循命名规范。&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;初始值&lt;/strong&gt;：可选，变量声明时赋的初始值。&lt;/li&gt;
&lt;/ul&gt;
&lt;h3&gt;示例&lt;/h3&gt;
&lt;pre&gt;&lt;code&gt;int age = 25;
double price = 19.99;
char gender = &apos;M&apos;;
String name = &quot;Tom&quot;;
boolean isActive = true;
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;也可以先声明后赋值：&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;int score;
score = 100;
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;变量名建议使用有意义的英文单词，遵循小驼峰命名法（如 studentName）。&lt;/p&gt;
&lt;h2&gt;5. Java中的键盘录入&lt;/h2&gt;
&lt;p&gt;在Java中，常用&lt;code&gt;Scanner&lt;/code&gt;类实现从键盘读取用户输入。&lt;code&gt;Scanner&lt;/code&gt;类位于&lt;code&gt;java.util&lt;/code&gt;包中。&lt;/p&gt;
&lt;h3&gt;基本用法&lt;/h3&gt;
&lt;ol&gt;
&lt;li&gt;导入包：&lt;/li&gt;
&lt;/ol&gt;
&lt;pre&gt;&lt;code&gt;import java.util.Scanner;
&lt;/code&gt;&lt;/pre&gt;
&lt;ol&gt;
&lt;li&gt;创建Scanner对象：&lt;/li&gt;
&lt;/ol&gt;
&lt;pre&gt;&lt;code&gt;Scanner sc = new Scanner(System.in);
&lt;/code&gt;&lt;/pre&gt;
&lt;ol&gt;
&lt;li&gt;读取输入：&lt;/li&gt;
&lt;/ol&gt;
&lt;ul&gt;
&lt;li&gt;读取字符串：&lt;code&gt;String str = sc.nextLine();&lt;/code&gt;&lt;/li&gt;
&lt;li&gt;读取整数：&lt;code&gt;int num = sc.nextInt();&lt;/code&gt;&lt;/li&gt;
&lt;li&gt;读取浮点数：&lt;code&gt;double d = sc.nextDouble();&lt;/code&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;h3&gt;示例代码&lt;/h3&gt;
&lt;pre&gt;&lt;code&gt;import java.util.Scanner;

public class InputDemo {
    public static void main(String[] args) {
        Scanner sc = new Scanner(System.in);
        System.out.print(&quot;请输入姓名：&quot;);
        String name = sc.nextLine();
        System.out.print(&quot;请输入年龄：&quot;);
        int age = sc.nextInt();
        System.out.println(&quot;姓名：&quot; + name + &quot;, 年龄：&quot; + age);
    }
}
&lt;/code&gt;&lt;/pre&gt;
&lt;blockquote&gt;
&lt;p&gt;提示：使用完Scanner后建议调用&lt;code&gt;sc.close();&lt;/code&gt;关闭资源。&lt;/p&gt;
&lt;/blockquote&gt;
&lt;h2&gt;6. IDEA中Java项目的结构介绍&lt;/h2&gt;
&lt;p&gt;在IntelliJ IDEA中创建的标准Java项目通常包含如下目录结构：&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;project-name/
├── .idea/                # IDEA项目配置文件夹
├── src/                  # 源代码目录
│   └── main/             # 主代码目录（Maven/Gradle项目）
│       ├── java/         # Java源代码
│       └── resources/    # 资源文件（配置、图片等）
│   └── test/             # 测试代码目录
│       ├── java/         # 测试用Java代码
│       └── resources/    # 测试资源文件
├── out/                  # 编译输出目录（普通项目）
├── target/               # 构建输出目录（Maven项目）
├── build/                # 构建输出目录（Gradle项目）
├── pom.xml               # Maven项目配置文件
├── build.gradle          # Gradle项目配置文件
└── ...                   # 其他文件
&lt;/code&gt;&lt;/pre&gt;
&lt;h3&gt;主要部分说明&lt;/h3&gt;
&lt;ul&gt;
&lt;li&gt;&lt;strong&gt;src/main/java/&lt;/strong&gt;：存放项目的主Java源代码。&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;src/main/resources/&lt;/strong&gt;：存放配置文件、图片等资源。&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;src/test/java/&lt;/strong&gt;：存放测试用的Java代码。&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;src/test/resources/&lt;/strong&gt;：存放测试相关的资源文件。&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;pom.xml&lt;/strong&gt;：Maven项目的依赖和构建配置文件。&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;build.gradle&lt;/strong&gt;：Gradle项目的依赖和构建配置文件。&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;out/&lt;/strong&gt;、&lt;strong&gt;target/&lt;/strong&gt;、&lt;strong&gt;build/&lt;/strong&gt;：编译或打包后生成的输出目录。&lt;/li&gt;
&lt;/ul&gt;
&lt;blockquote&gt;
&lt;p&gt;普通Java项目只有src和out，Maven/Gradle项目结构更规范，推荐使用。&lt;/p&gt;
&lt;/blockquote&gt;
&lt;h3&gt;项目、模块、类的概念与关系&lt;/h3&gt;
&lt;ul&gt;
&lt;li&gt;
&lt;p&gt;&lt;strong&gt;项目（Project）&lt;/strong&gt;&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;在IDEA中，项目是开发的整体工程，包含所有代码、资源、配置和依赖管理。一个项目可以包含一个或多个模块。&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;&lt;strong&gt;模块（Module）&lt;/strong&gt;&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;模块是项目中的功能单元，可以独立编译、运行和测试。每个模块有自己的源码、资源和依赖配置。大型项目通常会按功能或层次拆分为多个模块（如web、service、dao等）。&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;&lt;strong&gt;类（Class）&lt;/strong&gt;&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;类是Java程序的基本代码结构，用于描述对象的属性和行为。类文件（.java）位于模块的src目录下，是实现具体功能的最小单元。&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;h4&gt;三者关系&lt;/h4&gt;
&lt;ul&gt;
&lt;li&gt;一个&lt;strong&gt;项目&lt;/strong&gt;可以包含多个&lt;strong&gt;模块&lt;/strong&gt;。&lt;/li&gt;
&lt;li&gt;一个&lt;strong&gt;模块&lt;/strong&gt;可以包含多个&lt;strong&gt;类&lt;/strong&gt;。&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;类&lt;/strong&gt;是代码实现的最小单元，&lt;strong&gt;模块&lt;/strong&gt;是功能或层次的划分，&lt;strong&gt;项目&lt;/strong&gt;是整体工程的集合。&lt;/li&gt;
&lt;/ul&gt;
&lt;blockquote&gt;
&lt;p&gt;简单理解：项目 &amp;gt; 模块 &amp;gt; 类，三者层层包含，便于大型系统的分工、协作和维护。&lt;/p&gt;
&lt;/blockquote&gt;
&lt;h2&gt;7. Java运算符及其运算规则&lt;/h2&gt;
&lt;h3&gt;1. 算术运算符&lt;/h3&gt;
&lt;ul&gt;
&lt;li&gt;&lt;code&gt;+&lt;/code&gt; ：加法，两数相加。例如：a + b&lt;/li&gt;
&lt;li&gt;&lt;code&gt;-&lt;/code&gt; ：减法，两数相减。例如：a - b&lt;/li&gt;
&lt;li&gt;&lt;code&gt;*&lt;/code&gt; ：乘法，两数相乘。例如：a * b&lt;/li&gt;
&lt;li&gt;&lt;code&gt;/&lt;/code&gt; ：除法，两数相除（整除/浮点）。例如：a / b&lt;/li&gt;
&lt;li&gt;&lt;code&gt;%&lt;/code&gt; ：取余，取模，余数。例如：a % b&lt;/li&gt;
&lt;li&gt;&lt;code&gt;++&lt;/code&gt; ：自增，变量加1（前/后缀）。例如：a++ 或 ++a&lt;/li&gt;
&lt;li&gt;&lt;code&gt;--&lt;/code&gt; ：自减，变量减1（前/后缀）。例如：a-- 或 --a&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;&lt;strong&gt;示例：&lt;/strong&gt;&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;int a = 10, b = 3;
System.out.println(a + b); // 13
System.out.println(a - b); // 7
System.out.println(a * b); // 30
System.out.println(a / b); // 3
System.out.println(a % b); // 1
System.out.println(++a);   // 11
System.out.println(b--);   // 3（b变为2）
&lt;/code&gt;&lt;/pre&gt;
&lt;h3&gt;2. 赋值运算符&lt;/h3&gt;
&lt;ul&gt;
&lt;li&gt;&lt;code&gt;=&lt;/code&gt; ：赋值，将右侧值赋给左侧变量。例如：a = b&lt;/li&gt;
&lt;li&gt;&lt;code&gt;+=&lt;/code&gt; ：加后赋值，a = a + b。例如：a += b&lt;/li&gt;
&lt;li&gt;&lt;code&gt;-=&lt;/code&gt; ：减后赋值，a = a - b。例如：a -= b&lt;/li&gt;
&lt;li&gt;&lt;code&gt;*=&lt;/code&gt; ：乘后赋值，a = a * b。例如：a *= b&lt;/li&gt;
&lt;li&gt;&lt;code&gt;/=&lt;/code&gt; ：除后赋值，a = a / b。例如：a /= b&lt;/li&gt;
&lt;li&gt;&lt;code&gt;%=&lt;/code&gt; ：取余后赋值，a = a % b。例如：a %= b&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;&lt;strong&gt;示例：&lt;/strong&gt;&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;int a = 5;
a += 3; // a = 8
a -= 2; // a = 6
a *= 4; // a = 24
a /= 6; // a = 4
a %= 3; // a = 1
&lt;/code&gt;&lt;/pre&gt;
&lt;h3&gt;3. 比较（关系）运算符&lt;/h3&gt;
&lt;ul&gt;
&lt;li&gt;&lt;code&gt;==&lt;/code&gt; ：等于，判断两值是否相等。例如：a == b&lt;/li&gt;
&lt;li&gt;&lt;code&gt;!=&lt;/code&gt; ：不等于，判断两值是否不等。例如：a != b&lt;/li&gt;
&lt;li&gt;&lt;code&gt;&amp;gt;&lt;/code&gt; ：大于。例如：a &amp;gt; b&lt;/li&gt;
&lt;li&gt;&lt;code&gt;&amp;lt;&lt;/code&gt; ：小于。例如：a &amp;lt; b&lt;/li&gt;
&lt;li&gt;&lt;code&gt;&amp;gt;=&lt;/code&gt; ：大于等于。例如：a &amp;gt;= b&lt;/li&gt;
&lt;li&gt;&lt;code&gt;&amp;lt;=&lt;/code&gt; ：小于等于。例如：a &amp;lt;= b&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;&lt;strong&gt;示例：&lt;/strong&gt;&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;int a = 5, b = 8;
System.out.println(a == b); // false
System.out.println(a != b); // true
System.out.println(a &amp;gt; b);  // false
System.out.println(a &amp;lt; b);  // true
System.out.println(a &amp;gt;= 5); // true
System.out.println(b &amp;lt;= 8); // true
&lt;/code&gt;&lt;/pre&gt;
&lt;h3&gt;4. 逻辑运算符&lt;/h3&gt;
&lt;ul&gt;
&lt;li&gt;&lt;code&gt;&amp;amp;&amp;amp;&lt;/code&gt; ：逻辑与，两个条件都为true时结果为true。例如：a &amp;amp;&amp;amp; b&lt;/li&gt;
&lt;li&gt;&lt;code&gt;||&lt;/code&gt; ：逻辑或，两个条件有一个为true时结果为true。例如：a || b&lt;/li&gt;
&lt;li&gt;&lt;code&gt;!&lt;/code&gt; ：逻辑非，取反。例如：!a&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;&lt;strong&gt;示例：&lt;/strong&gt;&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;boolean x = true, y = false;
System.out.println(x &amp;amp;&amp;amp; y); // false
System.out.println(x || y); // true
System.out.println(!x);     // false
&lt;/code&gt;&lt;/pre&gt;
&lt;h3&gt;5. 位运算符&lt;/h3&gt;
&lt;ul&gt;
&lt;li&gt;&lt;code&gt;&amp;amp;&lt;/code&gt; ：按位与。例如：a &amp;amp; b&lt;/li&gt;
&lt;li&gt;&lt;code&gt;|&lt;/code&gt; ：按位或。例如：a | b&lt;/li&gt;
&lt;li&gt;&lt;code&gt;^&lt;/code&gt; ：按位异或。例如：a ^ b&lt;/li&gt;
&lt;li&gt;&lt;code&gt;~&lt;/code&gt; ：按位取反。例如：~a&lt;/li&gt;
&lt;li&gt;&lt;code&gt;&amp;lt;&amp;lt;&lt;/code&gt; ：左移。例如：a &amp;lt;&amp;lt; n&lt;/li&gt;
&lt;li&gt;&lt;code&gt;&amp;gt;&amp;gt;&lt;/code&gt; ：右移。例如：a &amp;gt;&amp;gt; n&lt;/li&gt;
&lt;li&gt;&lt;code&gt;&amp;gt;&amp;gt;&amp;gt;&lt;/code&gt; ：无符号右移。例如：a &amp;gt;&amp;gt;&amp;gt; n&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;&lt;strong&gt;示例：&lt;/strong&gt;&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;int a = 6, b = 3; // 6: 110, 3: 011
System.out.println(a &amp;amp; b);  // 2  (010)
System.out.println(a | b);  // 7  (111)
System.out.println(a ^ b);  // 5  (101)
System.out.println(~a);     // -7
System.out.println(a &amp;lt;&amp;lt; 1); // 12
System.out.println(a &amp;gt;&amp;gt; 1); // 3
System.out.println(a &amp;gt;&amp;gt;&amp;gt; 1);// 3
&lt;/code&gt;&lt;/pre&gt;
&lt;h3&gt;6. 条件（三元）运算符&lt;/h3&gt;
&lt;ul&gt;
&lt;li&gt;&lt;code&gt;?:&lt;/code&gt; ：条件运算符，格式为 条件 ? 值1 : 值2。例如：a &amp;gt; b ? x : y&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;&lt;strong&gt;示例：&lt;/strong&gt;&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;int a = 10, b = 20;
int max = (a &amp;gt; b) ? a : b; // max = 20
System.out.println(max);
&lt;/code&gt;&lt;/pre&gt;
&lt;h3&gt;7. 字符串连接运算符&lt;/h3&gt;
&lt;ul&gt;
&lt;li&gt;&lt;code&gt;+&lt;/code&gt; ：字符串拼接，只要有一个操作数是字符串，+ 就会拼接。例如：&quot;Hello&quot; + name&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;&lt;strong&gt;示例：&lt;/strong&gt;&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;String name = &quot;Java&quot;;
System.out.println(&quot;Hello, &quot; + name); // Hello, Java
System.out.println(&quot;结果：&quot; + 1 + 2); // 结果：12
System.out.println(1 + 2 + &quot;结果&quot;); // 3结果
&lt;/code&gt;&lt;/pre&gt;
&lt;h3&gt;8. instanceof 运算符&lt;/h3&gt;
&lt;ul&gt;
&lt;li&gt;&lt;code&gt;instanceof&lt;/code&gt; ：类型判断，判断对象是否为某个类的实例。例如：obj instanceof String&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;&lt;strong&gt;示例：&lt;/strong&gt;&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;Object obj = &quot;abc&quot;;
System.out.println(obj instanceof String); // true
System.out.println(obj instanceof Integer); // false
&lt;/code&gt;&lt;/pre&gt;
&lt;blockquote&gt;
&lt;p&gt;如需详细举例或对某类运算符深入讲解，可随时补充！&lt;/p&gt;
&lt;/blockquote&gt;
&lt;h2&gt;8. Java字符串运算规则&lt;/h2&gt;
&lt;ul&gt;
&lt;li&gt;
&lt;p&gt;&lt;strong&gt;字符串拼接&lt;/strong&gt;&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;使用 + 运算符可以将两个字符串拼接为一个新字符串。&lt;/li&gt;
&lt;li&gt;只要有一个操作数是字符串，+ 运算符就会把另一个操作数转换为字符串后再拼接。&lt;/li&gt;
&lt;li&gt;示例：&lt;pre&gt;&lt;code&gt;String s1 = &quot;Hello&quot; + &quot;World&quot;; // 结果：HelloWorld
String s2 = &quot;Java&quot; + 2025;      // 结果：Java2025
String s3 = 1 + 2 + &quot;abc&quot;;      // 结果：3abc（先算1+2，再拼接）
String s4 = &quot;abc&quot; + 1 + 2;      // 结果：abc12（从左到右依次拼接）
&lt;/code&gt;&lt;/pre&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;&lt;strong&gt;与其他类型的运算&lt;/strong&gt;&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;字符串与数字、字符、布尔等类型用 + 运算时，非字符串类型会自动转换为字符串。&lt;/li&gt;
&lt;li&gt;示例：&lt;pre&gt;&lt;code&gt;String s = &quot;结果：&quot; + true; // 结果：结果：true
&lt;/code&gt;&lt;/pre&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;&lt;strong&gt;不可变性&lt;/strong&gt;&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;字符串在Java中是不可变对象，每次拼接都会生成新的字符串对象。&lt;/li&gt;
&lt;li&gt;多次拼接建议使用StringBuilder/StringBuffer提高效率。&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;&lt;strong&gt;常见注意事项&lt;/strong&gt;&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;字符串比较内容要用 equals() 方法，不能用 ==。&lt;/li&gt;
&lt;li&gt;字符串拼接优先级低于算术运算，表达式中注意括号使用。&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;blockquote&gt;
&lt;p&gt;字符串运算是Java开发中最常见的操作之一，理解其规则有助于避免常见错误和提升性能。&lt;/p&gt;
&lt;/blockquote&gt;
&lt;h2&gt;9. 原码、反码与补码&lt;/h2&gt;
&lt;p&gt;在Java等计算机系统中，整数的底层存储采用二进制，负数的表示依赖于补码。理解原码、反码、补码有助于掌握位运算、溢出等底层原理。&lt;/p&gt;
&lt;h3&gt;1. 原码&lt;/h3&gt;
&lt;ul&gt;
&lt;li&gt;原码是最直接的二进制表示，最高位为符号位（0正1负），其余为数值部分。&lt;/li&gt;
&lt;li&gt;例如：
&lt;ul&gt;
&lt;li&gt;+7 的原码：0000 0111&lt;/li&gt;
&lt;li&gt;-7 的原码：1000 0111&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;h3&gt;2. 反码&lt;/h3&gt;
&lt;ul&gt;
&lt;li&gt;正数的反码与原码相同。&lt;/li&gt;
&lt;li&gt;负数的反码：符号位不变，数值部分按位取反（0变1，1变0）。&lt;/li&gt;
&lt;li&gt;例如：
&lt;ul&gt;
&lt;li&gt;+7 的反码：0000 0111&lt;/li&gt;
&lt;li&gt;-7 的反码：1111 1000&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;h3&gt;3. 补码&lt;/h3&gt;
&lt;ul&gt;
&lt;li&gt;正数的补码与原码相同。&lt;/li&gt;
&lt;li&gt;负数的补码：反码加1。&lt;/li&gt;
&lt;li&gt;例如：
&lt;ul&gt;
&lt;li&gt;+7 的补码：0000 0111&lt;/li&gt;
&lt;li&gt;-7 的补码：1111 1001&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;h3&gt;4. 转换规则总结&lt;/h3&gt;
&lt;ul&gt;
&lt;li&gt;正数：原码 = 反码 = 补码&lt;/li&gt;
&lt;li&gt;负数：补码 = 反码 + 1&lt;/li&gt;
&lt;li&gt;反码 = 补码 - 1&lt;/li&gt;
&lt;/ul&gt;
&lt;h3&gt;5. Java中的意义&lt;/h3&gt;
&lt;ul&gt;
&lt;li&gt;Java中的int、byte等整数类型，底层存储和运算都采用补码。&lt;/li&gt;
&lt;li&gt;补码的好处：加减法统一、便于硬件实现、只有一个零。&lt;/li&gt;
&lt;li&gt;位运算、溢出、负数右移等现象都与补码密切相关。&lt;/li&gt;
&lt;/ul&gt;
&lt;h3&gt;6. 示例&lt;/h3&gt;
&lt;pre&gt;&lt;code&gt;int a = 7;      // 二进制：0000 0111
int b = -7;     // 二进制（补码）：1111 1001
System.out.println(Integer.toBinaryString(a)); // 111
System.out.println(Integer.toBinaryString(b)); // 11111111111111111111111111111001
&lt;/code&gt;&lt;/pre&gt;
&lt;blockquote&gt;
&lt;p&gt;理解补码机制有助于深入掌握Java底层运算和调试技巧。&lt;/p&gt;
&lt;/blockquote&gt;
&lt;h2&gt;10. Java三大程序结构&lt;/h2&gt;
&lt;h3&gt;10.1 顺序结构&lt;/h3&gt;
&lt;ul&gt;
&lt;li&gt;程序从上到下依次执行每一条语句，中间没有任何判断和跳转。&lt;/li&gt;
&lt;li&gt;是最简单、最常见的结构。&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;&lt;strong&gt;示例：&lt;/strong&gt;&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;int a = 10;
int b = 20;
int sum = a + b;
System.out.println(&quot;和为：&quot; + sum);
&lt;/code&gt;&lt;/pre&gt;
&lt;h3&gt;10.2 分支结构（选择结构）&lt;/h3&gt;
&lt;ul&gt;
&lt;li&gt;根据条件判断，决定执行哪一部分代码。&lt;/li&gt;
&lt;li&gt;常用的分支语句有：if、if-else、if-else if-else、switch。&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;&lt;strong&gt;if语句示例：&lt;/strong&gt;&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;int score = 85;
if (score &amp;gt;= 60) {
    System.out.println(&quot;及格&quot;);
} else {
    System.out.println(&quot;不及格&quot;);
}
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;&lt;strong&gt;switch语句示例：&lt;/strong&gt;&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;int day = 3;
switch (day) {
    case 1:
        System.out.println(&quot;星期一&quot;);
        break;
    case 2:
        System.out.println(&quot;星期二&quot;);
        break;
    case 3:
        System.out.println(&quot;星期三&quot;);
        // break;
    default:
        System.out.println(&quot;其他&quot;);
}
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;&lt;strong&gt;case穿透（fall through）说明：&lt;/strong&gt;&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;如果某个case分支没有写break，程序会继续执行后续case或default中的代码，直到遇到break或switch结束。&lt;/li&gt;
&lt;li&gt;这称为&quot;case穿透&quot;或&quot;fall through&quot;。&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;&lt;strong&gt;穿透示例：&lt;/strong&gt;&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;int num = 2;
switch (num) {
    case 1:
        System.out.println(&quot;A&quot;);
    case 2:
        System.out.println(&quot;B&quot;);
    case 3:
        System.out.println(&quot;C&quot;);
    default:
        System.out.println(&quot;D&quot;);
}
// 输出：B
//      C
//      D
&lt;/code&gt;&lt;/pre&gt;
&lt;blockquote&gt;
&lt;p&gt;建议每个case后都加break，除非有意为之。&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;&lt;strong&gt;case -&amp;gt; 新写法（Java 14+）：&lt;/strong&gt;&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;从Java 14开始，switch语句支持&quot;case -&amp;gt;&quot;箭头写法，简化代码且不允许穿透。&lt;/li&gt;
&lt;li&gt;每个case只能执行对应的代码块，自动break，无需手动添加。&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;&lt;strong&gt;示例：&lt;/strong&gt;&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;int day = 2;
switch (day) {
    case 1 -&amp;gt; System.out.println(&quot;星期一&quot;);
    case 2 -&amp;gt; System.out.println(&quot;星期二&quot;);
    case 3, 4, 5 -&amp;gt; System.out.println(&quot;工作日&quot;);
    default -&amp;gt; System.out.println(&quot;周末或非法&quot;);
}
&lt;/code&gt;&lt;/pre&gt;
&lt;ul&gt;
&lt;li&gt;也可以用于赋值：&lt;/li&gt;
&lt;/ul&gt;
&lt;pre&gt;&lt;code&gt;String result = switch (day) {
    case 1 -&amp;gt; &quot;星期一&quot;;
    case 2 -&amp;gt; &quot;星期二&quot;;
    default -&amp;gt; &quot;其他&quot;;
};
System.out.println(result);
&lt;/code&gt;&lt;/pre&gt;
&lt;blockquote&gt;
&lt;p&gt;箭头写法更简洁、安全，推荐在支持的JDK版本中使用。&lt;/p&gt;
&lt;/blockquote&gt;
&lt;h3&gt;10.3 循环结构&lt;/h3&gt;
&lt;ul&gt;
&lt;li&gt;根据条件反复执行某段代码。&lt;/li&gt;
&lt;li&gt;常用的循环语句有：for、while、do-while。&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;&lt;strong&gt;for循环示例：&lt;/strong&gt;&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;for (int i = 1; i &amp;lt;= 5; i++) {
    System.out.println(&quot;第&quot; + i + &quot;次循环&quot;);
}
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;&lt;strong&gt;for循环运用场景：&lt;/strong&gt;&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;适用于已知循环次数的场景，如遍历数组、批量处理、计数循环等。&lt;/li&gt;
&lt;li&gt;例如：遍历数组、打印1~100、批量初始化对象等。&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;&lt;strong&gt;while循环示例：&lt;/strong&gt;&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;int i = 1;
while (i &amp;lt;= 5) {
    System.out.println(&quot;第&quot; + i + &quot;次循环&quot;);
    i++;
}
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;&lt;strong&gt;while循环运用场景：&lt;/strong&gt;&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;适用于循环次数不确定、依赖条件判断的场景，如用户输入、等待某个条件达成、读取文件等。&lt;/li&gt;
&lt;li&gt;例如：用户输入密码直到正确、读取文件直到结尾、网络连接重试等。&lt;/li&gt;
&lt;/ul&gt;
&lt;blockquote&gt;
&lt;p&gt;总结：for循环更适合计数型、范围型循环，while循环更适合条件型、事件驱动型循环。合理选择可提升代码可读性和健壮性。&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;&lt;strong&gt;do-while循环示例：&lt;/strong&gt;&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;int i = 1;
do {
    System.out.println(&quot;第&quot; + i + &quot;次循环&quot;);
    i++;
} while (i &amp;lt;= 5);
&lt;/code&gt;&lt;/pre&gt;
&lt;blockquote&gt;
&lt;p&gt;顺序、分支、循环结构是所有程序的基础，合理组合可实现各种复杂逻辑。&lt;/p&gt;
&lt;/blockquote&gt;
&lt;h2&gt;11. Java中的Random库&lt;/h2&gt;
&lt;p&gt;在Java中，&lt;code&gt;Random&lt;/code&gt;类用于生成伪随机数，位于&lt;code&gt;java.util&lt;/code&gt;包。&lt;/p&gt;
&lt;h3&gt;基本用法&lt;/h3&gt;
&lt;ol&gt;
&lt;li&gt;导入包：&lt;/li&gt;
&lt;/ol&gt;
&lt;pre&gt;&lt;code&gt;import java.util.Random;
&lt;/code&gt;&lt;/pre&gt;
&lt;ol&gt;
&lt;li&gt;创建Random对象：&lt;/li&gt;
&lt;/ol&gt;
&lt;pre&gt;&lt;code&gt;Random rand = new Random();
&lt;/code&gt;&lt;/pre&gt;
&lt;h3&gt;常用方法&lt;/h3&gt;
&lt;ul&gt;
&lt;li&gt;&lt;code&gt;nextInt()&lt;/code&gt;：生成一个int范围内的随机整数。&lt;/li&gt;
&lt;li&gt;&lt;code&gt;nextInt(n)&lt;/code&gt;：生成[0, n)范围内的随机整数。&lt;/li&gt;
&lt;li&gt;&lt;code&gt;nextDouble()&lt;/code&gt;：生成[0.0, 1.0)范围内的随机小数。&lt;/li&gt;
&lt;li&gt;&lt;code&gt;nextBoolean()&lt;/code&gt;：生成一个随机布尔值。&lt;/li&gt;
&lt;li&gt;&lt;code&gt;nextLong()&lt;/code&gt;：生成一个随机long值。&lt;/li&gt;
&lt;/ul&gt;
&lt;h3&gt;示例代码&lt;/h3&gt;
&lt;pre&gt;&lt;code&gt;import java.util.Random;

public class RandomDemo {
    public static void main(String[] args) {
        Random rand = new Random();
        int num = rand.nextInt(100); // 0~99的随机整数
        double d = rand.nextDouble(); // 0.0~1.0的随机小数
        boolean b = rand.nextBoolean(); // 随机布尔值
        System.out.println(&quot;随机整数：&quot; + num);
        System.out.println(&quot;随机小数：&quot; + d);
        System.out.println(&quot;随机布尔：&quot; + b);
    }
}
&lt;/code&gt;&lt;/pre&gt;
&lt;blockquote&gt;
&lt;p&gt;Random生成的随机数是伪随机数，种子相同则序列相同。Java 1.7+还可用&lt;code&gt;ThreadLocalRandom&lt;/code&gt;和&lt;code&gt;SecureRandom&lt;/code&gt;。&lt;/p&gt;
&lt;/blockquote&gt;
&lt;h2&gt;12. Java中的next命名规范详细解释&lt;/h2&gt;
&lt;p&gt;在Java标准库和第三方库中，许多方法以&lt;code&gt;next&lt;/code&gt;开头，这是一种广泛采用的命名规范，体现了&quot;获取序列中的下一个元素&quot;或&quot;生成下一个值&quot;的设计思想。&lt;/p&gt;
&lt;h3&gt;1. 设计思想&lt;/h3&gt;
&lt;ul&gt;
&lt;li&gt;&lt;code&gt;next&lt;/code&gt;强调&quot;顺序获取&quot;，通常用于遍历、生成、读取等需要依次处理数据的场景。&lt;/li&gt;
&lt;li&gt;以&lt;code&gt;nextXxx()&lt;/code&gt;命名的方法，往往每调用一次就返回序列中的下一个元素或值。&lt;/li&gt;
&lt;li&gt;这种命名方式让API的用途一目了然，易于理解和记忆。&lt;/li&gt;
&lt;/ul&gt;
&lt;h3&gt;2. 常见场景&lt;/h3&gt;
&lt;ul&gt;
&lt;li&gt;&lt;strong&gt;迭代器（Iterator）&lt;/strong&gt;：
&lt;ul&gt;
&lt;li&gt;&lt;code&gt;hasNext()&lt;/code&gt;判断是否还有下一个元素，&lt;code&gt;next()&lt;/code&gt;获取下一个元素。&lt;/li&gt;
&lt;li&gt;典型用法：&lt;pre&gt;&lt;code&gt;Iterator&amp;lt;String&amp;gt; it = list.iterator();
while (it.hasNext()) {
    String s = it.next();
    // 处理s
}
&lt;/code&gt;&lt;/pre&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;输入读取（Scanner）&lt;/strong&gt;：
&lt;ul&gt;
&lt;li&gt;&lt;code&gt;next()&lt;/code&gt;读取下一个以空白分隔的字符串。&lt;/li&gt;
&lt;li&gt;&lt;code&gt;nextInt()&lt;/code&gt;、&lt;code&gt;nextDouble()&lt;/code&gt;等读取下一个指定类型的输入。&lt;/li&gt;
&lt;li&gt;典型用法：&lt;pre&gt;&lt;code&gt;Scanner sc = new Scanner(System.in);
int num = sc.nextInt();
String word = sc.next();
&lt;/code&gt;&lt;/pre&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;随机数生成（Random）&lt;/strong&gt;：
&lt;ul&gt;
&lt;li&gt;&lt;code&gt;nextInt()&lt;/code&gt;、&lt;code&gt;nextDouble()&lt;/code&gt;等每次生成一个新的随机值。&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;枚举、流、生成器等&lt;/strong&gt;：
&lt;ul&gt;
&lt;li&gt;只要是&quot;顺序获取&quot;或&quot;生成下一个&quot;，都常用next命名。&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;h3&gt;3. 与hasNext的配合&lt;/h3&gt;
&lt;ul&gt;
&lt;li&gt;在迭代器、流等场景，通常有&lt;code&gt;hasNext()&lt;/code&gt;方法判断是否还有下一个元素，配合&lt;code&gt;next()&lt;/code&gt;安全遍历。&lt;/li&gt;
&lt;li&gt;这样可以避免越界异常（如NoSuchElementException）。&lt;/li&gt;
&lt;/ul&gt;
&lt;h3&gt;4. 易混淆点&lt;/h3&gt;
&lt;ul&gt;
&lt;li&gt;&lt;code&gt;next()&lt;/code&gt;和&lt;code&gt;nextLine()&lt;/code&gt;：Scanner的&lt;code&gt;next()&lt;/code&gt;读取一个单词，&lt;code&gt;nextLine()&lt;/code&gt;读取一整行。&lt;/li&gt;
&lt;li&gt;&lt;code&gt;next()&lt;/code&gt;通常不做类型转换，&lt;code&gt;nextInt()&lt;/code&gt;等会尝试将输入转换为指定类型。&lt;/li&gt;
&lt;/ul&gt;
&lt;h3&gt;5. 实际开发建议&lt;/h3&gt;
&lt;ul&gt;
&lt;li&gt;使用&lt;code&gt;nextXxx()&lt;/code&gt;方法时，建议先用&lt;code&gt;hasNextXxx()&lt;/code&gt;判断是否有下一个元素或输入，避免异常。&lt;/li&gt;
&lt;li&gt;理解&quot;next&quot;语义有助于快速掌握Java集合、输入、生成器等API的用法。&lt;/li&gt;
&lt;/ul&gt;
&lt;blockquote&gt;
&lt;p&gt;总结：以next开头的方法体现了Java对顺序处理、流式操作的高度抽象，是高效、可读代码的重要基础。&lt;/p&gt;
&lt;/blockquote&gt;
&lt;h2&gt;13. Java数组基础&lt;/h2&gt;
&lt;h3&gt;13.1 数组的概念&lt;/h3&gt;
&lt;ul&gt;
&lt;li&gt;数组是存储同一类型数据的有序集合，长度固定。&lt;/li&gt;
&lt;li&gt;每个元素通过下标（索引）访问，下标从0开始。&lt;/li&gt;
&lt;/ul&gt;
&lt;h3&gt;13.2 数组的声明与初始化&lt;/h3&gt;
&lt;ul&gt;
&lt;li&gt;声明数组：&lt;pre&gt;&lt;code&gt;int[] arr;
String[] names;
&lt;/code&gt;&lt;/pre&gt;
&lt;/li&gt;
&lt;li&gt;分配空间并初始化：&lt;pre&gt;&lt;code&gt;arr = new int[5]; // 长度为5，默认值为0
String[] cities = new String[3]; // 长度为3，默认值为null
&lt;/code&gt;&lt;/pre&gt;
&lt;/li&gt;
&lt;li&gt;声明+初始化（静态初始化）：&lt;pre&gt;&lt;code&gt;int[] nums = {1, 2, 3, 4, 5};
String[] colors = {&quot;red&quot;, &quot;green&quot;, &quot;blue&quot;};
&lt;/code&gt;&lt;/pre&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;h3&gt;13.3 访问和修改数组元素&lt;/h3&gt;
&lt;pre&gt;&lt;code&gt;int[] arr = {10, 20, 30};
System.out.println(arr[0]); // 10
arr[1] = 99;
System.out.println(arr[1]); // 99
&lt;/code&gt;&lt;/pre&gt;
&lt;h3&gt;13.4 数组的遍历&lt;/h3&gt;
&lt;ul&gt;
&lt;li&gt;for循环遍历：&lt;pre&gt;&lt;code&gt;for (int i = 0; i &amp;lt; arr.length; i++) {
    System.out.println(arr[i]);
}
&lt;/code&gt;&lt;/pre&gt;
&lt;/li&gt;
&lt;li&gt;增强for循环（for-each）：&lt;pre&gt;&lt;code&gt;for (int num : arr) {
    System.out.println(num);
}
&lt;/code&gt;&lt;/pre&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;h3&gt;13.5 数组的常用属性和特性&lt;/h3&gt;
&lt;ul&gt;
&lt;li&gt;&lt;code&gt;length&lt;/code&gt;：数组长度属性，如arr.length。&lt;/li&gt;
&lt;li&gt;数组一旦创建，长度不可变。&lt;/li&gt;
&lt;li&gt;支持多维数组（如int[][] matrix = new int[3][4];）。&lt;/li&gt;
&lt;li&gt;数组元素类型可以是基本类型或引用类型。&lt;/li&gt;
&lt;/ul&gt;
&lt;h3&gt;13.6 示例：求数组元素之和&lt;/h3&gt;
&lt;pre&gt;&lt;code&gt;int[] nums = {1, 2, 3, 4, 5};
int sum = 0;
for (int n : nums) {
    sum += n;
}
System.out.println(&quot;总和：&quot; + sum);
&lt;/code&gt;&lt;/pre&gt;
&lt;h3&gt;13.7 直接输出数组与地址格式说明&lt;/h3&gt;
&lt;ul&gt;
&lt;li&gt;直接输出数组对象（如System.out.println(arr);）时，显示的不是数组内容，而是数组的类型和哈希码地址。&lt;/li&gt;
&lt;li&gt;格式通常为：[类型标识@哈希码]，如：[I@6d06d69c
&lt;ul&gt;
&lt;li&gt;[I 表示int[]类型，@后为哈希码的十六进制。&lt;/li&gt;
&lt;li&gt;其他类型如String[]会显示为 [Ljava.lang.String;@xxxxxx&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;&lt;strong&gt;示例：&lt;/strong&gt;&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;int[] arr = {1, 2, 3};
System.out.println(arr); // 输出：[I@6d06d69c（示例）
&lt;/code&gt;&lt;/pre&gt;
&lt;ul&gt;
&lt;li&gt;如果要输出数组内容，需用循环或工具类：
&lt;ul&gt;
&lt;li&gt;for循环/增强for循环遍历输出&lt;/li&gt;
&lt;li&gt;使用Arrays.toString(arr)&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;&lt;strong&gt;示例：&lt;/strong&gt;&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;import java.util.Arrays;
int[] arr = {1, 2, 3};
System.out.println(Arrays.toString(arr)); // 输出：[1, 2, 3]
&lt;/code&gt;&lt;/pre&gt;
&lt;blockquote&gt;
&lt;p&gt;直接输出数组变量看到的是&quot;地址信息&quot;，不是数组元素本身。建议用Arrays.toString()等方法查看内容。&lt;/p&gt;
&lt;/blockquote&gt;
&lt;blockquote&gt;
&lt;p&gt;数组是Java中最基础的数据结构，后续可学习ArrayList等集合类实现更灵活的数据管理。&lt;/p&gt;
&lt;/blockquote&gt;
&lt;h3&gt;13.8 动态初始化时的默认值&lt;/h3&gt;
&lt;ul&gt;
&lt;li&gt;使用&lt;code&gt;new&lt;/code&gt;关键字动态初始化数组时，数组中的每个元素都会被自动赋予默认值。&lt;/li&gt;
&lt;li&gt;不同类型的数组默认值如下：
&lt;ul&gt;
&lt;li&gt;整型（int、byte、short、long）：默认值为0&lt;/li&gt;
&lt;li&gt;浮点型（float、double）：默认值为0.0&lt;/li&gt;
&lt;li&gt;字符型（char）：默认值为&apos;\u0000&apos;（空字符）&lt;/li&gt;
&lt;li&gt;布尔型（boolean）：默认值为false&lt;/li&gt;
&lt;li&gt;引用类型（如String、对象）：默认值为null&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;&lt;strong&gt;示例：&lt;/strong&gt;&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;int[] arr = new int[3];
System.out.println(Arrays.toString(arr)); // [0, 0, 0]

boolean[] flags = new boolean[2];
System.out.println(Arrays.toString(flags)); // [false, false]

String[] names = new String[2];
System.out.println(Arrays.toString(names)); // [null, null]
&lt;/code&gt;&lt;/pre&gt;
&lt;blockquote&gt;
&lt;p&gt;动态初始化时，数组元素的默认值由类型决定，无需手动赋值。&lt;/p&gt;
&lt;/blockquote&gt;
&lt;h2&gt;14. Java内存分布&lt;/h2&gt;
&lt;p&gt;Java程序运行时，JVM会将内存划分为不同的区域，每个区域负责不同类型的数据存储和管理。&lt;/p&gt;
&lt;h3&gt;1. 方法区（Method Area）&lt;/h3&gt;
&lt;ul&gt;
&lt;li&gt;存储类的结构信息（如类的元数据、静态变量、常量、运行时常量池等）。&lt;/li&gt;
&lt;li&gt;所有线程共享。&lt;/li&gt;
&lt;/ul&gt;
&lt;h3&gt;2. 堆（Heap）&lt;/h3&gt;
&lt;ul&gt;
&lt;li&gt;存储所有对象实例和数组。&lt;/li&gt;
&lt;li&gt;由垃圾回收器统一管理。&lt;/li&gt;
&lt;li&gt;所有线程共享。&lt;/li&gt;
&lt;/ul&gt;
&lt;h3&gt;3. 虚拟机栈（Stack）&lt;/h3&gt;
&lt;ul&gt;
&lt;li&gt;每个线程独有，存储方法调用时的局部变量、操作数栈、方法返回地址等。&lt;/li&gt;
&lt;li&gt;局部变量包括基本类型、对象引用等。&lt;/li&gt;
&lt;/ul&gt;
&lt;h3&gt;4. 本地方法栈（Native Method Stack）&lt;/h3&gt;
&lt;ul&gt;
&lt;li&gt;为JVM调用本地（Native）方法服务。&lt;/li&gt;
&lt;li&gt;每个线程独有。&lt;/li&gt;
&lt;/ul&gt;
&lt;h3&gt;5. 程序计数器（Program Counter Register）&lt;/h3&gt;
&lt;ul&gt;
&lt;li&gt;记录当前线程所执行字节码的行号指示器。&lt;/li&gt;
&lt;li&gt;每个线程独有。&lt;/li&gt;
&lt;/ul&gt;
&lt;h3&gt;6. 内存分布结构图&lt;/h3&gt;
&lt;pre&gt;&lt;code&gt;graph TD
  A[方法区 Method Area]
  B[堆 Heap]
  C[虚拟机栈 Stack]
  D[本地方法栈 Native Method Stack]
  E[程序计数器 PC Register]
  subgraph 线程1
    C
    D
    E
  end
  subgraph 线程2
    C2[虚拟机栈 Stack]
    D2[本地方法栈 Native Method Stack]
    E2[程序计数器 PC Register]
  end
  A --&amp;gt;|共享| B
  B --&amp;gt;|共享| C
  B --&amp;gt;|共享| D
  B --&amp;gt;|共享| E
&lt;/code&gt;&lt;/pre&gt;
&lt;h3&gt;7. 总结&lt;/h3&gt;
&lt;ul&gt;
&lt;li&gt;堆和方法区是所有线程共享的，栈、本地方法栈、程序计数器是线程私有的。&lt;/li&gt;
&lt;li&gt;对象实例存储在堆，局部变量存储在栈。&lt;/li&gt;
&lt;li&gt;合理理解内存分布有助于掌握对象生命周期、垃圾回收、线程安全等核心知识。&lt;/li&gt;
&lt;/ul&gt;
&lt;h2&gt;15. Java方法基础&lt;/h2&gt;
&lt;h3&gt;1. 方法的概念&lt;/h3&gt;
&lt;ul&gt;
&lt;li&gt;方法（Method）是完成特定功能的代码块，可以重复调用。&lt;/li&gt;
&lt;li&gt;方法有名称、参数列表、返回值类型和方法体。&lt;/li&gt;
&lt;li&gt;方法有助于代码复用、结构清晰和模块化。&lt;/li&gt;
&lt;/ul&gt;
&lt;h3&gt;2. 方法的定义格式&lt;/h3&gt;
&lt;pre&gt;&lt;code&gt;修饰符 返回值类型 方法名(参数列表) {
    // 方法体
    // 可选：return 返回值;
}
&lt;/code&gt;&lt;/pre&gt;
&lt;ul&gt;
&lt;li&gt;常见修饰符：public、private、static、final、abstract等。&lt;/li&gt;
&lt;li&gt;返回值类型：可以是基本类型、引用类型或void（无返回值）。&lt;/li&gt;
&lt;li&gt;参数列表：0个或多个参数，类型+名称，用逗号分隔。&lt;/li&gt;
&lt;/ul&gt;
&lt;h3&gt;3. 方法的调用&lt;/h3&gt;
&lt;ul&gt;
&lt;li&gt;语法：方法名(实参列表);&lt;/li&gt;
&lt;li&gt;静态方法可用类名调用：ClassName.methodName();&lt;/li&gt;
&lt;li&gt;非静态方法需通过对象调用：obj.methodName();&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;&lt;strong&gt;示例：&lt;/strong&gt;&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;public static int add(int a, int b) {
    return a + b;
}

public void sayHello(String name) {
    System.out.println(&quot;Hello, &quot; + name);
}

// 调用
int sum = add(3, 5); // 静态方法
MyClass obj = new MyClass();
obj.sayHello(&quot;Tom&quot;); // 实例方法
&lt;/code&gt;&lt;/pre&gt;
&lt;h3&gt;4. 方法的重载（Overload）&lt;/h3&gt;
&lt;ul&gt;
&lt;li&gt;同一个类中，方法名相同但参数列表不同（类型、个数、顺序不同），构成方法重载。&lt;/li&gt;
&lt;li&gt;返回值类型不同不能单独构成重载。&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;&lt;strong&gt;示例：&lt;/strong&gt;&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;public int max(int a, int b) {
    return a &amp;gt; b ? a : b;
}
public double max(double a, double b) {
    return a &amp;gt; b ? a : b;
}
&lt;/code&gt;&lt;/pre&gt;
&lt;h3&gt;5. 常见修饰符说明&lt;/h3&gt;
&lt;ul&gt;
&lt;li&gt;&lt;code&gt;public&lt;/code&gt;：公有方法，任何类都可访问。&lt;/li&gt;
&lt;li&gt;&lt;code&gt;private&lt;/code&gt;：私有方法，仅本类可访问。&lt;/li&gt;
&lt;li&gt;&lt;code&gt;static&lt;/code&gt;：静态方法，属于类本身。&lt;/li&gt;
&lt;li&gt;&lt;code&gt;final&lt;/code&gt;：最终方法，不能被子类重写。&lt;/li&gt;
&lt;li&gt;&lt;code&gt;abstract&lt;/code&gt;：抽象方法，无方法体，需子类实现。&lt;/li&gt;
&lt;/ul&gt;
&lt;h3&gt;6. void方法和return&lt;/h3&gt;
&lt;ul&gt;
&lt;li&gt;void方法无返回值，可用return;提前结束方法。&lt;/li&gt;
&lt;li&gt;有返回值方法必须用return返回对应类型的值。&lt;/li&gt;
&lt;/ul&gt;
&lt;h3&gt;7. 参数传递&lt;/h3&gt;
&lt;ul&gt;
&lt;li&gt;基本类型参数：值传递，方法内修改不影响外部变量。&lt;/li&gt;
&lt;li&gt;引用类型参数：传递对象引用，方法内可修改对象内容。&lt;/li&gt;
&lt;/ul&gt;
&lt;h3&gt;8. 方法参数传递机制详解&lt;/h3&gt;
&lt;p&gt;Java方法参数传递采用&quot;值传递&quot;机制，但根据参数类型（基本类型或引用类型），在堆和栈上的表现和影响不同。&lt;/p&gt;
&lt;h4&gt;1. 基本类型参数（int、double、char等）&lt;/h4&gt;
&lt;ul&gt;
&lt;li&gt;传递的是变量的&quot;值&quot;副本。&lt;/li&gt;
&lt;li&gt;方法内对参数的修改不会影响原变量。&lt;/li&gt;
&lt;li&gt;存储在栈内存中。&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;&lt;strong&gt;示例：&lt;/strong&gt;&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;public static void change(int x) {
    x = 100;
}
int a = 10;
change(a);
System.out.println(a); // 输出10，a未被改变
&lt;/code&gt;&lt;/pre&gt;
&lt;h4&gt;2. 引用类型参数（数组、对象等）&lt;/h4&gt;
&lt;ul&gt;
&lt;li&gt;传递的是对象引用的&quot;值&quot;副本（即对象在堆中的地址）。&lt;/li&gt;
&lt;li&gt;方法内通过引用可以修改堆中对象的内容，影响原对象。&lt;/li&gt;
&lt;li&gt;但如果在方法内让引用指向新对象，不会影响原对象。&lt;/li&gt;
&lt;li&gt;引用本身在栈内存，对象内容在堆内存。&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;&lt;strong&gt;示例1：修改对象内容&lt;/strong&gt;&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;public static void modify(int[] arr) {
    arr[0] = 99;
}
int[] nums = {1, 2, 3};
modify(nums);
System.out.println(nums[0]); // 输出99，原数组被修改
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;&lt;strong&gt;示例2：引用指向新对象&lt;/strong&gt;&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;public static void reassign(int[] arr) {
    arr = new int[]{7, 8, 9};
}
int[] nums = {1, 2, 3};
reassign(nums);
System.out.println(nums[0]); // 仍输出1，原数组未被替换
&lt;/code&gt;&lt;/pre&gt;
&lt;h4&gt;3. 内存分布简图&lt;/h4&gt;
&lt;ul&gt;
&lt;li&gt;基本类型参数：
&lt;ul&gt;
&lt;li&gt;变量和参数都在栈，互不影响。&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;引用类型参数：
&lt;ul&gt;
&lt;li&gt;引用变量在栈，实际对象在堆。&lt;/li&gt;
&lt;li&gt;传递的是&quot;引用的副本&quot;，可通过引用修改堆中对象内容。&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;blockquote&gt;
&lt;p&gt;总结：Java所有参数传递本质上都是值传递。基本类型传递值，引用类型传递引用的值（地址）。理解堆栈分布和引用机制，有助于避免参数修改的误区。&lt;/p&gt;
&lt;/blockquote&gt;
&lt;h2&gt;16. Java二维数组的创建与初始化&lt;/h2&gt;
&lt;h3&gt;1. 二维数组的声明&lt;/h3&gt;
&lt;ul&gt;
&lt;li&gt;语法：&lt;pre&gt;&lt;code&gt;int[][] matrix;
String[][] table;
&lt;/code&gt;&lt;/pre&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;h3&gt;2. 二维数组的创建&lt;/h3&gt;
&lt;ul&gt;
&lt;li&gt;指定行和列：&lt;pre&gt;&lt;code&gt;int[][] arr = new int[3][4]; // 3行4列，所有元素默认值为0
&lt;/code&gt;&lt;/pre&gt;
&lt;/li&gt;
&lt;li&gt;只指定行，列可后续单独分配（不规则数组）：&lt;pre&gt;&lt;code&gt;int[][] arr = new int[3][];
arr[0] = new int[2]; // 第一行2列
arr[1] = new int[4]; // 第二行4列
arr[2] = new int[3]; // 第三行3列
&lt;/code&gt;&lt;/pre&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;h3&gt;3. 二维数组的静态初始化&lt;/h3&gt;
&lt;ul&gt;
&lt;li&gt;声明+赋值：&lt;pre&gt;&lt;code&gt;int[][] nums = {
    {1, 2, 3},
    {4, 5, 6},
    {7, 8, 9}
};
&lt;/code&gt;&lt;/pre&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;h3&gt;4. 访问和遍历二维数组&lt;/h3&gt;
&lt;ul&gt;
&lt;li&gt;访问元素：&lt;code&gt;arr[i][j]&lt;/code&gt; 表示第i行第j列元素。&lt;/li&gt;
&lt;li&gt;遍历方式：&lt;pre&gt;&lt;code&gt;for (int i = 0; i &amp;lt; arr.length; i++) {
    for (int j = 0; j &amp;lt; arr[i].length; j++) {
        System.out.print(arr[i][j] + &quot; &quot;);
    }
    System.out.println();
}
&lt;/code&gt;&lt;/pre&gt;
&lt;/li&gt;
&lt;li&gt;增强for循环：&lt;pre&gt;&lt;code&gt;for (int[] row : arr) {
    for (int val : row) {
        System.out.print(val + &quot; &quot;);
    }
    System.out.println();
}
&lt;/code&gt;&lt;/pre&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;h3&gt;5. 特点说明&lt;/h3&gt;
&lt;ul&gt;
&lt;li&gt;Java二维数组本质上是&quot;数组的数组&quot;，每一行可以有不同的列数（不规则数组）。&lt;/li&gt;
&lt;li&gt;默认值规则与一维数组一致。&lt;/li&gt;
&lt;/ul&gt;
&lt;blockquote&gt;
&lt;p&gt;二维数组常用于矩阵、表格、棋盘等场景，是多维数据结构的基础。&lt;/p&gt;
&lt;/blockquote&gt;
&lt;h2&gt;17. Java面向对象编程（OOP）基础&lt;/h2&gt;
&lt;h3&gt;1. 类与对象&lt;/h3&gt;
&lt;ul&gt;
&lt;li&gt;&lt;strong&gt;类（Class）&lt;/strong&gt;：对象的模板或蓝图，定义属性和行为。&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;对象（Object）&lt;/strong&gt;：类的实例，实际存在的实体。&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;&lt;strong&gt;示例：&lt;/strong&gt;&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;class Person {
    String name;
    int age;
    void sayHello() {
        System.out.println(&quot;Hello, my name is &quot; + name);
    }
}
Person p = new Person();
p.name = &quot;Tom&quot;;
p.age = 20;
p.sayHello();
&lt;/code&gt;&lt;/pre&gt;
&lt;h3&gt;2. 封装（Encapsulation）&lt;/h3&gt;
&lt;ul&gt;
&lt;li&gt;将属性和方法封装在类内部，属性私有（private），通过公有方法（getter/setter）访问。&lt;/li&gt;
&lt;li&gt;提高安全性和可维护性。&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;&lt;strong&gt;示例：&lt;/strong&gt;&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;class Student {
    private int score;
    public int getScore() { return score; }
    public void setScore(int s) { score = s; }
}
&lt;/code&gt;&lt;/pre&gt;
&lt;h3&gt;3. 继承（Inheritance）&lt;/h3&gt;
&lt;ul&gt;
&lt;li&gt;子类通过&lt;code&gt;extends&lt;/code&gt;继承父类，获得父类的属性和方法。&lt;/li&gt;
&lt;li&gt;支持单继承。&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;&lt;strong&gt;示例：&lt;/strong&gt;&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;class Animal {
    void eat() { System.out.println(&quot;吃东西&quot;); }
}
class Dog extends Animal {
    void bark() { System.out.println(&quot;汪汪&quot;); }
}
Dog d = new Dog();
d.eat();
d.bark();
&lt;/code&gt;&lt;/pre&gt;
&lt;h3&gt;4. 多态（Polymorphism）&lt;/h3&gt;
&lt;ul&gt;
&lt;li&gt;父类引用指向子类对象，调用方法时表现出不同形态。&lt;/li&gt;
&lt;li&gt;体现为方法重写（Override）和接口实现。&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;&lt;strong&gt;示例：&lt;/strong&gt;&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;Animal a = new Dog();
a.eat(); // 调用Dog的eat方法（如果重写）
&lt;/code&gt;&lt;/pre&gt;
&lt;h3&gt;5. 构造方法（Constructor）&lt;/h3&gt;
&lt;ul&gt;
&lt;li&gt;与类名相同，无返回值，用于对象初始化。&lt;/li&gt;
&lt;li&gt;可重载多个构造方法。&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;&lt;strong&gt;示例：&lt;/strong&gt;&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;class Person {
    String name;
    Person() { name = &quot;无名&quot;; }
    Person(String n) { name = n; }
}
&lt;/code&gt;&lt;/pre&gt;
&lt;h3&gt;6. this与super关键字&lt;/h3&gt;
&lt;ul&gt;
&lt;li&gt;&lt;code&gt;this&lt;/code&gt;：当前对象的引用，区分成员变量和局部变量，调用本类方法/构造器。&lt;/li&gt;
&lt;li&gt;&lt;code&gt;super&lt;/code&gt;：父类对象的引用，调用父类属性、方法、构造器。&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;&lt;strong&gt;示例：&lt;/strong&gt;&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;class Parent {
    int x = 1;
}
class Child extends Parent {
    int x = 2;
    void print() {
        System.out.println(this.x); // 2
        System.out.println(super.x); // 1
    }
}
&lt;/code&gt;&lt;/pre&gt;
&lt;h3&gt;7. 方法重写（Override）与重载（Overload）&lt;/h3&gt;
&lt;ul&gt;
&lt;li&gt;&lt;strong&gt;重写&lt;/strong&gt;：子类重写父类方法，方法名、参数、返回值完全一致，需加@Override注解。&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;重载&lt;/strong&gt;：同类中方法名相同，参数列表不同。&lt;/li&gt;
&lt;/ul&gt;
&lt;h3&gt;8. final与abstract&lt;/h3&gt;
&lt;ul&gt;
&lt;li&gt;&lt;code&gt;final&lt;/code&gt;类不能被继承，final方法不能被重写，final变量为常量。&lt;/li&gt;
&lt;li&gt;&lt;code&gt;abstract&lt;/code&gt;类不能实例化，抽象方法无方法体，子类必须实现。&lt;/li&gt;
&lt;/ul&gt;
&lt;h3&gt;9. 接口（Interface）&lt;/h3&gt;
&lt;ul&gt;
&lt;li&gt;用&lt;code&gt;interface&lt;/code&gt;定义，所有方法默认public abstract，属性默认public static final。&lt;/li&gt;
&lt;li&gt;类用&lt;code&gt;implements&lt;/code&gt;实现接口，可多实现。&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;&lt;strong&gt;示例：&lt;/strong&gt;&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;interface USB {
    void connect();
}
class Mouse implements USB {
    public void connect() { System.out.println(&quot;鼠标连接&quot;); }
}
&lt;/code&gt;&lt;/pre&gt;
&lt;h3&gt;10. 静态成员（static）&lt;/h3&gt;
&lt;ul&gt;
&lt;li&gt;static变量/方法属于类本身，不依赖对象。&lt;/li&gt;
&lt;li&gt;可用类名直接访问。&lt;/li&gt;
&lt;/ul&gt;
&lt;h3&gt;11. 内部类&lt;/h3&gt;
&lt;ul&gt;
&lt;li&gt;类中定义的类，分为成员内部类、静态内部类、局部内部类、匿名内部类。&lt;/li&gt;
&lt;/ul&gt;
&lt;h3&gt;12. 对象的创建与内存分布&lt;/h3&gt;
&lt;ul&gt;
&lt;li&gt;new对象时，属性分配在堆，引用变量在栈。&lt;/li&gt;
&lt;li&gt;构造方法初始化对象。&lt;/li&gt;
&lt;/ul&gt;
&lt;h3&gt;13. 常见OOP原则&lt;/h3&gt;
&lt;ul&gt;
&lt;li&gt;单一职责、开闭原则、里氏替换、依赖倒置、接口隔离、迪米特法则等。&lt;/li&gt;
&lt;/ul&gt;
&lt;blockquote&gt;
&lt;p&gt;面向对象是Java的核心思想，掌握OOP有助于开发高内聚、低耦合、易维护的系统。&lt;/p&gt;
&lt;/blockquote&gt;
&lt;h3&gt;面向对象的内存分布说明&lt;/h3&gt;
&lt;ul&gt;
&lt;li&gt;&lt;strong&gt;栈内存（Stack）&lt;/strong&gt;：用于存储方法调用时的局部变量，包括对象引用变量（如Person p）。每个线程有自己的栈空间。&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;堆内存（Heap）&lt;/strong&gt;：用于存储所有new出来的对象实例和数组。对象的实际内容（属性等）都在堆中，所有线程共享。&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;方法区（Method Area）&lt;/strong&gt;：用于存储类的结构信息（如类的元数据、静态变量、常量、方法代码等），所有线程共享。&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;&lt;strong&gt;对象引用与内存分布关系：&lt;/strong&gt;&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;当你写&lt;code&gt;Person p = new Person();&lt;/code&gt;时：
&lt;ul&gt;
&lt;li&gt;&lt;code&gt;p&lt;/code&gt;变量在栈内存，保存的是堆中Person对象的地址（引用）。&lt;/li&gt;
&lt;li&gt;堆内存中存放Person对象的实际内容。&lt;/li&gt;
&lt;li&gt;类的结构信息（如方法、静态变量）在方法区。&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;多个引用变量可以在栈中分别保存同一个对象的地址，实现对象共享。&lt;/li&gt;
&lt;li&gt;对象的生命周期由是否有引用指向它决定，无引用时会被垃圾回收。&lt;/li&gt;
&lt;/ul&gt;
&lt;blockquote&gt;
&lt;p&gt;这种分布方式有助于实现对象的共享、封装和高效管理，是Java面向对象内存模型的基础。&lt;/p&gt;
&lt;/blockquote&gt;
&lt;h3&gt;Java对象与引用的内存分布详解&lt;/h3&gt;
&lt;h4&gt;1. 一个对象引用的情况&lt;/h4&gt;
&lt;ul&gt;
&lt;li&gt;当你写 &lt;code&gt;Person p = new Person();&lt;/code&gt; 时，发生了什么？
&lt;ol&gt;
&lt;li&gt;&lt;strong&gt;栈内存&lt;/strong&gt;：变量&lt;code&gt;p&lt;/code&gt;在栈上分配空间，存储的是一个&quot;地址&quot;。&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;堆内存&lt;/strong&gt;：&lt;code&gt;new Person()&lt;/code&gt;会在堆上开辟一块空间，存放Person对象的实际内容（属性等）。&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;引用关系&lt;/strong&gt;：&lt;code&gt;p&lt;/code&gt;保存的是堆中Person对象的地址（引用），通过&lt;code&gt;p&lt;/code&gt;可以访问和操作该对象。&lt;/li&gt;
&lt;/ol&gt;
&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;影响&lt;/strong&gt;：如果&lt;code&gt;p&lt;/code&gt;被赋值为&lt;code&gt;null&lt;/code&gt;，则它不再指向任何对象，但堆中的对象如果没有其他引用指向，会被垃圾回收。&lt;/li&gt;
&lt;/ul&gt;
&lt;h4&gt;2. 两个引用指向同一个对象&lt;/h4&gt;
&lt;ul&gt;
&lt;li&gt;代码示例：&lt;pre&gt;&lt;code&gt;Person p1 = new Person();
Person p2 = p1;
&lt;/code&gt;&lt;/pre&gt;
&lt;/li&gt;
&lt;li&gt;解释：
&lt;ol&gt;
&lt;li&gt;&lt;code&gt;p1&lt;/code&gt;和&lt;code&gt;p2&lt;/code&gt;都在栈内存中，各自保存一个地址。&lt;/li&gt;
&lt;li&gt;这两个地址其实是一样的，都指向同一个堆内存中的Person对象。&lt;/li&gt;
&lt;li&gt;通过&lt;code&gt;p1&lt;/code&gt;或&lt;code&gt;p2&lt;/code&gt;修改对象的属性，另一个引用也能看到变化（因为本质上是同一个对象）。&lt;/li&gt;
&lt;/ol&gt;
&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;影响&lt;/strong&gt;：只要有一个引用还指向该对象，对象就不会被回收。&lt;/li&gt;
&lt;/ul&gt;
&lt;h4&gt;3. 两个引用指向不同对象&lt;/h4&gt;
&lt;ul&gt;
&lt;li&gt;代码示例：&lt;pre&gt;&lt;code&gt;Person p1 = new Person();
Person p2 = new Person();
&lt;/code&gt;&lt;/pre&gt;
&lt;/li&gt;
&lt;li&gt;解释：
&lt;ol&gt;
&lt;li&gt;&lt;code&gt;p1&lt;/code&gt;和&lt;code&gt;p2&lt;/code&gt;都在栈内存，各自保存不同的地址。&lt;/li&gt;
&lt;li&gt;堆内存中有两个不同的Person对象，&lt;code&gt;p1&lt;/code&gt;和&lt;code&gt;p2&lt;/code&gt;分别指向各自的对象。&lt;/li&gt;
&lt;li&gt;修改&lt;code&gt;p1&lt;/code&gt;指向的对象不会影响&lt;code&gt;p2&lt;/code&gt;指向的对象，反之亦然。&lt;/li&gt;
&lt;/ol&gt;
&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;影响&lt;/strong&gt;：每个对象的生命周期独立，只有当没有引用指向某个对象时，该对象才会被回收。&lt;/li&gt;
&lt;/ul&gt;
&lt;hr /&gt;
&lt;p&gt;&lt;strong&gt;总结：&lt;/strong&gt;&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;栈内存存放引用变量（地址），堆内存存放实际对象内容。&lt;/li&gt;
&lt;li&gt;多个引用可以指向同一个对象，也可以各自指向不同对象。&lt;/li&gt;
&lt;li&gt;理解这种分布有助于掌握Java对象的共享、参数传递、垃圾回收等机制。&lt;/li&gt;
&lt;/ul&gt;
&lt;h2&gt;18.Java的基本数据类型与引用数据类型&lt;/h2&gt;
&lt;h3&gt;1. 基本数据类型（Primitive Types）&lt;/h3&gt;
&lt;ul&gt;
&lt;li&gt;Java共有8种基本数据类型，直接存储具体的数值，分配在栈内存。&lt;/li&gt;
&lt;li&gt;包括：
&lt;ul&gt;
&lt;li&gt;整型：byte（1字节）、short（2字节）、int（4字节）、long（8字节）&lt;/li&gt;
&lt;li&gt;浮点型：float（4字节）、double（8字节）&lt;/li&gt;
&lt;li&gt;字符型：char（2字节，存储单个字符）&lt;/li&gt;
&lt;li&gt;布尔型：boolean（1字节，true/false）&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;&lt;strong&gt;示例：&lt;/strong&gt;&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;int a = 10;
double d = 3.14;
char c = &apos;A&apos;;
boolean flag = true;
&lt;/code&gt;&lt;/pre&gt;
&lt;ul&gt;
&lt;li&gt;基本类型变量直接存储值，赋值和传参时是值的拷贝。&lt;/li&gt;
&lt;/ul&gt;
&lt;h3&gt;2. 引用数据类型（Reference Types）&lt;/h3&gt;
&lt;ul&gt;
&lt;li&gt;包括：类（对象）、数组、接口、枚举、String等。&lt;/li&gt;
&lt;li&gt;变量存储的是对象在堆内存的地址（引用），实际内容在堆内存。&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;&lt;strong&gt;示例：&lt;/strong&gt;&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;String s = &quot;Hello&quot;;
int[] arr = {1, 2, 3};
Person p = new Person();
&lt;/code&gt;&lt;/pre&gt;
&lt;ul&gt;
&lt;li&gt;引用类型变量存储在栈，指向堆中的实际对象。&lt;/li&gt;
&lt;li&gt;赋值和传参时是引用的拷贝（地址），可通过引用修改堆中对象内容。&lt;/li&gt;
&lt;/ul&gt;
&lt;h3&gt;3. 区别总结&lt;/h3&gt;
&lt;ul&gt;
&lt;li&gt;基本类型：存储值本身，内存分配在栈，速度快，不能为null。&lt;/li&gt;
&lt;li&gt;引用类型：存储地址，实际内容在堆，变量可为null，功能更丰富。&lt;/li&gt;
&lt;li&gt;基本类型有对应的包装类（如int对应Integer），用于集合等只能存引用类型的场景。&lt;/li&gt;
&lt;/ul&gt;
&lt;blockquote&gt;
&lt;p&gt;理解两者区别有助于掌握Java的内存管理、参数传递、对象操作等核心机制。&lt;/p&gt;
&lt;/blockquote&gt;
&lt;h2&gt;19.this关键字的内存原理&lt;/h2&gt;
&lt;h3&gt;1. this的本质&lt;/h3&gt;
&lt;ul&gt;
&lt;li&gt;this是Java中每个实例方法内部的一个隐式引用，指向当前调用该方法的对象本身。&lt;/li&gt;
&lt;li&gt;本质上，this是一个指向当前对象的引用变量，存储在栈帧的局部变量表中。&lt;/li&gt;
&lt;/ul&gt;
&lt;h3&gt;2. 内存表现&lt;/h3&gt;
&lt;ul&gt;
&lt;li&gt;当调用实例方法时，JVM会自动将当前对象的引用（即this）作为第一个参数传递给方法。&lt;/li&gt;
&lt;li&gt;在方法执行期间，this指向堆内存中当前对象实例。&lt;/li&gt;
&lt;li&gt;每个对象的方法调用时，this的值不同，始终指向当前操作的对象。&lt;/li&gt;
&lt;/ul&gt;
&lt;h3&gt;3. 作用和常见用法&lt;/h3&gt;
&lt;ul&gt;
&lt;li&gt;区分成员变量和局部变量（如构造方法参数与成员变量同名时）。&lt;/li&gt;
&lt;li&gt;在对象方法内部引用当前对象。&lt;/li&gt;
&lt;li&gt;在构造方法中调用其他构造方法（this(...)）。&lt;/li&gt;
&lt;li&gt;在链式调用中返回当前对象（如return this;）。&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;&lt;strong&gt;示例：&lt;/strong&gt;&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;class Person {
    String name;
    Person(String name) {
        this.name = name; // 区分成员变量和参数
    }
    void printName() {
        System.out.println(this.name); // this可省略
    }
    Person setName(String name) {
        this.name = name;
        return this; // 支持链式调用
    }
}
&lt;/code&gt;&lt;/pre&gt;
&lt;h3&gt;4. 注意事项&lt;/h3&gt;
&lt;ul&gt;
&lt;li&gt;静态方法中不能使用this，因为没有具体对象。&lt;/li&gt;
&lt;li&gt;this只能在实例方法和构造方法中使用。&lt;/li&gt;
&lt;/ul&gt;
&lt;blockquote&gt;
&lt;p&gt;理解this的内存原理有助于掌握对象方法的调用机制、链式编程和构造方法重载等高级用法。&lt;/p&gt;
&lt;/blockquote&gt;
&lt;h2&gt;20.Java对象数组&lt;/h2&gt;
&lt;h3&gt;1. 概念&lt;/h3&gt;
&lt;ul&gt;
&lt;li&gt;对象数组是存放对象引用的数组，每个元素都是某个类的对象引用。&lt;/li&gt;
&lt;li&gt;本质上是&quot;引用类型数组&quot;，数组本身在堆，元素为对象引用。&lt;/li&gt;
&lt;/ul&gt;
&lt;h3&gt;2. 声明与创建&lt;/h3&gt;
&lt;ul&gt;
&lt;li&gt;声明：&lt;pre&gt;&lt;code&gt;Person[] people;
&lt;/code&gt;&lt;/pre&gt;
&lt;/li&gt;
&lt;li&gt;创建数组空间：&lt;pre&gt;&lt;code&gt;people = new Person[3]; // 创建长度为3的对象数组，每个元素默认值为null
&lt;/code&gt;&lt;/pre&gt;
&lt;/li&gt;
&lt;li&gt;创建并初始化对象：&lt;pre&gt;&lt;code&gt;people[0] = new Person(&quot;Tom&quot;);
people[1] = new Person(&quot;Jerry&quot;);
people[2] = new Person(&quot;Alice&quot;);
&lt;/code&gt;&lt;/pre&gt;
&lt;/li&gt;
&lt;li&gt;声明+静态初始化：&lt;pre&gt;&lt;code&gt;Person[] group = {
    new Person(&quot;Tom&quot;),
    new Person(&quot;Jerry&quot;),
    new Person(&quot;Alice&quot;)
};
&lt;/code&gt;&lt;/pre&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;h3&gt;3. 遍历对象数组&lt;/h3&gt;
&lt;ul&gt;
&lt;li&gt;for循环或增强for循环：&lt;pre&gt;&lt;code&gt;for (int i = 0; i &amp;lt; people.length; i++) {
    if (people[i] != null) {
        people[i].sayHello();
    }
}
// 或
for (Person p : group) {
    p.sayHello();
}
&lt;/code&gt;&lt;/pre&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;h3&gt;4. 内存分布说明&lt;/h3&gt;
&lt;ul&gt;
&lt;li&gt;对象数组本身在堆内存，数组元素为对象引用（地址）。&lt;/li&gt;
&lt;li&gt;每个元素指向堆中的实际对象实例。&lt;/li&gt;
&lt;li&gt;未初始化的元素为null。&lt;/li&gt;
&lt;/ul&gt;
&lt;h3&gt;5. 注意事项&lt;/h3&gt;
&lt;ul&gt;
&lt;li&gt;创建对象数组时，只分配了引用空间，需逐个new对象。&lt;/li&gt;
&lt;li&gt;访问未初始化的元素会抛出NullPointerException。&lt;/li&gt;
&lt;/ul&gt;
&lt;blockquote&gt;
&lt;p&gt;对象数组常用于批量管理同类对象，如学生列表、商品清单等，是面向对象编程的重要应用。&lt;/p&gt;
&lt;/blockquote&gt;
&lt;h2&gt;21.Java字符串（String）常用API&lt;/h2&gt;
&lt;h3&gt;1. 字符串的创建&lt;/h3&gt;
&lt;ul&gt;
&lt;li&gt;直接赋值：&lt;pre&gt;&lt;code&gt;String s1 = &quot;Hello&quot;;
&lt;/code&gt;&lt;/pre&gt;
&lt;/li&gt;
&lt;li&gt;使用构造方法：&lt;pre&gt;&lt;code&gt;String s2 = new String(&quot;World&quot;);
&lt;/code&gt;&lt;/pre&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;h3&gt;2. 字符串的不可变性&lt;/h3&gt;
&lt;ul&gt;
&lt;li&gt;String对象一旦创建，内容不可更改，所有修改操作都会生成新字符串。&lt;/li&gt;
&lt;li&gt;适合做常量、作为Map的key等。&lt;/li&gt;
&lt;/ul&gt;
&lt;h3&gt;3. 常用API方法&lt;/h3&gt;
&lt;ul&gt;
&lt;li&gt;&lt;code&gt;length()&lt;/code&gt;：获取字符串长度&lt;/li&gt;
&lt;li&gt;&lt;code&gt;charAt(int index)&lt;/code&gt;：获取指定位置字符&lt;/li&gt;
&lt;li&gt;&lt;code&gt;equals(String other)&lt;/code&gt;：内容比较&lt;/li&gt;
&lt;li&gt;&lt;code&gt;equalsIgnoreCase(String other)&lt;/code&gt;：忽略大小写比较&lt;/li&gt;
&lt;li&gt;&lt;code&gt;compareTo(String other)&lt;/code&gt;：字典序比较&lt;/li&gt;
&lt;li&gt;&lt;code&gt;isEmpty()&lt;/code&gt;：是否为空串&lt;/li&gt;
&lt;li&gt;&lt;code&gt;toUpperCase()/toLowerCase()&lt;/code&gt;：转大/小写&lt;/li&gt;
&lt;li&gt;&lt;code&gt;substring(int begin, int end)&lt;/code&gt;：截取子串&lt;/li&gt;
&lt;li&gt;&lt;code&gt;indexOf(String str)&lt;/code&gt;：查找子串首次出现位置&lt;/li&gt;
&lt;li&gt;&lt;code&gt;lastIndexOf(String str)&lt;/code&gt;：查找子串最后出现位置&lt;/li&gt;
&lt;li&gt;&lt;code&gt;contains(String str)&lt;/code&gt;：是否包含子串&lt;/li&gt;
&lt;li&gt;&lt;code&gt;replace(old, new)&lt;/code&gt;：替换内容&lt;/li&gt;
&lt;li&gt;&lt;code&gt;split(String regex)&lt;/code&gt;：分割字符串&lt;/li&gt;
&lt;li&gt;&lt;code&gt;trim()&lt;/code&gt;：去除首尾空白&lt;/li&gt;
&lt;li&gt;&lt;code&gt;startsWith()/endsWith()&lt;/code&gt;：前缀/后缀判断&lt;/li&gt;
&lt;li&gt;&lt;code&gt;getBytes()&lt;/code&gt;：转为字节数组&lt;/li&gt;
&lt;li&gt;&lt;code&gt;toCharArray()&lt;/code&gt;：转为字符数组&lt;/li&gt;
&lt;/ul&gt;
&lt;h3&gt;4. 示例代码&lt;/h3&gt;
&lt;pre&gt;&lt;code&gt;String str = &quot;  Hello, Java!  &quot;;
System.out.println(str.length()); // 15
System.out.println(str.trim()); // &quot;Hello, Java!&quot;
System.out.println(str.substring(2, 7)); // &quot;Hello&quot;
System.out.println(str.toUpperCase()); // &quot;  HELLO, JAVA!  &quot;
System.out.println(str.replace(&quot;Java&quot;, &quot;World&quot;)); // &quot;  Hello, World!  &quot;
String[] arr = str.split(&quot;,&quot;);
System.out.println(arr[0]); // &quot;  Hello&quot;
&lt;/code&gt;&lt;/pre&gt;
&lt;h3&gt;5. 字符串拼接与效率&lt;/h3&gt;
&lt;ul&gt;
&lt;li&gt;使用&lt;code&gt;+&lt;/code&gt;拼接字符串简单但效率低，建议多次拼接时用&lt;code&gt;StringBuilder&lt;/code&gt;或&lt;code&gt;StringBuffer&lt;/code&gt;。&lt;/li&gt;
&lt;/ul&gt;
&lt;h3&gt;6. String构造方法参数说明&lt;/h3&gt;
&lt;ul&gt;
&lt;li&gt;&lt;code&gt;String s = new String()&lt;/code&gt; 这句代码中的括号内可以写多种内容，取决于String类的构造方法重载。&lt;/li&gt;
&lt;li&gt;常见构造方法有：
&lt;ul&gt;
&lt;li&gt;&lt;code&gt;new String()&lt;/code&gt;：创建空字符串，等价于&quot;&quot;&lt;/li&gt;
&lt;li&gt;&lt;code&gt;new String(String original)&lt;/code&gt;：根据已有字符串创建新字符串&lt;/li&gt;
&lt;li&gt;&lt;code&gt;new String(char[] value)&lt;/code&gt;：由字符数组创建字符串&lt;/li&gt;
&lt;li&gt;&lt;code&gt;new String(char[] value, int offset, int count)&lt;/code&gt;：由字符数组的部分内容创建字符串&lt;/li&gt;
&lt;li&gt;&lt;code&gt;new String(byte[] bytes)&lt;/code&gt;：由字节数组创建字符串（按平台默认编码）&lt;/li&gt;
&lt;li&gt;&lt;code&gt;new String(byte[] bytes, String charsetName)&lt;/code&gt;：由字节数组按指定编码创建字符串&lt;/li&gt;
&lt;li&gt;&lt;code&gt;new String(StringBuffer buffer)&lt;/code&gt;、&lt;code&gt;new String(StringBuilder builder)&lt;/code&gt;：由可变字符串对象创建&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;&lt;strong&gt;示例：&lt;/strong&gt;&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;String s1 = new String(); // 空字符串
String s2 = new String(&quot;abc&quot;);
char[] arr = {&apos;H&apos;, &apos;e&apos;, &apos;l&apos;, &apos;l&apos;, &apos;o&apos;};
String s3 = new String(arr); // &quot;Hello&quot;
String s4 = new String(arr, 1, 3); // &quot;ell&quot;
byte[] bytes = {65, 66, 67};
String s5 = new String(bytes); // &quot;ABC&quot;（默认编码）
String s6 = new String(bytes, &quot;UTF-8&quot;); // &quot;ABC&quot;
StringBuffer sb = new StringBuffer(&quot;hi&quot;);
String s7 = new String(sb);
&lt;/code&gt;&lt;/pre&gt;
&lt;ul&gt;
&lt;li&gt;通过不同构造方法，可以实现字符串与字符数组、字节数组、StringBuffer/StringBuilder等类型的灵活转换。&lt;/li&gt;
&lt;/ul&gt;
&lt;blockquote&gt;
&lt;p&gt;熟练掌握String API是Java开发的基础，能高效处理文本数据。&lt;/p&gt;
&lt;/blockquote&gt;
&lt;h2&gt;22.StringBuilder与StringBuffer&lt;/h2&gt;
&lt;h3&gt;1. 概念与区别&lt;/h3&gt;
&lt;ul&gt;
&lt;li&gt;&lt;strong&gt;StringBuilder&lt;/strong&gt; 和 &lt;strong&gt;StringBuffer&lt;/strong&gt; 都是可变字符串类，适合频繁修改字符串内容的场景。&lt;/li&gt;
&lt;li&gt;区别：
&lt;ul&gt;
&lt;li&gt;StringBuilder：线程不安全，效率高，JDK 1.5后推荐单线程场景使用。&lt;/li&gt;
&lt;li&gt;StringBuffer：线程安全，效率略低，适合多线程环境。&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;二者都比String拼接效率高，避免产生大量无用字符串对象。&lt;/li&gt;
&lt;/ul&gt;
&lt;h3&gt;2. 常用API&lt;/h3&gt;
&lt;ul&gt;
&lt;li&gt;&lt;code&gt;append(xxx)&lt;/code&gt;：追加内容&lt;/li&gt;
&lt;li&gt;&lt;code&gt;insert(offset, xxx)&lt;/code&gt;：插入内容&lt;/li&gt;
&lt;li&gt;&lt;code&gt;delete(start, end)&lt;/code&gt;：删除指定区间内容&lt;/li&gt;
&lt;li&gt;&lt;code&gt;replace(start, end, str)&lt;/code&gt;：替换内容&lt;/li&gt;
&lt;li&gt;&lt;code&gt;reverse()&lt;/code&gt;：反转字符串&lt;/li&gt;
&lt;li&gt;&lt;code&gt;toString()&lt;/code&gt;：转为不可变String&lt;/li&gt;
&lt;li&gt;&lt;code&gt;setCharAt(index, ch)&lt;/code&gt;：修改指定位置字符&lt;/li&gt;
&lt;li&gt;&lt;code&gt;length()&lt;/code&gt;：获取长度&lt;/li&gt;
&lt;/ul&gt;
&lt;h3&gt;3. 示例代码&lt;/h3&gt;
&lt;pre&gt;&lt;code&gt;StringBuilder sb = new StringBuilder(&quot;Hello&quot;);
sb.append(&quot;, Java!&quot;);
sb.insert(5, &quot; World&quot;);
sb.delete(0, 6);
sb.replace(0, 4, &quot;Hi&quot;);
sb.reverse();
System.out.println(sb.toString());

StringBuffer sbf = new StringBuffer();
sbf.append(&quot;abc&quot;).append(123);
System.out.println(sbf);
&lt;/code&gt;&lt;/pre&gt;
&lt;h3&gt;4. 适用场景&lt;/h3&gt;
&lt;ul&gt;
&lt;li&gt;StringBuilder：推荐用于单线程环境下的大量字符串拼接、修改，如循环拼接、临时字符串处理等。&lt;/li&gt;
&lt;li&gt;StringBuffer：用于多线程环境下对同一字符串变量的并发修改。&lt;/li&gt;
&lt;/ul&gt;
&lt;blockquote&gt;
&lt;p&gt;总结：字符串频繁拼接、修改时优先考虑StringBuilder，需线程安全时用StringBuffer，普通不可变字符串用String。&lt;/p&gt;
&lt;/blockquote&gt;
&lt;h3&gt;StringBuilder和StringBuffer的区别&lt;/h3&gt;
&lt;ol&gt;
&lt;li&gt;
&lt;p&gt;&lt;strong&gt;线程安全性&lt;/strong&gt;&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;strong&gt;StringBuffer&lt;/strong&gt;：线程安全。其大多数方法都用&lt;code&gt;synchronized&lt;/code&gt;修饰，多线程环境下可安全操作同一个StringBuffer对象。&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;StringBuilder&lt;/strong&gt;：线程不安全。没有同步机制，适合单线程环境，效率更高。&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;&lt;strong&gt;执行效率&lt;/strong&gt;&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;strong&gt;StringBuffer&lt;/strong&gt;：由于线程安全，方法有同步开销，效率略低。&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;StringBuilder&lt;/strong&gt;：无同步开销，执行速度更快，推荐在单线程下使用。&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;&lt;strong&gt;JDK版本&lt;/strong&gt;&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;strong&gt;StringBuffer&lt;/strong&gt;：JDK 1.0就有。&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;StringBuilder&lt;/strong&gt;：JDK 1.5引入，作为StringBuffer的高效替代。&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;&lt;strong&gt;用法和API&lt;/strong&gt;&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;二者API几乎完全一致，如&lt;code&gt;append()&lt;/code&gt;、&lt;code&gt;insert()&lt;/code&gt;、&lt;code&gt;delete()&lt;/code&gt;、&lt;code&gt;reverse()&lt;/code&gt;等。&lt;/li&gt;
&lt;li&gt;用法相同，唯一差别在于线程安全性和效率。&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;&lt;strong&gt;适用场景&lt;/strong&gt;&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;strong&gt;StringBuffer&lt;/strong&gt;：多线程环境下字符串频繁修改。&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;StringBuilder&lt;/strong&gt;：单线程环境下字符串频繁修改（如循环拼接、临时字符串处理等）。&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;/ol&gt;
&lt;p&gt;&lt;strong&gt;示例对比：&lt;/strong&gt;&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;StringBuffer sbf = new StringBuffer(&quot;abc&quot;);
sbf.append(123);

StringBuilder sbd = new StringBuilder(&quot;abc&quot;);
sbd.append(123);
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;&lt;strong&gt;总结：&lt;/strong&gt;&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;需要线程安全时用StringBuffer，否则优先用StringBuilder。&lt;/li&gt;
&lt;li&gt;普通不可变字符串用String，频繁拼接/修改用Builder或Buffer。&lt;/li&gt;
&lt;/ul&gt;
&lt;h2&gt;23.Java链式编程（链式调用）&lt;/h2&gt;
&lt;h3&gt;1. 概念&lt;/h3&gt;
&lt;ul&gt;
&lt;li&gt;链式编程（Fluent Interface/Method Chaining）是指通过连续调用对象的方法，每个方法返回当前对象自身（this），从而实现多个操作的连贯书写。&lt;/li&gt;
&lt;li&gt;常见于构建器模式、StringBuilder、集合操作等。&lt;/li&gt;
&lt;/ul&gt;
&lt;h3&gt;2. 实现原理&lt;/h3&gt;
&lt;ul&gt;
&lt;li&gt;方法返回&lt;code&gt;this&lt;/code&gt;（当前对象），使得可以继续调用该对象的其他方法。&lt;/li&gt;
&lt;li&gt;适用于需要对同一对象连续操作的场景。&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;&lt;strong&gt;示例：&lt;/strong&gt;&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;class Person {
    private String name;
    private int age;
    public Person setName(String name) {
        this.name = name;
        return this;
    }
    public Person setAge(int age) {
        this.age = age;
        return this;
    }
    public void show() {
        System.out.println(name + &quot;, &quot; + age);
    }
}

Person p = new Person().setName(&quot;Tom&quot;).setAge(20);
p.show();
&lt;/code&gt;&lt;/pre&gt;
&lt;h3&gt;3. 常见场景&lt;/h3&gt;
&lt;ul&gt;
&lt;li&gt;StringBuilder/StringBuffer的append、insert等方法：&lt;pre&gt;&lt;code&gt;StringBuilder sb = new StringBuilder().append(&quot;Hello&quot;).append(&quot;, &quot;).append(&quot;World&quot;);
&lt;/code&gt;&lt;/pre&gt;
&lt;/li&gt;
&lt;li&gt;Java集合的add、remove等方法（部分实现支持）：&lt;pre&gt;&lt;code&gt;List&amp;lt;String&amp;gt; list = new ArrayList&amp;lt;&amp;gt;();
list.add(&quot;a&quot;).add(&quot;b&quot;); // 需返回this
&lt;/code&gt;&lt;/pre&gt;
&lt;/li&gt;
&lt;li&gt;构建器模式（Builder Pattern）：用于复杂对象的灵活构建。&lt;/li&gt;
&lt;/ul&gt;
&lt;h3&gt;4. 优缺点&lt;/h3&gt;
&lt;ul&gt;
&lt;li&gt;&lt;strong&gt;优点&lt;/strong&gt;：代码简洁、可读性强、便于批量设置属性。&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;缺点&lt;/strong&gt;：不适合所有场景，方法需返回this，易导致调试困难。&lt;/li&gt;
&lt;/ul&gt;
&lt;blockquote&gt;
&lt;p&gt;链式编程是现代Java开发中常用的风格，尤其适合配置、构建、批量操作等场景。&lt;/p&gt;
&lt;/blockquote&gt;
&lt;h2&gt;24.StringJoiner的用法&lt;/h2&gt;
&lt;h3&gt;1. 概念&lt;/h3&gt;
&lt;ul&gt;
&lt;li&gt;StringJoiner是Java 8引入的字符串连接工具类，位于java.util包。&lt;/li&gt;
&lt;li&gt;用于高效拼接多个字符串，并可自定义分隔符、前缀和后缀。&lt;/li&gt;
&lt;/ul&gt;
&lt;h3&gt;2. 构造方法&lt;/h3&gt;
&lt;ul&gt;
&lt;li&gt;&lt;code&gt;StringJoiner(CharSequence delimiter)&lt;/code&gt;：指定分隔符。&lt;/li&gt;
&lt;li&gt;&lt;code&gt;StringJoiner(CharSequence delimiter, CharSequence prefix, CharSequence suffix)&lt;/code&gt;：指定分隔符、前缀和后缀。&lt;/li&gt;
&lt;/ul&gt;
&lt;h3&gt;3. 常用API&lt;/h3&gt;
&lt;ul&gt;
&lt;li&gt;&lt;code&gt;add(String element)&lt;/code&gt;：添加元素。&lt;/li&gt;
&lt;li&gt;&lt;code&gt;toString()&lt;/code&gt;：返回拼接后的字符串。&lt;/li&gt;
&lt;li&gt;&lt;code&gt;setEmptyValue(String emptyValue)&lt;/code&gt;：设置无元素时的返回值。&lt;/li&gt;
&lt;li&gt;&lt;code&gt;length()&lt;/code&gt;：当前拼接字符串的长度。&lt;/li&gt;
&lt;/ul&gt;
&lt;h3&gt;4. 示例代码&lt;/h3&gt;
&lt;pre&gt;&lt;code&gt;import java.util.StringJoiner;

StringJoiner sj = new StringJoiner(&quot;, &quot;);
sj.add(&quot;Java&quot;).add(&quot;Python&quot;).add(&quot;C++&quot;);
System.out.println(sj.toString()); // Java, Python, C++

StringJoiner sj2 = new StringJoiner(&quot;, &quot;, &quot;[&quot;, &quot;]&quot;);
sj2.add(&quot;A&quot;).add(&quot;B&quot;);
System.out.println(sj2); // [A, B]

StringJoiner sj3 = new StringJoiner(&quot;-&quot;);
System.out.println(sj3.setEmptyValue(&quot;空&quot;).toString()); // 空
&lt;/code&gt;&lt;/pre&gt;
&lt;h3&gt;5. 适用场景&lt;/h3&gt;
&lt;ul&gt;
&lt;li&gt;拼接集合、数组等多个字符串，需自定义分隔符、前后缀时。&lt;/li&gt;
&lt;li&gt;替代手动循环拼接，代码更简洁高效。&lt;/li&gt;
&lt;/ul&gt;
&lt;blockquote&gt;
&lt;p&gt;StringJoiner常与Stream API结合使用，也可用于String.join()等场景，是现代Java字符串拼接的推荐方式之一。&lt;/p&gt;
&lt;/blockquote&gt;
&lt;h2&gt;25.Java字符串（String）的底层原理&lt;/h2&gt;
&lt;h3&gt;1. 不可变性&lt;/h3&gt;
&lt;ul&gt;
&lt;li&gt;String对象一旦创建，内容不可更改（final修饰的char[]数组存储字符）。&lt;/li&gt;
&lt;li&gt;修改字符串（如拼接、替换）会生成新的String对象，原对象不变。&lt;/li&gt;
&lt;li&gt;不可变性带来线程安全、可缓存、可作为Map的key等优势。&lt;/li&gt;
&lt;/ul&gt;
&lt;h3&gt;2. 内存结构&lt;/h3&gt;
&lt;ul&gt;
&lt;li&gt;String底层由一个final char[]数组存储字符内容（JDK 9+为byte[]，支持压缩存储）。&lt;/li&gt;
&lt;li&gt;String对象本身在堆内存，char[]数组也在堆内存。&lt;/li&gt;
&lt;/ul&gt;
&lt;h3&gt;3. 字符串常量池&lt;/h3&gt;
&lt;ul&gt;
&lt;li&gt;Java为提高效率和节省内存，维护一个字符串常量池（String Pool）。&lt;/li&gt;
&lt;li&gt;直接赋值的字符串字面量会被放入常量池，池中相同内容的字符串只存一份。&lt;/li&gt;
&lt;li&gt;new String(&quot;abc&quot;)会在堆中新建对象，不会自动放入常量池。&lt;/li&gt;
&lt;li&gt;可用intern()方法将字符串加入常量池。&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;&lt;strong&gt;示例：&lt;/strong&gt;&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;String s1 = &quot;hello&quot;;
String s2 = &quot;hello&quot;;
System.out.println(s1 == s2); // true，指向常量池同一对象
String s3 = new String(&quot;hello&quot;);
System.out.println(s1 == s3); // false，s3为新对象
System.out.println(s1 == s3.intern()); // true
&lt;/code&gt;&lt;/pre&gt;
&lt;h3&gt;4. 字符串比较&lt;/h3&gt;
&lt;ul&gt;
&lt;li&gt;== 比较的是引用（地址），equals()比较的是内容。&lt;/li&gt;
&lt;li&gt;推荐用equals()判断字符串内容是否相等。&lt;/li&gt;
&lt;/ul&gt;
&lt;h3&gt;5. 字符串拼接机制&lt;/h3&gt;
&lt;ul&gt;
&lt;li&gt;字符串拼接（+）在编译期会优化为StringBuilder.append()，但多次拼接建议手动用StringBuilder。&lt;/li&gt;
&lt;li&gt;字符串常量拼接会在编译期合并，变量拼接则在运行时生成新对象。&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;&lt;strong&gt;示例：&lt;/strong&gt;&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;String a = &quot;he&quot; + &quot;llo&quot;; // 编译期合并为&quot;hello&quot;
String b = &quot;he&quot;;
String c = b + &quot;llo&quot;; // 运行时拼接
System.out.println(a == &quot;hello&quot;); // true
System.out.println(c == &quot;hello&quot;); // false
&lt;/code&gt;&lt;/pre&gt;
&lt;h3&gt;6. 性能影响&lt;/h3&gt;
&lt;ul&gt;
&lt;li&gt;频繁修改字符串建议用StringBuilder/StringBuffer，避免生成大量无用String对象。&lt;/li&gt;
&lt;li&gt;字符串不可变性有利于安全和性能优化，但需注意拼接和创建方式。&lt;/li&gt;
&lt;/ul&gt;
&lt;blockquote&gt;
&lt;p&gt;理解String的底层原理有助于编写高效、健壮的Java代码，避免常见性能和内存问题。&lt;/p&gt;
&lt;/blockquote&gt;
&lt;h3&gt;7. 字符串拼接的底层原理&lt;/h3&gt;
&lt;ul&gt;
&lt;li&gt;
&lt;p&gt;&lt;strong&gt;编译期优化&lt;/strong&gt;：&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;对于字符串常量的拼接（如&quot;a&quot; + &quot;b&quot;），编译器会在编译阶段直接合并为一个常量（&quot;ab&quot;），不会生成多余对象。&lt;/li&gt;
&lt;li&gt;例如：&lt;pre&gt;&lt;code&gt;String s = &quot;a&quot; + &quot;b&quot; + &quot;c&quot;; // 编译后等价于 String s = &quot;abc&quot;;
&lt;/code&gt;&lt;/pre&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;&lt;strong&gt;运行时机制&lt;/strong&gt;：&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;对于包含变量的拼接（如a + b），编译器会将其转换为StringBuilder的append()方法链式调用，最后toString()生成新字符串。&lt;/li&gt;
&lt;li&gt;例如：&lt;pre&gt;&lt;code&gt;String a = &quot;hello&quot;;
String b = &quot;world&quot;;
String s = a + b;
// 实际等价于：
// String s = new StringBuilder().append(a).append(b).toString();
&lt;/code&gt;&lt;/pre&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;&lt;strong&gt;多次拼接建议&lt;/strong&gt;：&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;在循环或大量拼接场景下，建议手动使用StringBuilder/StringBuffer，避免生成大量临时String对象，提升性能。&lt;/li&gt;
&lt;li&gt;例如：&lt;pre&gt;&lt;code&gt;StringBuilder sb = new StringBuilder();
for (int i = 0; i &amp;lt; 1000; i++) {
    sb.append(i);
}
String result = sb.toString();
&lt;/code&gt;&lt;/pre&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;&lt;strong&gt;常量拼接与变量拼接的区别&lt;/strong&gt;：&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;常量拼接在编译期完成，结果在常量池。&lt;/li&gt;
&lt;li&gt;变量拼接在运行期完成，结果在堆内存。&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;&lt;strong&gt;性能影响&lt;/strong&gt;：&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;频繁使用+拼接字符串会导致性能下降，尤其是在循环中。&lt;/li&gt;
&lt;li&gt;推荐用StringBuilder进行高效拼接。&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;blockquote&gt;
&lt;p&gt;理解字符串拼接的底层原理有助于写出高效、可维护的Java代码，避免无谓的内存浪费和性能瓶颈。&lt;/p&gt;
&lt;/blockquote&gt;
&lt;h3&gt;8. 直接赋值字符串与变量拼接字符串的==比较&lt;/h3&gt;
&lt;ul&gt;
&lt;li&gt;&lt;strong&gt;直接赋值字符串&lt;/strong&gt;（如String s1 = &quot;abc&quot;;）会被放入字符串常量池，池中相同内容的字符串只存一份。&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;变量拼接字符串&lt;/strong&gt;（如String s2 = a + b;）即使内容相同，结果是运行时新创建的对象，存放在堆内存，不会自动进入常量池。&lt;/li&gt;
&lt;li&gt;因此，内容相同但创建方式不同的字符串，用==比较时结果通常为false。&lt;/li&gt;
&lt;li&gt;== 比较的是引用（地址），不是内容。内容比较应使用equals()。&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;&lt;strong&gt;示例：&lt;/strong&gt;&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;String s1 = &quot;hello&quot;;
String s2 = &quot;he&quot; + &quot;llo&quot;; // 编译期常量拼接，等价于&quot;hello&quot;，与s1同一对象
System.out.println(s1 == s2); // true

String a = &quot;he&quot;;
String b = &quot;llo&quot;;
String s3 = a + b; // 运行时拼接，生成新对象
System.out.println(s1 == s3); // false
System.out.println(s1.equals(s3)); // true
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;&lt;strong&gt;重点总结：&lt;/strong&gt;&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;直接赋值或常量拼接的字符串，内容相同==为true。&lt;/li&gt;
&lt;li&gt;变量拼接的字符串，即使内容相同==为false。&lt;/li&gt;
&lt;li&gt;判断字符串内容相等，必须用equals()方法。&lt;/li&gt;
&lt;/ul&gt;
&lt;h2&gt;26.Java集合框架基础&lt;/h2&gt;
&lt;h3&gt;1. 集合的概念&lt;/h3&gt;
&lt;ul&gt;
&lt;li&gt;集合（Collection）是存储、操作一组数据的容器，支持动态扩容、去重、排序等功能。&lt;/li&gt;
&lt;li&gt;Java集合框架（Collection Framework）提供了丰富的数据结构和算法，位于java.util包。&lt;/li&gt;
&lt;li&gt;集合只能存引用类型（对象），基本类型需用包装类（如Integer、Double）。&lt;/li&gt;
&lt;/ul&gt;
&lt;h3&gt;2. 主要接口与体系结构&lt;/h3&gt;
&lt;ul&gt;
&lt;li&gt;&lt;strong&gt;Collection接口&lt;/strong&gt;：单值集合的根接口，子接口有List、Set、Queue等。&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;List接口&lt;/strong&gt;：有序、可重复元素，如ArrayList、LinkedList。&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Set接口&lt;/strong&gt;：无序、不可重复元素，如HashSet、TreeSet。&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Map接口&lt;/strong&gt;：键值对集合，如HashMap、TreeMap，不继承Collection。&lt;/li&gt;
&lt;/ul&gt;
&lt;pre&gt;&lt;code&gt;Collection
  ├── List（有序、可重复）
  │     ├── ArrayList
  │     └── LinkedList
  └── Set（无序、不可重复）
        ├── HashSet
        └── TreeSet
Map（键值对）
  ├── HashMap
  └── TreeMap
&lt;/code&gt;&lt;/pre&gt;
&lt;h3&gt;3. 常用实现类&lt;/h3&gt;
&lt;ul&gt;
&lt;li&gt;&lt;strong&gt;ArrayList&lt;/strong&gt;：基于数组，查询快，增删慢，常用。&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;LinkedList&lt;/strong&gt;：基于链表，增删快，查询慢。&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;HashSet&lt;/strong&gt;：基于哈希表，无序、唯一。&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;TreeSet&lt;/strong&gt;：基于红黑树，自动排序、唯一。&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;HashMap&lt;/strong&gt;：基于哈希表，键值对，无序，key唯一。&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;TreeMap&lt;/strong&gt;：基于红黑树，键值对，自动按key排序。&lt;/li&gt;
&lt;/ul&gt;
&lt;h3&gt;4. 基本用法与常见操作&lt;/h3&gt;
&lt;ul&gt;
&lt;li&gt;添加元素：add()/put()&lt;/li&gt;
&lt;li&gt;删除元素：remove()&lt;/li&gt;
&lt;li&gt;判断包含：contains()/containsKey()&lt;/li&gt;
&lt;li&gt;获取元素：get()/iterator()/for-each&lt;/li&gt;
&lt;li&gt;清空集合：clear()&lt;/li&gt;
&lt;li&gt;获取大小：size()&lt;/li&gt;
&lt;/ul&gt;
&lt;h3&gt;5. 示例代码&lt;/h3&gt;
&lt;pre&gt;&lt;code&gt;import java.util.*;

List&amp;lt;String&amp;gt; list = new ArrayList&amp;lt;&amp;gt;();
list.add(&quot;A&quot;);
list.add(&quot;B&quot;);
System.out.println(list.get(0)); // A

Set&amp;lt;Integer&amp;gt; set = new HashSet&amp;lt;&amp;gt;();
set.add(1);
set.add(2);
System.out.println(set.contains(1)); // true

Map&amp;lt;String, Integer&amp;gt; map = new HashMap&amp;lt;&amp;gt;();
map.put(&quot;Tom&quot;, 18);
map.put(&quot;Jerry&quot;, 20);
System.out.println(map.get(&quot;Tom&quot;)); // 18
&lt;/code&gt;&lt;/pre&gt;
&lt;h3&gt;6. 遍历集合&lt;/h3&gt;
&lt;ul&gt;
&lt;li&gt;for-each循环：&lt;pre&gt;&lt;code&gt;for (String s : list) {
    System.out.println(s);
}
&lt;/code&gt;&lt;/pre&gt;
&lt;/li&gt;
&lt;li&gt;迭代器遍历：&lt;pre&gt;&lt;code&gt;Iterator&amp;lt;Integer&amp;gt; it = set.iterator();
while (it.hasNext()) {
    System.out.println(it.next());
}
&lt;/code&gt;&lt;/pre&gt;
&lt;/li&gt;
&lt;li&gt;Map遍历：&lt;pre&gt;&lt;code&gt;for (Map.Entry&amp;lt;String, Integer&amp;gt; entry : map.entrySet()) {
    System.out.println(entry.getKey() + &quot;:&quot; + entry.getValue());
}
&lt;/code&gt;&lt;/pre&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;blockquote&gt;
&lt;p&gt;集合是Java开发中最常用的数据结构，掌握其用法有助于高效管理和处理数据。&lt;/p&gt;
&lt;/blockquote&gt;
&lt;h2&gt;27. Java泛型（Generics）&lt;/h2&gt;
&lt;h3&gt;1. 概念&lt;/h3&gt;
&lt;ul&gt;
&lt;li&gt;泛型是JDK 1.5引入的特性，用于在类、接口、方法中定义和使用类型参数，实现类型安全和代码复用。&lt;/li&gt;
&lt;li&gt;常用于集合类、工具类等，避免强制类型转换和运行时类型错误。&lt;/li&gt;
&lt;/ul&gt;
&lt;h3&gt;2. 基本语法&lt;/h3&gt;
&lt;ul&gt;
&lt;li&gt;泛型类/接口：&lt;pre&gt;&lt;code&gt;class Box&amp;lt;T&amp;gt; {
    private T value;
    public void set(T value) { this.value = value; }
    public T get() { return value; }
}
Box&amp;lt;Integer&amp;gt; box = new Box&amp;lt;&amp;gt;();
box.set(123);
&lt;/code&gt;&lt;/pre&gt;
&lt;/li&gt;
&lt;li&gt;泛型方法：&lt;pre&gt;&lt;code&gt;public &amp;lt;T&amp;gt; void print(T t) {
    System.out.println(t);
}
&lt;/code&gt;&lt;/pre&gt;
&lt;/li&gt;
&lt;li&gt;泛型接口：&lt;pre&gt;&lt;code&gt;interface Converter&amp;lt;F, T&amp;gt; {
    T convert(F from);
}
&lt;/code&gt;&lt;/pre&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;h3&gt;3. 集合中的泛型&lt;/h3&gt;
&lt;ul&gt;
&lt;li&gt;指定集合元素类型，避免强转：&lt;pre&gt;&lt;code&gt;List&amp;lt;String&amp;gt; list = new ArrayList&amp;lt;&amp;gt;();
list.add(&quot;abc&quot;);
String s = list.get(0); // 无需强转
&lt;/code&gt;&lt;/pre&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;h3&gt;4. 通配符与类型限定&lt;/h3&gt;
&lt;ul&gt;
&lt;li&gt;&lt;code&gt;?&lt;/code&gt;：通配符，表示未知类型。&lt;/li&gt;
&lt;li&gt;&lt;code&gt;? extends T&lt;/code&gt;：上限通配符，表示T或T的子类（如读取数据时）。&lt;/li&gt;
&lt;li&gt;&lt;code&gt;? super T&lt;/code&gt;：下限通配符，表示T或T的父类（如写入数据时）。&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;&lt;strong&gt;示例：&lt;/strong&gt;&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;List&amp;lt;? extends Number&amp;gt; nums = new ArrayList&amp;lt;Integer&amp;gt;(); // 只能读，不能写
List&amp;lt;? super Integer&amp;gt; ints = new ArrayList&amp;lt;Number&amp;gt;(); // 可写入Integer及其子类
&lt;/code&gt;&lt;/pre&gt;
&lt;h3&gt;5. 好处&lt;/h3&gt;
&lt;ul&gt;
&lt;li&gt;类型安全：编译期检查类型，避免ClassCastException。&lt;/li&gt;
&lt;li&gt;代码复用：同一泛型类/方法可适用于多种类型。&lt;/li&gt;
&lt;li&gt;可读性强：代码意图明确。&lt;/li&gt;
&lt;/ul&gt;
&lt;blockquote&gt;
&lt;p&gt;泛型是Java类型系统的重要组成部分，合理使用可提升代码的健壮性和灵活性。&lt;/p&gt;
&lt;/blockquote&gt;
&lt;h2&gt;28. 数组与集合的选择：什么时候用集合，什么时候用数组？&lt;/h2&gt;
&lt;p&gt;在Java开发中，选择&quot;集合&quot;还是&quot;数组&quot;主要取决于你的具体需求。下面详细说明它们各自的适用场景和选择依据：&lt;/p&gt;
&lt;h3&gt;一、数组适用场景&lt;/h3&gt;
&lt;ol&gt;
&lt;li&gt;&lt;strong&gt;元素数量固定且已知&lt;/strong&gt;&lt;br /&gt;
例如：存储一周7天的名称、12个月份等。&lt;br /&gt;
一旦创建，长度不可变。&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;对性能要求极高&lt;/strong&gt;&lt;br /&gt;
数组在内存中是连续分布，访问速度快，适合对性能有极高要求的场景（如底层算法、频繁索引访问）。&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;存储基本数据类型或对象引用&lt;/strong&gt;&lt;br /&gt;
可以存储任何类型（基本类型或引用类型），但类型必须一致。&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;内存占用可控&lt;/strong&gt;&lt;br /&gt;
数组没有额外的对象封装和元数据，内存开销小。&lt;/li&gt;
&lt;/ol&gt;
&lt;h3&gt;二、集合适用场景&lt;/h3&gt;
&lt;ol&gt;
&lt;li&gt;&lt;strong&gt;元素数量不确定或经常变化&lt;/strong&gt;&lt;br /&gt;
例如：用户动态输入、数据量随时增减的场景。&lt;br /&gt;
集合可以动态扩容，元素数量不受限制。&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;需要丰富的操作和功能&lt;/strong&gt;&lt;br /&gt;
集合类（如List、Set、Map）提供了丰富的API，如增删查改、排序、去重、查找等。&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;需要存储对象（引用类型）&lt;/strong&gt;&lt;br /&gt;
集合只能存储对象，不能直接存储基本数据类型（但可以用包装类）。&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;需要灵活的数据结构&lt;/strong&gt;&lt;br /&gt;
如链表、队列、栈、哈希表、树等，集合框架都提供了相应实现。&lt;/li&gt;
&lt;/ol&gt;
&lt;h3&gt;三、选择建议&lt;/h3&gt;
&lt;ul&gt;
&lt;li&gt;&lt;strong&gt;数据量固定且类型单一&lt;/strong&gt;：优先用数组。&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;数据量不确定或需频繁增删&lt;/strong&gt;：优先用集合。&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;需要高级操作（如排序、去重、查找）&lt;/strong&gt;：优先用集合。&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;只需存储基本类型且追求极致性能&lt;/strong&gt;：用基本类型数组。&lt;/li&gt;
&lt;/ul&gt;
&lt;h3&gt;四、举例说明&lt;/h3&gt;
&lt;ul&gt;
&lt;li&gt;&lt;strong&gt;数组&lt;/strong&gt;：&lt;pre&gt;&lt;code&gt;int[] scores = new int[5]; // 固定5个成绩
&lt;/code&gt;&lt;/pre&gt;
&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;集合&lt;/strong&gt;：&lt;pre&gt;&lt;code&gt;List&amp;lt;Integer&amp;gt; scoreList = new ArrayList&amp;lt;&amp;gt;(); // 可以随时添加、删除成绩
&lt;/code&gt;&lt;/pre&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;h3&gt;五、总结口诀&lt;/h3&gt;
&lt;blockquote&gt;
&lt;p&gt;&quot;定长用数组，变长用集合；操作多用集合，性能极致用数组。&quot;&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;如需更详细的代码示例或对比，也可以补充说明！&lt;/p&gt;
&lt;h2&gt;29. static关键字详解&lt;/h2&gt;
&lt;p&gt;static是Java中用于修饰成员变量、成员方法、代码块和内部类的关键字，表示&quot;静态&quot;，即属于类本身而不是某个对象。&lt;/p&gt;
&lt;h3&gt;一、static的作用&lt;/h3&gt;
&lt;ol&gt;
&lt;li&gt;
&lt;p&gt;&lt;strong&gt;修饰成员变量（静态变量）&lt;/strong&gt;&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;属于类本身，所有对象共享一份内存。&lt;/li&gt;
&lt;li&gt;类加载时初始化，优先于对象存在。&lt;/li&gt;
&lt;li&gt;访问方式：&lt;code&gt;类名.变量名&lt;/code&gt; 或 &lt;code&gt;对象.变量名&lt;/code&gt;（推荐前者）。&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;&lt;strong&gt;修饰成员方法（静态方法）&lt;/strong&gt;&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;不依赖对象，可以直接通过类名调用。&lt;/li&gt;
&lt;li&gt;不能访问非static成员（因为没有this对象）。&lt;/li&gt;
&lt;li&gt;常用于工具类方法（如Math类的静态方法）。&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;&lt;strong&gt;修饰代码块（静态代码块）&lt;/strong&gt;&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;随类加载自动执行且只执行一次。&lt;/li&gt;
&lt;li&gt;常用于类的初始化操作（如加载驱动、初始化静态资源）。&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;&lt;strong&gt;修饰内部类（静态内部类）&lt;/strong&gt;&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;静态内部类不依赖外部类对象，可以直接创建。&lt;/li&gt;
&lt;li&gt;只能访问外部类的静态成员。&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;/ol&gt;
&lt;h3&gt;二、static的常见用法&lt;/h3&gt;
&lt;ul&gt;
&lt;li&gt;静态变量：&lt;pre&gt;&lt;code&gt;public class Demo {
    static int count = 0; // 静态变量，所有对象共享
}
&lt;/code&gt;&lt;/pre&gt;
&lt;/li&gt;
&lt;li&gt;静态方法：&lt;pre&gt;&lt;code&gt;public class MathUtil {
    public static int add(int a, int b) {
        return a + b;
    }
}
// 调用：MathUtil.add(1, 2);
&lt;/code&gt;&lt;/pre&gt;
&lt;/li&gt;
&lt;li&gt;静态代码块：&lt;pre&gt;&lt;code&gt;public class InitDemo {
    static {
        System.out.println(&quot;类被加载时执行，只执行一次&quot;);
    }
}
&lt;/code&gt;&lt;/pre&gt;
&lt;/li&gt;
&lt;li&gt;静态内部类：&lt;pre&gt;&lt;code&gt;public class Outer {
    static class Inner {
        void show() {
            System.out.println(&quot;静态内部类方法&quot;);
        }
    }
}
// 创建静态内部类对象：Outer.Inner inner = new Outer.Inner();
&lt;/code&gt;&lt;/pre&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;h3&gt;三、static的适用场景&lt;/h3&gt;
&lt;ul&gt;
&lt;li&gt;工具类方法（如Arrays、Collections、Math等）&lt;/li&gt;
&lt;li&gt;计数器、全局常量（如public static final PI）&lt;/li&gt;
&lt;li&gt;只需一份数据且所有对象共享的场景&lt;/li&gt;
&lt;li&gt;工厂方法、单例模式等设计模式实现&lt;/li&gt;
&lt;/ul&gt;
&lt;h3&gt;四、注意事项&lt;/h3&gt;
&lt;ul&gt;
&lt;li&gt;静态方法不能直接访问非静态成员（没有this）。&lt;/li&gt;
&lt;li&gt;静态方法中不能使用super、this关键字。&lt;/li&gt;
&lt;li&gt;静态成员随着类的加载而存在，优先于对象。&lt;/li&gt;
&lt;li&gt;静态变量会被所有对象共享，修改会影响所有对象。&lt;/li&gt;
&lt;li&gt;静态代码块只执行一次。&lt;/li&gt;
&lt;/ul&gt;
&lt;h3&gt;五、常见面试点&lt;/h3&gt;
&lt;ul&gt;
&lt;li&gt;static和实例成员的区别？&lt;/li&gt;
&lt;li&gt;静态方法能否被重写？（不能，但可以被子类隐藏）&lt;/li&gt;
&lt;li&gt;main方法为什么是static？（JVM无需创建对象即可调用）&lt;/li&gt;
&lt;li&gt;静态变量和常量的区别？（常量需加final修饰）&lt;/li&gt;
&lt;/ul&gt;
&lt;h3&gt;六、示例代码&lt;/h3&gt;
&lt;pre&gt;&lt;code&gt;public class StaticDemo {
    static int count = 0; // 静态变量
    int id; // 实例变量

    static {
        System.out.println(&quot;静态代码块：类加载时执行&quot;);
    }

    public StaticDemo(int id) {
        this.id = id;
        count++;
    }

    public static void showCount() {
        System.out.println(&quot;创建了&quot; + count + &quot;个对象&quot;);
        // System.out.println(id); // 错误，不能访问非静态成员
    }

    public void showId() {
        System.out.println(&quot;对象id：&quot; + id);
    }

    public static void main(String[] args) {
        StaticDemo a = new StaticDemo(1);
        StaticDemo b = new StaticDemo(2);
        StaticDemo.showCount(); // 创建了2个对象
        a.showId(); // 对象id：1
        b.showId(); // 对象id：2
    }
}
&lt;/code&gt;&lt;/pre&gt;
&lt;blockquote&gt;
&lt;p&gt;static是Java类成员管理和工具方法设计的核心，理解其原理有助于写出高效、规范的Java代码。&lt;/p&gt;
&lt;/blockquote&gt;
&lt;h2&gt;30. Java中的继承（Inheritance）&lt;/h2&gt;
&lt;h3&gt;1. 概念&lt;/h3&gt;
&lt;ul&gt;
&lt;li&gt;继承是面向对象编程的三大特性之一，允许子类自动获得父类的属性和方法，实现代码复用和扩展。&lt;/li&gt;
&lt;li&gt;Java使用&lt;code&gt;extends&lt;/code&gt;关键字实现类的继承。&lt;/li&gt;
&lt;/ul&gt;
&lt;h3&gt;2. 语法&lt;/h3&gt;
&lt;pre&gt;&lt;code&gt;class 父类名 {
    // 父类内容
}
class 子类名 extends 父类名 {
    // 子类内容
}
&lt;/code&gt;&lt;/pre&gt;
&lt;h3&gt;3. 特性&lt;/h3&gt;
&lt;ul&gt;
&lt;li&gt;子类继承父类的非private成员（属性和方法）。&lt;/li&gt;
&lt;li&gt;子类可以新增自己的成员。&lt;/li&gt;
&lt;li&gt;子类可以重写（Override）父类的方法。&lt;/li&gt;
&lt;li&gt;Java只支持单继承（一个类只能有一个直接父类），但支持多层继承和多实现接口。&lt;/li&gt;
&lt;/ul&gt;
&lt;h3&gt;4. super关键字&lt;/h3&gt;
&lt;ul&gt;
&lt;li&gt;super用于在子类中引用父类的成员（属性、方法、构造器）。&lt;/li&gt;
&lt;li&gt;可用于区分同名成员、调用父类构造方法。&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;&lt;strong&gt;示例：&lt;/strong&gt;&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;class Animal {
    String name = &quot;动物&quot;;
    void eat() { System.out.println(&quot;吃东西&quot;); }
}
class Dog extends Animal {
    String name = &quot;狗&quot;;
    void eat() {
        super.eat(); // 调用父类方法
        System.out.println(&quot;狗吃骨头&quot;);
    }
    void printName() {
        System.out.println(super.name); // 父类name
        System.out.println(this.name);  // 子类name
    }
}
&lt;/code&gt;&lt;/pre&gt;
&lt;h3&gt;5. 构造方法调用&lt;/h3&gt;
&lt;ul&gt;
&lt;li&gt;子类构造方法会默认先调用父类的无参构造方法（super()），如需调用父类有参构造，需显式写super(参数)。&lt;/li&gt;
&lt;li&gt;父类构造方法执行完后才执行子类构造方法。&lt;/li&gt;
&lt;/ul&gt;
&lt;h3&gt;6. 方法重写（Override）&lt;/h3&gt;
&lt;ul&gt;
&lt;li&gt;子类可重写父类方法，方法名、参数、返回值完全一致，访问权限不能更低。&lt;/li&gt;
&lt;li&gt;重写方法可加@Override注解，提升可读性和安全性。&lt;/li&gt;
&lt;/ul&gt;
&lt;h3&gt;7. final/abstract与继承&lt;/h3&gt;
&lt;ul&gt;
&lt;li&gt;final类不能被继承，final方法不能被重写，final变量为常量。&lt;/li&gt;
&lt;li&gt;abstract类不能实例化，抽象方法无方法体，子类必须实现。&lt;/li&gt;
&lt;/ul&gt;
&lt;h3&gt;8. 单继承与多态&lt;/h3&gt;
&lt;ul&gt;
&lt;li&gt;Java类只支持单继承，但可多层继承和多实现接口。&lt;/li&gt;
&lt;li&gt;继承是实现多态的基础，父类引用可指向子类对象。&lt;/li&gt;
&lt;/ul&gt;
&lt;h3&gt;9. 常见面试点&lt;/h3&gt;
&lt;ul&gt;
&lt;li&gt;构造方法的调用顺序？（父类→子类）&lt;/li&gt;
&lt;li&gt;super和this的区别？&lt;/li&gt;
&lt;li&gt;方法重写与重载的区别？&lt;/li&gt;
&lt;li&gt;final/abstract与继承的关系？&lt;/li&gt;
&lt;li&gt;为什么Java不支持多继承？（避免菱形继承、简化设计）&lt;/li&gt;
&lt;/ul&gt;
&lt;h3&gt;10. 示例代码&lt;/h3&gt;
&lt;pre&gt;&lt;code&gt;class Animal {
    void speak() { System.out.println(&quot;动物叫&quot;); }
}
class Cat extends Animal {
    @Override
    void speak() { System.out.println(&quot;喵喵&quot;); }
}
public class Test {
    public static void main(String[] args) {
        Animal a = new Cat();
        a.speak(); // 输出：喵喵
    }
}
&lt;/code&gt;&lt;/pre&gt;
&lt;blockquote&gt;
&lt;p&gt;继承是Java代码复用和多态实现的基础，合理设计继承结构有助于提升系统的可维护性和扩展性。&lt;/p&gt;
&lt;/blockquote&gt;
&lt;h3&gt;11. 继承中成员变量和成员方法的访问与重写&lt;/h3&gt;
&lt;h4&gt;1. 成员变量&lt;/h4&gt;
&lt;ul&gt;
&lt;li&gt;子类可以定义与父类同名的成员变量，这种情况下，子类变量会&quot;隐藏&quot;父类变量。&lt;/li&gt;
&lt;li&gt;通过子类对象访问变量时，&lt;strong&gt;编译和运行都看引用类型&lt;/strong&gt;，即变量的类型由引用变量的声明类型决定。&lt;/li&gt;
&lt;li&gt;可用super关键字访问父类的同名变量。&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;&lt;strong&gt;示例：&lt;/strong&gt;&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;class Parent {
    int x = 1;
}
class Child extends Parent {
    int x = 2;
    void print() {
        System.out.println(x); // 2，子类变量
        System.out.println(super.x); // 1，父类变量
    }
}
Parent p = new Child();
System.out.println(p.x); // 1，引用类型为Parent
&lt;/code&gt;&lt;/pre&gt;
&lt;h4&gt;2. 成员方法&lt;/h4&gt;
&lt;ul&gt;
&lt;li&gt;子类可以重写（Override）父类的成员方法。&lt;/li&gt;
&lt;li&gt;方法重写时，&lt;strong&gt;编译看引用类型，运行看对象实际类型&lt;/strong&gt;（多态）。&lt;/li&gt;
&lt;li&gt;可用super关键字调用父类被重写的方法。&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;&lt;strong&gt;示例：&lt;/strong&gt;&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;class Parent {
    void show() { System.out.println(&quot;Parent&quot;); }
}
class Child extends Parent {
    @Override
    void show() { System.out.println(&quot;Child&quot;); }
}
Parent p = new Child();
p.show(); // 输出：Child
&lt;/code&gt;&lt;/pre&gt;
&lt;h4&gt;3. 访问权限&lt;/h4&gt;
&lt;ul&gt;
&lt;li&gt;private成员不能被继承，protected和public成员可被继承。&lt;/li&gt;
&lt;li&gt;子类重写方法时，访问权限不能低于父类方法。&lt;/li&gt;
&lt;/ul&gt;
&lt;h4&gt;4. super与this的区别&lt;/h4&gt;
&lt;ul&gt;
&lt;li&gt;this：指向当前对象（子类对象），可访问本类成员。&lt;/li&gt;
&lt;li&gt;super：指向父类对象（父类部分），可访问父类被隐藏/重写的成员。&lt;/li&gt;
&lt;/ul&gt;
&lt;blockquote&gt;
&lt;p&gt;理解继承中成员变量和方法的访问与重写规则，有助于正确使用多态、避免变量隐藏和方法覆盖带来的困惑。&lt;/p&gt;
&lt;/blockquote&gt;
&lt;h2&gt;30.2 Java方法重写（Override）详解&lt;/h2&gt;
&lt;h3&gt;1. 概念&lt;/h3&gt;
&lt;ul&gt;
&lt;li&gt;方法重写（Override）是指子类对父类继承的方法进行重新实现，方法名、参数列表、返回值类型完全一致。&lt;/li&gt;
&lt;li&gt;目的是让子类对象调用时有不同的行为，实现多态。&lt;/li&gt;
&lt;/ul&gt;
&lt;h3&gt;2. 语法规则&lt;/h3&gt;
&lt;ul&gt;
&lt;li&gt;子类方法必须与父类被重写方法的方法名、参数列表、返回值类型完全一致。&lt;/li&gt;
&lt;li&gt;访问权限不能低于父类（可相同或更大）。&lt;/li&gt;
&lt;li&gt;不能重写父类的private、static、final方法。&lt;/li&gt;
&lt;li&gt;子类方法抛出的异常不能比父类更宽泛。&lt;/li&gt;
&lt;li&gt;推荐加@Override注解，编译器会检查是否正确重写。&lt;/li&gt;
&lt;/ul&gt;
&lt;h3&gt;3. @Override注解&lt;/h3&gt;
&lt;ul&gt;
&lt;li&gt;放在重写方法前，提升代码可读性和安全性。&lt;/li&gt;
&lt;li&gt;如果方法签名不一致，编译器会报错。&lt;/li&gt;
&lt;/ul&gt;
&lt;h3&gt;4. 与重载（Overload）的区别&lt;/h3&gt;
&lt;ul&gt;
&lt;li&gt;&lt;strong&gt;重写&lt;/strong&gt;：子类对父类方法重新实现，方法签名完全一致，发生在继承体系中。&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;重载&lt;/strong&gt;：同一个类中方法名相同，参数列表不同，返回值类型可不同。&lt;/li&gt;
&lt;/ul&gt;
&lt;h3&gt;5. 常见面试点&lt;/h3&gt;
&lt;ul&gt;
&lt;li&gt;重写方法的访问权限、异常、返回值要求？&lt;/li&gt;
&lt;li&gt;static方法能否重写？（不能，只能被隐藏）&lt;/li&gt;
&lt;li&gt;构造方法能否重写？（不能）&lt;/li&gt;
&lt;li&gt;为什么要用@Override注解？&lt;/li&gt;
&lt;/ul&gt;
&lt;h3&gt;6. 示例代码&lt;/h3&gt;
&lt;pre&gt;&lt;code&gt;class Animal {
    void speak() { System.out.println(&quot;动物叫&quot;); }
}
class Dog extends Animal {
    @Override
    void speak() { System.out.println(&quot;汪汪&quot;); }
}
public class Test {
    public static void main(String[] args) {
        Animal a = new Dog();
        a.speak(); // 输出：汪汪
    }
}
&lt;/code&gt;&lt;/pre&gt;
&lt;blockquote&gt;
&lt;p&gt;方法重写是实现多态的基础，合理使用可提升代码的灵活性和可维护性。&lt;/p&gt;
&lt;/blockquote&gt;
&lt;h3&gt;12. 继承中构造方法的自动调用（super）&lt;/h3&gt;
&lt;ul&gt;
&lt;li&gt;在Java中，子类构造方法执行时会&lt;strong&gt;自动调用父类的构造方法&lt;/strong&gt;，以保证父类部分被正确初始化。&lt;/li&gt;
&lt;li&gt;如果子类构造方法没有显式写&lt;code&gt;super(...)&lt;/code&gt;，编译器会自动在第一行加上&lt;code&gt;super();&lt;/code&gt;，即调用父类的无参构造方法。&lt;/li&gt;
&lt;li&gt;如果父类没有无参构造方法，子类必须显式调用父类的有参构造方法，否则编译报错。&lt;/li&gt;
&lt;li&gt;&lt;code&gt;super(...)&lt;/code&gt;必须是子类构造方法的第一条语句。&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;&lt;strong&gt;调用顺序：&lt;/strong&gt;&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;先执行父类构造方法（super），再执行子类构造方法。&lt;/li&gt;
&lt;li&gt;多层继承时，先最顶层父类，再逐层向下。&lt;/li&gt;
&lt;/ol&gt;
&lt;p&gt;&lt;strong&gt;示例：&lt;/strong&gt;&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;class Parent {
    Parent() { System.out.println(&quot;父类构造&quot;); }
}
class Child extends Parent {
    Child() { System.out.println(&quot;子类构造&quot;); }
}
public class Test {
    public static void main(String[] args) {
        new Child();
        // 输出：
        // 父类构造
        // 子类构造
    }
}
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;&lt;strong&gt;有参构造情况：&lt;/strong&gt;&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;class Parent {
    Parent(String msg) { System.out.println(msg); }
}
class Child extends Parent {
    Child() { super(&quot;父类有参构造&quot;); System.out.println(&quot;子类构造&quot;); }
}
&lt;/code&gt;&lt;/pre&gt;
&lt;blockquote&gt;
&lt;p&gt;理解构造方法的自动调用顺序，有助于正确初始化继承体系中的对象，避免常见的编译和运行错误。&lt;/p&gt;
&lt;/blockquote&gt;
&lt;h3&gt;13. 构造方法中this(...)的含义与用法&lt;/h3&gt;
&lt;ul&gt;
&lt;li&gt;在Java构造方法中，&lt;code&gt;this(...)&lt;/code&gt;用于调用本类的另一个构造方法，实现构造方法之间的重用和统一初始化。&lt;/li&gt;
&lt;li&gt;&lt;code&gt;this(...)&lt;/code&gt;只能出现在构造方法的第一行，且不能与super(...)同时出现。&lt;/li&gt;
&lt;li&gt;通过this(...)可以实现构造方法的链式调用，最终会调用到某个没有this(...)的构造方法（通常是最全参的构造方法）。&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;&lt;strong&gt;示例：&lt;/strong&gt;&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;class Person {
    String name;
    int age;

    // 无参构造
    public Person() {
        this(&quot;无名氏&quot;, 0); // 调用有参构造
        System.out.println(&quot;无参构造执行&quot;);
    }

    // 有参构造
    public Person(String name, int age) {
        this.name = name;
        this.age = age;
        System.out.println(&quot;有参构造执行&quot;);
    }
}

public class Test {
    public static void main(String[] args) {
        Person p = new Person();
        // 输出：
        // 有参构造执行
        // 无参构造执行
    }
}
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;&lt;strong&gt;注意事项：&lt;/strong&gt;&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;this(...)只能用于构造方法，且必须是第一条语句。&lt;/li&gt;
&lt;li&gt;不能和super(...)同时出现。&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;&lt;strong&gt;作用总结：&lt;/strong&gt;&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;统一初始化逻辑，减少重复代码。&lt;/li&gt;
&lt;li&gt;便于维护和扩展构造方法。&lt;/li&gt;
&lt;/ul&gt;
&lt;blockquote&gt;
&lt;p&gt;在构造方法中用this(...)，就是&quot;让本类的另一个构造方法帮我初始化&quot;。&lt;/p&gt;
&lt;/blockquote&gt;
&lt;h3&gt;14. 多态（Polymorphism）&lt;/h3&gt;
&lt;p&gt;多态是面向对象编程的三大特性之一，指同一个行为具有多个不同表现形式或形态的能力。在Java中，多态主要通过继承和接口实现。&lt;/p&gt;
&lt;h4&gt;14.1 多态的概念&lt;/h4&gt;
&lt;ul&gt;
&lt;li&gt;&lt;strong&gt;多态&lt;/strong&gt;：同一个接口，使用不同的实例而执行不同操作。&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;编译时多态&lt;/strong&gt;：方法重载（Overloading）&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;运行时多态&lt;/strong&gt;：方法重写（Overriding）&lt;/li&gt;
&lt;/ul&gt;
&lt;h4&gt;14.2 多态的实现方式&lt;/h4&gt;
&lt;p&gt;&lt;strong&gt;1. 继承多态&lt;/strong&gt;&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;class Animal {
    public void makeSound() {
        System.out.println(&quot;动物发出声音&quot;);
    }
}

class Dog extends Animal {
    @Override
    public void makeSound() {
        System.out.println(&quot;汪汪汪&quot;);
    }
}

class Cat extends Animal {
    @Override
    public void makeSound() {
        System.out.println(&quot;喵喵喵&quot;);
    }
}

public class Test {
    public static void main(String[] args) {
        Animal animal1 = new Dog(); // 父类引用指向子类对象
        Animal animal2 = new Cat();
        
        animal1.makeSound(); // 输出：汪汪汪
        animal2.makeSound(); // 输出：喵喵喵
    }
}
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;&lt;strong&gt;2. 接口多态&lt;/strong&gt;&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;interface Flyable {
    void fly();
}

class Bird implements Flyable {
    @Override
    public void fly() {
        System.out.println(&quot;鸟儿飞翔&quot;);
    }
}

class Airplane implements Flyable {
    @Override
    public void fly() {
        System.out.println(&quot;飞机飞行&quot;);
    }
}

public class Test {
    public static void main(String[] args) {
        Flyable flyable1 = new Bird();
        Flyable flyable2 = new Airplane();
        
        flyable1.fly(); // 输出：鸟儿飞翔
        flyable2.fly(); // 输出：飞机飞行
    }
}
&lt;/code&gt;&lt;/pre&gt;
&lt;h4&gt;14.3 多态的特点&lt;/h4&gt;
&lt;p&gt;&lt;strong&gt;1. 向上转型（Upcasting）&lt;/strong&gt;&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;子类对象可以赋值给父类引用&lt;/li&gt;
&lt;li&gt;自动类型转换，安全可靠&lt;/li&gt;
&lt;/ul&gt;
&lt;pre&gt;&lt;code&gt;Animal animal = new Dog(); // 向上转型
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;&lt;strong&gt;2. 向下转型（Downcasting）&lt;/strong&gt;&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;父类引用转换为子类引用&lt;/li&gt;
&lt;li&gt;需要强制类型转换，可能抛出ClassCastException&lt;/li&gt;
&lt;/ul&gt;
&lt;pre&gt;&lt;code&gt;Animal animal = new Dog();
Dog dog = (Dog) animal; // 向下转型
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;&lt;strong&gt;3. instanceof运算符&lt;/strong&gt;&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;用于判断对象是否为某个类的实例&lt;/li&gt;
&lt;li&gt;避免ClassCastException异常&lt;/li&gt;
&lt;/ul&gt;
&lt;pre&gt;&lt;code&gt;Animal animal = new Dog();
if (animal instanceof Dog) {
    Dog dog = (Dog) animal;
    dog.makeSound();
}
&lt;/code&gt;&lt;/pre&gt;
&lt;h4&gt;14.4 多态的应用场景&lt;/h4&gt;
&lt;p&gt;&lt;strong&gt;1. 方法参数多态&lt;/strong&gt;&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;public class AnimalTrainer {
    public void train(Animal animal) {
        animal.makeSound();
    }
}

public class Test {
    public static void main(String[] args) {
        AnimalTrainer trainer = new AnimalTrainer();
        trainer.train(new Dog()); // 输出：汪汪汪
        trainer.train(new Cat()); // 输出：喵喵喵
    }
}
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;&lt;strong&gt;2. 方法返回值多态&lt;/strong&gt;&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;public class AnimalFactory {
    public static Animal createAnimal(String type) {
        if (&quot;dog&quot;.equals(type)) {
            return new Dog();
        } else if (&quot;cat&quot;.equals(type)) {
            return new Cat();
        }
        return null;
    }
}
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;&lt;strong&gt;3. 集合中的多态&lt;/strong&gt;&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;List&amp;lt;Animal&amp;gt; animals = new ArrayList&amp;lt;&amp;gt;();
animals.add(new Dog());
animals.add(new Cat());

for (Animal animal : animals) {
    animal.makeSound(); // 多态调用
}
&lt;/code&gt;&lt;/pre&gt;
&lt;h4&gt;14.5 多态的注意事项&lt;/h4&gt;
&lt;p&gt;&lt;strong&gt;1. 成员变量没有多态&lt;/strong&gt;&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;class Parent {
    int num = 10;
}

class Child extends Parent {
    int num = 20;
}

public class Test {
    public static void main(String[] args) {
        Parent p = new Child();
        System.out.println(p.num); // 输出：10（成员变量没有多态）
    }
}
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;&lt;strong&gt;2. 静态方法没有多态&lt;/strong&gt;&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;class Parent {
    public static void method() {
        System.out.println(&quot;父类静态方法&quot;);
    }
}

class Child extends Parent {
    public static void method() {
        System.out.println(&quot;子类静态方法&quot;);
    }
}

public class Test {
    public static void main(String[] args) {
        Parent p = new Child();
        p.method(); // 输出：父类静态方法（静态方法没有多态）
    }
}
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;&lt;strong&gt;3. 构造方法没有多态&lt;/strong&gt;&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;构造方法不能被重写，因此不存在多态&lt;/li&gt;
&lt;/ul&gt;
&lt;h4&gt;14.6 多态的优势&lt;/h4&gt;
&lt;ol&gt;
&lt;li&gt;&lt;strong&gt;可扩展性&lt;/strong&gt;：新增子类不需要修改现有代码&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;可维护性&lt;/strong&gt;：统一的接口，便于维护&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;可复用性&lt;/strong&gt;：一个方法可以处理多种类型的对象&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;灵活性&lt;/strong&gt;：运行时动态绑定，提高程序灵活性&lt;/li&gt;
&lt;/ol&gt;
&lt;h4&gt;14.7 实际应用示例&lt;/h4&gt;
&lt;p&gt;&lt;strong&gt;图形计算器&lt;/strong&gt;&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;abstract class Shape {
    abstract double getArea();
    abstract double getPerimeter();
}

class Circle extends Shape {
    private double radius;
    
    public Circle(double radius) {
        this.radius = radius;
    }
    
    @Override
    double getArea() {
        return Math.PI * radius * radius;
    }
    
    @Override
    double getPerimeter() {
        return 2 * Math.PI * radius;
    }
}

class Rectangle extends Shape {
    private double width, height;
    
    public Rectangle(double width, double height) {
        this.width = width;
        this.height = height;
    }
    
    @Override
    double getArea() {
        return width * height;
    }
    
    @Override
    double getPerimeter() {
        return 2 * (width + height);
    }
}

public class ShapeCalculator {
    public static void printShapeInfo(Shape shape) {
        System.out.println(&quot;面积：&quot; + shape.getArea());
        System.out.println(&quot;周长：&quot; + shape.getPerimeter());
    }
    
    public static void main(String[] args) {
        printShapeInfo(new Circle(5));
        printShapeInfo(new Rectangle(4, 6));
    }
}
&lt;/code&gt;&lt;/pre&gt;
&lt;blockquote&gt;
&lt;p&gt;多态是面向对象编程的核心特性，通过多态可以实现代码的灵活性和可扩展性，是设计优秀面向对象程序的重要基础。&lt;/p&gt;
&lt;/blockquote&gt;
&lt;h3&gt;15. 多态中成员变量与成员方法的调用机制&lt;/h3&gt;
&lt;p&gt;多态中成员变量和成员方法的调用行为存在重要差异，理解这些差异有助于正确使用多态特性。&lt;/p&gt;
&lt;h4&gt;15.1 成员变量的调用机制&lt;/h4&gt;
&lt;p&gt;&lt;strong&gt;核心规则：成员变量没有多态，编译时确定&lt;/strong&gt;&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;class Parent {
    int num = 10;
    String name = &quot;父类&quot;;
    
    public void showInfo() {
        System.out.println(&quot;父类方法中的num: &quot; + num);
        System.out.println(&quot;父类方法中的name: &quot; + name);
    }
}

class Child extends Parent {
    int num = 20;
    String name = &quot;子类&quot;;
    
    @Override
    public void showInfo() {
        System.out.println(&quot;子类方法中的num: &quot; + num);
        System.out.println(&quot;子类方法中的name: &quot; + name);
    }
}

public class Test {
    public static void main(String[] args) {
        Parent p = new Child(); // 父类引用指向子类对象
        
        // 成员变量调用 - 看引用类型
        System.out.println(p.num);    // 输出：10（父类的num）
        System.out.println(p.name);   // 输出：父类（父类的name）
        
        // 成员方法调用 - 看对象类型
        p.showInfo(); // 输出：
        // 子类方法中的num: 20
        // 子类方法中的name: 子类
    }
}
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;&lt;strong&gt;内存原理分析：&lt;/strong&gt;&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;内存中的对象结构：
Child对象 {
    Parent部分 {
        num = 10
        name = &quot;父类&quot;
    }
    Child部分 {
        num = 20  (隐藏了父类的num)
        name = &quot;子类&quot;  (隐藏了父类的name)
    }
}

当使用 Parent p = new Child() 时：
- p.num 访问的是 Parent 部分的 num (10)
- p.showInfo() 调用的是 Child 的 showInfo() 方法
- 在 Child 的 showInfo() 方法中，num 和 name 访问的是 Child 部分的变量
&lt;/code&gt;&lt;/pre&gt;
&lt;h4&gt;15.2 成员方法的调用机制&lt;/h4&gt;
&lt;p&gt;&lt;strong&gt;核心规则：成员方法有多态，运行时确定&lt;/strong&gt;&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;class Animal {
    public void eat() {
        System.out.println(&quot;动物吃东西&quot;);
    }
    
    public void sleep() {
        System.out.println(&quot;动物睡觉&quot;);
        this.eat(); // 调用的是实际对象的方法
    }
}

class Dog extends Animal {
    @Override
    public void eat() {
        System.out.println(&quot;狗吃骨头&quot;);
    }
    
    public void bark() {
        System.out.println(&quot;狗叫&quot;);
    }
}

public class Test {
    public static void main(String[] args) {
        Animal animal = new Dog();
        
        animal.eat();   // 输出：狗吃骨头（多态调用）
        animal.sleep(); // 输出：动物睡觉 + 狗吃骨头
        // animal.bark(); // 编译错误：父类引用无法调用子类特有方法
    }
}
&lt;/code&gt;&lt;/pre&gt;
&lt;h4&gt;15.3 静态成员的调用机制&lt;/h4&gt;
&lt;p&gt;&lt;strong&gt;核心规则：静态成员没有多态，编译时确定&lt;/strong&gt;&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;class Parent {
    static int staticNum = 100;
    
    public static void staticMethod() {
        System.out.println(&quot;父类静态方法&quot;);
    }
    
    public void instanceMethod() {
        System.out.println(&quot;父类实例方法&quot;);
    }
}

class Child extends Parent {
    static int staticNum = 200;
    
    public static void staticMethod() {
        System.out.println(&quot;子类静态方法&quot;);
    }
    
    @Override
    public void instanceMethod() {
        System.out.println(&quot;子类实例方法&quot;);
    }
}

public class Test {
    public static void main(String[] args) {
        Parent p = new Child();
        
        // 静态成员调用 - 看引用类型
        System.out.println(p.staticNum);     // 输出：100
        p.staticMethod();                    // 输出：父类静态方法
        
        // 实例方法调用 - 看对象类型
        p.instanceMethod();                  // 输出：子类实例方法
    }
}
&lt;/code&gt;&lt;/pre&gt;
&lt;h4&gt;15.4 构造方法中的调用&lt;/h4&gt;
&lt;pre&gt;&lt;code&gt;class Parent {
    int num = 10;
    
    public Parent() {
        System.out.println(&quot;父类构造方法中的num: &quot; + num);
        this.showNum();
    }
    
    public void showNum() {
        System.out.println(&quot;父类showNum方法中的num: &quot; + num);
    }
}

class Child extends Parent {
    int num = 20;
    
    public Child() {
        super(); // 调用父类构造方法
        System.out.println(&quot;子类构造方法中的num: &quot; + num);
        this.showNum();
    }
    
    @Override
    public void showNum() {
        System.out.println(&quot;子类showNum方法中的num: &quot; + num);
    }
}

public class Test {
    public static void main(String[] args) {
        Child child = new Child();
        // 输出：
        // 父类构造方法中的num: 10
        // 父类showNum方法中的num: 10
        // 子类构造方法中的num: 20
        // 子类showNum方法中的num: 20
    }
}
&lt;/code&gt;&lt;/pre&gt;
&lt;h4&gt;15.5 实际应用中的注意事项&lt;/h4&gt;
&lt;p&gt;&lt;strong&gt;1. 避免在父类方法中直接访问成员变量&lt;/strong&gt;&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;class Shape {
    protected double area;
    
    public void calculateArea() {
        // 子类重写此方法时，area的访问可能不符合预期
        System.out.println(&quot;面积: &quot; + area);
    }
}

class Circle extends Shape {
    private double radius;
    
    @Override
    public void calculateArea() {
        this.area = Math.PI * radius * radius; // 正确设置area
        super.calculateArea(); // 调用父类方法显示
    }
}
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;&lt;strong&gt;2. 使用getter/setter方法&lt;/strong&gt;&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;class Person {
    private String name;
    
    public String getName() {
        return name;
    }
    
    public void setName(String name) {
        this.name = name;
    }
}

class Student extends Person {
    private String studentId;
    
    @Override
    public String getName() {
        return &quot;学生: &quot; + super.getName();
    }
}
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;&lt;strong&gt;3. 方法重写时的变量访问&lt;/strong&gt;&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;class Base {
    protected int value = 10;
    
    public void display() {
        System.out.println(&quot;Base value: &quot; + value);
    }
}

class Derived extends Base {
    protected int value = 20;
    
    @Override
    public void display() {
        System.out.println(&quot;Derived value: &quot; + value);        // 20
        System.out.println(&quot;Base value: &quot; + super.value);     // 10
    }
}
&lt;/code&gt;&lt;/pre&gt;
&lt;h4&gt;15.6 总结对比&lt;/h4&gt;
&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;特性&lt;/th&gt;
&lt;th&gt;成员变量&lt;/th&gt;
&lt;th&gt;成员方法&lt;/th&gt;
&lt;th&gt;静态成员&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;多态性&lt;/td&gt;
&lt;td&gt;❌ 无多态&lt;/td&gt;
&lt;td&gt;✅ 有多态&lt;/td&gt;
&lt;td&gt;❌ 无多态&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;调用依据&lt;/td&gt;
&lt;td&gt;引用类型&lt;/td&gt;
&lt;td&gt;对象类型&lt;/td&gt;
&lt;td&gt;引用类型&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;确定时机&lt;/td&gt;
&lt;td&gt;编译时&lt;/td&gt;
&lt;td&gt;运行时&lt;/td&gt;
&lt;td&gt;编译时&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;内存访问&lt;/td&gt;
&lt;td&gt;直接访问&lt;/td&gt;
&lt;td&gt;动态绑定&lt;/td&gt;
&lt;td&gt;直接访问&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;
&lt;blockquote&gt;
&lt;p&gt;&lt;strong&gt;记忆口诀&lt;/strong&gt;：变量看引用，方法看对象，静态看引用。理解这个机制有助于避免多态使用中的常见错误。&lt;/p&gt;
&lt;/blockquote&gt;
&lt;h3&gt;16. 多态中的强制类型转换&lt;/h3&gt;
&lt;p&gt;多态中的类型转换是Java面向对象编程中的重要概念，包括向上转型和向下转型两种方式。&lt;/p&gt;
&lt;h4&gt;16.1 向上转型（Upcasting）&lt;/h4&gt;
&lt;p&gt;&lt;strong&gt;概念：&lt;/strong&gt; 子类对象赋值给父类引用，自动进行类型转换。&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;class Animal {
    public void eat() {
        System.out.println(&quot;动物吃东西&quot;);
    }
}

class Dog extends Animal {
    @Override
    public void eat() {
        System.out.println(&quot;狗吃骨头&quot;);
    }
    
    public void bark() {
        System.out.println(&quot;狗叫&quot;);
    }
}

public class Test {
    public static void main(String[] args) {
        // 向上转型 - 自动转换，安全可靠
        Animal animal = new Dog();
        animal.eat(); // 可以调用父类方法
        
        // animal.bark(); // 编译错误：父类引用无法调用子类特有方法
    }
}
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;&lt;strong&gt;特点：&lt;/strong&gt;&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;自动进行，无需强制转换&lt;/li&gt;
&lt;li&gt;安全可靠，不会抛出异常&lt;/li&gt;
&lt;li&gt;可以调用父类方法，无法调用子类特有方法&lt;/li&gt;
&lt;/ul&gt;
&lt;h4&gt;16.2 向下转型（Downcasting）&lt;/h4&gt;
&lt;p&gt;&lt;strong&gt;概念：&lt;/strong&gt; 父类引用转换为子类引用，需要强制类型转换。&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;class Animal {
    public void eat() {
        System.out.println(&quot;动物吃东西&quot;);
    }
}

class Dog extends Animal {
    @Override
    public void eat() {
        System.out.println(&quot;狗吃骨头&quot;);
    }
    
    public void bark() {
        System.out.println(&quot;狗叫&quot;);
    }
}

class Cat extends Animal {
    @Override
    public void eat() {
        System.out.println(&quot;猫吃鱼&quot;);
    }
    
    public void meow() {
        System.out.println(&quot;猫叫&quot;);
    }
}

public class Test {
    public static void main(String[] args) {
        Animal animal1 = new Dog();
        Animal animal2 = new Cat();
        
        // 向下转型 - 需要强制转换
        Dog dog = (Dog) animal1; // 成功
        dog.bark(); // 可以调用子类特有方法
        
        // Cat cat = (Cat) animal1; // 运行时异常：ClassCastException
    }
}
&lt;/code&gt;&lt;/pre&gt;
&lt;h4&gt;16.3 instanceof运算符&lt;/h4&gt;
&lt;p&gt;&lt;strong&gt;作用：&lt;/strong&gt; 判断对象是否为某个类的实例，避免ClassCastException异常。&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;public class Test {
    public static void main(String[] args) {
        Animal animal1 = new Dog();
        Animal animal2 = new Cat();
        
        // 使用instanceof进行安全转换
        if (animal1 instanceof Dog) {
            Dog dog = (Dog) animal1;
            dog.bark();
        }
        
        if (animal2 instanceof Cat) {
            Cat cat = (Cat) animal2;
            cat.meow();
        }
        
        // 检查是否为null
        Animal animal3 = null;
        System.out.println(animal3 instanceof Animal); // false，null不是任何类的实例
    }
}
&lt;/code&gt;&lt;/pre&gt;
&lt;h4&gt;16.4 类型转换的实际应用&lt;/h4&gt;
&lt;p&gt;&lt;strong&gt;1. 集合中的类型转换&lt;/strong&gt;&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;import java.util.*;

class Animal {
    public void eat() {
        System.out.println(&quot;动物吃东西&quot;);
    }
}

class Dog extends Animal {
    @Override
    public void eat() {
        System.out.println(&quot;狗吃骨头&quot;);
    }
    
    public void bark() {
        System.out.println(&quot;狗叫&quot;);
    }
}

class Cat extends Animal {
    @Override
    public void eat() {
        System.out.println(&quot;猫吃鱼&quot;);
    }
    
    public void meow() {
        System.out.println(&quot;猫叫&quot;);
    }
}

public class Test {
    public static void main(String[] args) {
        List&amp;lt;Animal&amp;gt; animals = new ArrayList&amp;lt;&amp;gt;();
        animals.add(new Dog());
        animals.add(new Cat());
        animals.add(new Dog());
        
        for (Animal animal : animals) {
            animal.eat(); // 多态调用
            
            // 根据类型调用特有方法
            if (animal instanceof Dog) {
                Dog dog = (Dog) animal;
                dog.bark();
            } else if (animal instanceof Cat) {
                Cat cat = (Cat) animal;
                cat.meow();
            }
        }
    }
}
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;&lt;strong&gt;2. 工厂模式中的类型转换&lt;/strong&gt;&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;interface Vehicle {
    void drive();
}

class Car implements Vehicle {
    @Override
    public void drive() {
        System.out.println(&quot;汽车行驶&quot;);
    }
    
    public void park() {
        System.out.println(&quot;汽车停车&quot;);
    }
}

class Motorcycle implements Vehicle {
    @Override
    public void drive() {
        System.out.println(&quot;摩托车行驶&quot;);
    }
    
    public void wheelie() {
        System.out.println(&quot;摩托车翘头&quot;);
    }
}

class VehicleFactory {
    public static Vehicle createVehicle(String type) {
        if (&quot;car&quot;.equals(type)) {
            return new Car();
        } else if (&quot;motorcycle&quot;.equals(type)) {
            return new Motorcycle();
        }
        return null;
    }
}

public class Test {
    public static void main(String[] args) {
        Vehicle vehicle1 = VehicleFactory.createVehicle(&quot;car&quot;);
        Vehicle vehicle2 = VehicleFactory.createVehicle(&quot;motorcycle&quot;);
        
        // 使用instanceof进行安全转换
        if (vehicle1 instanceof Car) {
            Car car = (Car) vehicle1;
            car.drive();
            car.park();
        }
        
        if (vehicle2 instanceof Motorcycle) {
            Motorcycle motorcycle = (Motorcycle) vehicle2;
            motorcycle.drive();
            motorcycle.wheelie();
        }
    }
}
&lt;/code&gt;&lt;/pre&gt;
&lt;h4&gt;16.5 类型转换的注意事项&lt;/h4&gt;
&lt;p&gt;&lt;strong&gt;1. 避免不必要的转换&lt;/strong&gt;&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;class Animal {
    public void eat() {
        System.out.println(&quot;动物吃东西&quot;);
    }
}

class Dog extends Animal {
    @Override
    public void eat() {
        System.out.println(&quot;狗吃骨头&quot;);
    }
}

public class Test {
    public static void main(String[] args) {
        Dog dog = new Dog();
        Animal animal = dog; // 向上转型
        
        // 不必要的向下转型
        Dog dog2 = (Dog) animal; // 可以，但不必要
        
        // 直接使用原始引用更好
        dog.eat(); // 更直接，无需转换
    }
}
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;&lt;strong&gt;2. 处理转换异常&lt;/strong&gt;&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;public class Test {
    public static void main(String[] args) {
        Animal animal = new Dog();
        
        try {
            Cat cat = (Cat) animal; // 会抛出ClassCastException
        } catch (ClassCastException e) {
            System.out.println(&quot;类型转换失败：&quot; + e.getMessage());
        }
        
        // 使用instanceof更安全
        if (animal instanceof Cat) {
            Cat cat = (Cat) animal;
            cat.meow();
        } else {
            System.out.println(&quot;animal不是Cat类型&quot;);
        }
    }
}
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;&lt;strong&gt;3. 接口类型转换&lt;/strong&gt;&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;interface Flyable {
    void fly();
}

interface Swimmable {
    void swim();
}

class Duck implements Flyable, Swimmable {
    @Override
    public void fly() {
        System.out.println(&quot;鸭子飞翔&quot;);
    }
    
    @Override
    public void swim() {
        System.out.println(&quot;鸭子游泳&quot;);
    }
}

public class Test {
    public static void main(String[] args) {
        Duck duck = new Duck();
        
        // 向上转型到接口
        Flyable flyable = duck;
        Swimmable swimmable = duck;
        
        // 向下转型回具体类
        Duck duck2 = (Duck) flyable;
        Duck duck3 = (Duck) swimmable;
        
        // 接口之间的转换
        if (flyable instanceof Swimmable) {
            Swimmable swimmable2 = (Swimmable) flyable;
            swimmable2.swim();
        }
    }
}
&lt;/code&gt;&lt;/pre&gt;
&lt;h4&gt;16.6 类型转换的最佳实践&lt;/h4&gt;
&lt;p&gt;&lt;strong&gt;1. 优先使用instanceof检查&lt;/strong&gt;&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;public void processAnimal(Animal animal) {
    if (animal instanceof Dog) {
        Dog dog = (Dog) animal;
        dog.bark();
    } else if (animal instanceof Cat) {
        Cat cat = (Cat) animal;
        cat.meow();
    } else {
        animal.eat(); // 使用父类方法
    }
}
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;&lt;strong&gt;2. 使用模式匹配（Java 14+）&lt;/strong&gt;&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;public void processAnimal(Animal animal) {
    if (animal instanceof Dog dog) {
        dog.bark(); // 自动转换
    } else if (animal instanceof Cat cat) {
        cat.meow(); // 自动转换
    } else {
        animal.eat();
    }
}
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;&lt;strong&gt;3. 避免过度使用类型转换&lt;/strong&gt;&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;// 不好的设计
public void process(Object obj) {
    if (obj instanceof String) {
        String str = (String) obj;
        // 处理字符串
    } else if (obj instanceof Integer) {
        Integer num = (Integer) obj;
        // 处理数字
    }
}

// 更好的设计 - 使用泛型
public &amp;lt;T&amp;gt; void process(T obj) {
    // 直接使用，无需类型转换
}
&lt;/code&gt;&lt;/pre&gt;
&lt;h4&gt;16.7 总结&lt;/h4&gt;
&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;转换类型&lt;/th&gt;
&lt;th&gt;语法&lt;/th&gt;
&lt;th&gt;安全性&lt;/th&gt;
&lt;th&gt;使用场景&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;向上转型&lt;/td&gt;
&lt;td&gt;自动&lt;/td&gt;
&lt;td&gt;安全&lt;/td&gt;
&lt;td&gt;多态使用&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;向下转型&lt;/td&gt;
&lt;td&gt;强制&lt;/td&gt;
&lt;td&gt;需检查&lt;/td&gt;
&lt;td&gt;调用子类特有方法&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;instanceof&lt;/td&gt;
&lt;td&gt;检查&lt;/td&gt;
&lt;td&gt;安全&lt;/td&gt;
&lt;td&gt;类型判断&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;
&lt;blockquote&gt;
&lt;p&gt;&lt;strong&gt;关键要点&lt;/strong&gt;：向上转型安全自动，向下转型需要检查，instanceof是安全转换的保障。合理使用类型转换可以充分利用多态的优势，但要注意避免ClassCastException异常。&lt;/p&gt;
&lt;/blockquote&gt;
&lt;h3&gt;17. Java包（package）的命名规范、作用与分类&lt;/h3&gt;
&lt;h4&gt;17.1 包的作用&lt;/h4&gt;
&lt;ul&gt;
&lt;li&gt;&lt;strong&gt;组织管理类文件&lt;/strong&gt;：将相关类、接口、枚举等组织在一起，便于项目结构清晰、维护方便。&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;避免命名冲突&lt;/strong&gt;：不同包下可以有同名的类，互不影响。&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;访问控制&lt;/strong&gt;：包提供包访问权限（default/package-private），有助于封装实现细节。&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;便于代码复用和分发&lt;/strong&gt;：包结构清晰，便于模块化开发和第三方库的分发。&lt;/li&gt;
&lt;/ul&gt;
&lt;h4&gt;17.2 包的命名规范&lt;/h4&gt;
&lt;ul&gt;
&lt;li&gt;&lt;strong&gt;全部小写&lt;/strong&gt;，多个单词用点&lt;code&gt;.&lt;/code&gt;分隔。&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;推荐使用反域名（Reverse Domain Name）命名法&lt;/strong&gt;，保证全局唯一性。
&lt;ul&gt;
&lt;li&gt;例如：&lt;code&gt;com.example.project.module&lt;/code&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;不要使用Java保留字或特殊字符&lt;/strong&gt;。&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;包名应简洁明了，体现所属公司、项目、模块等信息&lt;/strong&gt;。&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;常见命名结构&lt;/strong&gt;：
&lt;ul&gt;
&lt;li&gt;公司/组织域名反写 + 项目名 + 模块名 + 功能名&lt;/li&gt;
&lt;li&gt;例：&lt;code&gt;com.alibaba.fastjson.parser&lt;/code&gt;&lt;/li&gt;
&lt;li&gt;例：&lt;code&gt;org.springframework.context.annotation&lt;/code&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;避免使用下划线、连字符等特殊符号&lt;/strong&gt;。&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;&lt;strong&gt;示例：&lt;/strong&gt;&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;package com.example.myapp.service;
package org.apache.commons.lang3;
package cn.itcast.demo.util;
&lt;/code&gt;&lt;/pre&gt;
&lt;h4&gt;17.3 包的分类方法&lt;/h4&gt;
&lt;ol&gt;
&lt;li&gt;
&lt;p&gt;&lt;strong&gt;按功能分层&lt;/strong&gt;（推荐，最常见）&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;code&gt;controller&lt;/code&gt;：控制层（如Web接口、API入口）&lt;/li&gt;
&lt;li&gt;&lt;code&gt;service&lt;/code&gt;：业务逻辑层&lt;/li&gt;
&lt;li&gt;&lt;code&gt;dao&lt;/code&gt; / &lt;code&gt;repository&lt;/code&gt;：数据访问层&lt;/li&gt;
&lt;li&gt;&lt;code&gt;model&lt;/code&gt; / &lt;code&gt;entity&lt;/code&gt; / &lt;code&gt;domain&lt;/code&gt;：实体类、数据模型&lt;/li&gt;
&lt;li&gt;&lt;code&gt;util&lt;/code&gt; / &lt;code&gt;utils&lt;/code&gt;：工具类&lt;/li&gt;
&lt;li&gt;&lt;code&gt;config&lt;/code&gt;：配置类&lt;/li&gt;
&lt;li&gt;&lt;code&gt;exception&lt;/code&gt;：异常处理&lt;/li&gt;
&lt;li&gt;&lt;code&gt;constant&lt;/code&gt;：常量&lt;/li&gt;
&lt;li&gt;例：&lt;pre&gt;&lt;code&gt;com.example.project.controller
com.example.project.service
com.example.project.dao
com.example.project.model
com.example.project.util
&lt;/code&gt;&lt;/pre&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;&lt;strong&gt;按业务模块分类&lt;/strong&gt;&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;适用于大型项目，将不同业务模块分包管理&lt;/li&gt;
&lt;li&gt;例：&lt;pre&gt;&lt;code&gt;com.example.project.user
com.example.project.order
com.example.project.product
&lt;/code&gt;&lt;/pre&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;&lt;strong&gt;按技术/层次混合分类&lt;/strong&gt;&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;结合功能和业务模块&lt;/li&gt;
&lt;li&gt;例：&lt;pre&gt;&lt;code&gt;com.example.project.user.controller
com.example.project.user.service
com.example.project.order.controller
&lt;/code&gt;&lt;/pre&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;&lt;strong&gt;第三方/开源包命名&lt;/strong&gt;&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;通常以组织域名反写开头，如&lt;code&gt;org.apache&lt;/code&gt;, &lt;code&gt;com.google&lt;/code&gt;, &lt;code&gt;io.reactivex&lt;/code&gt;等&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;/ol&gt;
&lt;h4&gt;17.4 包的使用示例&lt;/h4&gt;
&lt;p&gt;&lt;strong&gt;定义包：&lt;/strong&gt;&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;package com.example.myapp.service;

public class UserService {
    // ...
}
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;&lt;strong&gt;导入包：&lt;/strong&gt;&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;import com.example.myapp.service.UserService;
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;&lt;strong&gt;包结构示意：&lt;/strong&gt;&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;src/
  └─ main/
      └─ java/
          └─ com/
              └─ example/
                  └─ myapp/
                      ├─ controller/
                      ├─ service/
                      ├─ dao/
                      ├─ model/
                      └─ util/
&lt;/code&gt;&lt;/pre&gt;
&lt;h4&gt;17.5 总结&lt;/h4&gt;
&lt;ul&gt;
&lt;li&gt;包的命名要规范、唯一、简洁，体现公司、项目、模块等信息。&lt;/li&gt;
&lt;li&gt;合理分类包结构有助于项目的可维护性、可扩展性和团队协作。&lt;/li&gt;
&lt;li&gt;推荐采用&quot;反域名+项目+模块+功能&quot;方式命名和分层。&lt;/li&gt;
&lt;/ul&gt;
&lt;blockquote&gt;
&lt;p&gt;&lt;strong&gt;最佳实践&lt;/strong&gt;：始终遵循公司/社区的包命名规范，保持包结构清晰有序，便于团队协作和代码管理。&lt;/p&gt;
&lt;/blockquote&gt;
&lt;h3&gt;18. final 关键词的作用与用法&lt;/h3&gt;
&lt;h4&gt;18.1 final 的基本作用&lt;/h4&gt;
&lt;p&gt;final 是 Java 的一个修饰符，可以用于&lt;strong&gt;变量&lt;/strong&gt;、&lt;strong&gt;方法&lt;/strong&gt;和&lt;strong&gt;类&lt;/strong&gt;，其核心作用是&quot;不可更改&quot;：&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;strong&gt;final 变量&lt;/strong&gt;：值不可更改（常量）&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;final 方法&lt;/strong&gt;：不可被子类重写&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;final 类&lt;/strong&gt;：不可被继承&lt;/li&gt;
&lt;/ul&gt;
&lt;h4&gt;18.2 final 用于变量&lt;/h4&gt;
&lt;ol&gt;
&lt;li&gt;
&lt;p&gt;&lt;strong&gt;修饰基本类型变量&lt;/strong&gt;&lt;br /&gt;
变量赋值后不可再更改&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;final int a = 10;
// a = 20; // 编译错误
&lt;/code&gt;&lt;/pre&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;&lt;strong&gt;修饰引用类型变量&lt;/strong&gt;&lt;br /&gt;
引用不可更改（即不能指向其他对象），但对象内容可变&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;final StringBuilder sb = new StringBuilder(&quot;hello&quot;);
sb.append(&quot; world&quot;); // 合法，对象内容可变
// sb = new StringBuilder(); // 编译错误，引用不可变
&lt;/code&gt;&lt;/pre&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;&lt;strong&gt;修饰成员变量&lt;/strong&gt;&lt;br /&gt;
必须在定义时或构造方法中初始化&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;class Person {
    final String name;
    Person(String name) {
        this.name = name; // 构造器中初始化
    }
}
&lt;/code&gt;&lt;/pre&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;&lt;strong&gt;修饰静态变量&lt;/strong&gt;&lt;br /&gt;
通常与 static 连用，定义全局常量&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;public static final double PI = 3.1415926;
&lt;/code&gt;&lt;/pre&gt;
&lt;/li&gt;
&lt;/ol&gt;
&lt;h4&gt;18.3 final 用于方法&lt;/h4&gt;
&lt;ul&gt;
&lt;li&gt;被 final 修饰的方法不能被子类重写（override），但可以被继承和重载（overload）。&lt;/li&gt;
&lt;/ul&gt;
&lt;pre&gt;&lt;code&gt;class Parent {
    public final void show() {
        System.out.println(&quot;父类方法&quot;);
    }
}
class Child extends Parent {
    // public void show() {} // 编译错误，不能重写
}
&lt;/code&gt;&lt;/pre&gt;
&lt;h4&gt;18.4 final 用于类&lt;/h4&gt;
&lt;ul&gt;
&lt;li&gt;被 final 修饰的类不能被继承，所有方法都隐式为 final。&lt;/li&gt;
&lt;/ul&gt;
&lt;pre&gt;&lt;code&gt;final class Constants {
    // ...
}
// class MyConstants extends Constants {} // 编译错误
&lt;/code&gt;&lt;/pre&gt;
&lt;ul&gt;
&lt;li&gt;常见应用：工具类（如 java.lang.String、java.lang.Math）&lt;/li&gt;
&lt;/ul&gt;
&lt;h4&gt;18.5 final 的注意事项&lt;/h4&gt;
&lt;ul&gt;
&lt;li&gt;final 变量必须初始化，且只能赋值一次。&lt;/li&gt;
&lt;li&gt;final 不能与 abstract 同时使用（final 意味着不可变，abstract 意味着需被重写，两者矛盾）。&lt;/li&gt;
&lt;li&gt;final 方法可以被子类继承，但不能被重写。&lt;/li&gt;
&lt;li&gt;final 类不能有子类，但可以创建对象。&lt;/li&gt;
&lt;/ul&gt;
&lt;h4&gt;18.6 典型应用场景&lt;/h4&gt;
&lt;ul&gt;
&lt;li&gt;定义常量（public static final）&lt;/li&gt;
&lt;li&gt;保证安全性（防止被继承或重写）&lt;/li&gt;
&lt;li&gt;设计不可变类（如 String）&lt;/li&gt;
&lt;/ul&gt;
&lt;h4&gt;18.7 示例总结&lt;/h4&gt;
&lt;pre&gt;&lt;code&gt;public final class MyUtils {
    public static final double PI = 3.14;

    public final void printHello() {
        System.out.println(&quot;Hello&quot;);
    }
}
&lt;/code&gt;&lt;/pre&gt;
&lt;blockquote&gt;
&lt;p&gt;&lt;strong&gt;最佳实践&lt;/strong&gt;：final 用于常量、工具类、不可变对象和防止继承/重写的场景，能提升代码安全性和可维护性。&lt;/p&gt;
&lt;/blockquote&gt;
&lt;h3&gt;19. Java 常量（Constant）的定义与使用&lt;/h3&gt;
&lt;h4&gt;19.1 什么是常量&lt;/h4&gt;
&lt;ul&gt;
&lt;li&gt;常量（Constant）指在程序运行过程中其值不可更改的量。&lt;/li&gt;
&lt;li&gt;在 Java 中，常量通常用 &lt;code&gt;final&lt;/code&gt; 关键字修饰，配合 &lt;code&gt;static&lt;/code&gt; 关键字可定义全局常量。&lt;/li&gt;
&lt;/ul&gt;
&lt;h4&gt;19.2 常量的定义方式&lt;/h4&gt;
&lt;ol&gt;
&lt;li&gt;
&lt;p&gt;&lt;strong&gt;局部常量&lt;/strong&gt;&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;final int DAYS_IN_WEEK = 7;
&lt;/code&gt;&lt;/pre&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;&lt;strong&gt;类常量（全局常量）&lt;/strong&gt;&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;推荐用 &lt;code&gt;public static final&lt;/code&gt; 修饰，通常放在类的顶部。&lt;/li&gt;
&lt;/ul&gt;
&lt;pre&gt;&lt;code&gt;public class MathConstants {
    public static final double PI = 3.1415926;
    public static final int MAX_SIZE = 1000;
}
&lt;/code&gt;&lt;/pre&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;&lt;strong&gt;接口常量&lt;/strong&gt;&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;接口中的变量默认是 &lt;code&gt;public static final&lt;/code&gt;，可省略修饰符。&lt;/li&gt;
&lt;/ul&gt;
&lt;pre&gt;&lt;code&gt;public interface Status {
    int SUCCESS = 1;
    int ERROR = 0;
}
&lt;/code&gt;&lt;/pre&gt;
&lt;/li&gt;
&lt;/ol&gt;
&lt;h4&gt;19.3 常量的命名规范&lt;/h4&gt;
&lt;ul&gt;
&lt;li&gt;&lt;strong&gt;全部大写&lt;/strong&gt;，单词间用下划线 &lt;code&gt;_&lt;/code&gt; 分隔。&lt;/li&gt;
&lt;li&gt;命名应简洁明了，表达常量含义。&lt;/li&gt;
&lt;li&gt;例：&lt;code&gt;MAX_VALUE&lt;/code&gt;, &lt;code&gt;DEFAULT_TIMEOUT&lt;/code&gt;, &lt;code&gt;PI&lt;/code&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;h4&gt;19.4 常量的使用场景&lt;/h4&gt;
&lt;ul&gt;
&lt;li&gt;表示不会改变的物理量、配置信息、状态码、魔法数字等。&lt;/li&gt;
&lt;li&gt;便于统一管理和维护，避免硬编码。&lt;/li&gt;
&lt;li&gt;提高代码可读性和可维护性。&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;&lt;strong&gt;示例：&lt;/strong&gt;&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;public class Config {
    public static final String BASE_URL = &quot;https://api.example.com&quot;;
    public static final int TIMEOUT_SECONDS = 30;
}
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;&lt;strong&gt;使用：&lt;/strong&gt;&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;System.out.println(Config.BASE_URL);
&lt;/code&gt;&lt;/pre&gt;
&lt;h4&gt;19.5 常量的最佳实践&lt;/h4&gt;
&lt;ul&gt;
&lt;li&gt;常量应集中管理，建议放在专门的常量类或接口中。&lt;/li&gt;
&lt;li&gt;不要在代码中直接写&quot;魔法数字&quot;或&quot;魔法字符串&quot;，应使用常量代替。&lt;/li&gt;
&lt;li&gt;常量一旦定义，不应再修改其值。&lt;/li&gt;
&lt;/ul&gt;
&lt;h4&gt;19.6 常量与变量的区别&lt;/h4&gt;
&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;特性&lt;/th&gt;
&lt;th&gt;常量（final）&lt;/th&gt;
&lt;th&gt;变量&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;值是否可变&lt;/td&gt;
&lt;td&gt;不可变&lt;/td&gt;
&lt;td&gt;可变&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;命名规范&lt;/td&gt;
&lt;td&gt;全大写+下划线&lt;/td&gt;
&lt;td&gt;小驼峰/下划线&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;赋值次数&lt;/td&gt;
&lt;td&gt;只能赋值一次&lt;/td&gt;
&lt;td&gt;可多次赋值&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;典型修饰&lt;/td&gt;
&lt;td&gt;static final&lt;/td&gt;
&lt;td&gt;无&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;
&lt;h4&gt;19.7 示例总结&lt;/h4&gt;
&lt;pre&gt;&lt;code&gt;public class Constants {
    public static final double PI = 3.1415926;
    public static final int MAX_USER = 1000;
    public static final String APP_NAME = &quot;MyApp&quot;;
}
&lt;/code&gt;&lt;/pre&gt;
&lt;blockquote&gt;
&lt;p&gt;&lt;strong&gt;最佳实践&lt;/strong&gt;：常量集中管理、命名规范、避免魔法数字，提升代码可维护性和可读性。&lt;/p&gt;
&lt;/blockquote&gt;
&lt;h3&gt;20. Java 字符串（String）是否不可变及原理&lt;/h3&gt;
&lt;h4&gt;20.1 不可变的含义&lt;/h4&gt;
&lt;ul&gt;
&lt;li&gt;&lt;strong&gt;不可变（immutable）&lt;/strong&gt;：一旦创建，字符串对象的内容（字符序列）就不能被更改。&lt;/li&gt;
&lt;li&gt;对字符串的任何修改操作（如拼接、替换、截取等）都会生成新的String对象，原有对象不会被改变。&lt;/li&gt;
&lt;/ul&gt;
&lt;h4&gt;20.2 不可变的实现原理&lt;/h4&gt;
&lt;ul&gt;
&lt;li&gt;String 类被声明为 &lt;code&gt;final&lt;/code&gt;，不能被继承。&lt;/li&gt;
&lt;li&gt;String 内部使用 &lt;code&gt;private final char[] value&lt;/code&gt;（JDK 8 及以前）或 &lt;code&gt;private final byte[] value&lt;/code&gt;（JDK 9+）存储字符数组，&lt;code&gt;final&lt;/code&gt; 保证了数组引用不可变。&lt;/li&gt;
&lt;li&gt;没有提供可以直接修改字符数组内容的方法。&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;&lt;strong&gt;源码片段（JDK 17）：&lt;/strong&gt;&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;public final class String implements java.io.Serializable, Comparable&amp;lt;String&amp;gt;, CharSequence {
    private final byte[] value; // JDK 9+ 用byte数组，JDK 8及以前用char[]
    // ...
}
&lt;/code&gt;&lt;/pre&gt;
&lt;h4&gt;20.3 不可变的好处&lt;/h4&gt;
&lt;ol&gt;
&lt;li&gt;&lt;strong&gt;安全性&lt;/strong&gt;&lt;br /&gt;
字符串常用于用户名、密码、URL等敏感信息，防止被恶意篡改。&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;线程安全&lt;/strong&gt;&lt;br /&gt;
多线程环境下可安全共享同一个字符串对象，无需加锁。&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;字符串常量池优化&lt;/strong&gt;&lt;br /&gt;
不可变性使得字符串可以被缓存和复用，提高内存利用率和性能。&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;哈希值缓存&lt;/strong&gt;&lt;br /&gt;
不可变对象的哈希值可缓存，提升作为 HashMap 键的效率。&lt;/li&gt;
&lt;/ol&gt;
&lt;h4&gt;20.4 示例说明&lt;/h4&gt;
&lt;pre&gt;&lt;code&gt;String s1 = &quot;hello&quot;;
String s2 = &quot;hello&quot;;
System.out.println(s1 == s2); // true，指向常量池同一对象
String s3 = new String(&quot;hello&quot;);
System.out.println(s1 == s3); // false，s3为新对象
System.out.println(s1 == s3.intern()); // true
&lt;/code&gt;&lt;/pre&gt;
&lt;ul&gt;
&lt;li&gt;s1 改变后，s2 仍然指向原来的字符串对象，原内容未被修改。&lt;/li&gt;
&lt;/ul&gt;
&lt;h4&gt;20.5 字符串可变的替代方案&lt;/h4&gt;
&lt;ul&gt;
&lt;li&gt;如果需要频繁修改字符串内容，建议使用 &lt;code&gt;StringBuilder&lt;/code&gt; 或 &lt;code&gt;StringBuffer&lt;/code&gt;，它们是可变的字符串容器，效率更高。&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;&lt;strong&gt;示例：&lt;/strong&gt;&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;StringBuilder sb = new StringBuilder(&quot;hello&quot;);
sb.append(&quot; world&quot;); // 直接在原对象上修改
System.out.println(sb.toString()); // hello world
&lt;/code&gt;&lt;/pre&gt;
&lt;h4&gt;20.6 总结&lt;/h4&gt;
&lt;ul&gt;
&lt;li&gt;&lt;strong&gt;String 是不可变的&lt;/strong&gt;，所有修改操作都会生成新对象。&lt;/li&gt;
&lt;li&gt;不可变性带来安全、线程安全、常量池优化等诸多好处。&lt;/li&gt;
&lt;li&gt;频繁修改字符串时应使用 StringBuilder/StringBuffer。&lt;/li&gt;
&lt;/ul&gt;
&lt;pre&gt;&lt;code&gt;
### 21. Java 权限修饰符（访问控制修饰符）

#### 21.1 权限修饰符的作用

- 用于控制类、成员变量、方法等的访问范围，保护数据安全，隐藏实现细节，实现封装。

#### 21.2 四种权限修饰符

| 修饰符         | 说明                   | 关键字      |
|----------------|------------------------|-------------|
| 公有           | 任何地方都可访问        | public      |
| 受保护         | 同包或子类可访问        | protected   |
| 默认（包访问）  | 仅同包可访问            | （无修饰符） |
| 私有           | 仅本类可访问            | private     |

#### 21.3 访问范围对比表

| 修饰符      | 同类 | 同包 | 子类（不同包） | 其他包 |
|-------------|------|------|---------------|--------|
| public      | ✔    | ✔    | ✔             | ✔      |
| protected   | ✔    | ✔    | ✔             | ✘      |
| 默认（无）  | ✔    | ✔    | ✘             | ✘      |
| private     | ✔    | ✘    | ✘             | ✘      |

#### 21.4 典型用法与示例

```java
public class Person {
    public String name;         // 任何地方都可访问
    protected int age;          // 同包和子类可访问
    String gender;              // 默认（包访问），仅同包可访问
    private String idCard;      // 仅本类可访问

    public void showName() {
        System.out.println(name); // 可访问
        System.out.println(idCard); // 可访问
    }
}

class Student extends Person {
    public void showInfo() {
        System.out.println(name);   // public，能访问
        System.out.println(age);    // protected，能访问
        // System.out.println(idCard); // private，不能访问
        System.out.println(gender); // 默认，同包能访问
    }
}
&lt;/code&gt;&lt;/pre&gt;
&lt;h4&gt;21.5 访问修饰符的使用建议&lt;/h4&gt;
&lt;ul&gt;
&lt;li&gt;&lt;strong&gt;private&lt;/strong&gt;：优先用于成员变量，保证封装性，通过 getter/setter 访问。&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;public&lt;/strong&gt;：用于对外开放的类、方法、常量等。&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;protected&lt;/strong&gt;：用于继承体系中对子类开放的成员。&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;默认（无修饰符）&lt;/strong&gt;：用于包内部协作的类和成员。&lt;/li&gt;
&lt;/ul&gt;
&lt;h4&gt;21.6 总结&lt;/h4&gt;
&lt;ul&gt;
&lt;li&gt;合理使用权限修饰符是实现封装、保护数据和模块化设计的基础。&lt;/li&gt;
&lt;li&gt;推荐&quot;属性私有、方法公有&quot;，通过方法暴露功能，隐藏实现细节。&lt;/li&gt;
&lt;/ul&gt;
&lt;blockquote&gt;
&lt;p&gt;&lt;strong&gt;最佳实践&lt;/strong&gt;：优先使用 private，必要时用 protected 或 public，尽量避免无修饰符暴露包外。&lt;/p&gt;
&lt;/blockquote&gt;
&lt;h3&gt;22. Java 代码块（初始化块）的类型与用法&lt;/h3&gt;
&lt;h4&gt;22.1 代码块的分类&lt;/h4&gt;
&lt;ol&gt;
&lt;li&gt;
&lt;p&gt;&lt;strong&gt;局部代码块&lt;/strong&gt;&lt;br /&gt;
出现在方法内部，用于限定变量作用域，常用于控制流程。&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;public void test() {
    {
        int x = 10;
        System.out.println(x);
    }
    // System.out.println(x); // 编译错误，x超出作用域
}
&lt;/code&gt;&lt;/pre&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;&lt;strong&gt;构造代码块（实例初始化块）&lt;/strong&gt;&lt;br /&gt;
直接写在类中、方法外，用 &lt;code&gt;{}&lt;/code&gt; 包裹。
每次创建对象时都会执行，优先于构造方法执行。
常用于多个构造方法间的公共初始化代码。&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;public class Person {
    {
        System.out.println(&quot;构造代码块执行&quot;);
    }
    public Person() {
        System.out.println(&quot;无参构造&quot;);
    }
    public Person(String name) {
        System.out.println(&quot;有参构造&quot;);
    }
}
// new Person(); // 输出：构造代码块执行  无参构造
// new Person(&quot;Tom&quot;); // 输出：构造代码块执行  有参构造
&lt;/code&gt;&lt;/pre&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;&lt;strong&gt;静态代码块（静态初始化块）&lt;/strong&gt;&lt;br /&gt;
用 &lt;code&gt;static {}&lt;/code&gt; 声明，属于类本身。
类加载时执行且只执行一次，常用于静态资源初始化。&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;public class Demo {
    static {
        System.out.println(&quot;静态代码块执行&quot;);
    }
}
// 类首次加载时输出：静态代码块执行
&lt;/code&gt;&lt;/pre&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;&lt;strong&gt;同步代码块&lt;/strong&gt;&lt;br /&gt;
用于多线程并发控制，&lt;code&gt;synchronized {}&lt;/code&gt;。&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;public void method() {
    synchronized(this) {
        // 线程安全的操作
    }
}
&lt;/code&gt;&lt;/pre&gt;
&lt;/li&gt;
&lt;/ol&gt;
&lt;h4&gt;22.2 执行顺序&lt;/h4&gt;
&lt;ul&gt;
&lt;li&gt;静态代码块 &amp;gt; 实例变量初始化 &amp;gt; 构造代码块 &amp;gt; 构造方法&lt;/li&gt;
&lt;li&gt;静态代码块只执行一次，实例相关代码块每次创建对象都会执行&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;&lt;strong&gt;示例：&lt;/strong&gt;&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;public class Order {
    static {
        System.out.println(&quot;1. 静态代码块&quot;);
    }
    {
        System.out.println(&quot;2. 构造代码块&quot;);
    }
    public Order() {
        System.out.println(&quot;3. 构造方法&quot;);
    }
}
// new Order();
// 输出：1. 静态代码块（类加载时）
//      2. 构造代码块
//      3. 构造方法
&lt;/code&gt;&lt;/pre&gt;
&lt;h4&gt;22.3 代码块的作用&lt;/h4&gt;
&lt;ul&gt;
&lt;li&gt;静态代码块：初始化静态资源、加载驱动、只执行一次&lt;/li&gt;
&lt;li&gt;构造代码块：多个构造方法间的公共初始化逻辑&lt;/li&gt;
&lt;li&gt;局部代码块：限定变量作用域，优化内存&lt;/li&gt;
&lt;li&gt;同步代码块：保证线程安全&lt;/li&gt;
&lt;/ul&gt;
&lt;h4&gt;22.4 注意事项&lt;/h4&gt;
&lt;ul&gt;
&lt;li&gt;静态代码块不能访问非静态成员&lt;/li&gt;
&lt;li&gt;构造代码块可访问实例成员&lt;/li&gt;
&lt;li&gt;代码块执行顺序影响对象初始化流程&lt;/li&gt;
&lt;li&gt;同步代码块需合理选择锁对象，避免死锁&lt;/li&gt;
&lt;/ul&gt;
&lt;h4&gt;22.5 典型应用场景&lt;/h4&gt;
&lt;ul&gt;
&lt;li&gt;静态代码块：数据库驱动注册、全局配置加载&lt;/li&gt;
&lt;li&gt;构造代码块：日志打印、对象通用初始化&lt;/li&gt;
&lt;li&gt;局部代码块：for/if/switch等流程控制&lt;/li&gt;
&lt;li&gt;同步代码块：多线程安全操作&lt;/li&gt;
&lt;/ul&gt;
&lt;h4&gt;22.6 总结&lt;/h4&gt;
&lt;ul&gt;
&lt;li&gt;合理使用代码块可提升代码复用性、可维护性和安全性。&lt;/li&gt;
&lt;li&gt;静态代码块适合一次性初始化，构造代码块适合对象级初始化，局部代码块适合临时变量管理。&lt;/li&gt;
&lt;/ul&gt;
&lt;blockquote&gt;
&lt;p&gt;&lt;strong&gt;最佳实践&lt;/strong&gt;：优先用构造方法实现初始化，代码块用于特殊场景和通用逻辑，避免滥用。&lt;/p&gt;
&lt;/blockquote&gt;
&lt;h3&gt;23. Java 抽象类（Abstract Class）&lt;/h3&gt;
&lt;h4&gt;23.1 抽象类的概念&lt;/h4&gt;
&lt;p&gt;抽象类是一种特殊的类，用 &lt;code&gt;abstract&lt;/code&gt; 关键字修饰，可以包含抽象方法和具体方法。
抽象类不能被实例化，只能被继承，子类必须实现所有抽象方法。&lt;/p&gt;
&lt;h4&gt;23.2 抽象类的语法&lt;/h4&gt;
&lt;pre&gt;&lt;code&gt;public abstract class Animal {
    // 抽象方法：没有方法体，子类必须实现
    public abstract void makeSound();
    
    // 具体方法：有方法体，子类可以直接使用
    public void sleep() {
        System.out.println(&quot;动物在睡觉&quot;);
    }
    
    // 构造方法：用于初始化
    public Animal() {
        System.out.println(&quot;动物被创建&quot;);
    }
    
    // 成员变量
    protected String name;
    protected int age;
}
&lt;/code&gt;&lt;/pre&gt;
&lt;h4&gt;23.3 抽象类的特点&lt;/h4&gt;
&lt;ol&gt;
&lt;li&gt;
&lt;p&gt;&lt;strong&gt;不能实例化&lt;/strong&gt;&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;// Animal animal = new Animal(); // 编译错误
&lt;/code&gt;&lt;/pre&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;&lt;strong&gt;可以包含抽象方法和具体方法&lt;/strong&gt;&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;public abstract class Shape {
    // 抽象方法
    public abstract double getArea();
    public abstract double getPerimeter();
    
    // 具体方法
    public void display() {
        System.out.println(&quot;这是一个形状&quot;);
    }
}
&lt;/code&gt;&lt;/pre&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;&lt;strong&gt;可以有构造方法&lt;/strong&gt;&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;public abstract class Vehicle {
    protected String brand;
    
    public Vehicle(String brand) {
        this.brand = brand;
    }
}
&lt;/code&gt;&lt;/pre&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;&lt;strong&gt;可以有成员变量&lt;/strong&gt;&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;public abstract class Person {
    protected String name;
    protected int age;
}
&lt;/code&gt;&lt;/pre&gt;
&lt;/li&gt;
&lt;/ol&gt;
&lt;h4&gt;23.4 抽象方法的特点&lt;/h4&gt;
&lt;ul&gt;
&lt;li&gt;用 &lt;code&gt;abstract&lt;/code&gt; 关键字修饰&lt;/li&gt;
&lt;li&gt;没有方法体（没有 &lt;code&gt;{}&lt;/code&gt;）&lt;/li&gt;
&lt;li&gt;子类必须实现&lt;/li&gt;
&lt;li&gt;不能是 &lt;code&gt;private&lt;/code&gt; 或 &lt;code&gt;static&lt;/code&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;pre&gt;&lt;code&gt;public abstract class Database {
    // 抽象方法
    public abstract void connect();
    public abstract void disconnect();
    public abstract void executeQuery(String sql);
    
    // 具体方法
    public void log(String message) {
        System.out.println(&quot;日志: &quot; + message);
    }
}
&lt;/code&gt;&lt;/pre&gt;
&lt;h4&gt;23.5 抽象类的继承&lt;/h4&gt;
&lt;pre&gt;&lt;code&gt;// 抽象类继承抽象类
public abstract class Bird extends Animal {
    public abstract void fly();
}

// 具体类继承抽象类
public class Dog extends Animal {
    @Override
    public void makeSound() {
        System.out.println(&quot;汪汪汪&quot;);
    }
}

public class Cat extends Animal {
    @Override
    public void makeSound() {
        System.out.println(&quot;喵喵喵&quot;);
    }
}
&lt;/code&gt;&lt;/pre&gt;
&lt;h4&gt;23.6 抽象类与接口的区别&lt;/h4&gt;
&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;特性&lt;/th&gt;
&lt;th&gt;抽象类&lt;/th&gt;
&lt;th&gt;接口&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;关键字&lt;/td&gt;
&lt;td&gt;&lt;code&gt;abstract class&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;&lt;code&gt;interface&lt;/code&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;构造方法&lt;/td&gt;
&lt;td&gt;可以有&lt;/td&gt;
&lt;td&gt;不能有&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;成员变量&lt;/td&gt;
&lt;td&gt;可以有各种修饰符&lt;/td&gt;
&lt;td&gt;默认 &lt;code&gt;public static final&lt;/code&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;方法实现&lt;/td&gt;
&lt;td&gt;可以有具体方法&lt;/td&gt;
&lt;td&gt;Java 8+ 可以有默认方法&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;继承&lt;/td&gt;
&lt;td&gt;单继承&lt;/td&gt;
&lt;td&gt;多实现&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;访问修饰符&lt;/td&gt;
&lt;td&gt;各种修饰符&lt;/td&gt;
&lt;td&gt;默认 &lt;code&gt;public&lt;/code&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;
&lt;h4&gt;23.7 抽象类的使用场景&lt;/h4&gt;
&lt;ol&gt;
&lt;li&gt;
&lt;p&gt;&lt;strong&gt;模板方法模式&lt;/strong&gt;&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;public abstract class Game {
    // 模板方法
    public final void play() {
        initialize();
        startPlay();
        endPlay();
    }
    
    // 抽象方法，子类实现
    protected abstract void initialize();
    protected abstract void startPlay();
    protected abstract void endPlay();
}

public class Cricket extends Game {
    @Override
    protected void initialize() {
        System.out.println(&quot;板球游戏初始化&quot;);
    }
    
    @Override
    protected void startPlay() {
        System.out.println(&quot;开始板球游戏&quot;);
    }
    
    @Override
    protected void endPlay() {
        System.out.println(&quot;结束板球游戏&quot;);
    }
}
&lt;/code&gt;&lt;/pre&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;&lt;strong&gt;代码复用&lt;/strong&gt;&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;public abstract class Employee {
    protected String name;
    protected double salary;
    
    public Employee(String name, double salary) {
        this.name = name;
        this.salary = salary;
    }
    
    // 通用方法
    public void work() {
        System.out.println(name + &quot; 在工作&quot;);
    }
    
    // 抽象方法，子类实现
    public abstract double calculateBonus();
}

public class Manager extends Employee {
    public Manager(String name, double salary) {
        super(name, salary);
    }
    
    @Override
    public double calculateBonus() {
        return salary * 0.2;
    }
}
&lt;/code&gt;&lt;/pre&gt;
&lt;/li&gt;
&lt;/ol&gt;
&lt;h4&gt;23.8 注意事项&lt;/h4&gt;
&lt;ol&gt;
&lt;li&gt;
&lt;p&gt;&lt;strong&gt;抽象类不能被 &lt;code&gt;final&lt;/code&gt; 修饰&lt;/strong&gt;&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;// public final abstract class Test {} // 编译错误
&lt;/code&gt;&lt;/pre&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;&lt;strong&gt;抽象方法不能是 &lt;code&gt;private&lt;/code&gt;&lt;/strong&gt;&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;public abstract class Test {
    // private abstract void method(); // 编译错误
    protected abstract void method(); // 正确
}
&lt;/code&gt;&lt;/pre&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;&lt;strong&gt;抽象方法不能是 &lt;code&gt;static&lt;/code&gt;&lt;/strong&gt;&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;public abstract class Test {
    // public static abstract void method(); // 编译错误
    public abstract void method(); // 正确
}
&lt;/code&gt;&lt;/pre&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;&lt;strong&gt;子类必须实现所有抽象方法&lt;/strong&gt;&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;public abstract class Parent {
    public abstract void method1();
    public abstract void method2();
}

public class Child extends Parent {
    @Override
    public void method1() {
        // 实现
    }
    
    @Override
    public void method2() {
        // 实现
    }
}
&lt;/code&gt;&lt;/pre&gt;
&lt;/li&gt;
&lt;/ol&gt;
&lt;h4&gt;23.9 抽象类的优势&lt;/h4&gt;
&lt;ol&gt;
&lt;li&gt;&lt;strong&gt;代码复用&lt;/strong&gt;：具体方法可以在子类中直接使用&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;强制规范&lt;/strong&gt;：抽象方法强制子类实现特定功能&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;封装性&lt;/strong&gt;：可以隐藏实现细节&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;扩展性&lt;/strong&gt;：易于添加新的子类&lt;/li&gt;
&lt;/ol&gt;
&lt;h4&gt;23.10 实际应用示例&lt;/h4&gt;
&lt;pre&gt;&lt;code&gt;// 数据库操作抽象类
public abstract class DatabaseOperation {
    protected Connection connection;
    
    public DatabaseOperation() {
        this.connection = getConnection();
    }
    
    // 模板方法
    public final void executeOperation() {
        openConnection();
        performOperation();
        closeConnection();
    }
    
    // 具体方法
    protected void openConnection() {
        System.out.println(&quot;打开数据库连接&quot;);
    }
    
    protected void closeConnection() {
        System.out.println(&quot;关闭数据库连接&quot;);
    }
    
    // 抽象方法
    protected abstract void performOperation();
    protected abstract Connection getConnection();
}

// 具体实现
public class MySQLOperation extends DatabaseOperation {
    @Override
    protected void performOperation() {
        System.out.println(&quot;执行MySQL操作&quot;);
    }
    
    @Override
    protected Connection getConnection() {
        return new MySQLConnection();
    }
}
&lt;/code&gt;&lt;/pre&gt;
&lt;h4&gt;23.11 总结&lt;/h4&gt;
&lt;ul&gt;
&lt;li&gt;抽象类提供了一种介于接口和具体类之间的抽象层次&lt;/li&gt;
&lt;li&gt;适合有共同属性和行为的类族设计&lt;/li&gt;
&lt;li&gt;支持代码复用和强制规范&lt;/li&gt;
&lt;li&gt;是面向对象设计中重要的抽象工具&lt;/li&gt;
&lt;/ul&gt;
&lt;blockquote&gt;
&lt;p&gt;&lt;strong&gt;最佳实践&lt;/strong&gt;：当多个类有共同属性和行为时，使用抽象类；当只需要定义规范时，使用接口。抽象类适合&quot;是什么&quot;的关系，接口适合&quot;能做什么&quot;的关系。&lt;/p&gt;
&lt;/blockquote&gt;
&lt;hr /&gt;
&lt;h3&gt;24. Java 接口（Interface）&lt;/h3&gt;
&lt;h4&gt;24.1 接口的概念&lt;/h4&gt;
&lt;ul&gt;
&lt;li&gt;接口（interface）是Java中用&lt;code&gt;interface&lt;/code&gt;关键字定义的一种引用类型，是抽象方法和常量值的集合。&lt;/li&gt;
&lt;li&gt;接口只定义规范（方法签名、常量），不包含具体实现。&lt;/li&gt;
&lt;li&gt;接口用于描述一组类应当遵循的行为标准，实现代码解耦和多态。&lt;/li&gt;
&lt;/ul&gt;
&lt;h4&gt;24.2 接口的语法与定义&lt;/h4&gt;
&lt;pre&gt;&lt;code&gt;public interface Animal {
    void makeSound(); // 抽象方法，默认public abstract
    int AGE = 10;     // 常量，默认public static final
}
&lt;/code&gt;&lt;/pre&gt;
&lt;ul&gt;
&lt;li&gt;接口中的方法默认是&lt;code&gt;public abstract&lt;/code&gt;，可以省略。&lt;/li&gt;
&lt;li&gt;接口中的变量默认是&lt;code&gt;public static final&lt;/code&gt;，必须初始化。&lt;/li&gt;
&lt;li&gt;接口不能有构造方法。&lt;/li&gt;
&lt;/ul&gt;
&lt;h4&gt;24.3 接口的实现与多继承&lt;/h4&gt;
&lt;ul&gt;
&lt;li&gt;类通过&lt;code&gt;implements&lt;/code&gt;关键字实现接口，可以实现多个接口（用逗号分隔）。&lt;/li&gt;
&lt;li&gt;一个类可以继承一个父类，同时实现多个接口。&lt;/li&gt;
&lt;li&gt;接口之间可以多继承（&lt;code&gt;extends&lt;/code&gt;），一个接口可以继承多个接口。&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;&lt;strong&gt;示例：&lt;/strong&gt;&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;public interface Flyable {
    void fly();
}

public interface Swimmable {
    void swim();
}

public class Duck implements Flyable, Swimmable {
    @Override
    public void fly() {
        System.out.println(&quot;鸭子飞翔&quot;);
    }
    @Override
    public void swim() {
        System.out.println(&quot;鸭子游泳&quot;);
    }
}
&lt;/code&gt;&lt;/pre&gt;
&lt;h4&gt;24.4 接口的默认方法与静态方法（Java 8+）&lt;/h4&gt;
&lt;ul&gt;
&lt;li&gt;接口可以有&lt;code&gt;default&lt;/code&gt;修饰的默认方法（有方法体），用于接口升级时的兼容。&lt;/li&gt;
&lt;li&gt;接口可以有&lt;code&gt;static&lt;/code&gt;修饰的静态方法（有方法体），只能通过接口名调用。&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;&lt;strong&gt;示例：&lt;/strong&gt;&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;public interface MyInterface {
    void abstractMethod();
    default void defaultMethod() {
        System.out.println(&quot;默认方法&quot;);
    }
    static void staticMethod() {
        System.out.println(&quot;静态方法&quot;);
    }
}

public class MyClass implements MyInterface {
    @Override
    public void abstractMethod() {
        System.out.println(&quot;实现抽象方法&quot;);
    }
}

// 调用
MyClass obj = new MyClass();
obj.defaultMethod(); // 默认方法
MyInterface.staticMethod(); // 静态方法
&lt;/code&gt;&lt;/pre&gt;
&lt;h4&gt;24.5 接口的特性与规则&lt;/h4&gt;
&lt;ul&gt;
&lt;li&gt;接口不能被实例化。&lt;/li&gt;
&lt;li&gt;实现类必须实现接口的所有抽象方法，否则实现类也必须声明为抽象类。&lt;/li&gt;
&lt;li&gt;接口支持多继承，类只支持单继承。&lt;/li&gt;
&lt;li&gt;接口可以有常量、抽象方法、默认方法、静态方法（Java 8+）、私有方法（Java 9+）。&lt;/li&gt;
&lt;li&gt;接口方法不能是private、protected、final、static（除非是静态方法实现）。&lt;/li&gt;
&lt;/ul&gt;
&lt;h4&gt;24.6 接口的使用场景&lt;/h4&gt;
&lt;ul&gt;
&lt;li&gt;规范API设计，定义一组类的统一行为。&lt;/li&gt;
&lt;li&gt;解耦系统结构，实现多态和灵活扩展。&lt;/li&gt;
&lt;li&gt;回调机制、策略模式、适配器模式等设计模式。&lt;/li&gt;
&lt;li&gt;JDK大量API（如Runnable、Comparator、List等）都基于接口设计。&lt;/li&gt;
&lt;/ul&gt;
&lt;h4&gt;24.7 示例代码&lt;/h4&gt;
&lt;pre&gt;&lt;code&gt;public interface USB {
    void connect();
    void disconnect();
}

public class Mouse implements USB {
    @Override
    public void connect() {
        System.out.println(&quot;鼠标已连接&quot;);
    }
    @Override
    public void disconnect() {
        System.out.println(&quot;鼠标已断开&quot;);
    }
}

public class Keyboard implements USB {
    @Override
    public void connect() {
        System.out.println(&quot;键盘已连接&quot;);
    }
    @Override
    public void disconnect() {
        System.out.println(&quot;键盘已断开&quot;);
    }
}

public class Computer {
    public void useUSB(USB usb) {
        usb.connect();
        System.out.println(&quot;设备工作中...&quot;);
        usb.disconnect();
    }
    public static void main(String[] args) {
        Computer pc = new Computer();
        pc.useUSB(new Mouse());
        pc.useUSB(new Keyboard());
    }
}
&lt;/code&gt;&lt;/pre&gt;
&lt;h4&gt;24.8 注意事项与补充&lt;/h4&gt;
&lt;ul&gt;
&lt;li&gt;一个类可以实现多个接口，接口之间用逗号分隔。&lt;/li&gt;
&lt;li&gt;接口可以继承多个接口，实现多继承特性。&lt;/li&gt;
&lt;li&gt;接口中的常量建议大写字母+下划线命名。&lt;/li&gt;
&lt;li&gt;接口适合做规范、回调、扩展点，避免滥用。&lt;/li&gt;
&lt;/ul&gt;
&lt;blockquote&gt;
&lt;p&gt;&lt;strong&gt;总结：&lt;/strong&gt; 接口是行为规范和扩展点，是Java实现多态和解耦的核心机制。合理设计接口有助于系统的灵活性、可维护性和可扩展性。&lt;/p&gt;
&lt;/blockquote&gt;
&lt;hr /&gt;
&lt;h3&gt;25. 适配器设计模式&lt;/h3&gt;
&lt;p&gt;适配器模式（Adapter Pattern）是一种结构型设计模式，它允许将一个类的接口转换成客户端所期望的另一个接口，使得原本由于接口不兼容而不能一起工作的类可以一起工作。&lt;/p&gt;
&lt;h4&gt;25.1 基本概念&lt;/h4&gt;
&lt;ul&gt;
&lt;li&gt;&lt;strong&gt;目标接口（Target）&lt;/strong&gt;：客户端所期望的接口&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;适配者（Adaptee）&lt;/strong&gt;：需要被适配的类或接口&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;适配器（Adapter）&lt;/strong&gt;：将适配者转换为目标接口的类&lt;/li&gt;
&lt;/ul&gt;
&lt;h4&gt;25.2 适配器的两种实现方式&lt;/h4&gt;
&lt;ol&gt;
&lt;li&gt;&lt;strong&gt;类适配器&lt;/strong&gt;：通过继承适配者来实现&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;对象适配器&lt;/strong&gt;：通过组合适配者来实现&lt;/li&gt;
&lt;/ol&gt;
&lt;h4&gt;25.3 类适配器模式&lt;/h4&gt;
&lt;p&gt;类适配器使用继承机制实现适配。&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;// 目标接口
public interface Target {
    void request();
}

// 适配者
public class Adaptee {
    public void specificRequest() {
        System.out.println(&quot;适配者的特殊请求&quot;);
    }
}

// 类适配器
public class ClassAdapter extends Adaptee implements Target {
    @Override
    public void request() {
        specificRequest(); // 调用适配者的方法
    }
}

// 客户端代码
public class Client {
    public static void main(String[] args) {
        Target target = new ClassAdapter();
        target.request(); // 通过适配器调用适配者的方法
    }
}
&lt;/code&gt;&lt;/pre&gt;
&lt;h4&gt;25.4 对象适配器模式&lt;/h4&gt;
&lt;p&gt;对象适配器使用组合机制实现适配。&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;// 目标接口
public interface Target {
    void request();
}

// 适配者
public class Adaptee {
    public void specificRequest() {
        System.out.println(&quot;适配者的特殊请求&quot;);
    }
}

// 对象适配器
public class ObjectAdapter implements Target {
    private Adaptee adaptee;

    public ObjectAdapter(Adaptee adaptee) {
        this.adaptee = adaptee;
    }

    @Override
    public void request() {
        adaptee.specificRequest(); // 调用适配者的方法
    }
}

// 客户端代码
public class Client {
    public static void main(String[] args) {
        Adaptee adaptee = new Adaptee();
        Target target = new ObjectAdapter(adaptee);
        target.request(); // 通过适配器调用适配者的方法
    }
}
&lt;/code&gt;&lt;/pre&gt;
&lt;h4&gt;25.5 实际应用场景&lt;/h4&gt;
&lt;p&gt;&lt;strong&gt;1. 电源适配器示例&lt;/strong&gt;&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;// 220V交流电
public class AC220 {
    public int output220V() {
        return 220;
    }
}

// 5V直流电接口
public interface DC5 {
    int output5V();
}

// 电源适配器
public class PowerAdapter implements DC5 {
    private AC220 ac220;

    public PowerAdapter(AC220 ac220) {
        this.ac220 = ac220;
    }

    @Override
    public int output5V() {
        int input = ac220.output220V();
        // 变压
        int output = input / 44;
        System.out.println(&quot;输入电压: &quot; + input + &quot;V -&amp;gt; 输出电压: &quot; + output + &quot;V&quot;);
        return output;
    }
}

// 使用示例
public class PowerAdapterDemo {
    public static void main(String[] args) {
        DC5 dc5 = new PowerAdapter(new AC220());
        dc5.output5V();
    }
}
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;&lt;strong&gt;2. 日期格式转换适配器&lt;/strong&gt;&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;// 老的日期接口
public class OldDateFormat {
    public String formatDate(Date date) {
        SimpleDateFormat sdf = new SimpleDateFormat(&quot;yyyy-MM-dd&quot;);
        return sdf.format(date);
    }
}

// 新的日期接口
public interface NewDateFormat {
    String formatDate(LocalDate date);
}

// 日期格式适配器
public class DateFormatAdapter implements NewDateFormat {
    private OldDateFormat oldFormat;

    public DateFormatAdapter(OldDateFormat oldFormat) {
        this.oldFormat = oldFormat;
    }

    @Override
    public String formatDate(LocalDate date) {
        // 将LocalDate转换为Date
        Date oldDate = Date.from(date.atStartOfDay(ZoneId.systemDefault()).toInstant());
        return oldFormat.formatDate(oldDate);
    }
}

// 使用示例
public class DateFormatDemo {
    public static void main(String[] args) {
        NewDateFormat adapter = new DateFormatAdapter(new OldDateFormat());
        String formattedDate = adapter.formatDate(LocalDate.now());
        System.out.println(&quot;格式化日期: &quot; + formattedDate);
    }
}
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;&lt;strong&gt;3. 第三方SDK适配&lt;/strong&gt;&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;// 第三方支付接口
public interface ThirdPartyPayment {
    void processPayment(String orderId, double amount);
}

// 支付宝SDK
public class AlipaySDK {
    public void payWithAlipay(String orderNo, double money) {
        System.out.println(&quot;使用支付宝支付: &quot; + money + &quot;元&quot;);
    }
}

// 微信支付SDK
public class WeChatPaySDK {
    public void pay(String orderId, float price) {
        System.out.println(&quot;使用微信支付: &quot; + price + &quot;元&quot;);
    }
}

// 统一支付接口
public interface UnifiedPayment {
    void pay(String orderId, double amount);
}

// 支付宝适配器
public class AlipayAdapter implements UnifiedPayment {
    private AlipaySDK alipaySDK;

    public AlipayAdapter(AlipaySDK alipaySDK) {
        this.alipaySDK = alipaySDK;
    }

    @Override
    public void pay(String orderId, double amount) {
        alipaySDK.payWithAlipay(orderId, amount);
    }
}

// 微信支付适配器
public class WeChatPayAdapter implements UnifiedPayment {
    private WeChatPaySDK weChatPaySDK;

    public WeChatPayAdapter(WeChatPaySDK weChatPaySDK) {
        this.weChatPaySDK = weChatPaySDK;
    }

    @Override
    public void pay(String orderId, double amount) {
        weChatPaySDK.pay(orderId, (float)amount);
    }
}

// 支付服务
public class PaymentService {
    private UnifiedPayment paymentMethod;

    public void setPaymentMethod(UnifiedPayment paymentMethod) {
        this.paymentMethod = paymentMethod;
    }

    public void processPayment(String orderId, double amount) {
        paymentMethod.pay(orderId, amount);
    }
}

// 使用示例
public class PaymentDemo {
    public static void main(String[] args) {
        PaymentService paymentService = new PaymentService();

        // 使用支付宝支付
        paymentService.setPaymentMethod(new AlipayAdapter(new AlipaySDK()));
        paymentService.processPayment(&quot;ORDER001&quot;, 100.00);

        // 使用微信支付
        paymentService.setPaymentMethod(new WeChatPayAdapter(new WeChatPaySDK()));
        paymentService.processPayment(&quot;ORDER002&quot;, 200.00);
    }
}
&lt;/code&gt;&lt;/pre&gt;
&lt;h4&gt;25.6 适配器模式的优缺点&lt;/h4&gt;
&lt;p&gt;&lt;strong&gt;优点：&lt;/strong&gt;&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;将目标类和适配者类解耦&lt;/li&gt;
&lt;li&gt;增加了类的透明性&lt;/li&gt;
&lt;li&gt;提高了类的复用性&lt;/li&gt;
&lt;li&gt;灵活性好&lt;/li&gt;
&lt;/ol&gt;
&lt;p&gt;&lt;strong&gt;缺点：&lt;/strong&gt;&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;过多使用适配器会让系统变得凌乱&lt;/li&gt;
&lt;li&gt;类适配器模式的缺点是不支持适配器的适配者类的子类&lt;/li&gt;
&lt;/ol&gt;
&lt;h4&gt;25.7 使用场景&lt;/h4&gt;
&lt;ol&gt;
&lt;li&gt;系统需要使用现有的类，但接口不符合要求&lt;/li&gt;
&lt;li&gt;想要建立一个可以重复使用的类，用于与一些彼此之间没有太大关联的类一起工作&lt;/li&gt;
&lt;li&gt;需要统一多个类的接口设计&lt;/li&gt;
&lt;li&gt;旧系统改造与升级&lt;/li&gt;
&lt;/ol&gt;
&lt;h4&gt;25.8 最佳实践&lt;/h4&gt;
&lt;ol&gt;
&lt;li&gt;优先使用对象适配器，更符合组合复用原则&lt;/li&gt;
&lt;li&gt;尽量保持适配器的简单性，只做必要的转换&lt;/li&gt;
&lt;li&gt;适配器类命名应当以&quot;Adapter&quot;结尾&lt;/li&gt;
&lt;li&gt;适配器中的代码应该是&quot;薄&quot;的，只做必要的接口转换&lt;/li&gt;
&lt;/ol&gt;
&lt;blockquote&gt;
&lt;p&gt;&lt;strong&gt;注意&lt;/strong&gt;：适配器模式是一种&quot;补偿模式&quot;，用来补救设计上的缺陷。如果在设计之初就能协调好接口，就不需要适配器模式了。&lt;/p&gt;
&lt;/blockquote&gt;
&lt;hr /&gt;
&lt;h3&gt;26. Java内部类&lt;/h3&gt;
&lt;p&gt;内部类（Inner Class）是定义在另一个类内部的类。Java支持四种内部类：成员内部类、静态内部类、局部内部类和匿名内部类。&lt;/p&gt;
&lt;h4&gt;26.1 成员内部类&lt;/h4&gt;
&lt;p&gt;成员内部类是最普通的内部类，作为外部类的一个成员存在，可以无限制地访问外部类的所有成员。&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;public class OuterClass {
    private String outerField = &quot;外部类字段&quot;;
    
    // 成员内部类
    public class InnerClass {
        private String innerField = &quot;内部类字段&quot;;
        
        public void innerMethod() {
            // 访问外部类的私有成员
            System.out.println(outerField);
            // 访问外部类的this引用
            System.out.println(OuterClass.this.outerField);
            // 访问内部类成员
            System.out.println(innerField);
        }
    }
    
    // 外部类方法
    public void createInner() {
        InnerClass inner = new InnerClass();
        inner.innerMethod();
    }
}

// 使用示例
public class Main {
    public static void main(String[] args) {
        // 创建外部类实例
        OuterClass outer = new OuterClass();
        // 创建内部类实例
        OuterClass.InnerClass inner = outer.new InnerClass();
        inner.innerMethod();
    }
}
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;&lt;strong&gt;特点：&lt;/strong&gt;&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;可以访问外部类的所有成员，包括私有成员&lt;/li&gt;
&lt;li&gt;必须先创建外部类实例，才能创建内部类实例&lt;/li&gt;
&lt;li&gt;内部类中可以使用OuterClass.this引用外部类实例&lt;/li&gt;
&lt;/ol&gt;
&lt;h4&gt;26.2 静态内部类&lt;/h4&gt;
&lt;p&gt;静态内部类（Static Nested Class）是使用static修饰的内部类，不依赖外部类实例。&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;public class OuterClass {
    private static String staticField = &quot;静态字段&quot;;
    private String instanceField = &quot;实例字段&quot;;
    
    // 静态内部类
    public static class StaticInnerClass {
        public void display() {
            // 可以访问外部类的静态成员
            System.out.println(staticField);
            // 不能直接访问外部类的实例成员
            // System.out.println(instanceField); // 编译错误
        }
    }
}

// 使用示例
public class Main {
    public static void main(String[] args) {
        // 直接创建静态内部类实例，不需要外部类实例
        OuterClass.StaticInnerClass inner = new OuterClass.StaticInnerClass();
        inner.display();
    }
}
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;&lt;strong&gt;特点：&lt;/strong&gt;&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;只能访问外部类的静态成员&lt;/li&gt;
&lt;li&gt;不需要外部类实例即可创建&lt;/li&gt;
&lt;li&gt;可以包含静态成员和实例成员&lt;/li&gt;
&lt;/ol&gt;
&lt;h4&gt;26.3 局部内部类&lt;/h4&gt;
&lt;p&gt;局部内部类是定义在方法或作用域内的类。&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;public class OuterClass {
    private String field = &quot;外部类字段&quot;;
    
    public void method(final String param) {
        final String localVar = &quot;局部变量&quot;;
        
        // 局部内部类
        class LocalInnerClass {
            public void print() {
                // 访问外部类成员
                System.out.println(field);
                // 访问方法参数和局部变量（必须是final或实际上的final）
                System.out.println(param);
                System.out.println(localVar);
            }
        }
        
        // 创建并使用局部内部类
        LocalInnerClass local = new LocalInnerClass();
        local.print();
    }
}
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;&lt;strong&gt;特点：&lt;/strong&gt;&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;只能在定义它的方法或作用域内使用&lt;/li&gt;
&lt;li&gt;可以访问外部类的所有成员&lt;/li&gt;
&lt;li&gt;可以访问所在方法的final或实际上final的局部变量&lt;/li&gt;
&lt;/ol&gt;
&lt;h4&gt;26.4 匿名内部类&lt;/h4&gt;
&lt;p&gt;匿名内部类是没有名字的内部类，必须继承一个父类或实现一个接口。&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;public interface Greeting {
    void greet();
}

public class OuterClass {
    public void sayHello() {
        // 匿名内部类实现接口
        Greeting greeting = new Greeting() {
            @Override
            public void greet() {
                System.out.println(&quot;Hello from anonymous class!&quot;);
            }
        };
        greeting.greet();
    }
    
    public void processRunnable() {
        // 匿名内部类实现Runnable接口
        Runnable runnable = new Runnable() {
            @Override
            public void run() {
                System.out.println(&quot;Running in anonymous class&quot;);
            }
        };
        new Thread(runnable).start();
        
        // 使用Lambda表达式（简化的匿名内部类）
        Runnable lambda = () -&amp;gt; System.out.println(&quot;Running in lambda&quot;);
        new Thread(lambda).start();
    }
}
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;&lt;strong&gt;特点：&lt;/strong&gt;&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;没有类名，创建时必须继承或实现一个接口&lt;/li&gt;
&lt;li&gt;只能使用一次&lt;/li&gt;
&lt;li&gt;可以访问外部类的所有成员&lt;/li&gt;
&lt;li&gt;经常用于事件处理和回调&lt;/li&gt;
&lt;/ol&gt;
&lt;h4&gt;26.5 实际应用场景&lt;/h4&gt;
&lt;p&gt;&lt;strong&gt;1. 适配器模式中使用匿名内部类&lt;/strong&gt;&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;public class Button {
    private OnClickListener listener;
    
    public interface OnClickListener {
        void onClick();
    }
    
    public void setOnClickListener(OnClickListener listener) {
        this.listener = listener;
    }
    
    public void click() {
        if (listener != null) {
            listener.onClick();
        }
    }
}

// 使用示例
public class Main {
    public static void main(String[] args) {
        Button button = new Button();
        
        // 使用匿名内部类设置点击监听器
        button.setOnClickListener(new Button.OnClickListener() {
            @Override
            public void onClick() {
                System.out.println(&quot;Button clicked!&quot;);
            }
        });
        
        // 使用Lambda表达式（简化形式）
        button.setOnClickListener(() -&amp;gt; System.out.println(&quot;Button clicked!&quot;));
        
        button.click();
    }
}
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;&lt;strong&gt;2. 构建器模式中使用静态内部类&lt;/strong&gt;&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;public class Person {
    private final String name;
    private final int age;
    private final String address;
    
    private Person(Builder builder) {
        this.name = builder.name;
        this.age = builder.age;
        this.address = builder.address;
    }
    
    // 静态内部类作为构建器
    public static class Builder {
        private String name;
        private int age;
        private String address;
        
        public Builder name(String name) {
            this.name = name;
            return this;
        }
        
        public Builder age(int age) {
            this.age = age;
            return this;
        }
        
        public Builder address(String address) {
            this.address = address;
            return this;
        }
        
        public Person build() {
            return new Person(this);
        }
    }
}

// 使用示例
Person person = new Person.Builder()
    .name(&quot;张三&quot;)
    .age(25)
    .address(&quot;北京&quot;)
    .build();
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;&lt;strong&gt;3. 数据封装中使用成员内部类&lt;/strong&gt;&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;public class LinkedList&amp;lt;E&amp;gt; {
    private Node&amp;lt;E&amp;gt; first;
    private int size;
    
    // 成员内部类封装节点实现
    private class Node&amp;lt;E&amp;gt; {
        E item;
        Node&amp;lt;E&amp;gt; next;
        
        Node(E element, Node&amp;lt;E&amp;gt; next) {
            this.item = element;
            this.next = next;
        }
    }
    
    public void add(E element) {
        Node&amp;lt;E&amp;gt; newNode = new Node&amp;lt;&amp;gt;(element, first);
        first = newNode;
        size++;
    }
}
&lt;/code&gt;&lt;/pre&gt;
&lt;h4&gt;26.6 内部类的优点&lt;/h4&gt;
&lt;ol&gt;
&lt;li&gt;&lt;strong&gt;封装性&lt;/strong&gt;：内部类可以访问外部类的私有成员&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;代码组织&lt;/strong&gt;：将相关的类放在一起，提高可读性&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;回调机制&lt;/strong&gt;：匿名内部类适合用于事件处理&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;数据隐藏&lt;/strong&gt;：可以对外隐藏实现细节&lt;/li&gt;
&lt;/ol&gt;
&lt;h4&gt;26.7 注意事项&lt;/h4&gt;
&lt;ol&gt;
&lt;li&gt;内部类会持有外部类的引用，可能导致内存泄漏&lt;/li&gt;
&lt;li&gt;过多使用内部类会使代码结构复杂&lt;/li&gt;
&lt;li&gt;静态内部类不持有外部类引用，内存效率更高&lt;/li&gt;
&lt;li&gt;局部内部类访问局部变量时，变量必须是final或实际上的final&lt;/li&gt;
&lt;/ol&gt;
&lt;h4&gt;26.8 最佳实践&lt;/h4&gt;
&lt;ol&gt;
&lt;li&gt;优先使用静态内部类，除非需要访问外部类的实例成员&lt;/li&gt;
&lt;li&gt;使用内部类封装实现细节&lt;/li&gt;
&lt;li&gt;合理使用匿名内部类简化代码&lt;/li&gt;
&lt;li&gt;注意内存泄漏问题，适时释放资源&lt;/li&gt;
&lt;/ol&gt;
&lt;blockquote&gt;
&lt;p&gt;&lt;strong&gt;提示&lt;/strong&gt;：在Java 8及以后的版本中，对于函数式接口，优先使用Lambda表达式替代匿名内部类，代码更简洁。&lt;/p&gt;
&lt;/blockquote&gt;
&lt;hr /&gt;
&lt;h2&gt;30.3 继承中构造方法的自动调用（super）&lt;/h2&gt;
&lt;ul&gt;
&lt;li&gt;在Java中，子类构造方法执行时会&lt;strong&gt;自动调用父类的构造方法&lt;/strong&gt;，以保证父类部分被正确初始化。&lt;/li&gt;
&lt;li&gt;如果子类构造方法没有显式写&lt;code&gt;super(...)&lt;/code&gt;，编译器会自动在第一行加上&lt;code&gt;super();&lt;/code&gt;，即调用父类的无参构造方法。&lt;/li&gt;
&lt;li&gt;如果父类没有无参构造方法，子类必须显式调用父类的有参构造方法，否则编译报错。&lt;/li&gt;
&lt;li&gt;&lt;code&gt;super(...)&lt;/code&gt;必须是子类构造方法的第一条语句。&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;&lt;strong&gt;调用顺序：&lt;/strong&gt;&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;先执行父类构造方法（super），再执行子类构造方法。&lt;/li&gt;
&lt;li&gt;多层继承时，先最顶层父类，再逐层向下。&lt;/li&gt;
&lt;/ol&gt;
&lt;p&gt;&lt;strong&gt;示例：&lt;/strong&gt;&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;class Parent {
    Parent() { System.out.println(&quot;父类构造&quot;); }
}
class Child extends Parent {
    Child() { System.out.println(&quot;子类构造&quot;); }
}
public class Test {
    public static void main(String[] args) {
        new Child();
        // 输出：
        // 父类构造
        // 子类构造
    }
}
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;&lt;strong&gt;有参构造情况：&lt;/strong&gt;&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;class Parent {
    Parent(String msg) { System.out.println(msg); }
}
class Child extends Parent {
    Child() { super(&quot;父类有参构造&quot;); System.out.println(&quot;子类构造&quot;); }
}
&lt;/code&gt;&lt;/pre&gt;
&lt;blockquote&gt;
&lt;p&gt;理解构造方法的自动调用顺序，有助于正确初始化继承体系中的对象，避免常见的编译和运行错误。&lt;/p&gt;
&lt;/blockquote&gt;
&lt;h2&gt;30.4 构造方法中this(...)的含义与用法&lt;/h2&gt;
&lt;ul&gt;
&lt;li&gt;在Java构造方法中，&lt;code&gt;this(...)&lt;/code&gt;用于调用本类的另一个构造方法，实现构造方法之间的重用和统一初始化。&lt;/li&gt;
&lt;li&gt;&lt;code&gt;this(...)&lt;/code&gt;只能出现在构造方法的第一行，且不能与super(...)同时出现。&lt;/li&gt;
&lt;li&gt;通过this(...)可以实现构造方法的链式调用，最终会调用到某个没有this(...)的构造方法（通常是最全参的构造方法）。&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;&lt;strong&gt;示例：&lt;/strong&gt;&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;class Person {
    String name;
    int age;

    // 无参构造
    public Person() {
        this(&quot;无名氏&quot;, 0); // 调用有参构造
        System.out.println(&quot;无参构造执行&quot;);
    }

    // 有参构造
    public Person(String name, int age) {
        this.name = name;
        this.age = age;
        System.out.println(&quot;有参构造执行&quot;);
    }
}

public class Test {
    public static void main(String[] args) {
        Person p = new Person();
        // 输出：
        // 有参构造执行
        // 无参构造执行
    }
}
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;&lt;strong&gt;注意事项：&lt;/strong&gt;&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;this(...)只能用于构造方法，且必须是第一条语句。&lt;/li&gt;
&lt;li&gt;不能和super(...)同时出现。&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;&lt;strong&gt;作用总结：&lt;/strong&gt;&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;统一初始化逻辑，减少重复代码。&lt;/li&gt;
&lt;li&gt;便于维护和扩展构造方法。&lt;/li&gt;
&lt;/ul&gt;
&lt;blockquote&gt;
&lt;p&gt;在构造方法中用this(...)，就是&quot;让本类的另一个构造方法帮我初始化&quot;。&lt;/p&gt;
&lt;/blockquote&gt;
&lt;h2&gt;31. 多态（Polymorphism）&lt;/h2&gt;
&lt;h3&gt;31.1 多态的概念与实现方式&lt;/h3&gt;
&lt;ul&gt;
&lt;li&gt;&lt;strong&gt;多态&lt;/strong&gt;：同一个接口，使用不同的实例而执行不同操作。&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;编译时多态&lt;/strong&gt;：方法重载（Overloading）&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;运行时多态&lt;/strong&gt;：方法重写（Overriding）&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;&lt;strong&gt;示例：&lt;/strong&gt;&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;interface Animal {
    void makeSound();
}

class Dog implements Animal {
    @Override
    public void makeSound() {
        System.out.println(&quot;汪汪汪&quot;);
    }
}

class Cat implements Animal {
    @Override
    public void makeSound() {
        System.out.println(&quot;喵喵喵&quot;);
    }
}

public class Test {
    public static void main(String[] args) {
        Animal animal1 = new Dog(); // 父类引用指向子类对象
        Animal animal2 = new Cat();
        
        animal1.makeSound(); // 输出：汪汪汪
        animal2.makeSound(); // 输出：喵喵喵
    }
}
&lt;/code&gt;&lt;/pre&gt;
&lt;h3&gt;31.2 多态的应用场景&lt;/h3&gt;
&lt;ul&gt;
&lt;li&gt;&lt;strong&gt;方法参数多态&lt;/strong&gt;：一个方法可以接受不同类型的参数，实现不同的行为。&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;方法返回值多态&lt;/strong&gt;：一个方法可以返回不同类型的对象，实现不同的行为。&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;集合中的多态&lt;/strong&gt;：如List、Set、Map等，可以存储不同类型的对象，实现统一处理。&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;&lt;strong&gt;示例：&lt;/strong&gt;&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;public class AnimalTrainer {
    public void train(Animal animal) {
        animal.makeSound();
    }
}

public class AnimalFactory {
    public static Animal createAnimal(String type) {
        if (&quot;dog&quot;.equals(type)) {
            return new Dog();
        } else if (&quot;cat&quot;.equals(type)) {
            return new Cat();
        }
        return null;
    }
}

public class Test {
    public static void main(String[] args) {
        AnimalTrainer trainer = new AnimalTrainer();
        trainer.train(new Dog()); // 输出：汪汪汪
        trainer.train(new Cat()); // 输出：喵喵喵

        Animal dog = AnimalFactory.createAnimal(&quot;dog&quot;);
        Animal cat = AnimalFactory.createAnimal(&quot;cat&quot;);
        dog.makeSound(); // 输出：汪汪汪
        cat.makeSound(); // 输出：喵喵喵
    }
}
&lt;/code&gt;&lt;/pre&gt;
&lt;h3&gt;31.3 多态的注意事项&lt;/h3&gt;
&lt;ul&gt;
&lt;li&gt;&lt;strong&gt;成员变量没有多态&lt;/strong&gt;：父类和子类的成员变量是独立的，子类不能访问父类的成员变量。&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;静态方法没有多态&lt;/strong&gt;：父类的静态方法不能被子类重写，子类可以定义同名的静态方法。&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;构造方法没有多态&lt;/strong&gt;：子类不能重写父类的构造方法。&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;&lt;strong&gt;示例：&lt;/strong&gt;&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;class Parent {
    int num = 10;
    String name = &quot;父类&quot;;
    
    public void showInfo() {
        System.out.println(&quot;父类方法中的num: &quot; + num);
        System.out.println(&quot;父类方法中的name: &quot; + name);
    }
}

class Child extends Parent {
    int num = 20;
    String name = &quot;子类&quot;;
    
    @Override
    public void showInfo() {
        System.out.println(&quot;子类方法中的num: &quot; + num);
        System.out.println(&quot;子类方法中的name: &quot; + name);
    }
}

public class Test {
    public static void main(String[] args) {
        Parent p = new Child(); // 父类引用指向子类对象
        
        // 成员变量调用 - 看引用类型
        System.out.println(p.num);    // 输出：10（父类的num）
        System.out.println(p.name);   // 输出：父类（父类的name）
        
        // 成员方法调用 - 看对象类型
        p.showInfo(); // 输出：
        // 子类方法中的num: 20
        // 子类方法中的name: 子类
    }
}
&lt;/code&gt;&lt;/pre&gt;
&lt;h3&gt;31.4 多态的优势与实际应用&lt;/h3&gt;
&lt;ol&gt;
&lt;li&gt;&lt;strong&gt;可扩展性&lt;/strong&gt;：新增子类不需要修改现有代码，只需添加新的子类即可。&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;可维护性&lt;/strong&gt;：统一的接口，便于维护和扩展。&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;可复用性&lt;/strong&gt;：一个方法可以处理多种类型的对象，提高代码的复用性。&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;灵活性&lt;/strong&gt;：运行时动态绑定，提高程序的灵活性。&lt;/li&gt;
&lt;/ol&gt;
&lt;p&gt;&lt;strong&gt;示例：&lt;/strong&gt;&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;public class AnimalTrainer {
    public void train(Animal animal) {
        animal.makeSound();
    }
}

public class AnimalFactory {
    public static Animal createAnimal(String type) {
        if (&quot;dog&quot;.equals(type)) {
            return new Dog();
        } else if (&quot;cat&quot;.equals(type)) {
            return new Cat();
        }
        return null;
    }
}

public class Test {
    public static void main(String[] args) {
        AnimalTrainer trainer = new AnimalTrainer();
        trainer.train(new Dog()); // 输出：汪汪汪
        trainer.train(new Cat()); // 输出：喵喵喵

        Animal dog = AnimalFactory.createAnimal(&quot;dog&quot;);
        Animal cat = AnimalFactory.createAnimal(&quot;cat&quot;);
        dog.makeSound(); // 输出：汪汪汪
        cat.makeSound(); // 输出：喵喵喵
    }
}
&lt;/code&gt;&lt;/pre&gt;
&lt;h3&gt;31.5 多态中成员变量与成员方法的调用机制&lt;/h3&gt;
&lt;p&gt;多态中成员变量和成员方法的调用行为存在重要差异，理解这些差异有助于正确使用多态特性。&lt;/p&gt;
&lt;h3&gt;32. 抽象类与抽象方法&lt;/h3&gt;
&lt;h4&gt;32.1 抽象类的概念&lt;/h4&gt;
&lt;ul&gt;
&lt;li&gt;抽象类（abstract class）是用&lt;code&gt;abstract&lt;/code&gt;关键字修饰的类，不能被实例化，只能被继承。&lt;/li&gt;
&lt;li&gt;抽象类可以包含抽象方法（没有方法体的方法）和普通方法（有方法体的方法）。&lt;/li&gt;
&lt;li&gt;抽象类用于描述一类事物的通用特征，具体实现由子类完成。&lt;/li&gt;
&lt;/ul&gt;
&lt;h4&gt;32.2 抽象方法的概念&lt;/h4&gt;
&lt;ul&gt;
&lt;li&gt;抽象方法用&lt;code&gt;abstract&lt;/code&gt;关键字修饰，没有方法体（只声明，不实现）。&lt;/li&gt;
&lt;li&gt;抽象方法只能出现在抽象类或接口中。&lt;/li&gt;
&lt;li&gt;子类继承抽象类后，必须实现所有抽象方法，否则子类也必须声明为抽象类。&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;&lt;strong&gt;抽象类和抽象方法的语法：&lt;/strong&gt;&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;public abstract class Animal {
    public abstract void makeSound(); // 抽象方法
    public void eat() {
        System.out.println(&quot;动物吃东西&quot;);
    }
}
&lt;/code&gt;&lt;/pre&gt;
&lt;h4&gt;32.3 特性与规则&lt;/h4&gt;
&lt;ul&gt;
&lt;li&gt;抽象类不能被实例化：&lt;code&gt;new Animal()&lt;/code&gt;会报错。&lt;/li&gt;
&lt;li&gt;抽象类可以有构造方法（用于子类初始化）。&lt;/li&gt;
&lt;li&gt;抽象类可以有成员变量、普通方法、静态方法、常量等。&lt;/li&gt;
&lt;li&gt;抽象类可以没有抽象方法（但一般有）。&lt;/li&gt;
&lt;li&gt;抽象方法不能有方法体，不能用&lt;code&gt;private&lt;/code&gt;、&lt;code&gt;static&lt;/code&gt;、&lt;code&gt;final&lt;/code&gt;修饰。&lt;/li&gt;
&lt;li&gt;子类必须实现所有抽象方法，除非子类也是抽象类。&lt;/li&gt;
&lt;/ul&gt;
&lt;h4&gt;32.4 使用场景&lt;/h4&gt;
&lt;ul&gt;
&lt;li&gt;抽象类用于描述一组具有共性但不完全相同的对象。&lt;/li&gt;
&lt;li&gt;适合做模板、基类，定义规范和通用行为，具体细节由子类实现。&lt;/li&gt;
&lt;li&gt;常用于框架、模板方法设计模式等。&lt;/li&gt;
&lt;/ul&gt;
&lt;h4&gt;32.5 示例代码&lt;/h4&gt;
&lt;pre&gt;&lt;code&gt;// 抽象类定义
public abstract class Animal {
    public abstract void makeSound(); // 抽象方法
    public void eat() {
        System.out.println(&quot;动物吃东西&quot;);
    }
}

// 具体子类实现
public class Dog extends Animal {
    @Override
    public void makeSound() {
        System.out.println(&quot;汪汪汪&quot;);
    }
}

public class Cat extends Animal {
    @Override
    public void makeSound() {
        System.out.println(&quot;喵喵喵&quot;);
    }
}

public class Test {
    public static void main(String[] args) {
        Animal dog = new Dog();
        Animal cat = new Cat();
        dog.makeSound(); // 输出：汪汪汪
        cat.makeSound(); // 输出：喵喵喵
        dog.eat(); // 输出：动物吃东西
    }
}
&lt;/code&gt;&lt;/pre&gt;
&lt;h4&gt;32.6 注意事项与补充&lt;/h4&gt;
&lt;ul&gt;
&lt;li&gt;抽象类可以有构造方法，但不能直接创建对象。&lt;/li&gt;
&lt;li&gt;抽象类的子类如果没有实现所有抽象方法，子类也必须声明为抽象类。&lt;/li&gt;
&lt;li&gt;抽象方法不能是private、static、final。&lt;/li&gt;
&lt;li&gt;抽象类可以有静态方法和常量。&lt;/li&gt;
&lt;li&gt;抽象类可以实现接口，也可以被其他类继承。&lt;/li&gt;
&lt;/ul&gt;
&lt;blockquote&gt;
&lt;p&gt;&lt;strong&gt;总结：&lt;/strong&gt; 抽象类是模板，抽象方法是规范。用抽象类可以统一一组子类的行为规范，强制子类实现核心功能，提升代码的可扩展性和可维护性。&lt;/p&gt;
&lt;/blockquote&gt;
&lt;hr /&gt;
&lt;h3&gt;33. 接口（Interface）&lt;/h3&gt;
&lt;h4&gt;33.1 接口的概念&lt;/h4&gt;
&lt;ul&gt;
&lt;li&gt;接口（interface）是Java中用&lt;code&gt;interface&lt;/code&gt;关键字定义的一种引用类型，是抽象方法和常量值的集合。&lt;/li&gt;
&lt;li&gt;接口只定义规范（方法签名、常量），不包含具体实现。&lt;/li&gt;
&lt;li&gt;接口用于描述一组类应当遵循的行为标准，实现代码解耦和多态。&lt;/li&gt;
&lt;/ul&gt;
&lt;h4&gt;33.2 接口的语法与定义&lt;/h4&gt;
&lt;pre&gt;&lt;code&gt;public interface Animal {
    void makeSound(); // 抽象方法，默认public abstract
    int AGE = 10;     // 常量，默认public static final
}
&lt;/code&gt;&lt;/pre&gt;
&lt;ul&gt;
&lt;li&gt;接口中的方法默认是&lt;code&gt;public abstract&lt;/code&gt;，可以省略。&lt;/li&gt;
&lt;li&gt;接口中的变量默认是&lt;code&gt;public static final&lt;/code&gt;，必须初始化。&lt;/li&gt;
&lt;li&gt;接口不能有构造方法。&lt;/li&gt;
&lt;/ul&gt;
&lt;h4&gt;33.3 接口的实现与多继承&lt;/h4&gt;
&lt;ul&gt;
&lt;li&gt;类通过&lt;code&gt;implements&lt;/code&gt;关键字实现接口，可以实现多个接口（用逗号分隔）。&lt;/li&gt;
&lt;li&gt;一个类可以继承一个父类，同时实现多个接口。&lt;/li&gt;
&lt;li&gt;接口之间可以多继承（&lt;code&gt;extends&lt;/code&gt;），一个接口可以继承多个接口。&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;&lt;strong&gt;示例：&lt;/strong&gt;&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;public interface Flyable {
    void fly();
}

public interface Swimmable {
    void swim();
}

public class Duck implements Flyable, Swimmable {
    @Override
    public void fly() {
        System.out.println(&quot;鸭子飞翔&quot;);
    }
    @Override
    public void swim() {
        System.out.println(&quot;鸭子游泳&quot;);
    }
}
&lt;/code&gt;&lt;/pre&gt;
&lt;h4&gt;33.4 接口的默认方法与静态方法（Java 8+）&lt;/h4&gt;
&lt;ul&gt;
&lt;li&gt;接口可以有&lt;code&gt;default&lt;/code&gt;修饰的默认方法（有方法体），用于接口升级时的兼容。&lt;/li&gt;
&lt;li&gt;接口可以有&lt;code&gt;static&lt;/code&gt;修饰的静态方法（有方法体），只能通过接口名调用。&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;&lt;strong&gt;示例：&lt;/strong&gt;&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;public interface MyInterface {
    void abstractMethod();
    default void defaultMethod() {
        System.out.println(&quot;默认方法&quot;);
    }
    static void staticMethod() {
        System.out.println(&quot;静态方法&quot;);
    }
}

public class MyClass implements MyInterface {
    @Override
    public void abstractMethod() {
        System.out.println(&quot;实现抽象方法&quot;);
    }
}

// 调用
MyClass obj = new MyClass();
obj.defaultMethod(); // 默认方法
MyInterface.staticMethod(); // 静态方法
&lt;/code&gt;&lt;/pre&gt;
&lt;h4&gt;33.5 接口的特性与规则&lt;/h4&gt;
&lt;ul&gt;
&lt;li&gt;接口不能被实例化。&lt;/li&gt;
&lt;li&gt;实现类必须实现接口的所有抽象方法，否则实现类也必须声明为抽象类。&lt;/li&gt;
&lt;li&gt;接口支持多继承，类只支持单继承。&lt;/li&gt;
&lt;li&gt;接口可以有常量、抽象方法、默认方法、静态方法（Java 8+）、私有方法（Java 9+）。&lt;/li&gt;
&lt;li&gt;接口方法不能是private、protected、final、static（除非是静态方法实现）。&lt;/li&gt;
&lt;/ul&gt;
&lt;h4&gt;33.6 接口的使用场景&lt;/h4&gt;
&lt;ul&gt;
&lt;li&gt;规范API设计，定义一组类的统一行为。&lt;/li&gt;
&lt;li&gt;解耦系统结构，实现多态和灵活扩展。&lt;/li&gt;
&lt;li&gt;回调机制、策略模式、适配器模式等设计模式。&lt;/li&gt;
&lt;li&gt;JDK大量API（如Runnable、Comparator、List等）都基于接口设计。&lt;/li&gt;
&lt;/ul&gt;
&lt;h4&gt;33.7 示例代码&lt;/h4&gt;
&lt;pre&gt;&lt;code&gt;public interface USB {
    void connect();
    void disconnect();
}

public class Mouse implements USB {
    @Override
    public void connect() {
        System.out.println(&quot;鼠标已连接&quot;);
    }
    @Override
    public void disconnect() {
        System.out.println(&quot;鼠标已断开&quot;);
    }
}

public class Keyboard implements USB {
    @Override
    public void connect() {
        System.out.println(&quot;键盘已连接&quot;);
    }
    @Override
    public void disconnect() {
        System.out.println(&quot;键盘已断开&quot;);
    }
}

public class Computer {
    public void useUSB(USB usb) {
        usb.connect();
        System.out.println(&quot;设备工作中...&quot;);
        usb.disconnect();
    }
    public static void main(String[] args) {
        Computer pc = new Computer();
        pc.useUSB(new Mouse());
        pc.useUSB(new Keyboard());
    }
}
&lt;/code&gt;&lt;/pre&gt;
&lt;h4&gt;33.8 注意事项与补充&lt;/h4&gt;
&lt;ul&gt;
&lt;li&gt;一个类可以实现多个接口，接口之间用逗号分隔。&lt;/li&gt;
&lt;li&gt;接口可以继承多个接口，实现多继承特性。&lt;/li&gt;
&lt;li&gt;接口中的常量建议大写字母+下划线命名。&lt;/li&gt;
&lt;li&gt;接口适合做规范、回调、扩展点，避免滥用。&lt;/li&gt;
&lt;/ul&gt;
&lt;blockquote&gt;
&lt;p&gt;&lt;strong&gt;总结：&lt;/strong&gt; 接口是行为规范和扩展点，是Java实现多态和解耦的核心机制。合理设计接口有助于系统的灵活性、可维护性和可扩展性。&lt;/p&gt;
&lt;/blockquote&gt;
&lt;hr /&gt;
&lt;h3&gt;34. 内部类（Inner Class）&lt;/h3&gt;
&lt;h4&gt;34.1 内部类的概念&lt;/h4&gt;
&lt;ul&gt;
&lt;li&gt;内部类是定义在另一个类内部的类。&lt;/li&gt;
&lt;li&gt;内部类可以访问外部类的成员，包括私有成员。&lt;/li&gt;
&lt;li&gt;内部类的主要作用是更好地组织代码、实现封装和逻辑关联。&lt;/li&gt;
&lt;/ul&gt;
&lt;h4&gt;34.2 内部类的分类&lt;/h4&gt;
&lt;ol&gt;
&lt;li&gt;&lt;strong&gt;成员内部类&lt;/strong&gt;（普通内部类）：定义在类的成员位置，没有static修饰。&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;静态内部类&lt;/strong&gt;：用static修饰，类似于外部类的静态成员。&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;局部内部类&lt;/strong&gt;：定义在方法、代码块、构造器内部，只在其作用域内有效。&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;匿名内部类&lt;/strong&gt;：没有名字的内部类，通常用于简化接口或抽象类的临时实现。&lt;/li&gt;
&lt;/ol&gt;
&lt;h4&gt;34.3 成员内部类&lt;/h4&gt;
&lt;ul&gt;
&lt;li&gt;作为外部类的成员存在，可以访问外部类的所有成员。&lt;/li&gt;
&lt;li&gt;需要通过外部类对象创建。&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;&lt;strong&gt;示例：&lt;/strong&gt;&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;public class Outer {
    private int data = 10;
    class Inner {
        public void show() {
            System.out.println(&quot;外部类data: &quot; + data);
        }
    }
    public void test() {
        Inner inner = new Inner();
        inner.show();
    }
}
&lt;/code&gt;&lt;/pre&gt;
&lt;h4&gt;34.4 静态内部类&lt;/h4&gt;
&lt;ul&gt;
&lt;li&gt;用static修饰，只能访问外部类的静态成员。&lt;/li&gt;
&lt;li&gt;可以直接通过外部类名创建。&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;&lt;strong&gt;示例：&lt;/strong&gt;&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;public class Outer {
    private static int data = 20;
    static class StaticInner {
        public void show() {
            System.out.println(&quot;外部类静态data: &quot; + data);
        }
    }
    public static void test() {
        StaticInner inner = new StaticInner();
        inner.show();
    }
}
&lt;/code&gt;&lt;/pre&gt;
&lt;h4&gt;34.5 局部内部类&lt;/h4&gt;
&lt;ul&gt;
&lt;li&gt;定义在方法、代码块、构造器内部。&lt;/li&gt;
&lt;li&gt;只能在其作用域内使用。&lt;/li&gt;
&lt;li&gt;可以访问外部类的成员和方法内的final或effectively final局部变量。&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;&lt;strong&gt;示例：&lt;/strong&gt;&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;public class Outer {
    public void method() {
        int num = 100;
        class LocalInner {
            public void show() {
                System.out.println(&quot;num: &quot; + num);
            }
        }
        LocalInner inner = new LocalInner();
        inner.show();
    }
}
&lt;/code&gt;&lt;/pre&gt;
&lt;h4&gt;34.6 匿名内部类&lt;/h4&gt;
&lt;ul&gt;
&lt;li&gt;没有名字的内部类，通常用于简化接口或抽象类的临时实现。&lt;/li&gt;
&lt;li&gt;常用于回调、事件监听等场景。&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;&lt;strong&gt;示例：&lt;/strong&gt;&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;public class Test {
    public static void main(String[] args) {
        Runnable r = new Runnable() {
            @Override
            public void run() {
                System.out.println(&quot;匿名内部类实现线程&quot;);
            }
        };
        new Thread(r).start();
        // 或者直接：
        new Thread(new Runnable() {
            @Override
            public void run() {
                System.out.println(&quot;直接传递匿名内部类&quot;);
            }
        }).start();
    }
}
&lt;/code&gt;&lt;/pre&gt;
&lt;h4&gt;34.7 内部类的特性与注意事项&lt;/h4&gt;
&lt;ul&gt;
&lt;li&gt;内部类可以访问外部类的所有成员，包括private。&lt;/li&gt;
&lt;li&gt;外部类访问成员内部类需通过对象，访问静态内部类可直接通过外部类名。&lt;/li&gt;
&lt;li&gt;局部内部类访问方法内变量时，该变量需为final或&quot;实际上final&quot;。&lt;/li&gt;
&lt;li&gt;匿名内部类不能有构造方法，只能重写父类或接口的方法。&lt;/li&gt;
&lt;li&gt;内部类有自己的字节码文件，命名格式为：外部类$内部类.class。&lt;/li&gt;
&lt;/ul&gt;
&lt;h4&gt;34.8 内部类的使用场景&lt;/h4&gt;
&lt;ul&gt;
&lt;li&gt;事件监听、回调机制（如GUI编程、线程等）。&lt;/li&gt;
&lt;li&gt;封装只为外部类服务的辅助类。&lt;/li&gt;
&lt;li&gt;实现与外部类强关联的功能。&lt;/li&gt;
&lt;/ul&gt;
&lt;blockquote&gt;
&lt;p&gt;&lt;strong&gt;总结：&lt;/strong&gt; 内部类是Java实现封装、逻辑关联和简化代码的重要机制。合理使用内部类可以提升代码的结构性和可维护性。&lt;/p&gt;
&lt;/blockquote&gt;
</content:encoded></item><item><title>MySQL视图、存储过程、触发器详解</title><link>https://meteor-comet.github.io/posts/mysql-views-procedures-triggers/</link><guid isPermaLink="true">https://meteor-comet.github.io/posts/mysql-views-procedures-triggers/</guid><description>数据库对象 / 视图 / 存储过程 / 触发器 / 数据库编程</description><pubDate>Wed, 01 Feb 2023 00:00:00 GMT</pubDate><content:encoded>&lt;h2&gt;学习目标&lt;/h2&gt;
&lt;ul&gt;
&lt;li&gt;掌握MySQL视图的创建、使用和管理&lt;/li&gt;
&lt;li&gt;理解WITH CHECK OPTION的安全机制和应用&lt;/li&gt;
&lt;li&gt;熟悉存储过程的编写和调用方法&lt;/li&gt;
&lt;li&gt;了解触发器的创建和应用场景&lt;/li&gt;
&lt;li&gt;学会使用数据库对象提高开发效率&lt;/li&gt;
&lt;li&gt;掌握数据库编程的最佳实践&lt;/li&gt;
&lt;/ul&gt;
&lt;h2&gt;学习计划&lt;/h2&gt;
&lt;ol&gt;
&lt;li&gt;MySQL视图（Views）详解
&lt;ul&gt;
&lt;li&gt;视图类型和创建方法&lt;/li&gt;
&lt;li&gt;WITH CHECK OPTION安全机制&lt;/li&gt;
&lt;li&gt;视图管理和优化&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;存储过程（Stored Procedures）开发&lt;/li&gt;
&lt;li&gt;触发器（Triggers）应用&lt;/li&gt;
&lt;li&gt;数据库对象管理&lt;/li&gt;
&lt;li&gt;性能优化和最佳实践&lt;/li&gt;
&lt;li&gt;实战案例和常见问题&lt;/li&gt;
&lt;/ol&gt;
&lt;hr /&gt;
&lt;h2&gt;1. MySQL视图（Views）&lt;/h2&gt;
&lt;h3&gt;1.1 视图概述&lt;/h3&gt;
&lt;p&gt;视图是基于一个或多个表的虚拟表，不存储实际数据，只存储查询定义。&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;视图的作用：&lt;/strong&gt;&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;简化复杂查询&lt;/li&gt;
&lt;li&gt;提供数据安全性&lt;/li&gt;
&lt;li&gt;隐藏表结构复杂性&lt;/li&gt;
&lt;li&gt;实现数据独立性&lt;/li&gt;
&lt;li&gt;通过WITH CHECK OPTION确保数据完整性&lt;/li&gt;
&lt;/ul&gt;
&lt;h3&gt;1.2 创建视图&lt;/h3&gt;
&lt;pre&gt;&lt;code&gt;-- 基本语法
CREATE VIEW view_name AS
SELECT column1, column2, ...
FROM table_name
WHERE condition;

-- 创建简单视图
CREATE VIEW user_info AS
SELECT id, name, email, created_at
FROM users
WHERE status = &apos;active&apos;;

-- 创建复杂视图（多表连接）
CREATE VIEW order_summary AS
SELECT 
    o.id as order_id,
    u.name as customer_name,
    o.order_date,
    o.total_amount,
    COUNT(oi.id) as item_count
FROM orders o
JOIN users u ON o.user_id = u.id
LEFT JOIN order_items oi ON o.id = oi.order_id
GROUP BY o.id, u.name, o.order_date, o.total_amount;
&lt;/code&gt;&lt;/pre&gt;
&lt;h3&gt;1.3 视图类型&lt;/h3&gt;
&lt;h4&gt;1.3.1 简单视图&lt;/h4&gt;
&lt;pre&gt;&lt;code&gt;-- 单表视图
CREATE VIEW active_users AS
SELECT id, name, email
FROM users
WHERE status = &apos;active&apos;;
&lt;/code&gt;&lt;/pre&gt;
&lt;h4&gt;1.3.2 复杂视图&lt;/h4&gt;
&lt;pre&gt;&lt;code&gt;-- 多表连接视图
CREATE VIEW customer_orders AS
SELECT 
    c.customer_id,
    c.customer_name,
    o.order_id,
    o.order_date,
    o.total_amount
FROM customers c
JOIN orders o ON c.customer_id = o.customer_id
WHERE o.status = &apos;completed&apos;;
&lt;/code&gt;&lt;/pre&gt;
&lt;h4&gt;1.3.3 可更新视图&lt;/h4&gt;
&lt;pre&gt;&lt;code&gt;-- 创建可更新视图
CREATE VIEW user_profiles AS
SELECT id, name, email, phone
FROM users
WHERE deleted_at IS NULL;

-- 通过视图更新数据
UPDATE user_profiles 
SET phone = &apos;123-456-7890&apos; 
WHERE id = 1;
&lt;/code&gt;&lt;/pre&gt;
&lt;h4&gt;1.3.4 WITH CHECK OPTION&lt;/h4&gt;
&lt;p&gt;&lt;code&gt;WITH CHECK OPTION&lt;/code&gt;是视图的一个重要安全特性，用于确保通过视图插入或更新的数据符合视图的WHERE条件。&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;作用：&lt;/strong&gt;&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;防止通过视图插入不符合条件的数据&lt;/li&gt;
&lt;li&gt;确保数据完整性&lt;/li&gt;
&lt;li&gt;增强视图的安全性&lt;/li&gt;
&lt;/ul&gt;
&lt;pre&gt;&lt;code&gt;-- 创建带WITH CHECK OPTION的视图
CREATE VIEW active_users_view AS
SELECT id, name, email, status
FROM users
WHERE status = &apos;active&apos;
WITH CHECK OPTION;

-- 尝试插入不符合条件的数据（会失败）
INSERT INTO active_users_view (name, email, status) 
VALUES (&apos;John&apos;, &apos;john@example.com&apos;, &apos;inactive&apos;);
-- 错误：CHECK OPTION failed

-- 插入符合条件的数据（成功）
INSERT INTO active_users_view (name, email, status) 
VALUES (&apos;Jane&apos;, &apos;jane@example.com&apos;, &apos;active&apos;);

-- 尝试更新为不符合条件的数据（会失败）
UPDATE active_users_view 
SET status = &apos;inactive&apos; 
WHERE id = 1;
-- 错误：CHECK OPTION failed

-- 更新为符合条件的数据（成功）
UPDATE active_users_view 
SET email = &apos;newemail@example.com&apos; 
WHERE id = 1;
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;&lt;strong&gt;WITH CHECK OPTION的类型：&lt;/strong&gt;&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;&lt;strong&gt;CASCADED（默认）&lt;/strong&gt;：检查当前视图和所有依赖视图的条件&lt;/li&gt;
&lt;/ol&gt;
&lt;pre&gt;&lt;code&gt;CREATE VIEW vip_users AS
SELECT id, name, email, vip_level
FROM active_users_view
WHERE vip_level &amp;gt;= 3
WITH CASCADED CHECK OPTION;
&lt;/code&gt;&lt;/pre&gt;
&lt;ol&gt;
&lt;li&gt;&lt;strong&gt;LOCAL&lt;/strong&gt;：只检查当前视图的条件&lt;/li&gt;
&lt;/ol&gt;
&lt;pre&gt;&lt;code&gt;CREATE VIEW premium_users AS
SELECT id, name, email, membership_type
FROM active_users_view
WHERE membership_type = &apos;premium&apos;
WITH LOCAL CHECK OPTION;
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;&lt;strong&gt;实际应用场景：&lt;/strong&gt;&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;-- 部门视图示例
CREATE VIEW hr_department AS
SELECT id, name, email, department, salary
FROM employees
WHERE department = &apos;HR&apos;
WITH CHECK OPTION;

-- 只能操作HR部门的员工数据
INSERT INTO hr_department (name, email, department, salary)
VALUES (&apos;Alice&apos;, &apos;alice@company.com&apos;, &apos;HR&apos;, 50000);

-- 尝试操作其他部门数据会失败
INSERT INTO hr_department (name, email, department, salary)
VALUES (&apos;Bob&apos;, &apos;bob@company.com&apos;, &apos;IT&apos;, 60000);
-- 错误：CHECK OPTION failed
&lt;/code&gt;&lt;/pre&gt;
&lt;h3&gt;1.4 视图管理&lt;/h3&gt;
&lt;pre&gt;&lt;code&gt;-- 查看所有视图
SHOW FULL TABLES WHERE Table_type = &apos;VIEW&apos;;

-- 查看视图定义
SHOW CREATE VIEW view_name;

-- 修改视图
CREATE OR REPLACE VIEW view_name AS
SELECT column1, column2, ...
FROM table_name
WHERE new_condition;

-- 删除视图
DROP VIEW IF EXISTS view_name;
&lt;/code&gt;&lt;/pre&gt;
&lt;hr /&gt;
&lt;h2&gt;2. 存储过程（Stored Procedures）&lt;/h2&gt;
&lt;h3&gt;2.1 存储过程概述&lt;/h3&gt;
&lt;p&gt;存储过程是一组预编译的SQL语句集合，可以接受参数并返回结果。&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;存储过程的优势：&lt;/strong&gt;&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;提高执行效率&lt;/li&gt;
&lt;li&gt;减少网络传输&lt;/li&gt;
&lt;li&gt;增强安全性&lt;/li&gt;
&lt;li&gt;便于维护&lt;/li&gt;
&lt;/ul&gt;
&lt;h3&gt;2.2 创建存储过程&lt;/h3&gt;
&lt;pre&gt;&lt;code&gt;-- 基本语法
DELIMITER //
CREATE PROCEDURE procedure_name(parameter_list)
BEGIN
    -- 存储过程体
    SQL_statements;
END //
DELIMITER ;

-- 简单存储过程
DELIMITER //
CREATE PROCEDURE GetActiveUsers()
BEGIN
    SELECT id, name, email
    FROM users
    WHERE status = &apos;active&apos;;
END //
DELIMITER ;

-- 带参数的存储过程
DELIMITER //
CREATE PROCEDURE GetUserById(IN user_id INT)
BEGIN
    SELECT id, name, email, created_at
    FROM users
    WHERE id = user_id;
END //
DELIMITER ;
&lt;/code&gt;&lt;/pre&gt;
&lt;h3&gt;2.3 参数类型&lt;/h3&gt;
&lt;h4&gt;2.3.1 IN参数（输入参数）&lt;/h4&gt;
&lt;pre&gt;&lt;code&gt;DELIMITER //
CREATE PROCEDURE GetUsersByStatus(IN user_status VARCHAR(20))
BEGIN
    SELECT id, name, email
    FROM users
    WHERE status = user_status;
END //
DELIMITER ;

-- 调用存储过程
CALL GetUsersByStatus(&apos;active&apos;);
&lt;/code&gt;&lt;/pre&gt;
&lt;h4&gt;2.3.2 OUT参数（输出参数）&lt;/h4&gt;
&lt;pre&gt;&lt;code&gt;DELIMITER //
CREATE PROCEDURE GetOrderStats(
    IN customer_id INT,
    OUT order_count INT,
    OUT total_amount DECIMAL(10,2)
)
BEGIN
    SELECT COUNT(*), SUM(total_amount)
    INTO order_count, total_amount
    FROM orders
    WHERE customer_id = customer_id;
END //
DELIMITER ;
&lt;/code&gt;&lt;/pre&gt;
&lt;h3&gt;2.4 变量和流程控制&lt;/h3&gt;
&lt;h4&gt;2.4.1 变量声明和使用&lt;/h4&gt;
&lt;pre&gt;&lt;code&gt;DELIMITER //
CREATE PROCEDURE CalculateOrderTotal(IN order_id INT)
BEGIN
    DECLARE total DECIMAL(10,2) DEFAULT 0;
    DECLARE item_count INT DEFAULT 0;
    
    -- 计算订单总金额
    SELECT SUM(quantity * price) INTO total
    FROM order_items
    WHERE order_id = order_id;
    
    -- 计算商品数量
    SELECT COUNT(*) INTO item_count
    FROM order_items
    WHERE order_id = order_id;
    
    -- 输出结果
    SELECT order_id, total, item_count;
END //
DELIMITER ;
&lt;/code&gt;&lt;/pre&gt;
&lt;h4&gt;2.4.2 条件语句&lt;/h4&gt;
&lt;pre&gt;&lt;code&gt;DELIMITER //
CREATE PROCEDURE ProcessOrder(IN order_id INT)
BEGIN
    DECLARE order_status VARCHAR(20);
    DECLARE order_amount DECIMAL(10,2);
    
    -- 获取订单信息
    SELECT status, total_amount 
    INTO order_status, order_amount
    FROM orders 
    WHERE id = order_id;
    
    -- 条件处理
    IF order_status = &apos;pending&apos; THEN
        UPDATE orders SET status = &apos;processing&apos; WHERE id = order_id;
        SELECT &apos;Order processing started&apos; as message;
    ELSEIF order_status = &apos;processing&apos; THEN
        UPDATE orders SET status = &apos;completed&apos; WHERE id = order_id;
        SELECT &apos;Order completed&apos; as message;
    ELSE
        SELECT &apos;Order status unchanged&apos; as message;
    END IF;
END //
DELIMITER ;
&lt;/code&gt;&lt;/pre&gt;
&lt;h4&gt;2.4.3 循环语句&lt;/h4&gt;
&lt;pre&gt;&lt;code&gt;DELIMITER //
CREATE PROCEDURE GenerateTestData(IN count INT)
BEGIN
    DECLARE i INT DEFAULT 1;
    
    WHILE i &amp;lt;= count DO
        INSERT INTO test_users (name, email, created_at)
        VALUES (
            CONCAT(&apos;User&apos;, i),
            CONCAT(&apos;user&apos;, i, &apos;@example.com&apos;),
            NOW()
        );
        SET i = i + 1;
    END WHILE;
    
    SELECT CONCAT(&apos;Generated &apos;, count, &apos; test users&apos;) as result;
END //
DELIMITER ;
&lt;/code&gt;&lt;/pre&gt;
&lt;h3&gt;2.5 错误处理&lt;/h3&gt;
&lt;pre&gt;&lt;code&gt;DELIMITER //
CREATE PROCEDURE SafeDeleteUser(IN user_id INT)
BEGIN
    DECLARE EXIT HANDLER FOR SQLEXCEPTION
    BEGIN
        ROLLBACK;
        SELECT &apos;Error occurred, transaction rolled back&apos; as message;
    END;
    
    START TRANSACTION;
    
    -- 检查用户是否存在
    IF NOT EXISTS (SELECT 1 FROM users WHERE id = user_id) THEN
        SIGNAL SQLSTATE &apos;45000&apos;
        SET MESSAGE_TEXT = &apos;User not found&apos;;
    END IF;
    
    -- 删除用户相关数据
    DELETE FROM user_orders WHERE user_id = user_id;
    DELETE FROM user_profiles WHERE user_id = user_id;
    DELETE FROM users WHERE id = user_id;
    
    COMMIT;
    SELECT &apos;User deleted successfully&apos; as message;
END //
DELIMITER ;
&lt;/code&gt;&lt;/pre&gt;
&lt;h3&gt;2.6 存储过程管理&lt;/h3&gt;
&lt;pre&gt;&lt;code&gt;-- 查看所有存储过程
SHOW PROCEDURE STATUS;

-- 查看特定存储过程
SHOW PROCEDURE STATUS WHERE Name = &apos;procedure_name&apos;;

-- 查看存储过程定义
SHOW CREATE PROCEDURE procedure_name;

-- 删除存储过程
DROP PROCEDURE IF EXISTS procedure_name;

-- 修改存储过程（需要先删除再创建）
DROP PROCEDURE IF EXISTS procedure_name;
CREATE PROCEDURE procedure_name(...)
BEGIN
    -- 新的存储过程体
END;
&lt;/code&gt;&lt;/pre&gt;
&lt;hr /&gt;
&lt;h2&gt;3. 触发器（Triggers）&lt;/h2&gt;
&lt;h3&gt;3.1 触发器概述&lt;/h3&gt;
&lt;p&gt;触发器是在表上定义的特殊存储过程，当特定事件发生时自动执行。&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;触发器类型：&lt;/strong&gt;&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;BEFORE INSERT&lt;/li&gt;
&lt;li&gt;AFTER INSERT&lt;/li&gt;
&lt;li&gt;BEFORE UPDATE&lt;/li&gt;
&lt;li&gt;AFTER UPDATE&lt;/li&gt;
&lt;li&gt;BEFORE DELETE&lt;/li&gt;
&lt;li&gt;AFTER DELETE&lt;/li&gt;
&lt;/ul&gt;
&lt;h3&gt;3.2 创建触发器&lt;/h3&gt;
&lt;pre&gt;&lt;code&gt;-- 基本语法
DELIMITER //
CREATE TRIGGER trigger_name
{BEFORE | AFTER} {INSERT | UPDATE | DELETE}
ON table_name
FOR EACH ROW
BEGIN
    -- 触发器体
    SQL_statements;
END //
DELIMITER ;

-- 创建INSERT触发器
DELIMITER //
CREATE TRIGGER after_user_insert
AFTER INSERT ON users
FOR EACH ROW
BEGIN
    INSERT INTO user_audit (user_id, action, created_at)
    VALUES (NEW.id, &apos;INSERT&apos;, NOW());
END //
DELIMITER ;
&lt;/code&gt;&lt;/pre&gt;
&lt;h3&gt;3.3 触发器中的OLD和NEW&lt;/h3&gt;
&lt;h4&gt;3.3.1 INSERT触发器&lt;/h4&gt;
&lt;pre&gt;&lt;code&gt;DELIMITER //
CREATE TRIGGER after_order_insert
AFTER INSERT ON orders
FOR EACH ROW
BEGIN
    -- NEW包含新插入的数据
    UPDATE customers 
    SET total_orders = total_orders + 1,
        last_order_date = NEW.order_date
    WHERE id = NEW.customer_id;
    
    -- 记录订单日志
    INSERT INTO order_logs (order_id, action, created_at)
    VALUES (NEW.id, &apos;ORDER_CREATED&apos;, NOW());
END //
DELIMITER ;
&lt;/code&gt;&lt;/pre&gt;
&lt;h4&gt;3.3.2 UPDATE触发器&lt;/h4&gt;
&lt;pre&gt;&lt;code&gt;DELIMITER //
CREATE TRIGGER before_user_update
BEFORE UPDATE ON users
FOR EACH ROW
BEGIN
    SET NEW.updated_at = NOW();
    
    -- 记录变更
    INSERT INTO user_changes (user_id, old_email, new_email, changed_at)
    VALUES (NEW.id, OLD.email, NEW.email, NOW());
END //
DELIMITER ;

-- 产品价格变更触发器
DELIMITER //
CREATE TRIGGER after_product_update
AFTER UPDATE ON products
FOR EACH ROW
BEGIN
    -- 记录价格变更
    IF OLD.price != NEW.price THEN
        INSERT INTO price_history (
            product_id, 
            old_price, 
            new_price, 
            changed_at
        ) VALUES (
            NEW.id, 
            OLD.price, 
            NEW.price, 
            NOW()
        );
    END IF;
    
    -- 更新库存警告
    IF NEW.stock_quantity &amp;lt; NEW.min_stock_level THEN
        INSERT INTO stock_alerts (product_id, message, created_at)
        VALUES (NEW.id, &apos;Low stock alert&apos;, NOW());
    END IF;
END //
DELIMITER ;
&lt;/code&gt;&lt;/pre&gt;
&lt;h4&gt;3.3.3 DELETE触发器&lt;/h4&gt;
&lt;pre&gt;&lt;code&gt;DELIMITER //
CREATE TRIGGER before_order_delete
BEFORE DELETE ON orders
FOR EACH ROW
BEGIN
    -- 检查订单状态
    IF OLD.status = &apos;completed&apos; THEN
        SIGNAL SQLSTATE &apos;45000&apos;
        SET MESSAGE_TEXT = &apos;Cannot delete completed orders&apos;;
    END IF;
    
    -- 记录删除操作
    INSERT INTO deletion_logs (table_name, record_id, deleted_at)
    VALUES (&apos;orders&apos;, OLD.id, NOW());
END //
DELIMITER ;
&lt;/code&gt;&lt;/pre&gt;
&lt;h3&gt;3.4 复杂触发器示例&lt;/h3&gt;
&lt;h4&gt;3.4.1 库存管理触发器&lt;/h4&gt;
&lt;pre&gt;&lt;code&gt;DELIMITER //
CREATE TRIGGER after_order_item_insert
AFTER INSERT ON order_items
FOR EACH ROW
BEGIN
    DECLARE current_stock INT;
    
    -- 获取当前库存
    SELECT stock_quantity INTO current_stock
    FROM products
    WHERE id = NEW.product_id;
    
    -- 更新库存
    UPDATE products 
    SET stock_quantity = stock_quantity - NEW.quantity
    WHERE id = NEW.product_id;
    
    -- 检查库存不足
    IF (current_stock - NEW.quantity) &amp;lt; 0 THEN
        SIGNAL SQLSTATE &apos;45000&apos;
        SET MESSAGE_TEXT = &apos;Insufficient stock&apos;;
    END IF;
    
    -- 记录库存变更
    INSERT INTO stock_movements (
        product_id, 
        quantity, 
        movement_type, 
        reference_id, 
        created_at
    ) VALUES (
        NEW.product_id, 
        -NEW.quantity, 
        &apos;SALE&apos;, 
        NEW.order_id, 
        NOW()
    );
END //
DELIMITER ;
&lt;/code&gt;&lt;/pre&gt;
&lt;h4&gt;3.4.2 数据完整性触发器&lt;/h4&gt;
&lt;pre&gt;&lt;code&gt;DELIMITER //
CREATE TRIGGER before_user_insert
BEFORE INSERT ON users
FOR EACH ROW
BEGIN
    -- 验证邮箱格式
    IF NEW.email NOT REGEXP &apos;^[A-Za-z0-9._%+-]+@[A-Za-z0-9.-]+\.[A-Za-z]{2,}$&apos; THEN
        SIGNAL SQLSTATE &apos;45000&apos;
        SET MESSAGE_TEXT = &apos;Invalid email format&apos;;
    END IF;
    
    -- 检查邮箱唯一性
    IF EXISTS (SELECT 1 FROM users WHERE email = NEW.email) THEN
        SIGNAL SQLSTATE &apos;45000&apos;
        SET MESSAGE_TEXT = &apos;Email already exists&apos;;
    END IF;
    
    -- 设置默认值
    SET NEW.created_at = NOW();
    SET NEW.status = &apos;active&apos;;
END //
DELIMITER ;
&lt;/code&gt;&lt;/pre&gt;
&lt;h3&gt;3.5 触发器管理&lt;/h3&gt;
&lt;pre&gt;&lt;code&gt;-- 查看所有触发器
SHOW TRIGGERS;

-- 查看特定表的触发器
SHOW TRIGGERS WHERE `Table` = &apos;table_name&apos;;

-- 查看触发器定义
SHOW CREATE TRIGGER trigger_name;

-- 删除触发器
DROP TRIGGER IF EXISTS trigger_name;
&lt;/code&gt;&lt;/pre&gt;
&lt;hr /&gt;
&lt;h2&gt;4. 实战案例&lt;/h2&gt;
&lt;h3&gt;4.1 电商系统示例&lt;/h3&gt;
&lt;h4&gt;4.1.1 订单处理流程&lt;/h4&gt;
&lt;pre&gt;&lt;code&gt;-- 创建订单处理存储过程
DELIMITER //
CREATE PROCEDURE ProcessOrder(IN order_id INT)
BEGIN
    DECLARE order_status VARCHAR(20);
    DECLARE customer_id INT;
    DECLARE total_amount DECIMAL(10,2);
    
    -- 获取订单信息
    SELECT status, customer_id, total_amount 
    INTO order_status, customer_id, total_amount
    FROM orders 
    WHERE id = order_id;
    
    -- 检查订单状态
    IF order_status != &apos;pending&apos; THEN
        SIGNAL SQLSTATE &apos;45000&apos;
        SET MESSAGE_TEXT = &apos;Order is not in pending status&apos;;
    END IF;
    
    -- 开始事务
    START TRANSACTION;
    
    -- 更新订单状态
    UPDATE orders SET status = &apos;processing&apos; WHERE id = order_id;
    
    -- 更新客户统计
    UPDATE customers 
    SET total_orders = total_orders + 1,
        total_spent = total_spent + total_amount
    WHERE id = customer_id;
    
    -- 记录处理日志
    INSERT INTO order_logs (order_id, action, created_at)
    VALUES (order_id, &apos;ORDER_PROCESSED&apos;, NOW());
    
    COMMIT;
    
    SELECT &apos;Order processed successfully&apos; as message;
END //
DELIMITER ;
&lt;/code&gt;&lt;/pre&gt;
&lt;h4&gt;4.1.2 库存管理视图&lt;/h4&gt;
&lt;pre&gt;&lt;code&gt;-- 创建库存状态视图
CREATE VIEW inventory_status AS
SELECT 
    p.id,
    p.name,
    p.stock_quantity,
    p.min_stock_level,
    CASE 
        WHEN p.stock_quantity = 0 THEN &apos;OUT_OF_STOCK&apos;
        WHEN p.stock_quantity &amp;lt;= p.min_stock_level THEN &apos;LOW_STOCK&apos;
        ELSE &apos;IN_STOCK&apos;
    END as stock_status,
    p.price,
    p.category_id,
    c.name as category_name
FROM products p
JOIN categories c ON p.category_id = c.id;

-- 使用视图查询
SELECT * FROM inventory_status WHERE stock_status = &apos;LOW_STOCK&apos;;
&lt;/code&gt;&lt;/pre&gt;
&lt;h3&gt;4.2 日志审计系统&lt;/h3&gt;
&lt;h4&gt;4.2.1 审计触发器&lt;/h4&gt;
&lt;pre&gt;&lt;code&gt;-- 创建通用审计触发器
DELIMITER //
CREATE TRIGGER audit_user_changes
AFTER UPDATE ON users
FOR EACH ROW
BEGIN
    -- 记录所有字段变更
    IF OLD.name != NEW.name THEN
        INSERT INTO audit_logs (table_name, record_id, field_name, old_value, new_value, changed_at)
        VALUES (&apos;users&apos;, NEW.id, &apos;name&apos;, OLD.name, NEW.name, NOW());
    END IF;
    
    IF OLD.email != NEW.email THEN
        INSERT INTO audit_logs (table_name, record_id, field_name, old_value, new_value, changed_at)
        VALUES (&apos;users&apos;, NEW.id, &apos;email&apos;, OLD.email, NEW.email, NOW());
    END IF;
    
    IF OLD.status != NEW.status THEN
        INSERT INTO audit_logs (table_name, record_id, field_name, old_value, new_value, changed_at)
        VALUES (&apos;users&apos;, NEW.id, &apos;status&apos;, OLD.status, NEW.status, NOW());
    END IF;
END //
DELIMITER ;
&lt;/code&gt;&lt;/pre&gt;
&lt;h4&gt;4.2.2 审计查询存储过程&lt;/h4&gt;
&lt;pre&gt;&lt;code&gt;DELIMITER //
CREATE PROCEDURE GetAuditTrail(
    IN table_name VARCHAR(50),
    IN record_id INT,
    IN start_date DATETIME,
    IN end_date DATETIME
)
BEGIN
    SELECT 
        al.field_name,
        al.old_value,
        al.new_value,
        al.changed_at,
        u.name as changed_by
    FROM audit_logs al
    LEFT JOIN users u ON al.changed_by = u.id
    WHERE al.table_name = table_name
    AND al.record_id = record_id
    AND al.changed_at BETWEEN start_date AND end_date
    ORDER BY al.changed_at DESC;
END //
DELIMITER ;
&lt;/code&gt;&lt;/pre&gt;
&lt;hr /&gt;
&lt;h2&gt;5. 性能优化和最佳实践&lt;/h2&gt;
&lt;h3&gt;5.1 视图最佳实践&lt;/h3&gt;
&lt;ul&gt;
&lt;li&gt;使用视图简化复杂查询&lt;/li&gt;
&lt;li&gt;避免在视图中使用ORDER BY（除非有LIMIT）&lt;/li&gt;
&lt;li&gt;合理使用索引提高视图性能&lt;/li&gt;
&lt;li&gt;定期检查和优化视图定义&lt;/li&gt;
&lt;/ul&gt;
&lt;h3&gt;5.2 存储过程最佳实践&lt;/h3&gt;
&lt;ul&gt;
&lt;li&gt;使用有意义的参数名和变量名&lt;/li&gt;
&lt;li&gt;添加适当的错误处理&lt;/li&gt;
&lt;li&gt;避免在存储过程中使用SELECT *&lt;/li&gt;
&lt;li&gt;合理使用事务&lt;/li&gt;
&lt;li&gt;添加注释说明存储过程功能&lt;/li&gt;
&lt;/ul&gt;
&lt;h3&gt;5.3 触发器最佳实践&lt;/h3&gt;
&lt;ul&gt;
&lt;li&gt;保持触发器逻辑简单&lt;/li&gt;
&lt;li&gt;避免在触发器中执行复杂查询&lt;/li&gt;
&lt;li&gt;使用适当的错误处理&lt;/li&gt;
&lt;li&gt;考虑触发器对性能的影响&lt;/li&gt;
&lt;li&gt;避免触发器链（触发器调用触发器）&lt;/li&gt;
&lt;/ul&gt;
&lt;h3&gt;5.4 性能优化建议&lt;/h3&gt;
&lt;ul&gt;
&lt;li&gt;为视图和存储过程中使用的表创建适当索引&lt;/li&gt;
&lt;li&gt;避免在循环中执行SQL语句&lt;/li&gt;
&lt;li&gt;使用批量操作代替单条操作&lt;/li&gt;
&lt;li&gt;定期分析执行计划&lt;/li&gt;
&lt;li&gt;监控数据库对象的使用情况&lt;/li&gt;
&lt;/ul&gt;
&lt;h3&gt;5.5 数据库对象管理&lt;/h3&gt;
&lt;pre&gt;&lt;code&gt;-- 权限管理
GRANT SELECT ON database_name.view_name TO &apos;username&apos;@&apos;host&apos;;
GRANT EXECUTE ON PROCEDURE database_name.procedure_name TO &apos;username&apos;@&apos;host&apos;;

-- 性能监控
EXPLAIN SELECT * FROM view_name WHERE condition;
SHOW PROFILES;
SHOW PROFILE FOR QUERY query_id;

-- 备份和恢复
mysqldump --routines --triggers database_name &amp;gt; backup.sql;
&lt;/code&gt;&lt;/pre&gt;
&lt;hr /&gt;
&lt;h2&gt;总结&lt;/h2&gt;
&lt;ul&gt;
&lt;li&gt;视图提供数据抽象和安全性&lt;/li&gt;
&lt;li&gt;存储过程提高执行效率和代码复用&lt;/li&gt;
&lt;li&gt;触发器实现数据完整性和业务逻辑&lt;/li&gt;
&lt;li&gt;合理使用数据库对象提高开发效率&lt;/li&gt;
&lt;li&gt;注意性能影响和维护成本&lt;/li&gt;
&lt;/ul&gt;
&lt;h2&gt;参考资料&lt;/h2&gt;
&lt;ul&gt;
&lt;li&gt;MySQL官方文档：Views, Stored Procedures, Triggers&lt;/li&gt;
&lt;li&gt;MySQL存储过程编程最佳实践&lt;/li&gt;
&lt;li&gt;数据库设计模式与最佳实践&lt;/li&gt;
&lt;/ul&gt;
</content:encoded></item><item><title>数据结构详解</title><link>https://meteor-comet.github.io/posts/data-structures-detailed/</link><guid isPermaLink="true">https://meteor-comet.github.io/posts/data-structures-detailed/</guid><description>数据结构 / 算法 / 编程基础 / 计算机科学</description><pubDate>Tue, 10 Jan 2023 00:00:00 GMT</pubDate><content:encoded>&lt;h2&gt;学习目标&lt;/h2&gt;
&lt;ul&gt;
&lt;li&gt;理解各种基本数据结构的概念和特点&lt;/li&gt;
&lt;li&gt;掌握常见数据结构的实现方式&lt;/li&gt;
&lt;li&gt;学会在实际编程中选择合适的数据结构&lt;/li&gt;
&lt;li&gt;理解时间和空间复杂度分析&lt;/li&gt;
&lt;li&gt;掌握数据结构在算法设计中的应用&lt;/li&gt;
&lt;/ul&gt;
&lt;h2&gt;学习计划&lt;/h2&gt;
&lt;ol&gt;
&lt;li&gt;数据结构概述与复杂度分析&lt;/li&gt;
&lt;li&gt;线性结构：数组、链表、栈、队列&lt;/li&gt;
&lt;li&gt;树形结构：二叉树、平衡树、堆&lt;/li&gt;
&lt;li&gt;图结构：表示方法与遍历算法&lt;/li&gt;
&lt;li&gt;哈希表与散列算法&lt;/li&gt;
&lt;li&gt;高级数据结构简介&lt;/li&gt;
&lt;li&gt;数据结构选择与应用实践&lt;/li&gt;
&lt;/ol&gt;
&lt;hr /&gt;
&lt;h2&gt;1. 数据结构概述&lt;/h2&gt;
&lt;h3&gt;1.1 什么是数据结构&lt;/h3&gt;
&lt;p&gt;数据结构是计算机中组织和存储数据的方式，它使得数据可以高效地被访问和修改。更确切地说，数据结构是数据值的集合、数据之间的关系以及对数据进行的操作。&lt;/p&gt;
&lt;h3&gt;1.2 数据结构的分类&lt;/h3&gt;
&lt;p&gt;数据结构主要分为以下几类：&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;strong&gt;线性结构&lt;/strong&gt;：数据元素之间存在一对一的线性关系&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;树形结构&lt;/strong&gt;：数据元素之间存在一对多的层次关系&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;图形结构&lt;/strong&gt;：数据元素之间存在多对多的任意关系&lt;/li&gt;
&lt;/ul&gt;
&lt;h3&gt;1.3 算法复杂度分析&lt;/h3&gt;
&lt;p&gt;算法复杂度分析是评估算法效率的重要手段，主要包括时间复杂度和空间复杂度两个方面。&lt;/p&gt;
&lt;h4&gt;1.3.1 时间复杂度表示法&lt;/h4&gt;
&lt;p&gt;时间复杂度是用来描述算法运行时间与输入规模之间关系的度量。我们通常使用大O记号（Big O Notation）来表示算法的最坏情况时间复杂度。&lt;/p&gt;
&lt;p&gt;大O记号描述的是算法运行时间的增长率，而非精确的运行时间。它关注的是当输入规模趋向于无穷大时，算法运行时间的主导项。&lt;/p&gt;
&lt;p&gt;除了大O记号之外，还有其他几种常用的复杂度表示法：&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;strong&gt;Ω记号（Big Omega Notation）&lt;/strong&gt;：表示算法最好情况下的时间复杂度，即算法运行时间的下界&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Θ记号（Big Theta Notation）&lt;/strong&gt;：表示算法平均情况下的时间复杂度，即算法运行时间的紧确界&lt;/li&gt;
&lt;/ul&gt;
&lt;h4&gt;1.3.2 常见时间复杂度详解&lt;/h4&gt;
&lt;p&gt;&lt;strong&gt;O(1) - 常数时间复杂度&lt;/strong&gt;
这是最优的时间复杂度，表示算法的执行时间不随输入规模的变化而变化。无论处理多少数据，算法都能在相同的时间内完成。例如访问数组中的某个元素，只需要通过索引直接定位即可，不需要遍历整个数组。&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;O(log n) - 对数时间复杂度&lt;/strong&gt;
这种复杂度常见于分治算法中，每次操作都能将问题规模减半。典型的例子是二分查找，每次比较都能排除一半的可能性。随着输入规模的增大，执行时间增长非常缓慢。&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;O(n) - 线性时间复杂度&lt;/strong&gt;
算法的执行时间与输入规模成正比。需要遍历所有输入数据一次才能得到结果。例如查找数组中的最大值，需要遍历整个数组。&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;O(n log n) - 线性对数时间复杂度&lt;/strong&gt;
这种复杂度常见于高效的排序算法中，如快速排序、归并排序和堆排序。它们通常采用分治策略，将问题分解为更小的子问题。&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;O(n²) - 平方时间复杂度&lt;/strong&gt;
这类算法通常涉及嵌套循环，内外两层循环都需要遍历所有数据。典型的例子是冒泡排序和选择排序。当输入规模较大时，执行时间会急剧增长。&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;O(2ⁿ) - 指数时间复杂度&lt;/strong&gt;
这类算法的执行时间随着输入规模呈指数级增长，常见于暴力搜索某些组合问题。即使是很小的输入规模，也可能导致无法接受的执行时间。&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;O(n!) - 阶乘时间复杂度&lt;/strong&gt;
这是最糟糕的时间复杂度之一，常见于生成所有排列的问题。随着输入规模的增长，执行时间会迅速变得不可行。&lt;/p&gt;
&lt;h4&gt;1.3.3 空间复杂度分析&lt;/h4&gt;
&lt;p&gt;空间复杂度是指算法在运行过程中临时占用存储空间大小的量度。同样使用大O记号来表示。&lt;/p&gt;
&lt;p&gt;需要注意的是，空间复杂度通常不包括输入数据本身占用的空间，只计算算法运行过程中额外申请的空间。&lt;/p&gt;
&lt;hr /&gt;
&lt;h2&gt;2. 线性结构&lt;/h2&gt;
&lt;h3&gt;2.1 数组（Array）&lt;/h3&gt;
&lt;p&gt;数组是最基本的数据结构，用于存储相同类型的元素。&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;特点：&lt;/strong&gt;&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;元素在内存中连续存储&lt;/li&gt;
&lt;li&gt;支持随机访问，通过索引直接定位元素&lt;/li&gt;
&lt;li&gt;大小固定&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;&lt;strong&gt;Java实现：&lt;/strong&gt;&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;public class MyArray {
    private int[] array;
    private int size;
    
    public MyArray(int capacity) {
        this.array = new int[capacity];
        this.size = 0;
    }
    
    // 插入元素到末尾
    public void insert(int element) {
        if (size &amp;gt;= array.length) {
            throw new RuntimeException(&quot;数组已满&quot;);
        }
        array[size++] = element;
    }
    
    // 根据索引删除元素
    public int delete(int index) {
        if (index &amp;lt; 0 || index &amp;gt;= size) {
            throw new RuntimeException(&quot;索引越界&quot;);
        }
        int deletedElement = array[index];
        // 将删除位置后的元素前移
        for (int i = index; i &amp;lt; size - 1; i++) {
            array[i] = array[i + 1];
        }
        size--;
        return deletedElement;
    }
    
    // 根据索引查找元素
    public int get(int index) {
        if (index &amp;lt; 0 || index &amp;gt;= size) {
            throw new RuntimeException(&quot;索引越界&quot;);
        }
        return array[index];
    }
    
    // 打印数组
    public void display() {
        System.out.print(&quot;[&quot;);
        for (int i = 0; i &amp;lt; size; i++) {
            System.out.print(array[i]);
            if (i &amp;lt; size - 1) {
                System.out.print(&quot;, &quot;);
            }
        }
        System.out.println(&quot;]&quot;);
    }
}
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;&lt;strong&gt;时间复杂度：&lt;/strong&gt;&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;访问：O(1)&lt;/li&gt;
&lt;li&gt;搜索：O(n)&lt;/li&gt;
&lt;li&gt;插入：O(n)&lt;/li&gt;
&lt;li&gt;删除：O(n)&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;&lt;strong&gt;空间复杂度：&lt;/strong&gt; O(n)&lt;/p&gt;
&lt;h3&gt;2.2 链表（Linked List）&lt;/h3&gt;
&lt;p&gt;链表是由节点组成的线性数据结构，每个节点包含数据和指向下一个节点的指针。&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;特点：&lt;/strong&gt;&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;元素在内存中非连续存储&lt;/li&gt;
&lt;li&gt;不支持随机访问&lt;/li&gt;
&lt;li&gt;大小动态变化&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;&lt;strong&gt;Java实现（单向链表）：&lt;/strong&gt;&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;public class ListNode {
    int val;
    ListNode next;
    
    ListNode() {}
    ListNode(int val) { this.val = val; }
    ListNode(int val, ListNode next) { this.val = val; this.next = next; }
}

public class MyLinkedList {
    private ListNode head;
    private int size;
    
    public MyLinkedList() {
        this.head = null;
        this.size = 0;
    }
    
    // 在头部插入元素
    public void insertFirst(int val) {
        ListNode newNode = new ListNode(val);
        newNode.next = head;
        head = newNode;
        size++;
    }
    
    // 在尾部插入元素
    public void insertLast(int val) {
        ListNode newNode = new ListNode(val);
        if (head == null) {
            head = newNode;
        } else {
            ListNode current = head;
            while (current.next != null) {
                current = current.next;
            }
            current.next = newNode;
        }
        size++;
    }
    
    // 删除第一个元素
    public int deleteFirst() {
        if (head == null) {
            throw new RuntimeException(&quot;链表为空&quot;);
        }
        int deletedVal = head.val;
        head = head.next;
        size--;
        return deletedVal;
    }
    
    // 根据索引查找元素
    public int get(int index) {
        if (index &amp;lt; 0 || index &amp;gt;= size) {
            throw new RuntimeException(&quot;索引越界&quot;);
        }
        ListNode current = head;
        for (int i = 0; i &amp;lt; index; i++) {
            current = current.next;
        }
        return current.val;
    }
    
    // 打印链表
    public void display() {
        ListNode current = head;
        System.out.print(&quot;[&quot;);
        while (current != null) {
            System.out.print(current.val);
            if (current.next != null) {
                System.out.print(&quot;, &quot;);
            }
            current = current.next;
        }
        System.out.println(&quot;]&quot;);
    }
}
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;&lt;strong&gt;类型：&lt;/strong&gt;&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;单向链表&lt;/li&gt;
&lt;li&gt;双向链表&lt;/li&gt;
&lt;li&gt;循环链表&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;&lt;strong&gt;时间复杂度：&lt;/strong&gt;&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;访问：O(n)&lt;/li&gt;
&lt;li&gt;搜索：O(n)&lt;/li&gt;
&lt;li&gt;插入：O(1)&lt;/li&gt;
&lt;li&gt;删除：O(1)&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;&lt;strong&gt;空间复杂度：&lt;/strong&gt; O(n)&lt;/p&gt;
&lt;h3&gt;2.3 栈（Stack）&lt;/h3&gt;
&lt;p&gt;栈是一种后进先出（LIFO）的数据结构。&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;基本操作：&lt;/strong&gt;&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;push：入栈&lt;/li&gt;
&lt;li&gt;pop：出栈&lt;/li&gt;
&lt;li&gt;peek/top：查看栈顶元素&lt;/li&gt;
&lt;li&gt;isEmpty：检查栈是否为空&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;&lt;strong&gt;Java实现（基于数组）：&lt;/strong&gt;&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;public class MyStack {
    private int[] stack;
    private int top;
    private int capacity;
    
    public MyStack(int capacity) {
        this.capacity = capacity;
        this.stack = new int[capacity];
        this.top = -1;
    }
    
    // 入栈
    public void push(int item) {
        if (isFull()) {
            throw new RuntimeException(&quot;栈已满&quot;);
        }
        stack[++top] = item;
    }
    
    // 出栈
    public int pop() {
        if (isEmpty()) {
            throw new RuntimeException(&quot;栈为空&quot;);
        }
        return stack[top--];
    }
    
    // 查看栈顶元素
    public int peek() {
        if (isEmpty()) {
            throw new RuntimeException(&quot;栈为空&quot;);
        }
        return stack[top];
    }
    
    // 检查栈是否为空
    public boolean isEmpty() {
        return top == -1;
    }
    
    // 检查栈是否已满
    public boolean isFull() {
        return top == capacity - 1;
    }
}
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;&lt;strong&gt;应用场景：&lt;/strong&gt;&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;函数调用栈&lt;/li&gt;
&lt;li&gt;表达式求值&lt;/li&gt;
&lt;li&gt;括号匹配&lt;/li&gt;
&lt;li&gt;浏览器历史记录&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;&lt;strong&gt;时间复杂度：&lt;/strong&gt;&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;push：O(1)&lt;/li&gt;
&lt;li&gt;pop：O(1)&lt;/li&gt;
&lt;li&gt;peek：O(1)&lt;/li&gt;
&lt;li&gt;isEmpty：O(1)&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;&lt;strong&gt;空间复杂度：&lt;/strong&gt; O(n)&lt;/p&gt;
&lt;h3&gt;2.4 队列（Queue）&lt;/h3&gt;
&lt;p&gt;队列是一种先进先出（FIFO）的数据结构。&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;基本操作：&lt;/strong&gt;&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;enqueue：入队&lt;/li&gt;
&lt;li&gt;dequeue：出队&lt;/li&gt;
&lt;li&gt;front：查看队首元素&lt;/li&gt;
&lt;li&gt;rear：查看队尾元素&lt;/li&gt;
&lt;li&gt;isEmpty：检查队列是否为空&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;&lt;strong&gt;Java实现（基于数组）：&lt;/strong&gt;&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;public class MyQueue {
    private int[] queue;
    private int front;
    private int rear;
    private int size;
    private int capacity;
    
    public MyQueue(int capacity) {
        this.capacity = capacity;
        this.queue = new int[capacity];
        this.front = 0;
        this.rear = -1;
        this.size = 0;
    }
    
    // 入队
    public void enqueue(int item) {
        if (isFull()) {
            throw new RuntimeException(&quot;队列已满&quot;);
        }
        rear = (rear + 1) % capacity;
        queue[rear] = item;
        size++;
    }
    
    // 出队
    public int dequeue() {
        if (isEmpty()) {
            throw new RuntimeException(&quot;队列为空&quot;);
        }
        int item = queue[front];
        front = (front + 1) % capacity;
        size--;
        return item;
    }
    
    // 查看队首元素
    public int front() {
        if (isEmpty()) {
            throw new RuntimeException(&quot;队列为空&quot;);
        }
        return queue[front];
    }
    
    // 查看队尾元素
    public int rear() {
        if (isEmpty()) {
            throw new RuntimeException(&quot;队列为空&quot;);
        }
        return queue[rear];
    }
    
    // 检查队列是否为空
    public boolean isEmpty() {
        return size == 0;
    }
    
    // 检查队列是否已满
    public boolean isFull() {
        return size == capacity;
    }
}
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;&lt;strong&gt;变种：&lt;/strong&gt;&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;双端队列（Deque）&lt;/li&gt;
&lt;li&gt;优先队列（Priority Queue）&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;&lt;strong&gt;时间复杂度：&lt;/strong&gt;&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;enqueue：O(1)&lt;/li&gt;
&lt;li&gt;dequeue：O(1)&lt;/li&gt;
&lt;li&gt;front：O(1)&lt;/li&gt;
&lt;li&gt;rear：O(1)&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;&lt;strong&gt;空间复杂度：&lt;/strong&gt; O(n)&lt;/p&gt;
&lt;hr /&gt;
&lt;h2&gt;3. 树形结构&lt;/h2&gt;
&lt;h3&gt;3.1 二叉树（Binary Tree）&lt;/h3&gt;
&lt;p&gt;二叉树是每个节点最多有两个子树的树结构，子树有左右之分。&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;性质：&lt;/strong&gt;&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;第i层最多有2^(i-1)个节点&lt;/li&gt;
&lt;li&gt;深度为k的二叉树最多有2^k-1个节点&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;&lt;strong&gt;Java实现：&lt;/strong&gt;&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;public class TreeNode {
    int val;
    TreeNode left;
    TreeNode right;
    
    TreeNode() {}
    TreeNode(int val) { this.val = val; }
    TreeNode(int val, TreeNode left, TreeNode right) {
        this.val = val;
        this.left = left;
        this.right = right;
    }
}

public class BinaryTree {
    TreeNode root;
    
    public BinaryTree() {
        this.root = null;
    }
    
    // 前序遍历（递归）
    public void preorderTraversal(TreeNode node) {
        if (node != null) {
            System.out.print(node.val + &quot; &quot;);
            preorderTraversal(node.left);
            preorderTraversal(node.right);
        }
    }
    
    // 中序遍历（递归）
    public void inorderTraversal(TreeNode node) {
        if (node != null) {
            inorderTraversal(node.left);
            System.out.print(node.val + &quot; &quot;);
            inorderTraversal(node.right);
        }
    }
    
    // 后序遍历（递归）
    public void postorderTraversal(TreeNode node) {
        if (node != null) {
            postorderTraversal(node.left);
            postorderTraversal(node.right);
            System.out.print(node.val + &quot; &quot;);
        }
    }
}
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;&lt;strong&gt;遍历方式：&lt;/strong&gt;&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;前序遍历（根-左-右）&lt;/li&gt;
&lt;li&gt;中序遍历（左-根-右）&lt;/li&gt;
&lt;li&gt;后序遍历（左-右-根）&lt;/li&gt;
&lt;li&gt;层序遍历（广度优先）&lt;/li&gt;
&lt;/ul&gt;
&lt;h3&gt;3.2 二叉搜索树（BST）&lt;/h3&gt;
&lt;p&gt;二叉搜索树是一种特殊的二叉树，满足以下性质：&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;左子树所有节点的值小于根节点的值&lt;/li&gt;
&lt;li&gt;右子树所有节点的值大于根节点的值&lt;/li&gt;
&lt;li&gt;左右子树也分别为二叉搜索树&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;&lt;strong&gt;Java实现：&lt;/strong&gt;&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;public class BST {
    class Node {
        int key;
        Node left, right;
        
        public Node(int item) {
            key = item;
            left = right = null;
        }
    }
    
    Node root;
    
    public BST() {
        root = null;
    }
    
    // 插入节点
    public void insert(int key) {
        root = insertRec(root, key);
    }
    
    private Node insertRec(Node root, int key) {
        if (root == null) {
            root = new Node(key);
            return root;
        }
        
        if (key &amp;lt; root.key)
            root.left = insertRec(root.left, key);
        else if (key &amp;gt; root.key)
            root.right = insertRec(root.right, key);
        
        return root;
    }
    
    // 搜索节点
    public boolean search(int key) {
        return searchRec(root, key);
    }
    
    private boolean searchRec(Node root, int key) {
        if (root == null)
            return false;
        
        if (root.key == key)
            return true;
        
        if (key &amp;lt; root.key)
            return searchRec(root.left, key);
        
        return searchRec(root.right, key);
    }
    
    // 删除节点
    public void deleteKey(int key) {
        root = deleteRec(root, key);
    }
    
    private Node deleteRec(Node root, int key) {
        if (root == null)
            return root;
        
        if (key &amp;lt; root.key)
            root.left = deleteRec(root.left, key);
        else if (key &amp;gt; root.key)
            root.right = deleteRec(root.right, key);
        else {
            // 节点只有一个子节点或没有子节点
            if (root.left == null)
                return root.right;
            else if (root.right == null)
                return root.left;
            
            // 节点有两个子节点，获取中序后继（右子树中的最小值）
            root.key = minValue(root.right);
            
            // 删除中序后继
            root.right = deleteRec(root.right, root.key);
        }
        
        return root;
    }
    
    private int minValue(Node root) {
        int minv = root.key;
        while (root.left != null) {
            minv = root.left.key;
            root = root.left;
        }
        return minv;
    }
}
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;&lt;strong&gt;时间复杂度：&lt;/strong&gt;&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;平均情况：O(log n)&lt;/li&gt;
&lt;li&gt;最坏情况：O(n)（退化为链表）&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;&lt;strong&gt;空间复杂度：&lt;/strong&gt; O(n)&lt;/p&gt;
&lt;h3&gt;3.3 树的旋转操作&lt;/h3&gt;
&lt;p&gt;树的旋转是在保持二叉搜索树性质的前提下，重新组织树结构的操作，主要用于平衡树。&lt;/p&gt;
&lt;h4&gt;3.3.1 右旋转（Right Rotation）&lt;/h4&gt;
&lt;pre&gt;&lt;code&gt;public TreeNode rightRotate(TreeNode y) {
    TreeNode x = y.left;
    TreeNode T2 = x.right;
    
    // 执行旋转
    x.right = y;
    y.left = T2;
    
    // 返回新的根节点
    return x;
}
&lt;/code&gt;&lt;/pre&gt;
&lt;h4&gt;3.3.2 左旋转（Left Rotation）&lt;/h4&gt;
&lt;pre&gt;&lt;code&gt;public TreeNode leftRotate(TreeNode x) {
    TreeNode y = x.right;
    TreeNode T2 = y.left;
    
    // 执行旋转
    y.left = x;
    x.right = T2;
    
    // 返回新的根节点
    return y;
}
&lt;/code&gt;&lt;/pre&gt;
&lt;h3&gt;3.4 AVL树&lt;/h3&gt;
&lt;p&gt;AVL树是高度平衡的二叉搜索树，任何节点的两个子树的高度差不超过1。&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;Java实现：&lt;/strong&gt;&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;public class AVLTree {
    class Node {
        int key, height;
        Node left, right;
        
        Node(int d) {
            key = d;
            height = 1;
        }
    }
    
    Node root;
    
    // 获取节点高度
    int height(Node N) {
        if (N == null)
            return 0;
        return N.height;
    }
    
    // 右旋转
    Node rightRotate(Node y) {
        Node x = y.left;
        Node T2 = x.right;
        
        // 执行旋转
        x.right = y;
        y.left = T2;
        
        // 更新高度
        y.height = Math.max(height(y.left), height(y.right)) + 1;
        x.height = Math.max(height(x.left), height(x.right)) + 1;
        
        // 返回新的根
        return x;
    }
    
    // 左旋转
    Node leftRotate(Node x) {
        Node y = x.right;
        Node T2 = y.left;
        
        // 执行旋转
        y.left = x;
        x.right = T2;
        
        // 更新高度
        x.height = Math.max(height(x.left), height(x.right)) + 1;
        y.height = Math.max(height(y.left), height(y.right)) + 1;
        
        // 返回新的根
        return y;
    }
    
    // 获取平衡因子
    int getBalance(Node N) {
        if (N == null)
            return 0;
        return height(N.left) - height(N.right);
    }
    
    // 插入节点
    Node insert(Node node, int key) {
        // 1. 执行正常的BST插入
        if (node == null)
            return (new Node(key));
        
        if (key &amp;lt; node.key)
            node.left = insert(node.left, key);
        else if (key &amp;gt; node.key)
            node.right = insert(node.right, key);
        else // 相等的键不允许
            return node;
        
        // 2. 更新祖先节点的高度
        node.height = 1 + Math.max(height(node.left),
                                  height(node.right));
        
        // 3. 获取平衡因子
        int balance = getBalance(node);
        
        // 如果节点变得不平衡，则有4种情况
        
        // 左左情况
        if (balance &amp;gt; 1 &amp;amp;&amp;amp; key &amp;lt; node.left.key)
            return rightRotate(node);
        
        // 右右情况
        if (balance &amp;lt; -1 &amp;amp;&amp;amp; key &amp;gt; node.right.key)
            return leftRotate(node);
        
        // 左右情况
        if (balance &amp;gt; 1 &amp;amp;&amp;amp; key &amp;gt; node.left.key) {
            node.left = leftRotate(node.left);
            return rightRotate(node);
        }
        
        // 右左情况
        if (balance &amp;lt; -1 &amp;amp;&amp;amp; key &amp;lt; node.right.key) {
            node.right = rightRotate(node.right);
            return leftRotate(node);
        }
        
        // 返回未改变的节点指针
        return node;
    }
}
&lt;/code&gt;&lt;/pre&gt;
&lt;h3&gt;3.5 红黑树（Red-Black Tree）&lt;/h3&gt;
&lt;p&gt;红黑树是一种自平衡的二叉搜索树，通过给每个节点着色（红色或黑色）并在插入和删除操作时维持特定的性质来保证平衡。&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;红黑树的性质：&lt;/strong&gt;&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;每个节点要么是红色，要么是黑色&lt;/li&gt;
&lt;li&gt;根节点是黑色&lt;/li&gt;
&lt;li&gt;所有叶子节点（NIL节点）都是黑色&lt;/li&gt;
&lt;li&gt;如果一个节点是红色，则它的两个子节点都是黑色（即不能有连续的红色节点）&lt;/li&gt;
&lt;li&gt;从任一节点到其每个叶子的所有路径都包含相同数目的黑色节点&lt;/li&gt;
&lt;/ol&gt;
&lt;p&gt;&lt;strong&gt;Java实现（简化版）：&lt;/strong&gt;&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;public class RedBlackTree {
    private final int RED = 0;
    private final int BLACK = 1;
    
    class Node {
        int data;
        int color;
        Node left;
        Node right;
        Node parent;
        boolean isNullNode;
        
        Node(int data) {
            this.data = data;
            this.color = RED; // 新节点默认为红色
            this.isNullNode = false;
        }
        
        Node() {
            this.isNullNode = true;
        }
    }
    
    private Node root;
    private Node nil; // 哨兵节点，代表所有的叶子节点
    
    public RedBlackTree() {
        nil = new Node();
        nil.color = BLACK;
        root = nil;
    }
    
    // 左旋转
    private void leftRotate(Node x) {
        Node y = x.right;
        x.right = y.left;
        
        if (!y.left.isNullNode) {
            y.left.parent = x;
        }
        
        y.parent = x.parent;
        
        if (x.parent.isNullNode) {
            root = y;
        } else if (x == x.parent.left) {
            x.parent.left = y;
        } else {
            x.parent.right = y;
        }
        
        y.left = x;
        x.parent = y;
    }
    
    // 右旋转
    private void rightRotate(Node y) {
        Node x = y.left;
        y.left = x.right;
        
        if (!x.right.isNullNode) {
            x.right.parent = y;
        }
        
        x.parent = y.parent;
        
        if (y.parent.isNullNode) {
            root = x;
        } else if (y == y.parent.right) {
            y.parent.right = x;
        } else {
            y.parent.left = x;
        }
        
        x.right = y;
        y.parent = x;
    }
    
    // 插入修复
    private void insertFixup(Node z) {
        while (z.parent.color == RED) {
            if (z.parent == z.parent.parent.left) {
                Node y = z.parent.parent.right;
                if (y.color == RED) {
                    // 情况1：叔叔节点是红色
                    z.parent.color = BLACK;
                    y.color = BLACK;
                    z.parent.parent.color = RED;
                    z = z.parent.parent;
                } else {
                    if (z == z.parent.right) {
                        // 情况2：叔叔节点是黑色，z是右孩子
                        z = z.parent;
                        leftRotate(z);
                    }
                    // 情况3：叔叔节点是黑色，z是左孩子
                    z.parent.color = BLACK;
                    z.parent.parent.color = RED;
                    rightRotate(z.parent.parent);
                }
            } else {
                Node y = z.parent.parent.left;
                if (y.color == RED) {
                    // 情况1：叔叔节点是红色
                    z.parent.color = BLACK;
                    y.color = BLACK;
                    z.parent.parent.color = RED;
                    z = z.parent.parent;
                } else {
                    if (z == z.parent.left) {
                        // 情况2：叔叔节点是黑色，z是左孩子
                        z = z.parent;
                        rightRotate(z);
                    }
                    // 情况3：叔叔节点是黑色，z是右孩子
                    z.parent.color = BLACK;
                    z.parent.parent.color = RED;
                    leftRotate(z.parent.parent);
                }
            }
        }
        root.color = BLACK;
    }
    
    // 插入节点
    public void insert(int data) {
        Node z = new Node(data);
        z.left = nil;
        z.right = nil;
        
        Node y = nil;
        Node x = root;
        
        // 找到插入位置
        while (!x.isNullNode) {
            y = x;
            if (z.data &amp;lt; x.data) {
                x = x.left;
            } else {
                x = x.right;
            }
        }
        
        z.parent = y;
        
        if (y.isNullNode) {
            root = z;
        } else if (z.data &amp;lt; y.data) {
            y.left = z;
        } else {
            y.right = z;
        }
        
        // 修复红黑树性质
        insertFixup(z);
    }
}
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;&lt;strong&gt;时间复杂度：&lt;/strong&gt;&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;搜索：O(log n)&lt;/li&gt;
&lt;li&gt;插入：O(log n)&lt;/li&gt;
&lt;li&gt;删除：O(log n)&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;&lt;strong&gt;空间复杂度：&lt;/strong&gt; O(n)&lt;/p&gt;
&lt;h3&gt;3.6 堆（Heap）&lt;/h3&gt;
&lt;p&gt;堆是一种特殊的完全二叉树，分为最大堆和最小堆。&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;性质：&lt;/strong&gt;&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;最大堆：父节点的值总是大于或等于其子节点的值&lt;/li&gt;
&lt;li&gt;最小堆：父节点的值总是小于或等于其子节点的值&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;&lt;strong&gt;Java实现（最小堆）：&lt;/strong&gt;&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;public class MinHeap {
    private int[] heap;
    private int size;
    private int maxSize;
    
    public MinHeap(int maxSize) {
        this.maxSize = maxSize;
        this.size = 0;
        heap = new int[this.maxSize + 1];
        heap[0] = Integer.MIN_VALUE;
    }
    
    // 获取父节点索引
    private int parent(int pos) {
        return pos / 2;
    }
    
    // 获取左子节点索引
    private int leftChild(int pos) {
        return (2 * pos);
    }
    
    // 获取右子节点索引
    private int rightChild(int pos) {
        return (2 * pos) + 1;
    }
    
    // 检查是否为叶子节点
    private boolean isLeaf(int pos) {
        return pos &amp;gt;= (size / 2) &amp;amp;&amp;amp; pos &amp;lt;= size;
    }
    
    // 交换两个节点
    private void swap(int fpos, int spos) {
        int tmp = heap[fpos];
        heap[fpos] = heap[spos];
        heap[spos] = tmp;
    }
    
    // 堆化操作（向下调整）
    private void minHeapify(int pos) {
        if (!isLeaf(pos)) {
            // 如果不是叶子节点且大于任一子节点
            if (heap[pos] &amp;gt; heap[leftChild(pos)] || 
                heap[pos] &amp;gt; heap[rightChild(pos)]) {
                
                // 与较小的子节点交换
                if (heap[leftChild(pos)] &amp;lt; heap[rightChild(pos)]) {
                    swap(pos, leftChild(pos));
                    minHeapify(leftChild(pos));
                } else {
                    swap(pos, rightChild(pos));
                    minHeapify(rightChild(pos));
                }
            }
        }
    }
    
    // 插入元素
    public void insert(int element) {
        if (size &amp;gt;= maxSize) {
            return;
        }
        heap[++size] = element;
        int current = size;
        
        // 向上调整
        while (heap[current] &amp;lt; heap[parent(current)]) {
            swap(current, parent(current));
            current = parent(current);
        }
    }
    
    // 移除最小元素
    public int remove() {
        int popped = heap[1];
        heap[1] = heap[size--];
        minHeapify(1);
        return popped;
    }
    
    // 构建堆
    public void minHeap() {
        for (int pos = (size / 2); pos &amp;gt;= 1; pos--) {
            minHeapify(pos);
        }
    }
}
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;&lt;strong&gt;应用场景：&lt;/strong&gt;&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;堆排序&lt;/li&gt;
&lt;li&gt;优先队列&lt;/li&gt;
&lt;li&gt;Top K问题&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;&lt;strong&gt;时间复杂度：&lt;/strong&gt;&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;插入：O(log n)&lt;/li&gt;
&lt;li&gt;删除最小元素：O(log n)&lt;/li&gt;
&lt;li&gt;获取最小元素：O(1)&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;&lt;strong&gt;空间复杂度：&lt;/strong&gt; O(n)&lt;/p&gt;
&lt;hr /&gt;
&lt;h2&gt;4. 图结构&lt;/h2&gt;
&lt;h3&gt;4.1 图的表示&lt;/h3&gt;
&lt;p&gt;图是由节点（顶点）和边组成的非线性数据结构。&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;表示方法：&lt;/strong&gt;&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;邻接矩阵&lt;/li&gt;
&lt;li&gt;邻接表&lt;/li&gt;
&lt;li&gt;边列表&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;&lt;strong&gt;Java实现（邻接表）：&lt;/strong&gt;&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;import java.util.*;
public class Graph {
    private int numVertices;
    private List&amp;lt;List&amp;lt;Integer&amp;gt;&amp;gt; adjLists;
    private boolean directed;
    
    Graph(int vertices, boolean directed) {
        this.numVertices = vertices;
        this.directed = directed;
        adjLists = new ArrayList&amp;lt;&amp;gt;();
        for (int i = 0; i &amp;lt; vertices; i++) {
            adjLists.add(new LinkedList&amp;lt;&amp;gt;());
        }
    }
    
    // 添加边
    void addEdge(int src, int dest) {
        adjLists.get(src).add(dest);
        if (!directed) {
            adjLists.get(dest).add(src);
        }
    }
    
    // 打印图
    void printGraph() {
        for (int v = 0; v &amp;lt; numVertices; v++) {
            System.out.println(&quot;顶点 &quot; + v + &quot;:&quot;);
            for (Integer p : adjLists.get(v)) {
                System.out.print(&quot; -&amp;gt; &quot; + p);
            }
            System.out.println();
        }
    }
}
&lt;/code&gt;&lt;/pre&gt;
&lt;h3&gt;4.2 图的遍历&lt;/h3&gt;
&lt;ul&gt;
&lt;li&gt;&lt;strong&gt;深度优先搜索（DFS）&lt;/strong&gt;：类似于树的前序遍历&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;广度优先搜索（BFS）&lt;/strong&gt;：类似于树的层序遍历&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;&lt;strong&gt;Java实现（DFS和BFS）：&lt;/strong&gt;&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;import java.util.*;

public class GraphTraversal {
    private int numVertices;
    private List&amp;lt;List&amp;lt;Integer&amp;gt;&amp;gt; adjLists;
    
    GraphTraversal(int vertices) {
        this.numVertices = vertices;
        adjLists = new ArrayList&amp;lt;&amp;gt;();
        for (int i = 0; i &amp;lt; vertices; i++) {
            adjLists.add(new LinkedList&amp;lt;&amp;gt;());
        }
    }
    
    void addEdge(int src, int dest) {
        adjLists.get(src).add(dest);
        adjLists.get(dest).add(src);
    }
    
    // 深度优先搜索
    void DFSUtil(int vertex, boolean[] visited) {
        visited[vertex] = true;
        System.out.print(vertex + &quot; &quot;);
        
        Iterator&amp;lt;Integer&amp;gt; ite = adjLists.get(vertex).iterator();
        while (ite.hasNext()) {
            int adj = ite.next();
            if (!visited[adj])
                DFSUtil(adj, visited);
        }
    }
    
    void DFS(int vertex) {
        boolean[] visited = new boolean[numVertices];
        DFSUtil(vertex, visited);
    }
    
    // 广度优先搜索
    void BFS(int vertex) {
        boolean[] visited = new boolean[numVertices];
        Queue&amp;lt;Integer&amp;gt; queue = new LinkedList&amp;lt;&amp;gt;();
        
        visited[vertex] = true;
        queue.add(vertex);
        
        while (!queue.isEmpty()) {
            int s = queue.poll();
            System.out.print(s + &quot; &quot;);
            
            Iterator&amp;lt;Integer&amp;gt; ite = adjLists.get(s).listIterator();
            while (ite.hasNext()) {
                int adj = ite.next();
                if (!visited[adj]) {
                    visited[adj] = true;
                    queue.add(adj);
                }
            }
        }
    }
}
&lt;/code&gt;&lt;/pre&gt;
&lt;h3&gt;4.3 最短路径算法&lt;/h3&gt;
&lt;ul&gt;
&lt;li&gt;Dijkstra算法&lt;/li&gt;
&lt;li&gt;Floyd-Warshall算法&lt;/li&gt;
&lt;li&gt;Bellman-Ford算法&lt;/li&gt;
&lt;/ul&gt;
&lt;h3&gt;4.4 最小生成树&lt;/h3&gt;
&lt;ul&gt;
&lt;li&gt;Prim算法&lt;/li&gt;
&lt;li&gt;Kruskal算法&lt;/li&gt;
&lt;/ul&gt;
&lt;hr /&gt;
&lt;h2&gt;5. 哈希表&lt;/h2&gt;
&lt;h3&gt;5.1 哈希函数&lt;/h3&gt;
&lt;p&gt;哈希函数是将输入（键）映射到固定范围输出的函数。&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;常见哈希函数：&lt;/strong&gt;&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;除法哈希法&lt;/li&gt;
&lt;li&gt;乘法哈希法&lt;/li&gt;
&lt;li&gt;全域哈希法&lt;/li&gt;
&lt;/ul&gt;
&lt;h3&gt;5.2 冲突解决&lt;/h3&gt;
&lt;p&gt;当不同键映射到同一位置时发生冲突。&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;解决方案：&lt;/strong&gt;&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;链地址法（拉链法）&lt;/li&gt;
&lt;li&gt;开放寻址法
&lt;ul&gt;
&lt;li&gt;线性探测&lt;/li&gt;
&lt;li&gt;二次探测&lt;/li&gt;
&lt;li&gt;双重哈希&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;&lt;strong&gt;Java实现（链地址法）：&lt;/strong&gt;&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;import java.util.ArrayList;

public class HashTable&amp;lt;K, V&amp;gt; {
    private class Entry&amp;lt;K, V&amp;gt; {
        K key;
        V value;
        Entry&amp;lt;K, V&amp;gt; next;
        
        public Entry(K key, V value) {
            this.key = key;
            this.value = value;
            this.next = null;
        }
    }
    
    private int size;
    private ArrayList&amp;lt;Entry&amp;lt;K, V&amp;gt;&amp;gt; bucketArray;
    
    public HashTable() {
        bucketArray = new ArrayList&amp;lt;&amp;gt;();
        size = 0;
        
        // 创建空桶
        for (int i = 0; i &amp;lt; 10; i++) {
            bucketArray.add(null);
        }
    }
    
    public int getSize() {
        return size;
    }
    
    public boolean isEmpty() {
        return getSize() == 0;
    }
    
    // 哈希函数
    private int getBucketIndex(K key) {
        int hashCode = key.hashCode();
        int index = hashCode % bucketArray.size();
        return index &amp;lt; 0 ? index * -1 : index;
    }
    
    // 添加键值对
    public void put(K key, V value) {
        int bucketIndex = getBucketIndex(key);
        Entry&amp;lt;K, V&amp;gt; head = bucketArray.get(bucketIndex);
        
        // 检查键是否已存在
        while (head != null) {
            if (head.key.equals(key)) {
                head.value = value;
                return;
            }
            head = head.next;
        }
        
        // 插入新节点在链表头部
        size++;
        head = bucketArray.get(bucketIndex);
        Entry&amp;lt;K, V&amp;gt; newNode = new Entry&amp;lt;&amp;gt;(key, value);
        newNode.next = head;
        bucketArray.set(bucketIndex, newNode);
        
        // 如果负载因子超过阈值，则扩容
        if ((1.0 * size) / bucketArray.size() &amp;gt;= 0.7) {
            ArrayList&amp;lt;Entry&amp;lt;K, V&amp;gt;&amp;gt; temp = bucketArray;
            bucketArray = new ArrayList&amp;lt;&amp;gt;();
            size = 0;
            for (int i = 0; i &amp;lt; 2 * temp.size(); i++) {
                bucketArray.add(null);
            }
            
            for (Entry&amp;lt;K, V&amp;gt; headNode : temp) {
                while (headNode != null) {
                    put(headNode.key, headNode.value);
                    headNode = headNode.next;
                }
            }
        }
    }
    
    // 获取值
    public V get(K key) {
        int bucketIndex = getBucketIndex(key);
        Entry&amp;lt;K, V&amp;gt; head = bucketArray.get(bucketIndex);
        
        // 搜索键在链表中
        while (head != null) {
            if (head.key.equals(key)) {
                return head.value;
            }
            head = head.next;
        }
        
        // 如果找不到键
        return null;
    }
    
    // 删除键值对
    public V remove(K key) {
        int bucketIndex = getBucketIndex(key);
        
        Entry&amp;lt;K, V&amp;gt; head = bucketArray.get(bucketIndex);
        
        Entry&amp;lt;K, V&amp;gt; prev = null;
        while (head != null) {
            if (head.key.equals(key))
                break;
            
            prev = head;
            head = head.next;
        }
        
        if (head == null)
            return null;
        
        size--;
        
        if (prev != null)
            prev.next = head.next;
        else
            bucketArray.set(bucketIndex, head.next);
        
        return head.value;
    }
}
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;&lt;strong&gt;时间复杂度：&lt;/strong&gt;&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;平均情况：O(1)&lt;/li&gt;
&lt;li&gt;最坏情况：O(n)&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;&lt;strong&gt;空间复杂度：&lt;/strong&gt; O(n)&lt;/p&gt;
&lt;hr /&gt;
&lt;h2&gt;6. 算法详解&lt;/h2&gt;
&lt;h3&gt;6.1 排序算法&lt;/h3&gt;
&lt;h4&gt;6.1.1 冒泡排序（Bubble Sort）&lt;/h4&gt;
&lt;p&gt;冒泡排序是一种简单的排序算法，它重复地遍历要排序的数列，一次比较两个元素，如果他们的顺序错误就把他们交换过来。遍历数列的工作是重复地进行直到没有再需要交换，也就是说该数列已经排序完成。&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;public class BubbleSort {
    public static void bubbleSort(int[] arr) {
        int n = arr.length;
        for (int i = 0; i &amp;lt; n - 1; i++) {
            for (int j = 0; j &amp;lt; n - i - 1; j++) {
                if (arr[j] &amp;gt; arr[j + 1]) {
                    // 交换元素
                    int temp = arr[j];
                    arr[j] = arr[j + 1];
                    arr[j + 1] = temp;
                }
            }
        }
    }
}
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;&lt;strong&gt;时间复杂度：&lt;/strong&gt;&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;最好情况：O(n)（已经排序的情况）&lt;/li&gt;
&lt;li&gt;平均情况：O(n²)&lt;/li&gt;
&lt;li&gt;最坏情况：O(n²)（逆序排列）&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;&lt;strong&gt;空间复杂度：&lt;/strong&gt; O(1)&lt;/p&gt;
&lt;h4&gt;6.1.2 选择排序（Selection Sort）&lt;/h4&gt;
&lt;p&gt;选择排序是一种简单直观的排序算法。它的工作原理是每一次从待排序的数据元素中选出最小（或最大）的一个元素，存放在序列的起始位置，直到全部待排序的数据元素排完。&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;public class SelectionSort {
    public static void selectionSort(int[] arr) {
        int n = arr.length;
        
        // 选择排序的主要逻辑
        for (int i = 0; i &amp;lt; n - 1; i++) {
            // 找到最小元素的索引
            int minIdx = i;
            for (int j = i + 1; j &amp;lt; n; j++) {
                if (arr[j] &amp;lt; arr[minIdx]) {
                    minIdx = j;
                }
            }
            
            // 交换找到的最小元素与第一个元素
            int temp = arr[minIdx];
            arr[minIdx] = arr[i];
            arr[i] = temp;
        }
    }
}
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;&lt;strong&gt;时间复杂度：&lt;/strong&gt;&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;最好情况：O(n²)&lt;/li&gt;
&lt;li&gt;平均情况：O(n²)&lt;/li&gt;
&lt;li&gt;最坏情况：O(n²)&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;&lt;strong&gt;空间复杂度：&lt;/strong&gt; O(1)&lt;/p&gt;
&lt;h4&gt;6.1.3 插入排序（Insertion Sort）&lt;/h4&gt;
&lt;p&gt;插入排序是一种简单直观的排序算法。它的工作原理是通过构建有序序列，对于未排序数据，在已排序序列中从后向前扫描，找到相应位置并插入。&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;public class InsertionSort {
    public static void insertionSort(int[] arr) {
        int n = arr.length;
        for (int i = 1; i &amp;lt; n; ++i) {
            int key = arr[i];
            int j = i - 1;
            
            // 将大于key的元素向后移动一位
            while (j &amp;gt;= 0 &amp;amp;&amp;amp; arr[j] &amp;gt; key) {
                arr[j + 1] = arr[j];
                j = j - 1;
            }
            arr[j + 1] = key;
        }
    }
}
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;&lt;strong&gt;时间复杂度：&lt;/strong&gt;&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;最好情况：O(n)（已经排序的情况）&lt;/li&gt;
&lt;li&gt;平均情况：O(n²)&lt;/li&gt;
&lt;li&gt;最坏情况：O(n²)（逆序排列）&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;&lt;strong&gt;空间复杂度：&lt;/strong&gt; O(1)&lt;/p&gt;
&lt;h4&gt;6.1.4 归并排序（Merge Sort）&lt;/h4&gt;
&lt;p&gt;归并排序是一种分治算法。它将已有序的子序列合并，得到完全有序的序列；即先使每个子序列有序，再使子序列段间有序。&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;public class MergeSort {
    public static void mergeSort(int[] arr, int l, int r) {
        if (l &amp;lt; r) {
            // 找到中点
            int m = (l + r) / 2;
            
            // 递归排序第一和第二部分
            mergeSort(arr, l, m);
            mergeSort(arr, m + 1, r);
            
            // 合并已排序的部分
            merge(arr, l, m, r);
        }
    }
    
    public static void merge(int[] arr, int l, int m, int r) {
        // 找到两个子数组的大小
        int n1 = m - l + 1;
        int n2 = r - m;
        
        // 创建临时数组
        int[] L = new int[n1];
        int[] R = new int[n2];
        
        // 复制数据到临时数组
        for (int i = 0; i &amp;lt; n1; ++i)
            L[i] = arr[l + i];
        for (int j = 0; j &amp;lt; n2; ++j)
            R[j] = arr[m + 1 + j];
        
        // 合并临时数组回到arr[l..r]
        int i = 0, j = 0;
        int k = l;
        while (i &amp;lt; n1 &amp;amp;&amp;amp; j &amp;lt; n2) {
            if (L[i] &amp;lt;= R[j]) {
                arr[k] = L[i];
                i++;
            } else {
                arr[k] = R[j];
                j++;
            }
            k++;
        }
        
        // 复制L[]的剩余元素
        while (i &amp;lt; n1) {
            arr[k] = L[i];
            i++;
            k++;
        }
        
        // 复制R[]的剩余元素
        while (j &amp;lt; n2) {
            arr[k] = R[j];
            j++;
            k++;
        }
    }
}
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;&lt;strong&gt;时间复杂度：&lt;/strong&gt;&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;最好情况：O(n log n)&lt;/li&gt;
&lt;li&gt;平均情况：O(n log n)&lt;/li&gt;
&lt;li&gt;最坏情况：O(n log n)&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;&lt;strong&gt;空间复杂度：&lt;/strong&gt; O(n)&lt;/p&gt;
&lt;h4&gt;6.1.5 快速排序（Quick Sort）&lt;/h4&gt;
&lt;p&gt;快速排序是一种分治算法。它选择一个元素作为基准（pivot），将数组分为两部分，一部分小于基准，另一部分大于基准，然后递归地对这两部分进行排序。&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;public class QuickSort {
    public static void quickSort(int[] arr, int low, int high) {
        if (low &amp;lt; high) {
            // pi是分区索引，arr[pi]现在在正确位置
            int pi = partition(arr, low, high);
            
            // 分别对基准元素前后两部分进行排序
            quickSort(arr, low, pi - 1);
            quickSort(arr, pi + 1, high);
        }
    }
    
    public static int partition(int[] arr, int low, int high) {
        int pivot = arr[high];
        int i = (low - 1); // 较小元素的索引
        
        for (int j = low; j &amp;lt; high; j++) {
            // 如果当前元素小于或等于基准
            if (arr[j] &amp;lt;= pivot) {
                i++;
                
                // 交换arr[i]和arr[j]
                int temp = arr[i];
                arr[i] = arr[j];
                arr[j] = temp;
            }
        }
        
        // 交换arr[i+1]和arr[high]（即基准元素）
        int temp = arr[i + 1];
        arr[i + 1] = arr[high];
        arr[high] = temp;
        
        return i + 1;
    }
}
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;&lt;strong&gt;时间复杂度：&lt;/strong&gt;&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;最好情况：O(n log n)&lt;/li&gt;
&lt;li&gt;平均情况：O(n log n)&lt;/li&gt;
&lt;li&gt;最坏情况：O(n²)（每次选的基准都是最大或最小元素）&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;&lt;strong&gt;空间复杂度：&lt;/strong&gt; O(log n)&lt;/p&gt;
&lt;h4&gt;6.1.6 堆排序（Heap Sort）&lt;/h4&gt;
&lt;p&gt;堆排序是一种基于比较的排序算法，利用堆这种数据结构来设计算法。&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;public class HeapSort {
    public static void heapSort(int[] arr) {
        int n = arr.length;
        
        // 构建最大堆（从最后一个非叶子节点开始）
        for (int i = n / 2 - 1; i &amp;gt;= 0; i--)
            heapify(arr, n, i);
        
        // 一个个从堆顶取出元素
        for (int i = n - 1; i &amp;gt; 0; i--) {
            // 将当前最大元素移到末尾
            int temp = arr[0];
            arr[0] = arr[i];
            arr[i] = temp;
            
            // 重新调整堆
            heapify(arr, i, 0);
        }
    }
    
    public static void heapify(int[] arr, int n, int i) {
        int largest = i; // 初始化最大为根节点
        int l = 2 * i + 1; // 左子节点
        int r = 2 * i + 2; // 右子节点
        
        // 如果左子节点大于根节点
        if (l &amp;lt; n &amp;amp;&amp;amp; arr[l] &amp;gt; arr[largest])
            largest = l;
        
        // 如果右子节点大于目前的最大值
        if (r &amp;lt; n &amp;amp;&amp;amp; arr[r] &amp;gt; arr[largest])
            largest = r;
        
        // 如果最大值不是根节点
        if (largest != i) {
            int swap = arr[i];
            arr[i] = arr[largest];
            arr[largest] = swap;
            
            // 递归地调整受影响的子树
            heapify(arr, n, largest);
        }
    }
}
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;&lt;strong&gt;时间复杂度：&lt;/strong&gt;&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;最好情况：O(n log n)&lt;/li&gt;
&lt;li&gt;平均情况：O(n log n)&lt;/li&gt;
&lt;li&gt;最坏情况：O(n log n)&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;&lt;strong&gt;空间复杂度：&lt;/strong&gt; O(1)&lt;/p&gt;
&lt;h3&gt;6.2 贪心算法（Greedy Algorithm）&lt;/h3&gt;
&lt;p&gt;贪心算法是一种在每一步选择中都采取在当前状态下最好或最优（即最有利）的选择，从而希望导致结果是最好或最优的算法。&lt;/p&gt;
&lt;h4&gt;6.2.1 活动选择问题&lt;/h4&gt;
&lt;p&gt;活动选择问题是贪心算法的经典应用之一。给定n个活动，每个活动都有开始时间和结束时间，选择最多数量的活动，使得它们在时间上不冲突。&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;import java.util.*;
public class ActivitySelection {
    // 表示活动的类
    static class Activity {
        int start, finish;
        
        public Activity(int start, int finish) {
            this.start = start;
            this.finish = finish;
        }
    }
    
    // 按照结束时间排序的比较器
    static class Compare implements Comparator&amp;lt;Activity&amp;gt; {
        public int compare(Activity s1, Activity s2) {
            return s1.finish - s2.finish;
        }
    }
    
    // 返回最大数量的不冲突活动
    public static void printMaxActivities(Activity[] activities) {
        // 按结束时间排序
        Arrays.sort(activities, new Compare());
        
        System.out.println(&quot;选择的活动:&quot;);
        
        // 第一个活动总是被选择
        int i = 0;
        System.out.println(&quot;(&quot; + activities[i].start + &quot;, &quot; + activities[i].finish + &quot;)&quot;);
        
        // 考虑其余活动
        for (int j = 1; j &amp;lt; activities.length; j++) {
            // 如果这个活动的开始时间大于等于前一个选定活动的结束时间，则选择它
            if (activities[j].start &amp;gt;= activities[i].finish) {
                System.out.println(&quot;(&quot; + activities[j].start + &quot;, &quot; + activities[j].finish + &quot;)&quot;);
                i = j;
            }
        }
    }
}
&lt;/code&gt;&lt;/pre&gt;
&lt;h4&gt;6.2.2 分数背包问题&lt;/h4&gt;
&lt;p&gt;分数背包问题也是贪心算法的经典应用。与0/1背包问题不同，我们可以取物品的一部分。&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;import java.util.*;
public class FractionalKnapsack {
    // 物品类
    static class Item {
        int value, weight;
        double ratio; // 价值密度
        
        public Item(int value, int weight) {
            this.value = value;
            this.weight = weight;
            this.ratio = (double) value / weight;
        }
    }
    
    // 按价值密度降序排序的比较器
    static class Compare implements Comparator&amp;lt;Item&amp;gt; {
        public int compare(Item a, Item b) {
            return Double.compare(b.ratio, a.ratio);
        }
    }
    
    // 返回最大价值
    public static double fractionalKnapsack(int W, Item[] items) {
        // 按价值密度排序
        Arrays.sort(items, new Compare());
        
        int curWeight = 0; // 当前重量
        double finalValue = 0.0; // 最终价值
        
        for (int i = 0; i &amp;lt; items.length; i++) {
            // 如果可以完全放入
            if (curWeight + items[i].weight &amp;lt;= W) {
                curWeight += items[i].weight;
                finalValue += items[i].value;
            } 
            // 如果只能放入一部分
            else {
                int remain = W - curWeight;
                finalValue += items[i].value * ((double) remain / items[i].weight);
                break;
            }
        }
        
        return finalValue;
    }
}
&lt;/code&gt;&lt;/pre&gt;
&lt;h3&gt;6.3 回溯算法（Backtracking）&lt;/h3&gt;
&lt;p&gt;回溯算法是一种通过探索所有可能的候选解来找出所有解的算法。如果候选解被确认不是一个解（或者至少不是最后一个解），回溯算法会通过在上一步进行一些变化来抛弃该解，即&quot;回溯&quot;并尝试其他可能。&lt;/p&gt;
&lt;h4&gt;6.3.1 N皇后问题&lt;/h4&gt;
&lt;p&gt;N皇后问题是回溯算法的经典问题。目标是在N×N的棋盘上放置N个皇后，使得它们互不攻击。&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;public class NQueens {
    final int N = 4;
    
    // 打印解决方案
    void printSolution(int board[][]) {
        for (int i = 0; i &amp;lt; N; i++) {
            for (int j = 0; j &amp;lt; N; j++)
                System.out.print(&quot; &quot; + board[i][j] + &quot; &quot;);
            System.out.println();
        }
    }
    
    // 检查是否可以在board[row][col]放置皇后
    boolean isSafe(int board[][], int row, int col) {
        int i, j;
        
        // 检查当前行左边是否有皇后
        for (i = 0; i &amp;lt; col; i++)
            if (board[row][i] == 1)
                return false;
        
        // 检查左上对角线是否有皇后
        for (i = row, j = col; i &amp;gt;= 0 &amp;amp;&amp;amp; j &amp;gt;= 0; i--, j--)
            if (board[i][j] == 1)
                return false;
        
        // 检查左下对角线是否有皇后
        for (i = row, j = col; j &amp;gt;= 0 &amp;amp;&amp;amp; i &amp;lt; N; i++, j--)
            if (board[i][j] == 1)
                return false;
        
        return true;
    }
    
    // 使用回溯解决N皇后问题的主要函数
    boolean solveNQUtil(int board[][], int col) {
        // 如果所有皇后都被放置，则返回true
        if (col &amp;gt;= N)
            return true;
        
        // 考虑这一列中的每一行并尝试放置皇后
        for (int i = 0; i &amp;lt; N; i++) {
            // 检查是否可以放置皇后
            if (isSafe(board, i, col)) {
                // 放置皇后
                board[i][col] = 1;
                
                // 递归放置其余皇后
                if (solveNQUtil(board, col + 1) == true)
                    return true;
                
                // 如果放置皇后在board[i][col]不能导致解决方案，则移除皇后
                board[i][col] = 0;
            }
        }
        
        // 如果皇后不能放置在任何行中，则返回false
        return false;
    }
    
    // 解决N皇后问题
    boolean solveNQ() {
        int[][] board = new int[N][N];
        
        if (solveNQUtil(board, 0) == false) {
            System.out.print(&quot;Solution does not exist&quot;);
            return false;
        }
        
        printSolution(board);
        return true;
    }
}
&lt;/code&gt;&lt;/pre&gt;
&lt;h4&gt;6.3.2 图的着色问题&lt;/h4&gt;
&lt;p&gt;图着色问题要求为图的所有顶点着色，使得相邻顶点的颜色不同，且使用的颜色数最少。&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;public class GraphColoring {
    final int V = 4; // 顶点数
    
    // 打印解决方案
    void printSolution(int[] color) {
        System.out.println(&quot;解决方案: &quot;);
        for (int i = 0; i &amp;lt; V; i++)
            System.out.println(&quot;顶点 &quot; + i + &quot; -&amp;gt; 颜色 &quot; + color[i]);
    }
    
    // 检查分配给v的颜色是否与相邻顶点的颜色相同
    boolean isSafe(int v, int[][] graph, int[] color, int c) {
        for (int i = 0; i &amp;lt; V; i++)
            if (graph[v][i] == 1 &amp;amp;&amp;amp; c == color[i])
                return false;
        return true;
    }
    
    // 图着色的主要递归函数
    boolean graphColoringUtil(int[][] graph, int m, int[] color, int v) {
        // 如果所有顶点都被着色，则返回true
        if (v == V)
            return true;
        
        // 尝试不同的颜色
        for (int c = 1; c &amp;lt;= m; c++) {
            // 检查是否可以分配颜色c到顶点v
            if (isSafe(v, graph, color, c)) {
                color[v] = c;
                
                // 递归为剩余顶点着色
                if (graphColoringUtil(graph, m, color, v + 1))
                    return true;
                
                // 如果给顶点v分配颜色c没有导致解决方案，则移除颜色
                color[v] = 0;
            }
        }
        
        // 如果不能为该顶点着色，则返回false
        return false;
    }
    
    // 图着色的主要函数
    boolean graphColoring(int[][] graph, int m) {
        int[] color = new int[V];
        
        // 调用递归辅助函数解决着色问题
        if (!graphColoringUtil(graph, m, color, 0)) {
            System.out.println(&quot;解决方案不存在&quot;);
            return false;
        }
        
        // 打印解决方案
        printSolution(color);
        return true;
    }
}
&lt;/code&gt;&lt;/pre&gt;
&lt;h3&gt;6.4 动态规划（Dynamic Programming）&lt;/h3&gt;
&lt;p&gt;动态规划是一种通过把原问题分解为相对简单的子问题的方式求解复杂问题的方法。动态规划常常适用于有重叠子问题和最优子结构性质的问题。&lt;/p&gt;
&lt;h4&gt;6.4.1 斐波那契数列&lt;/h4&gt;
&lt;p&gt;斐波那契数列是动态规划的经典例子，用来演示重叠子问题的概念。&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;public class Fibonacci {
    // 递归方法（效率低）
    public static int fibRecursive(int n) {
        if (n &amp;lt;= 1)
            return n;
        return fibRecursive(n - 1) + fibRecursive(n - 2);
    }
    
    // 动态规划方法（自底向上）
    public static int fibDP(int n) {
        if (n &amp;lt;= 1)
            return n;
        
        int[] dp = new int[n + 1];
        dp[0] = 0;
        dp[1] = 1;
        
        for (int i = 2; i &amp;lt;= n; i++) {
            dp[i] = dp[i - 1] + dp[i - 2];
        }
        
        return dp[n];
    }
    
    // 空间优化的动态规划方法
    public static int fibOptimized(int n) {
        if (n &amp;lt;= 1)
            return n;
        
        int prev2 = 0;
        int prev1 = 1;
        int current = 0;
        
        for (int i = 2; i &amp;lt;= n; i++) {
            current = prev1 + prev2;
            prev2 = prev1;
            prev1 = current;
        }
        
        return current;
    }
}
&lt;/code&gt;&lt;/pre&gt;
&lt;h4&gt;6.4.2 0/1背包问题&lt;/h4&gt;
&lt;p&gt;0/1背包问题是动态规划的经典应用之一。&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;public class Knapsack {
    // 返回最大价值
    public static int knapSack(int W, int[] wt, int[] val, int n) {
        int[][] dp = new int[n + 1][W + 1];
        
        // 构建dp表
        for (int i = 0; i &amp;lt;= n; i++) {
            for (int w = 0; w &amp;lt;= W; w++) {
                if (i == 0 || w == 0)
                    dp[i][w] = 0;
                else if (wt[i - 1] &amp;lt;= w)
                    dp[i][w] = Math.max(val[i - 1] + dp[i - 1][w - wt[i - 1]], dp[i - 1][w]);
                else
                    dp[i][w] = dp[i - 1][w];
            }
        }
        
        return dp[n][W];
    }
}
&lt;/code&gt;&lt;/pre&gt;
&lt;h4&gt;6.4.3 最长公共子序列（LCS）&lt;/h4&gt;
&lt;p&gt;最长公共子序列问题是寻找两个序列共同拥有的最长子序列。&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;public class LCS {
    // 返回最长公共子序列的长度
    public static int lcs(String X, String Y, int m, int n) {
        int[][] dp = new int[m + 1][n + 1];
        
        for (int i = 0; i &amp;lt;= m; i++) {
            for (int j = 0; j &amp;lt;= n; j++) {
                if (i == 0 || j == 0)
                    dp[i][j] = 0;
                else if (X.charAt(i - 1) == Y.charAt(j - 1))
                    dp[i][j] = dp[i - 1][j - 1] + 1;
                else
                    dp[i][j] = Math.max(dp[i - 1][j], dp[i][j - 1]);
            }
        }
        
        return dp[m][n];
    }
    
    // 打印最长公共子序列
    public static void printLCS(String X, String Y, int m, int n) {
        int[][] dp = new int[m + 1][n + 1];
        
        // 构建dp表
        for (int i = 0; i &amp;lt;= m; i++) {
            for (int j = 0; j &amp;lt;= n; j++) {
                if (i == 0 || j == 0)
                    dp[i][j] = 0;
                else if (X.charAt(i - 1) == Y.charAt(j - 1))
                    dp[i][j] = dp[i - 1][j - 1] + 1;
                else
                    dp[i][j] = Math.max(dp[i - 1][j], dp[i][j - 1]);
            }
        }
        
        // 通过dp表构造LCS
        int index = dp[m][n];
        char[] lcs = new char[index + 1];
        lcs[index] = &apos;\0&apos;; // 设置终止符
        
        int i = m, j = n;
        while (i &amp;gt; 0 &amp;amp;&amp;amp; j &amp;gt; 0) {
            // 如果当前字符匹配
            if (X.charAt(i - 1) == Y.charAt(j - 1)) {
                lcs[index - 1] = X.charAt(i - 1); // 将字符放入结果中
                i--;
                j--;
                index--;
            }
            // 否则找出较大的值并向上或向左移动
            else if (dp[i - 1][j] &amp;gt; dp[i][j - 1])
                i--;
            else
                j--;
        }
        
        System.out.print(&quot;LCS of &quot; + X + &quot; and &quot; + Y + &quot; is &quot;);
        for (int k = 0; k &amp;lt;= dp[m][n]; k++)
            System.out.print(lcs[k]);
    }
}
&lt;/code&gt;&lt;/pre&gt;
&lt;hr /&gt;
&lt;h2&gt;7. 高级数据结构简介&lt;/h2&gt;
&lt;h3&gt;7.1 并查集（Disjoint Set）&lt;/h3&gt;
&lt;p&gt;用于处理不相交集合的合并及查询问题。&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;操作：&lt;/strong&gt;&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;find：查找元素所属集合&lt;/li&gt;
&lt;li&gt;union：合并两个集合&lt;/li&gt;
&lt;/ul&gt;
&lt;h3&gt;7.2 线段树（Segment Tree）&lt;/h3&gt;
&lt;p&gt;用于处理区间查询问题的数据结构。&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;特点：&lt;/strong&gt;&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;建立时间复杂度：O(n)&lt;/li&gt;
&lt;li&gt;查询时间复杂度：O(log n)&lt;/li&gt;
&lt;li&gt;更新时间复杂度：O(log n)&lt;/li&gt;
&lt;/ul&gt;
&lt;h3&gt;7.3 字典树（Trie）&lt;/h3&gt;
&lt;p&gt;用于高效存储和查找字符串集合的数据结构。&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;应用场景：&lt;/strong&gt;&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;自动补全&lt;/li&gt;
&lt;li&gt;拼写检查&lt;/li&gt;
&lt;li&gt;IP路由&lt;/li&gt;
&lt;/ul&gt;
&lt;h3&gt;7.4 跳表（Skip List）&lt;/h3&gt;
&lt;p&gt;一种概率性数据结构，可以在O(log n)时间内完成查找、插入和删除操作。&lt;/p&gt;
&lt;hr /&gt;
&lt;h2&gt;8. 数据结构选择与应用实践&lt;/h2&gt;
&lt;h3&gt;8.1 如何选择合适的数据结构&lt;/h3&gt;
&lt;ol&gt;
&lt;li&gt;&lt;strong&gt;考虑操作频率&lt;/strong&gt;：哪些操作最频繁？&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;考虑时间复杂度&lt;/strong&gt;：各项操作的时间要求是什么？&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;考虑空间复杂度&lt;/strong&gt;：内存使用限制如何？&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;考虑实现难度&lt;/strong&gt;：团队的技术水平能否支撑？&lt;/li&gt;
&lt;/ol&gt;
&lt;h3&gt;8.2 实际应用场景&lt;/h3&gt;
&lt;ul&gt;
&lt;li&gt;&lt;strong&gt;缓存系统&lt;/strong&gt;：哈希表 + 双向链表（LRU Cache）&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;搜索引擎&lt;/strong&gt;：倒排索引（哈希表 + 链表）&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;社交网络&lt;/strong&gt;：图结构&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;游戏开发&lt;/strong&gt;：四叉树/八叉树用于碰撞检测&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;编译器&lt;/strong&gt;：栈用于语法分析&lt;/li&gt;
&lt;/ul&gt;
&lt;hr /&gt;
&lt;h2&gt;总结&lt;/h2&gt;
&lt;h3&gt;核心要点&lt;/h3&gt;
&lt;ul&gt;
&lt;li&gt;数据结构是算法设计的基础，选择合适的数据结构能显著提升程序性能&lt;/li&gt;
&lt;li&gt;理解各种数据结构的特点和适用场景对于软件开发至关重要&lt;/li&gt;
&lt;li&gt;复杂度分析是评估数据结构性能的重要工具&lt;/li&gt;
&lt;li&gt;实际应用中往往需要组合多种数据结构解决问题&lt;/li&gt;
&lt;/ul&gt;
&lt;h3&gt;学习建议&lt;/h3&gt;
&lt;ol&gt;
&lt;li&gt;&lt;strong&gt;理论与实践结合&lt;/strong&gt;：不仅要理解概念，还要动手实现&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;多做练习题&lt;/strong&gt;：通过算法题加深对数据结构的理解&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;阅读优秀源码&lt;/strong&gt;：学习开源项目中数据结构的应用&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;关注实际应用&lt;/strong&gt;：了解各种数据结构在工业界的应用场景&lt;/li&gt;
&lt;/ol&gt;
&lt;hr /&gt;
&lt;h2&gt;参考资料&lt;/h2&gt;
&lt;ul&gt;
&lt;li&gt;《算法导论》&lt;/li&gt;
&lt;li&gt;《数据结构与算法分析》&lt;/li&gt;
&lt;li&gt;LeetCode算法题解&lt;/li&gt;
&lt;li&gt;GeeksforGeeks数据结构教程&lt;/li&gt;
&lt;li&gt;各大厂技术博客&lt;/li&gt;
&lt;/ul&gt;
</content:encoded></item><item><title>鸿蒙开发设备调试工具HDC学习日志</title><link>https://meteor-comet.github.io/posts/harmony-hdc-learn/</link><guid isPermaLink="true">https://meteor-comet.github.io/posts/harmony-hdc-learn/</guid><description>原文：https://blog.csdn.net/ToBeTheEnder/article/details/139325200</description><pubDate>Sun, 30 Oct 2022 00:00:00 GMT</pubDate><content:encoded>&lt;p&gt;&lt;strong&gt;由于鸿蒙生态还处于初期，官方提供的hdc命令还在不断修改中，这篇文档更新不及时，请以&lt;a href=&quot;https://github.com/codematrixer/awesome-hdc&quot;&gt;github&lt;/a&gt;和&lt;a href=&quot;https://developer.huawei.com/consumer/cn/doc/harmonyos-guides-V3/ide-command-line-hdc-0000001237908229-V3#section1756645732914/&quot;&gt;官方文档&lt;/a&gt;为准&lt;/strong&gt;&lt;/p&gt;
&lt;p&gt;HDC（OpenHarmony Device Connector） 是为鸿蒙开发/测试人员提供的用于设备调试的命令行工具，类似Android端的ADB工具。&lt;/p&gt;
&lt;p&gt;HDC主要有三部分组成&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;&lt;code&gt;hdc client&lt;/code&gt;：运行于电脑上的客户端，用户可以在电脑命令终端（windows cmd/linux shell）下请求执行相应的hdc命令。&lt;/li&gt;
&lt;li&gt;&lt;code&gt;hdc server&lt;/code&gt;：作为后台进程也运行于电脑上，server管理client和设备端daemon之间通信包括连接的复用、数据通信包的收发，以及个别本地命令的直接处理。&lt;/li&gt;
&lt;li&gt;&lt;code&gt;hdc daemon&lt;/code&gt;：daemon部署于OpenHarmony设备端作为守护进程按需运行，负责处理来自client端请求。&lt;/li&gt;
&lt;/ol&gt;
&lt;h2&gt;HDC安装&lt;/h2&gt;
&lt;ul&gt;
&lt;li&gt;
&lt;p&gt;下载 &lt;a href=&quot;https://developer.huawei.com/consumer/cn/download/&quot;&gt;Command Line Tools&lt;/a&gt; 并解压 ↳&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;或者在DevEco Studio的设置里的OpenHarmony SDK下载（推荐）&lt;/p&gt;
&lt;/blockquote&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;&lt;code&gt;hdc&lt;/code&gt;文件在&lt;code&gt;command-line-tools/sdk/default/toolchains&lt;/code&gt;目录下 ↳&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;DevEco Studio下载的在OpenHarmony\Sdk\版本号\toolchains下&lt;/p&gt;
&lt;/blockquote&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;配置电脑环境变量，以Windows为例：&lt;/p&gt;
&lt;p&gt;在系统变量中添加：&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;
&lt;p&gt;变量名：HM_SDK_HOME
变量值：D:\Harmony_dev\commandline-tools-windows-x64-5.0.3.900\command-line-tools\sdk\default&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;DevEco Studio下载：盘符:\OpenHarmony\Sdk&lt;/p&gt;
&lt;/blockquote&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;然后在PATH里面添加%HM_SDK_HOME%\openharmony\toolchains&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;DevEco Studio下载：%HM_SDK_HOME%\版本号\toolchains&lt;/p&gt;
&lt;/blockquote&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;在CMD输入一下命令查看是否配置成功&lt;/p&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;pre&gt;&lt;code&gt;hdc -v
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;也可以自行编译安装：参考鸿蒙官方gitee文档&lt;/p&gt;
&lt;h2&gt;基本用法&lt;/h2&gt;
&lt;h3&gt;基本语法&lt;/h3&gt;
&lt;pre&gt;&lt;code&gt;hdc -t &amp;lt;connectKey&amp;gt; &amp;lt;command&amp;gt;
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;如果只有一个设备/模拟器连接时，可以省略掉&lt;code&gt;-t &amp;lt;connectKey&amp;gt;&lt;/code&gt; 这一部分，直接使用&lt;code&gt;hdc &amp;lt;command&amp;gt;&lt;/code&gt;。在多个设备/模拟器连接的情况下需要指定&lt;code&gt;-t&lt;/code&gt; 参数， &lt;code&gt;connectKey&lt;/code&gt;可以通过&lt;code&gt;hdc list targets&lt;/code&gt;命令获取，对应Android里的&lt;code&gt;adb devices&lt;/code&gt;获取的&lt;code&gt;serialNumber&lt;/code&gt;。&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;$ hdc list targets

127.0.0.1:5555    //&amp;lt;IP&amp;gt;:&amp;lt;Port&amp;gt;形式的connectKey ，一般为无线连接的设备或模拟器
FMR0223C13000649
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;比如给&lt;code&gt;FMR0223C13000649&lt;/code&gt; 这个设备安装应用：&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;$ hdc -t FMR0223C13000649 install entry-default-signed.hap

[Info]App install path:/Users/develop/entry-default-signed.hap, queuesize:0, msg:install bundle successfully.
AppMod finish
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;&lt;strong&gt;注意事项&lt;/strong&gt;&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;使用&lt;code&gt;hdc&lt;/code&gt;，如果出现异常，可以尝试通过&lt;code&gt;hdc kill -r&lt;/code&gt;命令杀掉并重启hdc服务。&lt;/li&gt;
&lt;li&gt;如果出现&lt;code&gt;hdc list targets&lt;/code&gt;获取不到设备信息的情况，可以通过任务管理器查看是否有hdc进程存在。若进程存在，则通过&lt;code&gt;hdc kill -r&lt;/code&gt;命令杀掉该进程。&lt;/li&gt;
&lt;/ul&gt;
&lt;h2&gt;设备连接管理&lt;/h2&gt;
&lt;h3&gt;查看HDC版本&lt;/h3&gt;
&lt;pre&gt;&lt;code&gt;$ hdc -v

Ver: 3.1.0b
&lt;/code&gt;&lt;/pre&gt;
&lt;h3&gt;启动/停止 HDC Server&lt;/h3&gt;
&lt;p&gt;停止&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;$ hdc kill

Kill server finish
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;重启&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;$ hdc start -r
&lt;/code&gt;&lt;/pre&gt;
&lt;h3&gt;查询设备列表&lt;/h3&gt;
&lt;pre&gt;&lt;code&gt;$ hdc list targets

127.0.0.1:5555
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;&lt;code&gt;-v&lt;/code&gt; 选项 显示详细信息&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;$ hdc list targets -v

127.0.0.1:5555          TCP     Connected       localhost       hdc
COM3            UART    Ready   unknown...      hdc
COM4            UART    Ready   unknown...      hdc
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;输出的内容第一列为设备的&lt;code&gt;connectKey&lt;/code&gt;， 第二列是设备&lt;code&gt;连接方式&lt;/code&gt;，第三列为设备&lt;code&gt;连接状态&lt;/code&gt;，第四列暂时未知&lt;/p&gt;
&lt;h3&gt;查询设备UDID&lt;/h3&gt;
&lt;pre&gt;&lt;code&gt;$ hdc shell bm get --udid

udid of current device is :
454D55057494E058383609DC0A6C85F774AA9B67B4A14250C2BFA00000000000
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;这个&lt;code&gt;udid&lt;/code&gt;在用开发者账号打包时，需要添加这个&lt;code&gt;udid&lt;/code&gt;到对应的&lt;code&gt;profile&lt;/code&gt;文件中&lt;/p&gt;
&lt;h3&gt;重启手机&lt;/h3&gt;
&lt;pre&gt;&lt;code&gt;$ hdc target boot
&lt;/code&gt;&lt;/pre&gt;
&lt;h2&gt;查看设备信息&lt;/h2&gt;
&lt;h3&gt;名称&lt;/h3&gt;
&lt;pre&gt;&lt;code&gt;$ hdc shell param get const.product.name               

emulator
&lt;/code&gt;&lt;/pre&gt;
&lt;h3&gt;Brand&lt;/h3&gt;
&lt;pre&gt;&lt;code&gt;$ hdc shell param get const.product.brand

HUAWEI 
&lt;/code&gt;&lt;/pre&gt;
&lt;h3&gt;Model&lt;/h3&gt;
&lt;pre&gt;&lt;code&gt;$ hdc shell param get const.product.model

emulator 
&lt;/code&gt;&lt;/pre&gt;
&lt;h3&gt;系统版本&lt;/h3&gt;
&lt;pre&gt;&lt;code&gt;$ hdc shell param get const.product.software.version                                      

emulator 5.0.0.102(SP1DEVC00E102R4P11log) 
&lt;/code&gt;&lt;/pre&gt;
&lt;h3&gt;OS版本&lt;/h3&gt;
&lt;pre&gt;&lt;code&gt;$ hdc shell param get const.ohos.apiversion  

13
&lt;/code&gt;&lt;/pre&gt;
&lt;h3&gt;CPU架构&lt;/h3&gt;
&lt;pre&gt;&lt;code&gt;$ hdc  shell param get const.product.cpu.abilist  

x86_64 
&lt;/code&gt;&lt;/pre&gt;
&lt;h3&gt;分辩率&lt;/h3&gt;
&lt;pre&gt;&lt;code&gt;$ hdc shell hidumper -s RenderService -a screen


-------------------------------[ability]-------------------------------


----------------------------------RenderService----------------------------------
-- ScreenInfo
screen[0]: id=0, powerstatus=POWER_STATUS_ON, backlight=1, screenType=EXTERNAL_TYPE, render size: 1260x2720, physical screen resolution: 1260x2720, isvirtual=false, skipFrameInterval_:
1

  supportedMode[0]: 1260x2720, refreshrate=60
  activeMode: 1260x2720, refreshrate=60
  capability: name=express_display, phywidth=72, phyheight=156,supportlayers=10, virtualDispCount=1, propCount=0, type=DISP_INTF_HDMI, supportWriteBack=false
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;执行上述命令后，解析返回内容，可以通过正则表达式提取&lt;code&gt;1260x2720&lt;/code&gt;&lt;/p&gt;
&lt;h3&gt;wlan IP&lt;/h3&gt;
&lt;pre&gt;&lt;code&gt;$ hdc shell ifconfig

lo        Link encap:Local Loopback  
          inet addr:127.0.0.1  Mask:255.0.0.0 
          UP LOOPBACK RUNNING  MTU:65536  Metric:1
          RX packets:0 errors:0 dropped:0 overruns:0 frame:0
          TX packets:0 errors:0 dropped:0 overruns:0 carrier:0
          collisions:0 txqueuelen:1000
          RX bytes:0 TX bytes:0

eth0      Link encap:Ethernet  HWaddr 52:54:00:12:34:56
          inet addr:10.0.2.15  Bcast:10.0.2.255  Mask:255.255.255.0
          UP BROADCAST RUNNING MULTICAST  MTU:1500  Metric:1
          RX packets:73 errors:0 dropped:0 overruns:0 frame:0
          TX packets:152 errors:0 dropped:0 overruns:0 carrier:0
          collisions:0 txqueuelen:1000
          RX bytes:25919 TX bytes:39850

wifi_eth  Link encap:Ethernet  HWaddr 52:54:00:12:34:57
          UP BROADCAST RUNNING MULTICAST  MTU:1500  Metric:1
          RX packets:4 errors:0 dropped:0 overruns:0 frame:0
          TX packets:7 errors:0 dropped:0 overruns:0 carrier:0
          collisions:0 txqueuelen:1000
          RX bytes:440 TX bytes:602

wlan0     Link encap:Ethernet  HWaddr 52:54:00:12:34:57
          UP BROADCAST MULTICAST  MTU:1500  Metric:1
          RX packets:0 errors:0 dropped:0 overruns:0 frame:0
          TX packets:0 errors:0 dropped:0 overruns:0 carrier:0
          collisions:0 txqueuelen:1000
          RX bytes:0 TX bytes:0

&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;注意：这个命令在Beta3版本之前，会提示&lt;code&gt;Cannot open netlink socket: Permission denied&lt;/code&gt;，需要升级系统。&lt;/p&gt;
&lt;h3&gt;电量/温度&lt;/h3&gt;
&lt;pre&gt;&lt;code&gt;$ hdc shell hidumper -s BatteryService -a -i                

-------------------------------[ability]-------------------------------


----------------------------------BatteryService----------------------------------
Current time: 2024-10-30 20:32:06.695
capacity: 100
batteryLevel: 1
chargingStatus: 0
healthState: 1
pluggedType: 0
voltage: 4000000
present: 1
technology: Li-ion
nowCurrent: 900000
currentAverage: -1
totalEnergy: 5000000
remainingEnergy: -1
remainingChargeTime: 0
temperature: 250
chargeType: 0
&lt;/code&gt;&lt;/pre&gt;
&lt;h3&gt;查看屏幕信息&lt;/h3&gt;
&lt;pre&gt;&lt;code&gt;$ hdc shell hidumper -s DisplayManagerService -a -a

-------------------------------[ability]-------------------------------


----------------------------------DisplayManagerService----------------------------------
-------------- DMS FREEZED PID LIST  --------------
-------------- DMS KEY EVENTS LIST  --------------
[10-30 20:07:03.254]: Dms construct.
[10-30 20:07:04.173]: Default screen id : 0
[10-30 20:07:04.173]: OnScreenChange triggered. screenId: 0  screenEvent: 0
[10-30 20:07:04.174]: Dms OnScreenChange register success.
[10-30 20:07:04.176]: CreateScreenProperty by rsInterface success.
[10-30 20:07:04.199]: GetScreenPower state:0 screenId:0
[10-30 20:07:04.199]: create screen session success.
[10-30 20:07:04.202]: Dms RefreshRateChange register success.
[10-30 20:07:04.215]: Dms subscribed to sensor successfully.
[10-30 20:07:04.215]: Dms init end.
[10-30 20:07:04.234]: Dms onstart end.
[10-30 20:07:07.383]: GetScreenPower state:0 screenId:0
[10-30 20:07:07.402]: GetScreenPower state:0 screenId:0
[10-30 20:07:07.896]: set client userId: 100 newScbPid: 1175 clientName: com.ohos.sceneboard
[10-30 20:07:07.898]: currentUserId: 0  currentScbPId: -1  newUserId: 100  newScbPid: 1175  coldBoot: 1
-------------- DMS Multi User Info --------------
[oldScbPid:]
[userId:] 100
[ScbPid:] 1175
---------------- Screen ID: 0 ----------------
FoldStatus:                   UNKNOWN
[SCREEN SESSION]
Name:                         UNKNOWN
RSScreenId:                   0
activeModes&amp;lt;id, W, H, RS&amp;gt;:    0, 1260, 2720, 60
SourceMode:                   0
ScreenCombination:            0
Orientation:                  0
Rotation:                     0
ScreenRequestedOrientation:   0
[RS INFO]
GraphicPixelFormat:           0
[CUTOUT INFO]
WaterFall_L&amp;lt;X,Y,W,H&amp;gt;:         0, 0, 0, 0
WaterFall_T&amp;lt;X,Y,W,H&amp;gt;:         0, 0, 0, 0
WaterFall_R&amp;lt;X,Y,W,H&amp;gt;:         0, 0, 0, 0
WaterFall_B&amp;lt;X,Y,W,H&amp;gt;:         0, 0, 0, 0
BoundingRects&amp;lt;X,Y,W,H&amp;gt;:       [494, 36, 273, 72]
[SCREEN INFO]
VirtualWidth:                 387
VirtualHeight:                836
LastParentId:                 18446744073709551615
ParentId:                     1
IsScreenGroup:                0
VirtualPixelRatio:            3.25
Rotation:                     0
Orientation:                  0
SourceMode:                   0
ScreenType:                   1
[SCREEN PROPERTY]
Rotation:                     0
Density:                      3.25
DensityInCurResolution:       3.25
PhyWidth:                     72
PhyHeight:                    156
RefreshRate:                  60
VirtualPixelRatio:            3.25
ScreenRotation:               0
Orientation:                  0
DisplayOrientation:           0
GetScreenType:                1
ReqOrientation:               0
DPI&amp;lt;X, Y&amp;gt;:                    444.5, 442.871
Offset&amp;lt;X, Y&amp;gt;:                 0, 0
Bounds&amp;lt;L,T,W,H&amp;gt;:              0, 0, 1260, 2720,
PhyBounds&amp;lt;L,T,W,H&amp;gt;:           0, 0, 1260, 2720,
AvailableArea&amp;lt;X,Y,W,H&amp;gt;        0, 0, 1260, 2720,
DefaultDeviceRotationOffset   270
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;执行上述命令后，解析返回内容，通过正则提取需要的信息，比如屏幕尺寸分辨率Bounds，VirtualWidth，VirtualHeight，PhyWidth，PhyHeight，ScreenRotation&lt;/p&gt;
&lt;h3&gt;查看屏幕旋转状态&lt;/h3&gt;
&lt;pre&gt;&lt;code&gt;$ hdc shell hidumper -s DisplayManagerService -a -a

-------------------------------[ability]-------------------------------


----------------------------------DisplayManagerService----------------------------------
-------------- DMS FREEZED PID LIST  --------------
-------------- DMS KEY EVENTS LIST  --------------
[10-30 20:07:03.254]: Dms construct.
[10-30 20:07:04.173]: Default screen id : 0
[10-30 20:07:04.173]: OnScreenChange triggered. screenId: 0  screenEvent: 0
[10-30 20:07:04.174]: Dms OnScreenChange register success.
[10-30 20:07:04.176]: CreateScreenProperty by rsInterface success.
[10-30 20:07:04.199]: GetScreenPower state:0 screenId:0
[10-30 20:07:04.199]: create screen session success.
[10-30 20:07:04.202]: Dms RefreshRateChange register success.
[10-30 20:07:04.215]: Dms subscribed to sensor successfully.
[10-30 20:07:04.215]: Dms init end.
[10-30 20:07:04.234]: Dms onstart end.
[10-30 20:07:07.383]: GetScreenPower state:0 screenId:0
[10-30 20:07:07.402]: GetScreenPower state:0 screenId:0
[10-30 20:07:07.896]: set client userId: 100 newScbPid: 1175 clientName: com.ohos.sceneboard
[10-30 20:07:07.898]: currentUserId: 0  currentScbPId: -1  newUserId: 100  newScbPid: 1175  coldBoot: 1
-------------- DMS Multi User Info --------------
[oldScbPid:]
[userId:] 100
[ScbPid:] 1175
---------------- Screen ID: 0 ----------------
FoldStatus:                   UNKNOWN
[SCREEN SESSION]
Name:                         UNKNOWN
RSScreenId:                   0
activeModes&amp;lt;id, W, H, RS&amp;gt;:    0, 1260, 2720, 60
SourceMode:                   0
ScreenCombination:            0
Orientation:                  0
Rotation:                     0
ScreenRequestedOrientation:   0
[RS INFO]
GraphicPixelFormat:           0
[CUTOUT INFO]
WaterFall_L&amp;lt;X,Y,W,H&amp;gt;:         0, 0, 0, 0
WaterFall_T&amp;lt;X,Y,W,H&amp;gt;:         0, 0, 0, 0
WaterFall_R&amp;lt;X,Y,W,H&amp;gt;:         0, 0, 0, 0
WaterFall_B&amp;lt;X,Y,W,H&amp;gt;:         0, 0, 0, 0
BoundingRects&amp;lt;X,Y,W,H&amp;gt;:       [494, 36, 273, 72]
[SCREEN INFO]
VirtualWidth:                 387
VirtualHeight:                836
LastParentId:                 18446744073709551615
ParentId:                     1
IsScreenGroup:                0
VirtualPixelRatio:            3.25
Rotation:                     0
Orientation:                  0
SourceMode:                   0
ScreenType:                   1
[SCREEN PROPERTY]
Rotation:                     0
Density:                      3.25
DensityInCurResolution:       3.25
PhyWidth:                     72
PhyHeight:                    156
RefreshRate:                  60
VirtualPixelRatio:            3.25
ScreenRotation:               0
Orientation:                  0
DisplayOrientation:           0
GetScreenType:                1
ReqOrientation:               0
DPI&amp;lt;X, Y&amp;gt;:                    444.5, 442.871
Offset&amp;lt;X, Y&amp;gt;:                 0, 0
Bounds&amp;lt;L,T,W,H&amp;gt;:              0, 0, 1260, 2720,
PhyBounds&amp;lt;L,T,W,H&amp;gt;:           0, 0, 1260, 2720,
AvailableArea&amp;lt;X,Y,W,H&amp;gt;        0, 0, 1260, 2720,
DefaultDeviceRotationOffset   270
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;通过上面的查看屏幕信息命令，通过正则提取ScreenRotation字段即可，ScreenRotation有四个值：&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;0：未旋转&lt;/li&gt;
&lt;li&gt;90：顺时针旋转90度&lt;/li&gt;
&lt;li&gt;180：顺时针旋转180度&lt;/li&gt;
&lt;li&gt;270：顺时针旋转270度&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;备注：目前旋转状态只能查看，不支持设置&lt;/p&gt;
&lt;h3&gt;查看屏幕亮屏状态&lt;/h3&gt;
&lt;pre&gt;&lt;code&gt;$ hdc shell hidumper -s PowerManagerService -a -s

-------------------------------[ability]-------------------------------


----------------------------------PowerManagerService----------------------------------
POWER STATE DUMP:
Current State: DIM  Reason: 1  Time: 2929230
ScreenOffTime: Timeout=30000ms
DUMP DETAILS:
Last Screen On: 2905219
Last Screen Off: 0
Last SuspendDevice: 0
Last WakeupDevice: 0
Last Refresh: 2905219
DUMP EACH STATES:
State: AWAKE   Reason: REFRESH   Time: 2898718
   Failure: INIT   Reason:    From: AWAKE   Time: 0

State: FREEZE   Reason: UNKNOWN   Time: 0
   Failure: INIT   Reason:    From: AWAKE   Time: 0

State: INACTIVE   Reason: UNKNOWN   Time: 0
   Failure: INIT   Reason:    From: AWAKE   Time: 0

State: STAND_BY   Reason: UNKNOWN   Time: 0
   Failure: INIT   Reason:    From: AWAKE   Time: 0

State: DOZE   Reason: UNKNOWN   Time: 0
   Failure: INIT   Reason:    From: AWAKE   Time: 0

State: SLEEP   Reason: UNKNOWN   Time: 0
   Failure: INIT   Reason:    From: AWAKE   Time: 0

State: HIBERNATE   Reason: INIT   Time: 0
   Failure: INIT   Reason:    From: AWAKE   Time: 0

State: SHUTDOWN   Reason: INIT   Time: 0
   Failure: INIT   Reason:    From: AWAKE   Time: 0

State: DIM   Reason: TIMEOUT   Time: 2929230
   Failure: INIT   Reason:    From: AWAKE   Time: 0
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;屏幕状态有这几种：&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;INACTIVE&lt;/li&gt;
&lt;li&gt;SLEEP&lt;/li&gt;
&lt;li&gt;AWAKE&lt;/li&gt;
&lt;/ul&gt;
&lt;h3&gt;点亮屏幕（唤醒）&lt;/h3&gt;
&lt;pre&gt;&lt;code&gt;$ hdc shell power-shell wakeup

WakeupDevice is called
&lt;/code&gt;&lt;/pre&gt;
&lt;h3&gt;查看网络状态&lt;/h3&gt;
&lt;p&gt;&lt;strong&gt;联网状态&lt;/strong&gt;&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;$ hdc shell hidumper -s NetConnManager

-------------------------------[ability]-------------------------------


----------------------------------NetConnManager----------------------------------
Net connect Info:
        SupplierId: 1003
        NetId: 100
        ConnStat: 1
        IsAvailable: 1
        IsRoaming: 0
        Strength: 0
        Frequency: 0
        LinkUpBandwidthKbps: 0
        LinkDownBandwidthKbps: 0
        Uid: 0
Dns result Info:
        netId: 100
        totalReports: 1
        failReports: 0
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;&lt;strong&gt;wifi信息&lt;/strong&gt;&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;$ hdc shell hidumper -s WifiDevice

-------------------------------[ability]-------------------------------


----------------------------------WifiDevice----------------------------------
WiFi active state: activated

WiFi connection status: not connected

Country Code: CN
&lt;/code&gt;&lt;/pre&gt;
&lt;h2&gt;应用管理&lt;/h2&gt;
&lt;h3&gt;安装应用&lt;/h3&gt;
&lt;pre&gt;&lt;code&gt;$ hdc app install BrowserCE-1.2.1.hap

[Info]App install path:F:\DevEcoStudioProjects\test\BrowserCE-1.2.1.hap, queuesize:0, msg:install bundle successfully. 
AppMod finish
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;或者&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;$ hdc install BrowserCE-1.2.1.hap

[Info]App install path:F:\DevEcoStudioProjects\test\BrowserCE-1.2.1.hap, queuesize:0, msg:install bundle successfully. 
AppMod finish
&lt;/code&gt;&lt;/pre&gt;
&lt;h3&gt;卸载应用&lt;/h3&gt;
&lt;pre&gt;&lt;code&gt;$ hdc app uninstall org.ohosdev.browserce  #使用hdc shell &quot;aa dump -a&quot;或者hdc shell aa dump -l找到包名

[Info]App uninstall path:, queuesize:0, msg:uninstall bundle successfully. 
AppMod finish
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;或者&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;$ hdc uninstall org.ohosdev.browserce

[Info]App uninstall path:, queuesize:0, msg:uninstall bundle successfully.
AppMod finish
&lt;/code&gt;&lt;/pre&gt;
&lt;h3&gt;获取应用列表&lt;/h3&gt;
&lt;pre&gt;&lt;code&gt;$ hdc shell bm dump -a

ID: 100:
        com.example.test
        com.huawei.hmos.calendardata
        com.huawei.hmos.filemanager
        com.huawei.hmos.files
        com.huawei.hmos.hipreview
        com.huawei.hmos.hiviewx
        com.huawei.hmos.huaweicast
        com.huawei.hmos.inputmethod
        com.huawei.hmos.instantshare
        com.huawei.hmos.mediacontroller
        com.huawei.hmos.ouc
        com.huawei.hmos.photos
        com.huawei.hmos.projectmenu
        com.huawei.hmos.screenrecorder
        com.huawei.hmos.screenshot
        com.huawei.hmos.security.privacycenter
        com.huawei.hmos.settings
        com.huawei.hmos.superhub
        com.huawei.hmos.themedataservice
        com.huawei.hmsapp.intelligent
        com.huawei.msdp.antimisoperation
        com.ohos.UserFile.ExternalFileManager
        com.ohos.amsdialog
        com.ohos.backgroundtaskmgr.resources
        com.ohos.certmanager
        com.ohos.devicemanagerui
        com.ohos.dlpmanager
        com.ohos.formrenderservice
        com.ohos.inputmethodchoosedialog
        com.ohos.medialibrary.medialibrarydata
        com.ohos.notificationdialog
        com.ohos.nweb
        com.ohos.pasteboarddialog
        com.ohos.permissionmanager
        com.ohos.powerdialog
        com.ohos.ringtonelibrary.ringtonelibrarydata
        com.ohos.sceneboard
        com.ohos.settingsdata
        com.ohos.useriam.authwidget
        com.usb.right
        ohos.global.systemres
        org.ohosdev.browserce
&lt;/code&gt;&lt;/pre&gt;
&lt;h3&gt;启动应用&lt;/h3&gt;
&lt;p&gt;通过启动&lt;code&gt;Ability&lt;/code&gt;来拉起&lt;code&gt;APP&lt;/code&gt;&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;hdc shell aa start -a {abilityName} -b {bundleName} 
hdc shell aa start -a org.ohosdev.browserce:entry:MainAbility -b org.ohosdev.browserce #运行出错了，我不知道是什么错误
&lt;/code&gt;&lt;/pre&gt;
&lt;ul&gt;
&lt;li&gt;
&lt;p&gt;其中&lt;code&gt;bundleName&lt;/code&gt;可以通过&lt;code&gt;hdc shell bm dump -a&lt;/code&gt;获取&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;其中&lt;code&gt;abilityName&lt;/code&gt;可以通过如下命令获取（查看当前任务栈的ability信息）&lt;/p&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;pre&gt;&lt;code&gt;$ hdc shell aa dump -l    # 运行命令前需要手动打开app

User ID #100
  current mission lists:{
    Mission ID #52  mission name #[#org.ohosdev.browserce:entry:MainAbility]  lockedState #0  mission affinity #[]
      AbilityRecord ID #29
        app name [org.ohosdev.browserce]
        main name [MainAbility]
        bundle name [org.ohosdev.browserce]
        ability type [PAGE]
        state #FOREGROUND  start time [3745716]
        app state #FOREGROUND
        ready #1  window attached #0  launcher #0
        callee connections:
        isKeepAlive: false
 }
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;里面的EntryAbility就是你要打开app的Ability名称&lt;/p&gt;
&lt;h3&gt;退出应用&lt;/h3&gt;
&lt;p&gt;强制退出应用&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;hdc shell aa force-stop org.ohosdev.browserce

force stop process successfully.
&lt;/code&gt;&lt;/pre&gt;
&lt;ul&gt;
&lt;li&gt;其中&lt;code&gt;bundleName&lt;/code&gt;可以通过&lt;code&gt;hdc shell bm dump -a&lt;/code&gt;获取&lt;/li&gt;
&lt;/ul&gt;
&lt;h3&gt;获取应用版本&lt;/h3&gt;
&lt;pre&gt;&lt;code&gt;$ hdc shell bm dump -n org.ohosdev.browserce

org.ohosdev.browserce:
{
    &quot;appId&quot;: &quot;org.ohosdev.browserce_BAcYHDciANDwxnn0GmXdAoxvjcK9ZJkZXWnTwGwP3UnJcLVXkLJWKtmSv2EpQG5aRZYGLYOQTHeid7drnQR+7No=&quot;,
    &quot;appIdentifier&quot;: &quot;&quot;,
    &quot;appIndex&quot;: 0,
..............
..............
..............
    &quot;vendor&quot;: &quot;ohosdev&quot;,
    &quot;versionCode&quot;: 1000004,
    &quot;versionName&quot;: &quot;1.2.1&quot;
}
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;执行上述命令后，再解析json, 提取&lt;code&gt;versionName&lt;/code&gt;字段即可&lt;/p&gt;
&lt;h3&gt;Dump应用信息&lt;/h3&gt;
&lt;p&gt;&lt;strong&gt;aa dump&lt;/strong&gt;&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;$ hdc shell aa dump -h

usage: aa dump &amp;lt;options&amp;gt;
options list:
  -h, --help                   list available commands
  -a, --all                    dump all abilities
  -l, --mission-list           dump mission list
  -i, --ability                dump abilityRecordId
  -e, --extension              dump elementName (FA: serviceAbilityRecords,Stage: ExtensionRecords)
  -p, --pending                dump pendingWantRecordId
  -r, --process                dump process
  -d, --data                   dump the data abilities
  -u, --userId                 userId
  -c, --client                 client
  -c, -u are auxiliary parameters and cannot be used alone
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;&lt;strong&gt;bm dump&lt;/strong&gt;&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;$ hdc shell bm dump -h

usage: bm dump &amp;lt;options&amp;gt;
options list:
  -h, --help                           list available commands
  -a, --all                            list all bundles in system
  -n, --bundle-name &amp;lt;bundle-name&amp;gt;      list the bundle info by a bundle name
  -s, --shortcut-info                  list the shortcut info
  -d, --device-id &amp;lt;device-id&amp;gt;          specify a device id
  -u, --user-id &amp;lt;user-id&amp;gt;              specify a user id
&lt;/code&gt;&lt;/pre&gt;
&lt;h4&gt;获取应用 Ability信息&lt;/h4&gt;
&lt;pre&gt;&lt;code&gt;$ hdc shell aa dump -l    //运行命令前需要手动打开app

User ID #100
  current mission lists:{
    Mission ID #53  mission name #[#org.ohosdev.browserce:entry:MainAbility]  lockedState #0  mission affinity #[]
      AbilityRecord ID #48
        app name [org.ohosdev.browserce]
        main name [MainAbility]
        bundle name [org.ohosdev.browserce]
        ability type [PAGE]
        state #FOREGROUND  start time [4737386]
        app state #FOREGROUND
        ready #1  window attached #0  launcher #0
        callee connections:
        isKeepAlive: false
 }
&lt;/code&gt;&lt;/pre&gt;
&lt;h4&gt;获取应用详情&lt;/h4&gt;
&lt;p&gt;查询该应用的详细信息&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;$ hdc shell bm dump -n org.ohosdev.browserce

org.ohosdev.browserce:
{
    &quot;appId&quot;: &quot;org.ohosdev.browserce_BAcYHDciANDwxnn0GmXdAoxvjcK9ZJkZXWnTwGwP3UnJcLVXkLJWKtmSv2EpQG5aRZYGLYOQTHeid7drnQR+7No=&quot;,
    &quot;appIdentifier&quot;: &quot;&quot;,
    &quot;appIndex&quot;: 0,
..............
..............
..............
    &quot;vendor&quot;: &quot;ohosdev&quot;,
    &quot;versionCode&quot;: 1000004,
    &quot;versionName&quot;: &quot;1.2.1&quot;
}
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;通过这个命令可以获取到很多应用的关键信息，比如&lt;code&gt;reqPermissions&lt;/code&gt;，&lt;code&gt;version&lt;/code&gt;，&lt;code&gt;abilities&lt;/code&gt;等等&lt;/p&gt;
&lt;h3&gt;清除应用数据&lt;/h3&gt;
&lt;pre&gt;&lt;code&gt;$ hdc shell bm clean -h

usage: bm clean &amp;lt;options&amp;gt;
options list:
  -h, --help                                      list available commands
  -n, --bundle-name  &amp;lt;bundle-name&amp;gt;                bundle name
  -c, --cache                                     clean bundle cache files by bundle name
  -d, --data                                      clean bundle data files by bundle name
  -u, --user-id &amp;lt;user-id&amp;gt;                         specify a user id
&lt;/code&gt;&lt;/pre&gt;
&lt;h4&gt;清除应用缓存&lt;/h4&gt;
&lt;pre&gt;&lt;code&gt;$ hdc shell bm clean -n org.ohosdev.browserce  -c

clean bundle cache files successfully.
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;其中&lt;code&gt;bundleName&lt;/code&gt;可以通过&lt;code&gt;hdc shell bm dump -a&lt;/code&gt;获取， 比如&lt;code&gt;com.kuaishou.hmapp&lt;/code&gt;&lt;/p&gt;
&lt;h4&gt;清除应用数据&lt;/h4&gt;
&lt;pre&gt;&lt;code&gt;$ hdc shell bm clean -n org.ohosdev.browserce -d 

clean bundle data files successfully.
&lt;/code&gt;&lt;/pre&gt;
&lt;h3&gt;显示可调试应用列表&lt;/h3&gt;
&lt;pre&gt;&lt;code&gt;$  hdc jpid

1175
1200
1536
1712

$ hdc track-jpid

0000

&lt;/code&gt;&lt;/pre&gt;
&lt;ul&gt;
&lt;li&gt;&lt;code&gt;jpid&lt;/code&gt;显示可调试应用列表&lt;/li&gt;
&lt;li&gt;&lt;code&gt;track-jpid&lt;/code&gt;动态显示可调试应用列表。&lt;/li&gt;
&lt;/ul&gt;
&lt;h2&gt;端口转发&lt;/h2&gt;
&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;命令&lt;/th&gt;
&lt;th&gt;说明&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;fport ls&lt;/td&gt;
&lt;td&gt;展示全部“端口转发主机端口转发数据到设备侧端口”的转发任务&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;fport local remote&lt;/td&gt;
&lt;td&gt;端口转发主机端口转发数据到设备侧端口&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;fport rm local remote&lt;/td&gt;
&lt;td&gt;删除指定“端口转发主机端口转发数据到设备侧端口”的转发任务&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;rport ls&lt;/td&gt;
&lt;td&gt;展示全部“端口转发设备侧端口转发数据到主机端口”的转发任务&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;rport local remote&lt;/td&gt;
&lt;td&gt;端口转发设备侧端口转发数据到主机端口&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;rport rm local remote&lt;/td&gt;
&lt;td&gt;删除指定“端口转发设备侧端口转发数据到主机端口”的转发任务&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;
&lt;h3&gt;显示端口转发列表&lt;/h3&gt;
&lt;p&gt;展示电脑端口转发到手机端口的列表&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;$ hdc fport ls

#因为用的模拟器，所以是空
[Empty]
#正常应该是以下形式
FMR0223C13000649    tcp:7912 tcp:7912    [Forward]
&lt;/code&gt;&lt;/pre&gt;
&lt;h3&gt;本地端口转发到手机&lt;/h3&gt;
&lt;p&gt;将本地电脑的&lt;code&gt;7913&lt;/code&gt;端口转发到手机&lt;code&gt;7912&lt;/code&gt;端口&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;$ hdc fport tcp:7913 tcp:7912

Forwardport result:OK
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;这个命令非常实用，比如我再手机上实现了一个 &lt;code&gt;http&lt;/code&gt;服务，没有这个命令前需要通过手机&lt;code&gt;ip:port&lt;/code&gt;来访问，这就需要提前知道手机的&lt;code&gt;wlanIP&lt;/code&gt;，执行这个命令后可以直接通过&lt;code&gt;localhost:localPort&lt;/code&gt;来访问手机里的服务。&lt;/p&gt;
&lt;h3&gt;删除端口转发任务&lt;/h3&gt;
&lt;pre&gt;&lt;code&gt;$ hdc fport rm tcp:7913 tcp:7912
Remove forward ruler success, ruler:tcp:7913 tcp:7912

$ hdc fport ls
[Empty]

&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;同理，&lt;code&gt;rport&lt;/code&gt;命令表示手机端口转发到电脑端口，我就不一一举例了.&lt;/p&gt;
&lt;h2&gt;无线调试&lt;/h2&gt;
&lt;ol&gt;
&lt;li&gt;在手机上开启5555端口 &lt;code&gt;hdc -t {SERIAL} tmode port {PORT}&lt;/code&gt;&lt;/li&gt;
&lt;li&gt;连接手机上的端口 &lt;code&gt;hdc -t {SERIAL} tconn {WLANIP}:{PORT}&lt;/code&gt;&lt;/li&gt;
&lt;li&gt;恢复手机USB连接 &lt;code&gt;hdc -t {SERIAL} tmode usb&lt;/code&gt;&lt;/li&gt;
&lt;/ol&gt;
&lt;p&gt;&lt;strong&gt;示例&lt;/strong&gt;&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;$ hdc tmode port 5555

$ hdc tconn 172.31.124.84:5555
Connect OK

$ hdc list targets
172.31.124.84:5555

$ hdc tmode usb      
Set device run mode successful.

&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;不过目前这个无线调试，会导致该手机USB连接方式断开，导致无法进行端口转发，每次进行无线调试时，需要知道手机的wlanip才行。&lt;br /&gt;
这个问题也在和鸿蒙方沟通，待解决。&lt;br /&gt;
记个TODO.&lt;/p&gt;
&lt;h2&gt;文件传输&lt;/h2&gt;
&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;命令&lt;/th&gt;
&lt;th&gt;说明&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;file send local remote&lt;/td&gt;
&lt;td&gt;从本地发送文件至远端设备&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;file recv remote local&lt;/td&gt;
&lt;td&gt;从远端设备发送文件至本地&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;
&lt;h3&gt;从本地电脑发送文件至手机&lt;/h3&gt;
&lt;pre&gt;&lt;code&gt;$ hdc file send Harmony.png /data/local/tmp/

hdc file send Harmony.png /data/local/tmp/
FileTransfer finish, Size:12454, File count = 1, time:7ms rate:1779.14kB/s
&lt;/code&gt;&lt;/pre&gt;
&lt;h3&gt;从手机拷贝文件至本地电脑&lt;/h3&gt;
&lt;pre&gt;&lt;code&gt;$  hdc file recv /data/local/tmp/2.jpeg ./     
#这个图是下面截图的图
FileTransfer finish, Size:111128, File count = 1, time:7ms rate:15875.43kB/s
&lt;/code&gt;&lt;/pre&gt;
&lt;h2&gt;UI模拟操作(点击滑动等)&lt;/h2&gt;
&lt;p&gt;支持操作类型：&lt;code&gt;点击&lt;/code&gt; &lt;code&gt;双击&lt;/code&gt; &lt;code&gt;长按&lt;/code&gt; &lt;code&gt;慢滑&lt;/code&gt; &lt;code&gt;快滑&lt;/code&gt; &lt;code&gt;拖拽&lt;/code&gt; &lt;code&gt;输入文字&lt;/code&gt; &lt;code&gt;KeyEvent&lt;/code&gt;&lt;/p&gt;
&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;配置参数名&lt;/th&gt;
&lt;th&gt;配置参数含义&lt;/th&gt;
&lt;th&gt;配置参数取值&lt;/th&gt;
&lt;th&gt;示例&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;click&lt;/td&gt;
&lt;td&gt;模拟单击操作&lt;/td&gt;
&lt;td&gt;point_x (必选参数,点击x坐标点) point_y (必选参数,点击y坐标点)&lt;/td&gt;
&lt;td&gt;hdc shell uitest uiInput click point_x point_y&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;doubleClick&lt;/td&gt;
&lt;td&gt;模拟双击操作&lt;/td&gt;
&lt;td&gt;point_x (必选参数,双击x坐标点) point_y (必选参数,双击y坐标点)&lt;/td&gt;
&lt;td&gt;hdc shell uitest uiInput doubleClick point_x point_y&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;longClick&lt;/td&gt;
&lt;td&gt;模拟长按操作&lt;/td&gt;
&lt;td&gt;point_x (必选参数,长按x坐标点) point_y (必选参数,长按y坐标点)&lt;/td&gt;
&lt;td&gt;hdc shell uitest uiInput longClick point_x point_y&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;fling&lt;/td&gt;
&lt;td&gt;模拟快滑操作&lt;/td&gt;
&lt;td&gt;from_x (必选参数,滑动起点x坐标) from_y(必选参数,滑动起点y坐标) to_x(必选参数,滑动终点x坐标) to_y(必选参数,滑动终点y坐标) swipeVelocityPps_ (可选参数,滑动速度,取值范围: 200-40000, 默认值: 600, 单位: px/s) stepLength(可选参数,滑动步长,默认值:滑动距离/50, 单位: px)&lt;/td&gt;
&lt;td&gt;hdc shell uitest uiInput fling from_x from_y to_x to_y swipeVelocityPps_ stepLength&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;swipe&lt;/td&gt;
&lt;td&gt;模拟慢滑操作&lt;/td&gt;
&lt;td&gt;from_x (必选参数,滑动起点x坐标) from_y(必选参数,滑动起点y坐标) to_x(必选参数,滑动终点x坐标) to_y(必选参数,滑动终点y坐标) swipeVelocityPps_ (可选参数,滑动速度,取值范围: 200-40000, 默认值: 600, 单位: px/s)&lt;/td&gt;
&lt;td&gt;hdc shell uitest uiInput swipe from_x from_y to_x to_y swipeVelocityPps_&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;drag&lt;/td&gt;
&lt;td&gt;模拟拖拽操作&lt;/td&gt;
&lt;td&gt;from_x (必选参数,拖拽起点x坐标) from_y(必选参数,拖拽起点y坐标) to_x(必选参数,拖拽终点x坐标) to_y(必选参数,拖拽终点y坐标) swipeVelocityPps_ (可选参数,滑动速度,取值范围: 200-40000, 默认值: 600, 单位: px/s)&lt;/td&gt;
&lt;td&gt;hdc shell uitest uiInput drag from_x from_y to_x to_y swipeVelocityPps_&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;dircFling&lt;/td&gt;
&lt;td&gt;模拟指定方向滑动操作&lt;/td&gt;
&lt;td&gt;direction (可选参数,滑动方向,可选值: [0,1,2,3], 滑动方向: [左,右,上,下],默认值: 0) swipeVelocityPps_ (可选参数,滑动速度,取值范围: 200-40000, 默认值: 600, 单位: px/s) stepLength(可选参数,滑动步长,默认值:滑动距离/50, 单位: px)&lt;/td&gt;
&lt;td&gt;hdc shell uitest uiInput dircFling direction swipeVelocityPps_ stepLength&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;inputText&lt;/td&gt;
&lt;td&gt;模拟输入框输入文本操作&lt;/td&gt;
&lt;td&gt;point_x (必选参数,输入框x坐标点) point_y (必选参数,输入框y坐标点) input(输入文本)&lt;/td&gt;
&lt;td&gt;hdc shell uitest uiInput inputText point_x point_y text&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;keyEvent&lt;/td&gt;
&lt;td&gt;模拟实体按键事件(如:键盘,电源键,返回上一级,返回桌面等),以及组合按键操作&lt;/td&gt;
&lt;td&gt;keyID (必选参数,实体按键对应ID) keyID2 (可选参数,实体按键对应ID)&lt;/td&gt;
&lt;td&gt;hdc shell uitest uiInput keyEvent keyID&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;
&lt;p&gt;&lt;strong&gt;举例&lt;/strong&gt;&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;//点击
hdc shell uitest uiInput click 100 100

//双击
hdc shell uitest uiInput doubleClick 100 100

//长按
hdc shell uitest uiInput longClick 100 100

//快滑
hdc shell uitest uiInput fling 10 10 200 200 500

//慢滑
hdc shell uitest uiInput swipe 10 10 200 200 500

//拖拽
hdc shell uitest uiInput drag 10 10 100 100 500

//左滑
hdc shell uitest uiInput dircFling 0 500

//右滑
hdc shell uitest uiInput dircFling 1 600

//上滑
hdc shell uitest uiInput dircFling 2

//下滑
hdc shell uitest uiInput dircFling 3

//输入框输入
hdc shell uitest uiInput inputText 100 100 hello

//返回主页
hdc shell uitest uiInput keyEvent Home

//返回上一步
hdc shell uitest uiInput keyEvent Back

//组合键粘贴操作
hdc shell uitest uiInput keyEvent 2072 2038
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;&lt;code&gt;keyEvent&lt;/code&gt;映射表可以参考这个文档：&lt;a href=&quot;https://docs.openharmony.cn/pages/v4.1/en/application-dev/reference/apis-input-kit/js-apis-keycode.md&quot;&gt;https://docs.openharmony.cn/pages/v4.1/en/application-dev/reference/apis-input-kit/js-apis-keycode.md&lt;/a&gt;&lt;/p&gt;
&lt;h2&gt;屏幕截图&lt;/h2&gt;
&lt;p&gt;hdc提供了两种截图命令&lt;/p&gt;
&lt;p&gt;方式一&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;$ hdc shell uitest screenCap
// 默认存储路径：/data/local/tmp，文件名：时间戳 + .png。
ScreenCap saved to /data/local/tmp/screenCap_757158519.png


$ hdc shell uitest screenCap -p /data/local/tmp/1.png
// 指定存储路径和文件名。
ScreenCap saved to /data/local/tmp/1.png
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;【推荐】方式二&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;$ hdc shell snapshot_display -f /data/local/tmp/2.jpeg
// 截图完成后可以通过 hdc file recv 命令导入到本地


process: display 0: width 1260, height 2720
snapshot: pixel format is: 3
snapshot: convert rgba8888 to rgb888 successfully.

success: snapshot display 0 , write to /data/local/tmp/2.jpeg as jpeg, width 1260, height 2720
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;方式二的截图性能效率远远高于方式一&lt;/p&gt;
&lt;h2&gt;屏幕录屏&lt;/h2&gt;
&lt;p&gt;相关hdc命令还未支持，官方在开发中。。。&lt;/p&gt;
&lt;p&gt;我这边通过python脚本实现了录屏功能，使用方法如下&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;cd awesome-hdc/scripts
pip3 install -r requirements.txt

python3 screen_recroding.py
&lt;/code&gt;&lt;/pre&gt;
&lt;h2&gt;打开Scheme (URL)&lt;/h2&gt;
&lt;pre&gt;&lt;code&gt;$ hdc shell aa start -U http://www.baidu.com
start ability successfully.

$ hdc shell aa start -U kwai://home
&lt;/code&gt;&lt;/pre&gt;
&lt;h2&gt;获取页面布局信息（控件树）&lt;/h2&gt;
&lt;pre&gt;&lt;code&gt;$ hdc shell uitest dumpLayout -p {saveDumpPath}   # 运行命令前需要手动打开app，进入对应页面

DumpLayout saved to:/data/local/tmp/layout_407568854.json
&lt;/code&gt;&lt;/pre&gt;
&lt;ul&gt;
&lt;li&gt;&lt;code&gt;-p&lt;/code&gt;表示控件树保存的目录，如果不指定，则默认保存在手机的&lt;code&gt;/data/local/tmp&lt;/code&gt;目录&lt;br /&gt;
&lt;code&gt;/data/local/tmp/layout_407568854.json&lt;/code&gt;文件内容如下：&lt;/li&gt;
&lt;/ul&gt;
&lt;pre&gt;&lt;code&gt;{
    &quot;attributes&quot;: {
        &quot;accessibilityId&quot;: &quot;&quot;,
        &quot;bounds&quot;: &quot;[0,0][1260,2720]&quot;,
        &quot;checkable&quot;: &quot;&quot;,
        &quot;checked&quot;: &quot;&quot;,
        &quot;clickable&quot;: &quot;&quot;,
        &quot;description&quot;: &quot;&quot;,
        &quot;enabled&quot;: &quot;&quot;,
        &quot;focused&quot;: &quot;&quot;,
        &quot;hostWindowId&quot;: &quot;&quot;,
        &quot;id&quot;: &quot;&quot;,
        &quot;key&quot;: &quot;&quot;,
        &quot;longClickable&quot;: &quot;&quot;,
        &quot;origBounds&quot;: &quot;&quot;,
        &quot;scrollable&quot;: &quot;&quot;,
        &quot;selected&quot;: &quot;&quot;,
        &quot;text&quot;: &quot;&quot;,
        &quot;type&quot;: &quot;&quot;
    },
    &quot;children&quot;: [
    	
      ...
      
    ]
&lt;/code&gt;&lt;/pre&gt;
&lt;h2&gt;录制用户操作&lt;/h2&gt;
&lt;p&gt;将当前界面操作记录到&lt;code&gt;/data/local/tmp/layout/record.csv&lt;/code&gt;，结束录制操作使用&lt;code&gt;Ctrl+C&lt;/code&gt;结束录制&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;$  hdc shell uitest uiRecord record

windowBounds : (0,0,1260,2720)
Current ForAbility :SCBDesktop13, SCBDesktop13
The result will be written in csv file at location: /data/local/tmp/record.csv
Started Recording Successfully...
click , fingerNumber:1 , 
        finger1:click:  at Widget( id: AppIcon_Image_org.ohosdev.browserce_16777230_0, text: , type: Image) ; from Widget(id: AppIcon_Image_org.ohosdev.browserce_16777230_0, type: Imag
e, text: ) ;  to Point(x:194, y:411) ;
click , fingerNumber:1 , 
        finger1:click:  at Point(x:620, y:987) ; from Point(x:620, y:987)  to Point(x:620, y:983) ;
doubleClick , fingerNumber:1 , 
        finger1:doubleClick:  at Point(x:158, y:2633) ; from Point(x:158, y:2633)  to Point(x:158, y:2633) ;
click , fingerNumber:1 , 
        finger1:click:  at Point(x:829, y:2587) ; from Point(x:829, y:2587)  to Point(x:829, y:2587) ;
recent , fingerNumber:1 , 
        finger1:from Point(x:652, y:2697)  to Point(x:694, y:1719) ;
fling , fingerNumber:1 , 
        finger1:from Point(x:662, y:1833)  to Point(x:637, y:724) ; Off-hand speed:2183.83, Step length:38;
fling , fingerNumber:1 , 
        finger1:from Widget(id: SwiperPage_Grid_WorkSpace_1, type: Grid, text: ) ; to Widget(id: AppIcon_Image_com.huawei.hmos.files_16777217_0, type: Image, text: ) ; Off-hand speed:3
50.328, Step length:42;
fling , fingerNumber:1 , 
        finger1:from Widget(id: SwiperPage_Grid_WorkSpace_1, type: Grid, text: ) ; to Widget(id: AppName_text_com.huawei.hmos.files_文件管理_m2wnkjsholhzucg0wrs, type: Text, text: 文件
管理) ; Off-hand speed:562.963, Step length:92;
fling , fingerNumber:1 , 
        finger1:from Widget(id: SwiperPage_Grid_WorkSpace_1, type: Grid, text: ) ;  to Point(x:573, y:144) ; Off-hand speed:128.394, Step length:42;
fling , fingerNumber:1 , 
        finger1:from Widget(id: SwiperPage_Grid_WorkSpace_1, type: Grid, text: ) ; to Widget(id: StatusBarBackground_Row_0, type: Row, text: ) ; Off-hand speed:3966.14, Step length:45;
1259966024, 12023, key, 1, 2045, , 
No operation detected for 5 seconds,
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;支持两种方式查看数据:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;code&gt;uiRecord record&lt;/code&gt;, 将事件的位置坐标写入文件&lt;/li&gt;
&lt;li&gt;&lt;code&gt;uiRecord read&lt;/code&gt;, 将文件内容打印到控制台&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;录制完成后，再将&lt;code&gt;csv&lt;/code&gt;文件拷贝到电脑上&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;$ hdc file recv  /data/local/tmp/layout/record.csv ./record.csv

1
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;&lt;code&gt;record&lt;/code&gt;数据字段含义请参考如下示例数据&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;{
  &quot;ABILITY&quot;: &quot;com.ohos.launcher.MainAbility&quot;, // 前台应用界面
  &quot;BUNDLE&quot;: &quot;com.ohos.launcher&quot;, // 操作应用
  &quot;CENTER_X&quot;: &quot;&quot;, // 模拟捏合中心X, pinch事件
  &quot;CENTER_Y&quot;: &quot;&quot;, // 模拟捏合中心Y, pinch事件
  &quot;EVENT_TYPE&quot;: &quot;pointer&quot;, //  
  &quot;LENGTH&quot;: &quot;0&quot;, // 总体步长
  &quot;OP_TYPE&quot;: &quot;click&quot;, //事件类型，当前支持点击、双击、长按、拖拽、捏合、滑动、抛滑动作录制
  &quot;VELO&quot;: &quot;0.000000&quot;, // 离手速度
  &quot;direction.X&quot;: &quot;0.000000&quot;,// 总体移动X方向
  &quot;direction.Y&quot;: &quot;0.000000&quot;, // 总体移动Y方向
  &quot;duration&quot;: 33885000.0, // 手势操作持续时间
  &quot;fingerList&quot;: [{
      &quot;LENGTH&quot;: &quot;0&quot;, // 总体步长
      &quot;MAX_VEL&quot;: &quot;40000&quot;, // 最大速度
      &quot;VELO&quot;: &quot;0.000000&quot;, // 离手速度
      &quot;W1_BOUNDS&quot;: &quot;{&quot;bottom&quot;:361,&quot;left&quot;:37,&quot;right&quot;:118,&quot;top&quot;:280}&quot;, // 起点控件bounds
      &quot;W1_HIER&quot;: &quot;ROOT,3,0,0,0,0,0,0,0,0,5,0,0,0,0,0,0,0&quot;, // 起点控件hierarchy
      &quot;W1_ID&quot;: &quot;&quot;, // 起点控件id
      &quot;W1_Text&quot;: &quot;&quot;, // 起点控件text
      &quot;W1_Type&quot;: &quot;Image&quot;, // 起点控件类型
      &quot;W2_BOUNDS&quot;: &quot;{&quot;bottom&quot;:361,&quot;left&quot;:37,&quot;right&quot;:118,&quot;top&quot;:280}&quot;, // 终点控件bounds
      &quot;W2_HIER&quot;: &quot;ROOT,3,0,0,0,0,0,0,0,0,5,0,0,0,0,0,0,0&quot;, // 终点控件hierarchy
      &quot;W2_ID&quot;: &quot;&quot;, // 终点控件id
      &quot;W2_Text&quot;: &quot;&quot;, // 终点控件text
      &quot;W2_Type&quot;: &quot;Image&quot;, // 终点控件类型
      &quot;X2_POSI&quot;: &quot;47&quot;, // 终点X
      &quot;X_POSI&quot;: &quot;47&quot;, // 起点X
      &quot;Y2_POSI&quot;: &quot;301&quot;, // 终点Y
      &quot;Y_POSI&quot;: &quot;301&quot;, // 起点Y
      &quot;direction.X&quot;: &quot;0.000000&quot;, // x方向移动量
      &quot;direction.Y&quot;: &quot;0.000000&quot; // Y方向移动量
  }],
  &quot;fingerNumber&quot;: &quot;1&quot; //手指数量
}
&lt;/code&gt;&lt;/pre&gt;
&lt;h2&gt;系统日志（log）&lt;/h2&gt;
&lt;pre&gt;&lt;code&gt;$ hdc hilog -h

Usage:
-h --help
  Show all help information.
  Show single help information with option:
  query/clear/buffer/stats/persist/private/kmsg/flowcontrol/baselevel/domain/combo
Querying logs options:
  No option performs a blocking read and keeps printing.
  -x --exit
    Performs a non-blocking read and exits when all logs in buffer are printed.
  -a &amp;lt;n&amp;gt;, --head=&amp;lt;n&amp;gt;
    Show n lines logs on head of buffer.
  -z &amp;lt;n&amp;gt;, --tail=&amp;lt;n&amp;gt;
    Show n lines logs on tail of buffer.
  -t &amp;lt;type&amp;gt;, --type=&amp;lt;type&amp;gt;
    Show specific type/types logs with format: type1,type2,type3
    Don&apos;t show specific type/types logs with format: ^type1,type2,type3
    Type coule be: app/core/init/kmsg, kmsg can&apos;t combine with others.
    Default types are: app,core,init.
  -L &amp;lt;level&amp;gt;, --level=&amp;lt;level&amp;gt;
    Show specific level/levels logs with format: level1,level2,level3
    Don&apos;t show specific level/levels logs with format: ^level1,level2,level3
    Long and short level string are both accepted
    Long level string coule be: DEBUG/INFO/WARN/ERROR/FATAL.
    Short level string coule be: D/I/W/E/F.
    Default levels are all levels.
  -D &amp;lt;domain&amp;gt;, --domain=&amp;lt;domain&amp;gt;
    Show specific domain/domains logs with format: domain1,domain2,doman3
    Don&apos;t show specific domain/domains logs with format: ^domain1,domain2,doman3
    Max domain count is 5.
    See domain description at the end of this message.
  -T &amp;lt;tag&amp;gt;, --tag=&amp;lt;tag&amp;gt;
    Show specific tag/tags logs with format: tag1,tag2,tag3
    Don&apos;t show specific tag/tags logs with format: ^tag1,tag2,tag3
    Max tag count is 10.
  -P &amp;lt;pid&amp;gt;, --pid=&amp;lt;pid&amp;gt;
    Show specific pid/pids logs with format: pid1,pid2,pid3
    Don&apos;t show specific domain/domains logs with format: ^pid1,pid2,pid3
    Max pid count is 5.
  -e &amp;lt;expr&amp;gt;, --regex=&amp;lt;expr&amp;gt;
    Show the logs which match the regular expression &amp;lt;expr&amp;gt;.
  -v &amp;lt;format&amp;gt;, --format=&amp;lt;format&amp;gt;
    Show logs in different formats, options are:
      color or colour      display colorful logs by log level.i.e.
        DEBUG        INFO        WARN        ERROR       FATAL
      time format options are(single accepted):
        time       display local time, this is default.
        epoch      display the time from 1970/1/1.
        monotonic  display the cpu time from bootup.
      time accuracy format options are(single accepted):
        msec       display time by millisecond, this is default.
        usec       display time by microsecond.
        nsec       display time by nanosecond.
      year       display the year when -v time is specified.
      zone       display the time zone when -v time is specified.
    Different types of formats can be combined, such as:
    -v color -v time -v msec -v year -v zone.
-r
  Remove all logs in hilogd buffer, advanced option:
  -t &amp;lt;type&amp;gt;, --type=&amp;lt;type&amp;gt;
    Remove specific type/types logs in buffer with format: type1,type2,type3
    Type coule be: app/core/init/kmsg.
    Default types are: app,core
-g
  Query hilogd buffer size, advanced option:
  -t &amp;lt;type&amp;gt;, --type=&amp;lt;type&amp;gt;
    Query specific type/types buffer size with format: type1,type2,type3
    Type coule be: app/core/init/kmsg.
    Default types are: app,core
-G &amp;lt;size&amp;gt;, --buffer-size=&amp;lt;size&amp;gt;
  Set hilogd buffer size, &amp;lt;size&amp;gt; could be number or number with unit.
  Unit could be: B/K/M/G which represents Byte/Kilobyte/Megabyte/Gigabyte.
  &amp;lt;size&amp;gt; range: [64.0K,64.0K].
  Advanced option:
  -t &amp;lt;type&amp;gt;, --type=&amp;lt;type&amp;gt;
    Set specific type/types log buffer size with format: type1,type2,type3
    Type coule be: app/core/init/kmsg.
    Default types are: app,core
  **It&apos;s a persistant configuration**
-s, --statistics
  Query log statistic information.
  Set param persist.sys.hilog.stats true to enable statistic.
  Set param persist.sys.hilog.stats.tag true to enable statistic of log tag.
-S
  Clear hilogd statistic information.
-w &amp;lt;control&amp;gt;,--write=&amp;lt;control&amp;gt;
  Log persistance task control, options are:
    query      query tasks informations
    stop       stop all tasks
    start      start one task
    clear      clear /data/log/hilog/hilog*.gz
  Persistance task is used for saving logs in files.
  The files are saved in directory: /data/log/hilog/
  Advanced options:
  -f &amp;lt;filename&amp;gt;, --filename=&amp;lt;filename&amp;gt;
    Set log file name, name should be valid of Linux FS.
  -l &amp;lt;length&amp;gt;, --length=&amp;lt;length&amp;gt;
    Set single log file size. &amp;lt;length&amp;gt; could be number or number with unit.
    Unit could be: B/K/M/G which represents Byte/Kilobyte/Megabyte/Gigabyte.
    &amp;lt;length&amp;gt; range: [64.0K, 512.0M].
  -n &amp;lt;number&amp;gt;, --number&amp;lt;number&amp;gt;
    Set max log file numbers, log file rotate when files count over this number.
    &amp;lt;number&amp;gt; range: [2, 1000].
  -m &amp;lt;compress algorithm&amp;gt;,--stream=&amp;lt;compress algorithm&amp;gt;
    Set log file compressed algorithm, options are:
      none       write file with non-compressed logs.
      zlib       write file with zlib compressed logs.
  -j &amp;lt;jobid&amp;gt;, --jobid&amp;lt;jobid&amp;gt;
    Start/stop specific task of &amp;lt;jobid&amp;gt;.
    &amp;lt;jobid&amp;gt; range: [10, 0xffffffff).
  User can start task with options (t/L/D/T/P/e/v) as if using them when &quot;Query logs&quot; too.
  **It&apos;s a persistant configuration**
-p &amp;lt;on/off&amp;gt;, --privacy &amp;lt;on/off&amp;gt;
  Set HILOG api privacy formatter feature on or off.
  **It&apos;s a temporary configuration, will be lost after reboot**
-k &amp;lt;on/off&amp;gt;, --kmsg &amp;lt;on/off&amp;gt;
  Set hilogd storing kmsg log feature on or off
  **It&apos;s a persistant configuration**
-Q &amp;lt;control-type&amp;gt;
  Set log flow-control feature on or off, options are:
    pidon     process flow control on
    pidoff    process flow control off
    domainon  domain flow control on
    domainoff domain flow control off
  **It&apos;s a temporary configuration, will be lost after reboot**
-b &amp;lt;loglevel&amp;gt;, --baselevel=&amp;lt;loglevel&amp;gt;
  Set global loggable level to &amp;lt;loglevel&amp;gt;
  Long and short level string are both accepted.
  Long level string coule be: DEBUG/INFO/WARN/ERROR/FATAL/X.
  Short level string coule be: D/I/W/E/F/X.
  X means that loggable level is higher than the max level, no log could be printed.
  Advanced options:
  -D &amp;lt;domain&amp;gt;, --domain=&amp;lt;domain&amp;gt;
    Set specific domain loggable level.
    See domain description at the end of this message.
  -T &amp;lt;tag&amp;gt;, --tag=&amp;lt;tag&amp;gt;
    Set specific tag loggable level.
    The priority is: tag level &amp;gt; domain level &amp;gt; global level.
  **It&apos;s a temporary configuration, will be lost after reboot**
The first layer options can&apos;t be used in combination, ILLEGAL expamples:
    hilog -S -s; hilog -w start -r; hilog -p on -k on -b D


Domain description:
  Log type &quot;core&quot; &amp;amp; &quot;init&quot; are used for OS subsystems, the range is [0xd000000,  0xd0fffff]
  Log type &quot;app&quot; is used for applications, the range is [0x0,  0xffff]
  To reduce redundant info when printing logs, only last five hex numbers of domain are printed
  So if user wants to use -D option to filter OS logs, user should add 0xD0 as prefix to the printed domain:
  Exapmle: hilog -D 0xD0xxxxx
  The xxxxx is the domain string printed in logs.


Dictionary description:
-d &amp;lt;path&amp;gt;, --dictionary=&amp;lt;path&amp;gt;
  Set elf file path, name should be valid of Linux FS.
  Rescan the elf file in the system to generate a full data dictionary file
&lt;/code&gt;&lt;/pre&gt;
&lt;h2&gt;导出日志&lt;/h2&gt;
&lt;pre&gt;&lt;code&gt;$ hdc file recv data/log/hilog/ ./


FileTransfer finish, Size:18754808, File count = 42, time:646ms rate:29032.21kB/s
&lt;/code&gt;&lt;/pre&gt;
&lt;h2&gt;导出crash日志&lt;/h2&gt;
&lt;pre&gt;&lt;code&gt;$ hdc file recv data/log/faultlog/faultlogger/ ./

FileTransfer finish, Size:129048, File count = 1, time:9ms rate:14338.67kB/s
&lt;/code&gt;&lt;/pre&gt;
&lt;h2&gt;hidumper工具&lt;/h2&gt;
&lt;pre&gt;&lt;code&gt;$ hdc shell hidumper -h

usage:
  -h                          |help text for the tool
  -lc                         |a list of system information clusters
  -ls                         |a list of system abilities
  -c                          |all system information clusters
  -c [base system]            |system information clusters labeled &quot;base&quot; and &quot;system&quot;
  -s                          |all system abilities
  -s [SA0 SA1]                |system abilities labeled &quot;SA0&quot; and &quot;SA1&quot;
  -s [SA] -a [&apos;-h&apos;]           |system ability labeled &quot;SA&quot; with arguments &quot;-h&quot; specified
  -e                          |faultlogs of crash history
  --net [pid]                 |dump network information; if pid is specified, dump traffic usage of specified pid
  --storage [pid]             |dump storage information; if pid is specified, dump /proc/pid/io
  -p                          |processes information, include list and information of processes and threads
  -p [pid]                    |dump threads under pid, includes smap, block channel, execute time, mountinfo
  --cpuusage [pid]            |dump cpu usage by processes and category; if PID is specified, dump category usage of specified pid
  --cpufreq                   |dump real CPU frequency of each core
  --mem [pid]                 |dump memory usage of total; dump memory usage of specified pid if pid was specified
  --zip                       |compress output to /data/log/hidumper
  --mem-smaps pid [-v]        |display statistic in /proc/pid/smaps, use -v specify more details
  --mem-jsheap pid [-T tid] [--gc]  |triggerGC and dumpHeapSnapshot under pid and tid
&lt;/code&gt;&lt;/pre&gt;
&lt;h3&gt;list system abilities&lt;/h3&gt;
&lt;pre&gt;&lt;code&gt;$ hdc shell hidumper -ls

System ability list:
SystemAbilityManager             RenderService                    AbilityManagerService
DataObserverMgr                  UriPermissionMgr                 AccountMgr
BundleMgr                        FormMgr                          ApplicationManagerService
AccessibilityManagerService      UserIdmService                   UserAuthService
AuthExecutorMgrService           PinAuthService                   FaceAuthService
WifiDevice                       WifiHotspot                      WifiScan
1125                             1126                             NetConnManager
NetPolicyManager                 NetStatsManager                  VPNManager
EthernetManager                  NetsysNative                     DistributedNet
HiviewService                    HiviewFaultLogger                HiviewSysEventService
1204                             HiDumperService                  XpowerManager
HiDumperCpuService               DistributedKvData                ContinuationManagerService
ResourceSched                    BackgroundTaskManager            WorkSchedule
SocPerfService                   DeviceUsageStatistics            MemoryManagerService
ConcurrentTaskService            DeviceStandbyService             1918
1919                             LocationLocator                  2901
DeviceStatusService              2903                             2904
2908                             AudioDistributed                 PlayerDistributedService
CameraService                    AudioPolicyService               AVSessionService                 
AVCodecService                   MediaKeySystemService            3013
MultimodalInput                  DistributedNotificationService   CommonEventService
PowerManagerService              BatteryService                   ThermalService
BatteryStatisticsService         DisplayPowerManagerService       AccessTokenManagerService
PrivacyManagerService            KeystoreService                  CertManagerService
RiskAnalysisManagerService       DataCollectManagerService        3526
DlpCreService                    SensorService                    MiscDeviceService
PasteboardService                TimeService                      InputMethodService
ScreenlockService                WallpaperManagerService          3706
ParamWatcher                     SysParamDevice                   TelephonyCallManager
TelephonyCellularCall            TelephonyCellularData            TelephonySmsMms
TelephonyStateRegistry           TelephonyCoreService             UsbService
4353                             WindowManagerService             DisplayManagerService
DSoftbus                         DeviceAuthService                DeviceManagerService
StorageDaemon                    StorageManager                   HdfDeviceServiceManager
DistributedDeviceProfile         EcologicalRuleManager            AppDomainVerifyManager
UiService                        UiAppearanceService              AssetService
65570                            65728                            65777
65850                            65926                            65958
66054                            70633                            70635
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;获取到abilities后，就可以指定service获取相关的信息。 比如通过RenderService获取一些信息&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;$ hdc shell hidumper -s RenderService             

-------------------------------[ability]-------------------------------


----------------------------------RenderService----------------------------------
------Graphic2D--RenderSerice ------
Usage:
 h                             |help text for the tool
screen                         |dump all screen infomation in the system
surface                        |dump all surface information
composer fps                   |dump the fps info of composer
[surface name] fps             |dump the fps info of surface
composer fpsClear              |clear the fps info of composer
[windowname] fps               |dump the fps info of window
[windowname] hitchs            |dump the hitchs info of window
[surface name] fpsClear        |clear the fps info of surface
nodeNotOnTree                  |dump nodeNotOnTree info
allSurfacesMem                 |dump surface mem info
RSTree                         |dump RSTree info
EventParamList                 |dump EventParamList info
allInfo                        |dump all info
dumpMem                        |dump Cache
trimMem cpu/gpu/shader         |release Cache
surfacenode [id]               |dump node info
fpsCount                       |dump the refresh rate counts info
clearFpsCount                  |clear the refresh rate counts info
flushJankStatsRs|flush rs jank stats hisysevent
&lt;/code&gt;&lt;/pre&gt;
&lt;h3&gt;RenderService&lt;/h3&gt;
&lt;p&gt;&lt;strong&gt;获取分辩率&lt;/strong&gt;&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;$ hdc shell hidumper -s RenderService -a screen 

-------------------------------[ability]-------------------------------


----------------------------------RenderService----------------------------------
-- ScreenInfo
screen[0]: id=0, powerstatus=POWER_STATUS_ON, backlight=1, screenType=EXTERNAL_TYPE, render size: 1260x2720, physical screen resolution: 1260x2720, isvirtual=false, skipFrameInterval_:
1

  supportedMode[0]: 1260x2720, refreshrate=60
  activeMode: 1260x2720, refreshrate=60
  capability: name=express_display, phywidth=72, phyheight=156,supportlayers=10, virtualDispCount=1, propCount=0, type=DISP_INTF_HDMI, supportWriteBack=false
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;&lt;strong&gt;获取帧率&lt;/strong&gt;&lt;br /&gt;
首先执行如下命令进入到shell环境&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;$ hdc shell

&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;然后执行&lt;code&gt;hidumper [surface name] fps&lt;/code&gt; , 例如&lt;code&gt;composer fps&lt;/code&gt;&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;$ hidumper -s RenderService -a &quot;composer fps&quot;
#不知道为什么都是零

-------------------------------[ability]-------------------------------


----------------------------------RenderService----------------------------------

-- The recently fps records info of screens:

The fps of screen [Id:0] is:
0
0
0
0
0
0
0
0
0
0
0
0
0
0
0
&lt;/code&gt;&lt;/pre&gt;
&lt;h3&gt;DisplayManagerService&lt;/h3&gt;
&lt;pre&gt;&lt;code&gt;$ hdc shell hidumper -s DisplayManagerService

-------------------------------[ability]-------------------------------


----------------------------------DisplayManagerService----------------------------------
-------------- DMS FREEZED PID LIST  --------------
-------------- DMS KEY EVENTS LIST  --------------
[10-31 09:54:28.367]: Dms construct.
[10-31 09:54:30.663]: Default screen id : 0
[10-31 09:54:30.663]: Dms OnScreenChange register success.
[10-31 09:54:30.663]: OnScreenChange triggered. screenId: 0  screenEvent: 0
[10-31 09:54:30.671]: GetScreenPower state:0 screenId:0
[10-31 09:54:30.672]: Dms subscribed to sensor successfully.
[10-31 09:54:30.672]: Dms init end.
[10-31 09:54:30.672]: CreateScreenProperty by rsInterface success.
[10-31 09:54:30.682]: create screen session success.
[10-31 09:54:30.687]: Dms onstart end.
[10-31 09:54:30.690]: Dms RefreshRateChange register success.
[10-31 09:54:31.850]: GetScreenPower state:0 screenId:0
[10-31 09:54:32.156]: GetScreenPower state:0 screenId:0
[10-31 09:54:34.584]: set client userId: 100 newScbPid: 1215 clientName: com.ohos.sceneboard
[10-31 09:54:34.587]: currentUserId: 0  currentScbPId: -1  newUserId: 100  newScbPid: 1215  coldBoot: 1
-------------- DMS Multi User Info --------------
[oldScbPid:]
[userId:] 100
[ScbPid:] 1215
Usage:
 -h                             |help text for the tool
 -a                             |dump all screen information in the system
 -z                             |switch to fold half status
 -y                             |switch to expand status
 -p                             |switch to fold status
 -f                             |get to fold status
&lt;/code&gt;&lt;/pre&gt;
&lt;h3&gt;PowerManagerService&lt;/h3&gt;
&lt;pre&gt;&lt;code&gt;$ hdc shell hidumper -s PowerManagerService

-------------------------------[ability]-------------------------------


----------------------------------PowerManagerService----------------------------------
Power manager dump options:
  [-h] [-runninglock]
  description of the cmd option:
    -a: show dump info of all power modules.
    -h: show this help.
    -r: show the information of runninglock.
    -s: show the information of power state machine.
    -d: show power off dialog.
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;例如上文提到的获取屏幕状态&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;$ hdc shell hidumper -s PowerManagerService -a -s

&lt;/code&gt;&lt;/pre&gt;
&lt;h3&gt;BatteryService&lt;/h3&gt;
&lt;pre&gt;&lt;code&gt;$ hdc shell hidumper -s BatteryService

-------------------------------[ability]-------------------------------


----------------------------------BatteryService----------------------------------
Usage:
      -h: dump help
      -i: dump battery info
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;例如上文提到的获取电量温度&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;$ hdc shell hidumper -s BatteryService -a -i                

&lt;/code&gt;&lt;/pre&gt;
&lt;h3&gt;NetConnManager&lt;/h3&gt;
&lt;pre&gt;&lt;code&gt;$ hdc shell hidumper -s NetConnManager

-------------------------------[ability]-------------------------------


----------------------------------NetConnManager----------------------------------
Net connect Info:
	defaultNetSupplier_ is nullptr
	SupplierId:
	NetId: 0
	ConnStat: 0
	IsAvailable:
	IsRoaming: 0
	Strength: 0
	Frequency: 0
	LinkUpBandwidthKbps: 0
	LinkDownBandwidthKbps: 0
	Uid: 0
Dns result Info:
	netId: 0
	totalReports: 2
	failReports: 2

&lt;/code&gt;&lt;/pre&gt;
&lt;h3&gt;StorageManager&lt;/h3&gt;
&lt;h2&gt;aa工具&lt;/h2&gt;
&lt;p&gt;Ability assistant（Ability助手，简称为aa），是实现应用及测试用例启动功能的工具，为开发者提供基本的应用调试和测试能力，例如启动应用组件、强制停止进程、打印应用组件相关信息等。&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;$ hdc shell aa help
usage: aa &amp;lt;command&amp;gt; &amp;lt;options&amp;gt;
These are common aa commands list:
  help                        list available commands
  start                       start ability with options
  stop-service                stop service with options
  dump                        dump the ability info
  force-stop &amp;lt;bundle-name&amp;gt;    force stop the process with bundle name
  attach                      attach application to enter debug mdoe
  detach                      detach application to exit debug mode
  test                        start the test framework with options
  appdebug                    set / cancel / get waiting debug status
&lt;/code&gt;&lt;/pre&gt;
&lt;h3&gt;start&lt;/h3&gt;
&lt;h3&gt;stop-service&lt;/h3&gt;
&lt;h3&gt;force-stop&lt;/h3&gt;
&lt;h3&gt;test&lt;/h3&gt;
&lt;h3&gt;attach&lt;/h3&gt;
&lt;h3&gt;detach&lt;/h3&gt;
&lt;h3&gt;appdebug&lt;/h3&gt;
&lt;p&gt;详细介绍请参考文档：&lt;a href=&quot;https://gitee.com/openharmony/docs/blob/master/zh-cn/application-dev/tools/aa-tool.md&quot;&gt;https://gitee.com/openharmony/docs/blob/master/zh-cn/application-dev/tools/aa-tool.md&lt;/a&gt;&lt;/p&gt;
&lt;h2&gt;bm工具&lt;/h2&gt;
&lt;p&gt;Bundle Manager（包管理工具，简称bm）是实现应用安装、卸载、更新、查询等功能的工具，bm为开发者提供基本的应用安装包的调试能力，例如：安装应用，卸载应用，查询安装包信息等。&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;$ hdc shell bm help
usage: bm &amp;lt;command&amp;gt; &amp;lt;options&amp;gt;
These are common bm commands list:
  help         list available commands
  install      install a bundle with options
  uninstall    uninstall a bundle with options
  dump         dump the bundle info
  get          obtain device udid
  quickfix     quick fix, including query and install
  compile      Compile the software package
  dump-overlay dump overlay info of the specific overlay bundle
  dump-target-overlay dump overlay info of the specific target bundle
  dump-dependencies dump dependencies by given bundle name and module name
  dump-shared dump inter-application shared library information by bundle name
  clean        clean the bundle data
&lt;/code&gt;&lt;/pre&gt;
&lt;h3&gt;install&lt;/h3&gt;
&lt;h3&gt;uninstall&lt;/h3&gt;
&lt;h3&gt;dump&lt;/h3&gt;
&lt;h3&gt;clean&lt;/h3&gt;
&lt;h3&gt;enable&lt;/h3&gt;
&lt;h3&gt;disable&lt;/h3&gt;
&lt;h3&gt;get&lt;/h3&gt;
&lt;p&gt;详细介绍请参考文档：&lt;a href=&quot;https://gitee.com/openharmony/docs/blob/master/zh-cn/application-dev/tools/bm-tool.md&quot;&gt;https://gitee.com/openharmony/docs/blob/master/zh-cn/application-dev/tools/bm-tool.md&lt;/a&gt;&lt;/p&gt;
&lt;h2&gt;param工具&lt;/h2&gt;
&lt;p&gt;param是为开发人员提供用于操作系统参数的工具，该工具只支持标准系统。&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;$ hdc shell param                                   
Command list:
    param ls [-r] [name]                            --display system parameter
    param get [name]                                --get system parameter
    param set name value                            --set system parameter
    param wait name [value] [timeout]               --wait system parameter
    param dump [verbose]                            --dump system parameter
    param shell [-p] [name] [-u] [username] [-g] [groupname]    --shell system parameter
    param save    
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;详细介绍请参考文档：&lt;a href=&quot;https://gitee.com/openharmony/docs/blob/master/zh-cn/application-dev/tools/param-tool.md&quot;&gt;https://gitee.com/openharmony/docs/blob/master/zh-cn/application-dev/tools/param-tool.md&lt;/a&gt;&lt;/p&gt;
&lt;h2&gt;Instrument Test&lt;/h2&gt;
&lt;p&gt;主要用来做APP 的UI自动化测试，将应用测试包安装到测试设备上，在cmd窗口中执行aa命令，完成对用例测试。&lt;/p&gt;
&lt;p&gt;&lt;code&gt;aa test&lt;/code&gt;命令执行配置参数&lt;/p&gt;
&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;执行参数全写&lt;/th&gt;
&lt;th&gt;执行参数缩写&lt;/th&gt;
&lt;th&gt;执行参数含义&lt;/th&gt;
&lt;th&gt;执行参数示例&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;–bundleName&lt;/td&gt;
&lt;td&gt;-b&lt;/td&gt;
&lt;td&gt;应用 Bundle 名称&lt;/td&gt;
&lt;td&gt;-b com.test.example&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;–packageName&lt;/td&gt;
&lt;td&gt;-p&lt;/td&gt;
&lt;td&gt;应用模块名，适用于 FA 模型应用&lt;/td&gt;
&lt;td&gt;-p com.test.example.entry&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;–moduleName&lt;/td&gt;
&lt;td&gt;-m&lt;/td&gt;
&lt;td&gt;应用模块名，适用于 STAGE 模型应用&lt;/td&gt;
&lt;td&gt;-m entry&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;NA&lt;/td&gt;
&lt;td&gt;-s&lt;/td&gt;
&lt;td&gt;特定参数，以 &amp;lt;key, value&amp;gt; 键值对方式传入&lt;/td&gt;
&lt;td&gt;-s unittest /ets/testrunner/OpenHarmonyTestRunner&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;
&lt;pre&gt;&lt;code&gt;$ hdc shell aa test -h
usage: aa test &amp;lt;options&amp;gt;
options list:
  -h, --help                                             list available commands
  -b &amp;lt;bundle-name&amp;gt; -s unittest &amp;lt;test-runner&amp;gt;             start the test framework with options
                  [-p &amp;lt;package-name&amp;gt;]                    the name of package with test-runner, required for the FA model
                  [-m &amp;lt;module-name&amp;gt;]                     the name of module with test-runner, required for the STAGE model
                  [-s class &amp;lt;test-class&amp;gt;]
                  [-s level &amp;lt;test-level&amp;gt;]
                  [-s size &amp;lt;test-size&amp;gt;]
                  [-s testType &amp;lt;test-testType&amp;gt;]
                  [-s timeout &amp;lt;test-timeout&amp;gt;]
                  [-s &amp;lt;any-key&amp;gt; &amp;lt;any-value&amp;gt;]
                  [-w &amp;lt;wait-time&amp;gt;]
                  [-D]
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;&lt;strong&gt;举例&lt;/strong&gt;&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;$ hdc shell aa test -b com.example.myapplication -m entry_test -s unittest /ets/testrunner/OpenHarmonyTestRunner -s class UiTestDemo timeout 15000

&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;查看测试结果&lt;br /&gt;
cmd模式执行过程,会打印如下相关日志信息。&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;OHOS_REPORT_STATUS: class=testStop
OHOS_REPORT_STATUS: current=1
OHOS_REPORT_STATUS: id=JS
OHOS_REPORT_STATUS: numtests=447
OHOS_REPORT_STATUS: stream=
OHOS_REPORT_STATUS: test=stop_0
OHOS_REPORT_STATUS_CODE: 1

OHOS_REPORT_STATUS: class=testStop
OHOS_REPORT_STATUS: current=1
OHOS_REPORT_STATUS: id=JS
OHOS_REPORT_STATUS: numtests=447
OHOS_REPORT_STATUS: stream=
OHOS_REPORT_STATUS: test=stop_0
OHOS_REPORT_STATUS_CODE: 0
OHOS_REPORT_STATUS: consuming=4

OHOS_REPORT_RESULT: stream=Tests run: 447, Failure: 0, Error: 1, Pass: 201, Ignore: 245
OHOS_REPORT_CODE: 0

OHOS_REPORT_RESULT: breakOnError model, Stopping whole test suite if one specific test case failed or error
OHOS_REPORT_STATUS: taskconsuming=16029
&lt;/code&gt;&lt;/pre&gt;
&lt;h2&gt;性能工具&lt;/h2&gt;
&lt;p&gt;&lt;code&gt;SmartPerf&lt;/code&gt;是一款基于系统开发的性能功耗测试工具，操作简单易用。工具可以检测性能、功耗相关指标，包括&lt;code&gt;FPS&lt;/code&gt;、&lt;code&gt;CPU&lt;/code&gt;、&lt;code&gt;GPU&lt;/code&gt;、&lt;code&gt;RAM&lt;/code&gt;、&lt;code&gt;Temp&lt;/code&gt;等，通过量化的指标项了解应用性能状况。在开发过程中，使用的可能是有屏或无屏设备，对此&lt;code&gt;SmartPerf&lt;/code&gt;提供了两种方式：分别是&lt;code&gt;SmartPerf-Device&lt;/code&gt;和&lt;code&gt;SmartPerf-Daemon&lt;/code&gt;。&lt;code&gt;SmartPerf-Device&lt;/code&gt;适用于有屏设备，支持可视化操作。测试时是通过悬浮窗的开始和暂停来实时展示性能指标数据，保存后可生成数据报告，在报告中可分析各指标数据详情。&lt;code&gt;SmartPerf-Daemon&lt;/code&gt;支持&lt;code&gt;shell命令行&lt;/code&gt;方式，同时适用于有屏和无屏设备。&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;CPU：每秒读取一次设备节点下CPU大中小核的频点和各核使用率，衡量应用占用CPU资源的情况，占用过多的CPU资源会导致芯片发烫。&lt;/li&gt;
&lt;li&gt;GPU：每秒读取一次设备节点下GPU的频点和负载信息，衡量应用占用GPU资源的情况，当GPU占用过多时，会导致性能下降，应用程序的运行速度变慢。&lt;/li&gt;
&lt;li&gt;FPS：应用界面每秒刷新次数，衡量应用画面的流畅度，FPS越高表示图像流畅度越好，用户体验也越好。&lt;/li&gt;
&lt;li&gt;POWER：每秒读取一次设备节点下的电流及电压信息。&lt;/li&gt;
&lt;li&gt;TEMP：每秒读取一次设备节点下电池温度、系统芯片温度等信息。&lt;/li&gt;
&lt;li&gt;RAM：每秒读取一次应用进程的实际物理内存，衡量应用的内存占比情况。&lt;/li&gt;
&lt;li&gt;snapshot：每秒截取一张应用界面截图。&lt;/li&gt;
&lt;/ul&gt;
&lt;pre&gt;&lt;code&gt;$ hdc shell

# SP_daemon

// 查看daemon进程是否存在
# ps -ef | grep SP_daemon
shell        11005     1 37469777 10:27:28 ? 213502-23:34:22 SP_daemon
shell        11033  9753 141940753 10:27:37 136:0 213502-23:34:22 grep SP_daemon
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;执行查看帮助命令&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;# SP_daemon --help
OpenHarmony performance testing tool SmartPerf command-line version
Usage: SP_daemon [options] [arguments]

options:
 -N             set the collection times(default value is 0) range[1,2147483647], for example: -N 10
 -PKG           set package name, must add, for example: -PKG ohos.samples.ecg
 -c             get device CPU frequency and CPU usage, process CPU usage and CPU load ..
 -g             get device GPU frequency and GPU load
 -f             get app refresh fps(frames per second) and fps jitters and refreshrate
 -profilerfps   get refresh fps and timestamp
 -sections      set collection time period(using with profilerfps)
 -t             get remaining battery power and temperature..
 -p             get battery power consumption and voltage
 -r             get process memory and total memory
 -snapshot      get screen capture
 -net           get uplink and downlink traffic
 -start         collection start command
 -stop          collection stop command
 -VIEW          set layler, for example: -VIEW DisplayNode
 -screen        get screen resolution
 -OUT           set csv output path.
 -d             get device DDR information
 -m             get other memory
example:
SP_daemon -N 20 -c -g -t -p -r -m -net -snapshot -d
SP_daemon -N 20 -PKG ohos.samples.ecg -c -g -t -p -f -r -m -net -snapshot -d
SP_daemon -start -c
SP_daemon -stop
SP_daemon -screen
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;基于&lt;code&gt;hdc&lt;/code&gt;命令行的&lt;code&gt;SmartPerf&lt;/code&gt;性能工具使用详细文档参考这个：https://gitee.com/openharmony/docs/blob/master/zh-cn/application-dev/application-test/smartperf-guidelines.md&lt;/p&gt;
&lt;h2&gt;参考链接&lt;/h2&gt;
&lt;ul&gt;
&lt;li&gt;&lt;a href=&quot;https://gitee.com/openharmony/developtools%5C_hdc&quot;&gt;https://gitee.com/openharmony/developtools\_hdc&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;https://developer.huawei.com/consumer/cn/doc/harmonyos-guides-V2/ide-command-line-hdc-0000001237908229-V2#section116322265308&quot;&gt;https://developer.huawei.com/consumer/cn/doc/harmonyos-guides-V2/ide-command-line-hdc-0000001237908229-V2#section116322265308&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;https://gitee.com/openharmony/docs/blob/master/zh-cn/application-dev/application-test/smartperf-guidelines.md&quot;&gt;https://gitee.com/openharmony/docs/blob/master/zh-cn/application-dev/application-test/smartperf-guidelines.md&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;https://gitee.com/openharmony/docs/blob/master/zh-cn/application-dev/tools/aa-tool.md&quot;&gt;https://gitee.com/openharmony/docs/blob/master/zh-cn/application-dev/tools/aa-tool.md&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;https://gitee.com/openharmony/docs/blob/master/zh-cn/application-dev/tools/bm-tool.md&quot;&gt;https://gitee.com/openharmony/docs/blob/master/zh-cn/application-dev/tools/bm-tool.md&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;https://gitee.com/openharmony/docs/blob/master/zh-cn/application-dev/tools/param-tool.md&quot;&gt;https://gitee.com/openharmony/docs/blob/master/zh-cn/application-dev/tools/param-tool.md&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;https://github.com/mzlogin/awesome-adb&quot;&gt;https://github.com/mzlogin/awesome-adb&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;
</content:encoded></item><item><title>Python数据分析三剑客：NumPy、Pandas、Matplotlib详解</title><link>https://meteor-comet.github.io/posts/numpy-pandas-matplotlib-learn/</link><guid isPermaLink="true">https://meteor-comet.github.io/posts/numpy-pandas-matplotlib-learn/</guid><description>数据科学 / 数据分析 / 机器学习 / Python编程</description><pubDate>Thu, 01 Sep 2022 00:00:00 GMT</pubDate><content:encoded>&lt;h2&gt;学习目标&lt;/h2&gt;
&lt;ul&gt;
&lt;li&gt;掌握NumPy数组操作和数学计算&lt;/li&gt;
&lt;li&gt;熟练使用Pandas进行数据处理和分析&lt;/li&gt;
&lt;li&gt;学会使用Matplotlib创建各种数据可视化&lt;/li&gt;
&lt;li&gt;理解三个库的协同工作方式&lt;/li&gt;
&lt;li&gt;掌握数据科学工作流程&lt;/li&gt;
&lt;li&gt;学会处理真实世界的数据分析问题&lt;/li&gt;
&lt;/ul&gt;
&lt;h2&gt;学习计划&lt;/h2&gt;
&lt;ol&gt;
&lt;li&gt;NumPy基础：数组操作和数学计算&lt;/li&gt;
&lt;li&gt;Pandas核心：数据结构和数据处理&lt;/li&gt;
&lt;li&gt;Matplotlib可视化：图表创建和样式设置&lt;/li&gt;
&lt;li&gt;三库协同：完整的数据分析工作流&lt;/li&gt;
&lt;li&gt;实战案例：真实数据分析项目&lt;/li&gt;
&lt;li&gt;性能优化和最佳实践&lt;/li&gt;
&lt;/ol&gt;
&lt;hr /&gt;
&lt;h2&gt;1. NumPy基础&lt;/h2&gt;
&lt;h3&gt;1.1 NumPy概述&lt;/h3&gt;
&lt;p&gt;NumPy是Python科学计算的基础库，提供了高性能的多维数组对象和数学函数。&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;NumPy的核心特性：&lt;/strong&gt;&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;多维数组（ndarray）&lt;/li&gt;
&lt;li&gt;广播机制&lt;/li&gt;
&lt;li&gt;线性代数运算&lt;/li&gt;
&lt;li&gt;随机数生成&lt;/li&gt;
&lt;li&gt;傅里叶变换&lt;/li&gt;
&lt;/ul&gt;
&lt;h3&gt;1.2 创建数组&lt;/h3&gt;
&lt;pre&gt;&lt;code&gt;import numpy as np

# 创建数组的不同方法
# 从列表创建
arr1 = np.array([1, 2, 3, 4, 5])
print(&quot;一维数组:&quot;, arr1)

# 创建二维数组
arr2 = np.array([[1, 2, 3], [4, 5, 6]])
print(&quot;二维数组:\n&quot;, arr2)

# 使用zeros创建
zeros_arr = np.zeros((3, 4))
print(&quot;零数组:\n&quot;, zeros_arr)

# 使用ones创建
ones_arr = np.ones((2, 3))
print(&quot;一数组:\n&quot;, ones_arr)

# 使用arange创建
range_arr = np.arange(0, 10, 2)
print(&quot;范围数组:&quot;, range_arr)

# 使用linspace创建
linspace_arr = np.linspace(0, 1, 5)
print(&quot;线性空间数组:&quot;, linspace_arr)

# 随机数组
random_arr = np.random.rand(3, 3)
print(&quot;随机数组:\n&quot;, random_arr)
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;&lt;strong&gt;预期输出：&lt;/strong&gt;&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;一维数组: [1 2 3 4 5]
二维数组:
 [[1 2 3]
  [4 5 6]]
零数组:
 [[0. 0. 0. 0.]
  [0. 0. 0. 0.]
  [0. 0. 0. 0.]]
一数组:
 [[1. 1. 1.]
  [1. 1. 1.]]
范围数组: [0 2 4 6 8]
线性空间数组: [0.   0.25 0.5  0.75 1.  ]
随机数组:
 [[... 3x3 随机小数 ...]]
&lt;/code&gt;&lt;/pre&gt;
&lt;h3&gt;1.3 数组操作&lt;/h3&gt;
&lt;pre&gt;&lt;code&gt;# 数组形状和维度
arr = np.array([[1, 2, 3], [4, 5, 6]])
print(&quot;数组形状:&quot;, arr.shape)
print(&quot;数组维度:&quot;, arr.ndim)
print(&quot;数组大小:&quot;, arr.size)
print(&quot;数据类型:&quot;, arr.dtype)

# 重塑数组
reshaped = arr.reshape(3, 2)
print(&quot;重塑后:\n&quot;, reshaped)

# 数组索引和切片
print(&quot;第一行:&quot;, arr[0])
print(&quot;第一列:&quot;, arr[:, 0])
print(&quot;子数组:&quot;, arr[0:1, 1:3])

# 布尔索引
bool_mask = arr &amp;gt; 3
print(&quot;布尔掩码:\n&quot;, bool_mask)
print(&quot;条件选择:&quot;, arr[bool_mask])
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;&lt;strong&gt;预期输出：&lt;/strong&gt;&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;数组形状: (2, 3)
数组维度: 2
数组大小: 6
数据类型: int64
重塑后:
 [[1 2]
  [3 4]
  [5 6]]
第一行: [1 2 3]
第一列: [1 4]
子数组: [[2 3]]
布尔掩码:
 [[False False False]
  [ True  True  True]]
条件选择: [4 5 6]
&lt;/code&gt;&lt;/pre&gt;
&lt;h3&gt;1.4 数学运算&lt;/h3&gt;
&lt;pre&gt;&lt;code&gt;# 基本数学运算
a = np.array([1, 2, 3, 4])
b = np.array([5, 6, 7, 8])

print(&quot;加法:&quot;, a + b)
print(&quot;乘法:&quot;, a * b)
print(&quot;平方:&quot;, a ** 2)
print(&quot;平方根:&quot;, np.sqrt(a))

# 统计函数
data = np.array([1, 2, 3, 4, 5, 6, 7, 8, 9, 10])
print(&quot;平均值:&quot;, np.mean(data))
print(&quot;中位数:&quot;, np.median(data))
print(&quot;标准差:&quot;, np.std(data))
print(&quot;方差:&quot;, np.var(data))
print(&quot;最大值:&quot;, np.max(data))
print(&quot;最小值:&quot;, np.min(data))

# 线性代数运算
matrix = np.array([[1, 2], [3, 4]])
print(&quot;矩阵:\n&quot;, matrix)
print(&quot;行列式:&quot;, np.linalg.det(matrix))
print(&quot;逆矩阵:\n&quot;, np.linalg.inv(matrix))
print(&quot;特征值:&quot;, np.linalg.eigvals(matrix))
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;&lt;strong&gt;预期输出：&lt;/strong&gt;&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;加法: [ 6  8 10 12]
乘法: [ 5 12 21 32]
平方: [ 1  4  9 16]
平方根: [1.         1.41421356 1.73205081 2.        ]
平均值: 5.5
中位数: 5.5
标准差: 2.8722813232690143
方差: 8.25
最大值: 10
最小值: 1
矩阵:
 [[1 2]
  [3 4]]
行列式: -2.0000000000000004
逆矩阵:
 [[-2.   1. ]
  [ 1.5 -0.5]]
特征值: [-0.37228132  5.37228132]
&lt;/code&gt;&lt;/pre&gt;
&lt;h3&gt;1.5 广播机制&lt;/h3&gt;
&lt;pre&gt;&lt;code&gt;# 广播示例
arr = np.array([[1, 2, 3], [4, 5, 6]])
scalar = 2

# 标量与数组运算
print(&quot;数组 + 标量:\n&quot;, arr + scalar)
print(&quot;数组 * 标量:\n&quot;, arr * scalar)

# 不同形状数组的广播
arr1 = np.array([[1, 2, 3], [4, 5, 6]])
arr2 = np.array([10, 20, 30])

print(&quot;广播加法:\n&quot;, arr1 + arr2)
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;&lt;strong&gt;预期输出：&lt;/strong&gt;&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;数组 + 标量:
 [[ 3  4  5]
  [ 6  7  8]]
数组 * 标量:
 [[ 2  4  6]
  [ 8 10 12]]
广播加法:
 [[11 22 33]
  [14 25 36]]
&lt;/code&gt;&lt;/pre&gt;
&lt;hr /&gt;
&lt;h2&gt;2. Pandas核心&lt;/h2&gt;
&lt;h3&gt;2.1 Pandas概述&lt;/h3&gt;
&lt;p&gt;Pandas是Python数据分析的核心库，提供了高性能、易用的数据结构和数据分析工具。&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;Pandas的主要数据结构：&lt;/strong&gt;&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;Series：一维带标签数组&lt;/li&gt;
&lt;li&gt;DataFrame：二维表格数据结构&lt;/li&gt;
&lt;li&gt;Panel：三维数据结构（已弃用）&lt;/li&gt;
&lt;/ul&gt;
&lt;h3&gt;2.2 Series操作&lt;/h3&gt;
&lt;pre&gt;&lt;code&gt;import pandas as pd

# 创建Series
s1 = pd.Series([1, 3, 5, 7, 9])
print(&quot;Series:&quot;, s1)

# 带索引的Series
s2 = pd.Series([1, 2, 3, 4], index=[&apos;a&apos;, &apos;b&apos;, &apos;c&apos;, &apos;d&apos;])
print(&quot;带索引Series:\n&quot;, s2)

# 从字典创建Series
dict_data = {&apos;a&apos;: 1, &apos;b&apos;: 2, &apos;c&apos;: 3}
s3 = pd.Series(dict_data)
print(&quot;从字典创建:\n&quot;, s3)

# Series操作
print(&quot;索引:&quot;, s2.index)
print(&quot;值:&quot;, s2.values)
print(&quot;数据类型:&quot;, s2.dtype)
print(&quot;大小:&quot;, s2.size)

# 数学运算
print(&quot;求和:&quot;, s2.sum())
print(&quot;平均值:&quot;, s2.mean())
print(&quot;标准差:&quot;, s2.std())
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;&lt;strong&gt;预期输出：&lt;/strong&gt;&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;Series: 0    1
1    3
2    5
3    7
4    9
dtype: int64
带索引Series:
 a    1
b    2
c    3
d    4
dtype: int64
从字典创建:
 a    1
b    2
c    3
dtype: int64
索引: Index([&apos;a&apos;, &apos;b&apos;, &apos;c&apos;, &apos;d&apos;], dtype=&apos;object&apos;)
值: [1 2 3 4]
数据类型: int64
大小: 4
求和: 10
平均值: 2.5
标准差: 1.2909944487358056
&lt;/code&gt;&lt;/pre&gt;
&lt;h3&gt;2.3 DataFrame操作&lt;/h3&gt;
&lt;pre&gt;&lt;code&gt;# 创建DataFrame
data = {
    &apos;name&apos;: [&apos;Alice&apos;, &apos;Bob&apos;, &apos;Charlie&apos;, &apos;David&apos;],
    &apos;age&apos;: [25, 30, 35, 40],
    &apos;city&apos;: [&apos;New York&apos;, &apos;London&apos;, &apos;Paris&apos;, &apos;Tokyo&apos;],
    &apos;salary&apos;: [50000, 60000, 70000, 80000]
}
df = pd.DataFrame(data)
print(&quot;DataFrame:\n&quot;, df)

# 从CSV文件读取
# df = pd.read_csv(&apos;data.csv&apos;)

# 基本属性
print(&quot;形状:&quot;, df.shape)
print(&quot;列名:&quot;, df.columns)
print(&quot;索引:&quot;, df.index)
print(&quot;数据类型:\n&quot;, df.dtypes)
print(&quot;信息:\n&quot;, df.info())

# 查看数据
print(&quot;前3行:\n&quot;, df.head(3))
print(&quot;后2行:\n&quot;, df.tail(2))
print(&quot;描述性统计:\n&quot;, df.describe())
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;&lt;strong&gt;预期输出：&lt;/strong&gt;&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;DataFrame:
      name  age      city  salary
0    Alice   25  New York   50000
1      Bob   30    London   60000
2  Charlie   35     Paris   70000
3    David   40     Tokyo   80000
形状: (4, 4)
列名: Index([&apos;name&apos;, &apos;age&apos;, &apos;city&apos;, &apos;salary&apos;], dtype=&apos;object&apos;)
索引: RangeIndex(start=0, stop=4, step=1)
数据类型:
 name      object
 age        int64
 city     object
 salary    int64
dtype: object
信息:
 None  ← 在网页中 info() 返回 None，表结构打印在控制台
前3行:
      name  age      city  salary
0    Alice   25  New York   50000
1      Bob   30    London   60000
2  Charlie   35     Paris   70000
后2行:
    name  age   city  salary
2  Charlie   35  Paris   70000
3    David   40  Tokyo   80000
描述性统计:
              age        salary
count   4.000000      4.000000
mean   32.500000  65000.000000
std     6.454972  12909.944487
min    25.000000  50000.000000
25%    28.750000  57500.000000
50%    32.500000  65000.000000
75%    36.250000  72500.000000
max    40.000000  80000.000000
&lt;/code&gt;&lt;/pre&gt;
&lt;h3&gt;2.4 数据选择和索引&lt;/h3&gt;
&lt;pre&gt;&lt;code&gt;# 列选择
print(&quot;选择单列:&quot;, df[&apos;name&apos;])
print(&quot;选择多列:\n&quot;, df[[&apos;name&apos;, &apos;age&apos;]])

# 行选择
print(&quot;第一行:&quot;, df.iloc[0])
print(&quot;前两行:\n&quot;, df.iloc[0:2])

# 条件选择
young_people = df[df[&apos;age&apos;] &amp;lt; 35]
print(&quot;年轻人:\n&quot;, young_people)

high_salary = df[df[&apos;salary&apos;] &amp;gt; 60000]
print(&quot;高薪人员:\n&quot;, high_salary)

# 复合条件
filtered = df[(df[&apos;age&apos;] &amp;gt; 30) &amp;amp; (df[&apos;salary&apos;] &amp;gt; 60000)]
print(&quot;复合条件筛选:\n&quot;, filtered)
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;&lt;strong&gt;预期输出：&lt;/strong&gt;&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;选择单列: 0      Alice
1        Bob
2    Charlie
3      David
Name: name, dtype: object
选择多列:
      name  age
0    Alice   25
1      Bob   30
2  Charlie   35
3    David   40
第一行: name      Alice
age           25
city     New York
salary      50000
Name: 0, dtype: object
前两行:
      name  age      city  salary
0    Alice   25  New York   50000
1      Bob   30    London   60000
年轻人:
      name  age      city  salary
0    Alice   25  New York   50000
1      Bob   30    London   60000
高薪人员:
      name  age    city  salary
2  Charlie   35   Paris   70000
3    David   40   Tokyo   80000
复合条件筛选:
      name  age   city  salary
2  Charlie   35  Paris   70000
3    David   40  Tokyo   80000
&lt;/code&gt;&lt;/pre&gt;
&lt;h3&gt;2.5 数据处理&lt;/h3&gt;
&lt;pre&gt;&lt;code&gt;# 缺失值处理
df_with_na = pd.DataFrame({
    &apos;A&apos;: [1, 2, np.nan, 4],
    &apos;B&apos;: [5, np.nan, np.nan, 8],
    &apos;C&apos;: [9, 10, 11, np.nan]
})
print(&quot;原始数据:\n&quot;, df_with_na)

# 检查缺失值
print(&quot;缺失值统计:\n&quot;, df_with_na.isnull().sum())

# 删除缺失值
df_cleaned = df_with_na.dropna()
print(&quot;删除缺失值后:\n&quot;, df_cleaned)

# 填充缺失值
df_filled = df_with_na.fillna(0)
print(&quot;填充0后:\n&quot;, df_filled)

# 数据排序
df_sorted = df.sort_values(&apos;age&apos;, ascending=False)
print(&quot;按年龄排序:\n&quot;, df_sorted)

# 数据分组
grouped = df.groupby(&apos;city&apos;)[&apos;salary&apos;].mean()
print(&quot;按城市分组平均薪资:\n&quot;, grouped)
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;&lt;strong&gt;预期输出：&lt;/strong&gt;&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;原始数据:
      A    B     C
0  1.0  5.0   9.0
1  2.0  NaN  10.0
2  NaN  NaN  11.0
3  4.0  8.0   NaN
缺失值统计:
A    1
B    2
C    1
dtype: int64
删除缺失值后:
     A    B    C
0  1.0  5.0  9.0
填充0后:
     A    B     C
0  1.0  5.0   9.0
1  2.0  0.0  10.0
2  0.0  0.0  11.0
3  4.0  8.0   0.0
按年龄排序:
      name  age      city  salary
3    David   40     Tokyo   80000
2  Charlie   35     Paris   70000
1      Bob   30    London   60000
0    Alice   25  New York   50000
按城市分组平均薪资:
 city
London      60000.0
New York    50000.0
Paris       70000.0
Tokyo       80000.0
Name: salary, dtype: float64
&lt;/code&gt;&lt;/pre&gt;
&lt;h3&gt;2.6 数据合并和连接&lt;/h3&gt;
&lt;pre&gt;&lt;code&gt;# 创建两个DataFrame
df1 = pd.DataFrame({
    &apos;id&apos;: [1, 2, 3, 4],
    &apos;name&apos;: [&apos;Alice&apos;, &apos;Bob&apos;, &apos;Charlie&apos;, &apos;David&apos;]
})

df2 = pd.DataFrame({
    &apos;id&apos;: [1, 2, 3, 5],
    &apos;salary&apos;: [50000, 60000, 70000, 90000]
})

# 内连接
merged_inner = pd.merge(df1, df2, on=&apos;id&apos;, how=&apos;inner&apos;)
print(&quot;内连接:\n&quot;, merged_inner)

# 左连接
merged_left = pd.merge(df1, df2, on=&apos;id&apos;, how=&apos;left&apos;)
print(&quot;左连接:\n&quot;, merged_left)

# 外连接
merged_outer = pd.merge(df1, df2, on=&apos;id&apos;, how=&apos;outer&apos;)
print(&quot;外连接:\n&quot;, merged_outer)

# 连接操作
concatenated = pd.concat([df1, df2], axis=1)
print(&quot;连接:\n&quot;, concatenated)
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;&lt;strong&gt;预期输出：&lt;/strong&gt;&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;内连接:
    id     name  salary
0   1    Alice   50000
1   2      Bob   60000
2   3  Charlie   70000
左连接:
    id     name   salary
0   1    Alice  50000.0
1   2      Bob  60000.0
2   3  Charlie  70000.0
3   4    David      NaN
外连接:
    id     name   salary
0   1    Alice  50000.0
1   2      Bob  60000.0
2   3  Charlie  70000.0
3   4    David      NaN
4   5      NaN  90000.0
连接:
    id     name   id   salary
0   1    Alice  1.0  50000.0
1   2      Bob  2.0  60000.0
2   3  Charlie  3.0  70000.0
3   4    David  5.0  90000.0
&lt;/code&gt;&lt;/pre&gt;
&lt;hr /&gt;
&lt;h2&gt;3. Matplotlib可视化&lt;/h2&gt;
&lt;h3&gt;3.1 Matplotlib概述&lt;/h3&gt;
&lt;p&gt;Matplotlib是Python最流行的绘图库，可以创建各种静态、动态和交互式图表。&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;Matplotlib的主要特性：&lt;/strong&gt;&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;支持多种图表类型&lt;/li&gt;
&lt;li&gt;高度可定制&lt;/li&gt;
&lt;li&gt;支持多种输出格式&lt;/li&gt;
&lt;li&gt;与NumPy和Pandas完美集成&lt;/li&gt;
&lt;/ul&gt;
&lt;h3&gt;3.2 基础绘图&lt;/h3&gt;
&lt;pre&gt;&lt;code&gt;import matplotlib.pyplot as plt
import numpy as np

# 设置中文字体
plt.rcParams[&apos;font.sans-serif&apos;] = [&apos;SimHei&apos;]
plt.rcParams[&apos;axes.unicode_minus&apos;] = False

# 创建数据
x = np.linspace(0, 10, 100)
y = np.sin(x)

# 基础线图
plt.figure(figsize=(10, 6))
plt.plot(x, y, &apos;b-&apos;, linewidth=2, label=&apos;sin(x)&apos;)
plt.title(&apos;正弦函数&apos;)
plt.xlabel(&apos;x&apos;)
plt.ylabel(&apos;y&apos;)
plt.legend()
plt.grid(True)
plt.show()

# 散点图
x_scatter = np.random.randn(100)
y_scatter = np.random.randn(100)

plt.figure(figsize=(8, 6))
plt.scatter(x_scatter, y_scatter, alpha=0.6)
plt.title(&apos;散点图&apos;)
plt.xlabel(&apos;x&apos;)
plt.ylabel(&apos;y&apos;)
plt.show()
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;【预期输出（图形效果说明）】&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;图1：线图“正弦函数”，蓝色实线，带网格与图例。
图2：散点图，100 个点，透明度 0.6。
&lt;/code&gt;&lt;/pre&gt;
&lt;h3&gt;3.3 多子图&lt;/h3&gt;
&lt;pre&gt;&lt;code&gt;# 创建子图
fig, axes = plt.subplots(2, 2, figsize=(12, 8))

# 第一个子图：线图
x = np.linspace(0, 2*np.pi, 100)
axes[0, 0].plot(x, np.sin(x))
axes[0, 0].set_title(&apos;正弦函数&apos;)
axes[0, 0].grid(True)

# 第二个子图：余弦函数
axes[0, 1].plot(x, np.cos(x), &apos;r-&apos;)
axes[0, 1].set_title(&apos;余弦函数&apos;)
axes[0, 1].grid(True)

# 第三个子图：散点图
x_scatter = np.random.randn(50)
y_scatter = np.random.randn(50)
axes[1, 0].scatter(x_scatter, y_scatter)
axes[1, 0].set_title(&apos;随机散点图&apos;)

# 第四个子图：柱状图
categories = [&apos;A&apos;, &apos;B&apos;, &apos;C&apos;, &apos;D&apos;]
values = [4, 3, 2, 1]
axes[1, 1].bar(categories, values)
axes[1, 1].set_title(&apos;柱状图&apos;)

plt.tight_layout()
plt.show()
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;【预期输出（图形效果说明）】&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;2x2 子图：左上 sin 曲线，右上 cos 曲线，左下散点，右下柱状图。
&lt;/code&gt;&lt;/pre&gt;
&lt;h3&gt;3.4 统计图表&lt;/h3&gt;
&lt;pre&gt;&lt;code&gt;# 柱状图
categories = [&apos;苹果&apos;, &apos;香蕉&apos;, &apos;橙子&apos;, &apos;葡萄&apos;]
sales = [23, 45, 56, 78]

plt.figure(figsize=(8, 6))
bars = plt.bar(categories, sales, color=[&apos;red&apos;, &apos;yellow&apos;, &apos;orange&apos;, &apos;purple&apos;])
plt.title(&apos;水果销售统计&apos;)
plt.xlabel(&apos;水果种类&apos;)
plt.ylabel(&apos;销售量&apos;)

# 添加数值标签
for bar, value in zip(bars, sales):
    plt.text(bar.get_x() + bar.get_width()/2, bar.get_height() + 1, 
             str(value), ha=&apos;center&apos;, va=&apos;bottom&apos;)

plt.show()

# 饼图
sizes = [30, 25, 20, 15, 10]
labels = [&apos;苹果&apos;, &apos;香蕉&apos;, &apos;橙子&apos;, &apos;葡萄&apos;, &apos;其他&apos;]
colors = [&apos;red&apos;, &apos;yellow&apos;, &apos;orange&apos;, &apos;purple&apos;, &apos;gray&apos;]

plt.figure(figsize=(8, 8))
plt.pie(sizes, labels=labels, colors=colors, autopct=&apos;%1.1f%%&apos;, startangle=90)
plt.title(&apos;水果销售比例&apos;)
plt.axis(&apos;equal&apos;)
plt.show()

# 直方图
data = np.random.normal(0, 1, 1000)

plt.figure(figsize=(8, 6))
plt.hist(data, bins=30, alpha=0.7, color=&apos;skyblue&apos;, edgecolor=&apos;black&apos;)
plt.title(&apos;正态分布直方图&apos;)
plt.xlabel(&apos;值&apos;)
plt.ylabel(&apos;频次&apos;)
plt.grid(True, alpha=0.3)
plt.show()
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;【预期输出（图形效果说明）】&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;柱状图带数值标签；饼图为圆形含百分比；直方图 30 个箱，网格可见。
&lt;/code&gt;&lt;/pre&gt;
&lt;h3&gt;3.5 高级可视化&lt;/h3&gt;
&lt;pre&gt;&lt;code&gt;# 热力图
import seaborn as sns

# 创建相关性矩阵
np.random.seed(0)
data = np.random.randn(100, 4)
df_corr = pd.DataFrame(data, columns=[&apos;A&apos;, &apos;B&apos;, &apos;C&apos;, &apos;D&apos;])
correlation_matrix = df_corr.corr()

plt.figure(figsize=(8, 6))
sns.heatmap(correlation_matrix, annot=True, cmap=&apos;coolwarm&apos;, center=0)
plt.title(&apos;相关性热力图&apos;)
plt.show()

# 箱线图
data_box = [np.random.normal(0, std, 100) for std in range(1, 4)]
labels = [&apos;组1&apos;, &apos;组2&apos;, &apos;组3&apos;]

plt.figure(figsize=(8, 6))
plt.boxplot(data_box, labels=labels)
plt.title(&apos;箱线图&apos;)
plt.ylabel(&apos;值&apos;)
plt.show()

# 3D图
from mpl_toolkits.mplot3d import Axes3D

fig = plt.figure(figsize=(10, 8))
ax = fig.add_subplot(111, projection=&apos;3d&apos;)

x = np.linspace(-5, 5, 100)
y = np.linspace(-5, 5, 100)
X, Y = np.meshgrid(x, y)
Z = np.sin(np.sqrt(X**2 + Y**2))

surf = ax.plot_surface(X, Y, Z, cmap=&apos;viridis&apos;)
ax.set_title(&apos;3D表面图&apos;)
ax.set_xlabel(&apos;X&apos;)
ax.set_ylabel(&apos;Y&apos;)
ax.set_zlabel(&apos;Z&apos;)

fig.colorbar(surf)
plt.show()
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;【预期输出（图形效果说明）】&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;热力图显示 4x4 相关系数矩阵；箱线图 3 组；3D 表面图带颜色映射与colorbar。
&lt;/code&gt;&lt;/pre&gt;
&lt;hr /&gt;
&lt;h2&gt;4. 三库协同工作&lt;/h2&gt;
&lt;h3&gt;4.1 数据科学工作流&lt;/h3&gt;
&lt;pre&gt;&lt;code&gt;# 完整的数据分析示例
import numpy as np
import pandas as pd
import matplotlib.pyplot as plt
import seaborn as sns

# 1. 数据生成/加载
np.random.seed(42)
n_samples = 1000

# 生成模拟数据
data = {
    &apos;age&apos;: np.random.normal(35, 10, n_samples),
    &apos;income&apos;: np.random.normal(50000, 15000, n_samples),
    &apos;education_years&apos;: np.random.normal(16, 3, n_samples),
    &apos;satisfaction&apos;: np.random.uniform(1, 10, n_samples)
}

df = pd.DataFrame(data)

# 2. 数据探索
print(&quot;数据基本信息:&quot;)
print(df.info())
print(&quot;\n描述性统计:&quot;)
print(df.describe())

# 3. 数据清洗
# 处理异常值
df_clean = df[(df[&apos;age&apos;] &amp;gt; 0) &amp;amp; (df[&apos;age&apos;] &amp;lt; 100)]
df_clean = df_clean[(df_clean[&apos;income&apos;] &amp;gt; 0) &amp;amp; (df_clean[&apos;income&apos;] &amp;lt; 200000)]

print(f&quot;清洗后数据量: {len(df_clean)}&quot;)

# 4. 数据分析
# 相关性分析
correlation = df_clean.corr()
print(&quot;\n相关性矩阵:&quot;)
print(correlation)

# 5. 数据可视化
fig, axes = plt.subplots(2, 2, figsize=(15, 12))

# 年龄分布
axes[0, 0].hist(df_clean[&apos;age&apos;], bins=30, alpha=0.7, color=&apos;skyblue&apos;)
axes[0, 0].set_title(&apos;年龄分布&apos;)
axes[0, 0].set_xlabel(&apos;年龄&apos;)
axes[0, 0].set_ylabel(&apos;频次&apos;)

# 收入分布
axes[0, 1].hist(df_clean[&apos;income&apos;], bins=30, alpha=0.7, color=&apos;lightgreen&apos;)
axes[0, 1].set_title(&apos;收入分布&apos;)
axes[0, 1].set_xlabel(&apos;收入&apos;)
axes[0, 1].set_ylabel(&apos;频次&apos;)

# 年龄vs收入散点图
axes[1, 0].scatter(df_clean[&apos;age&apos;], df_clean[&apos;income&apos;], alpha=0.6)
axes[1, 0].set_title(&apos;年龄 vs 收入&apos;)
axes[1, 0].set_xlabel(&apos;年龄&apos;)
axes[1, 0].set_ylabel(&apos;收入&apos;)

# 相关性热力图
sns.heatmap(correlation, annot=True, cmap=&apos;coolwarm&apos;, ax=axes[1, 1])
axes[1, 1].set_title(&apos;相关性热力图&apos;)

plt.tight_layout()
plt.show()

# 6. 统计分析
print(&quot;\n统计分析结果:&quot;)
print(f&quot;平均年龄: {df_clean[&apos;age&apos;].mean():.2f}&quot;)
print(f&quot;平均收入: {df_clean[&apos;income&apos;].mean():.2f}&quot;)
print(f&quot;年龄与收入相关系数: {df_clean[&apos;age&apos;].corr(df_clean[&apos;income&apos;]):.3f}&quot;)
&lt;/code&gt;&lt;/pre&gt;
&lt;h3&gt;4.2 时间序列分析&lt;/h3&gt;
&lt;pre&gt;&lt;code&gt;# 时间序列数据处理
import pandas as pd
import numpy as np
import matplotlib.pyplot as plt

# 创建时间序列数据
dates = pd.date_range(&apos;2023-01-01&apos;, periods=365, freq=&apos;D&apos;)
np.random.seed(42)

# 生成模拟销售数据
sales_data = {
    &apos;date&apos;: dates,
    &apos;sales&apos;: np.random.normal(1000, 200, 365) + 50 * np.sin(np.arange(365) * 2 * np.pi / 365),
    &apos;temperature&apos;: np.random.normal(20, 10, 365) + 15 * np.sin(np.arange(365) * 2 * np.pi / 365)
}

df_ts = pd.DataFrame(sales_data)
df_ts.set_index(&apos;date&apos;, inplace=True)

# 时间序列分析
plt.figure(figsize=(15, 10))

# 销售趋势
plt.subplot(3, 1, 1)
plt.plot(df_ts.index, df_ts[&apos;sales&apos;])
plt.title(&apos;销售趋势&apos;)
plt.ylabel(&apos;销售额&apos;)

# 温度趋势
plt.subplot(3, 1, 2)
plt.plot(df_ts.index, df_ts[&apos;temperature&apos;])
plt.title(&apos;温度趋势&apos;)
plt.ylabel(&apos;温度 (°C)&apos;)

# 销售vs温度散点图
plt.subplot(3, 1, 3)
plt.scatter(df_ts[&apos;temperature&apos;], df_ts[&apos;sales&apos;], alpha=0.6)
plt.title(&apos;温度 vs 销售额&apos;)
plt.xlabel(&apos;温度 (°C)&apos;)
plt.ylabel(&apos;销售额&apos;)

plt.tight_layout()
plt.show()

# 月度统计
monthly_sales = df_ts[&apos;sales&apos;].resample(&apos;M&apos;).mean()
monthly_temp = df_ts[&apos;temperature&apos;].resample(&apos;M&apos;).mean()

print(&quot;月度平均销售额:&quot;)
print(monthly_sales)
print(&quot;\n月度平均温度:&quot;)
print(monthly_temp)
&lt;/code&gt;&lt;/pre&gt;
&lt;hr /&gt;
&lt;h2&gt;5. 实战案例&lt;/h2&gt;
&lt;h3&gt;5.1 股票数据分析&lt;/h3&gt;
&lt;pre&gt;&lt;code&gt;# 股票数据分析示例
import yfinance as yf
import pandas as pd
import numpy as np
import matplotlib.pyplot as plt

# 获取股票数据（这里使用模拟数据）
np.random.seed(42)
dates = pd.date_range(&apos;2023-01-01&apos;, periods=252, freq=&apos;B&apos;)
stock_data = {
    &apos;Date&apos;: dates,
    &apos;Close&apos;: 100 + np.cumsum(np.random.randn(252) * 0.02),
    &apos;Volume&apos;: np.random.randint(1000000, 5000000, 252)
}

df_stock = pd.DataFrame(stock_data)
df_stock.set_index(&apos;Date&apos;, inplace=True)

# 计算技术指标
df_stock[&apos;SMA_20&apos;] = df_stock[&apos;Close&apos;].rolling(window=20).mean()
df_stock[&apos;SMA_50&apos;] = df_stock[&apos;Close&apos;].rolling(window=50).mean()
df_stock[&apos;Returns&apos;] = df_stock[&apos;Close&apos;].pct_change()

# 可视化
fig, axes = plt.subplots(3, 1, figsize=(15, 12))

# 股价走势
axes[0].plot(df_stock.index, df_stock[&apos;Close&apos;], label=&apos;收盘价&apos;, linewidth=2)
axes[0].plot(df_stock.index, df_stock[&apos;SMA_20&apos;], label=&apos;20日均线&apos;, alpha=0.7)
axes[0].plot(df_stock.index, df_stock[&apos;SMA_50&apos;], label=&apos;50日均线&apos;, alpha=0.7)
axes[0].set_title(&apos;股票价格走势&apos;)
axes[0].set_ylabel(&apos;价格&apos;)
axes[0].legend()
axes[0].grid(True, alpha=0.3)

# 成交量
axes[1].bar(df_stock.index, df_stock[&apos;Volume&apos;], alpha=0.7, color=&apos;orange&apos;)
axes[1].set_title(&apos;成交量&apos;)
axes[1].set_ylabel(&apos;成交量&apos;)

# 收益率分布
axes[2].hist(df_stock[&apos;Returns&apos;].dropna(), bins=50, alpha=0.7, color=&apos;green&apos;)
axes[2].set_title(&apos;收益率分布&apos;)
axes[2].set_xlabel(&apos;收益率&apos;)
axes[2].set_ylabel(&apos;频次&apos;)

plt.tight_layout()
plt.show()

# 统计分析
print(&quot;股票数据分析结果:&quot;)
print(f&quot;平均收盘价: {df_stock[&apos;Close&apos;].mean():.2f}&quot;)
print(f&quot;最高价: {df_stock[&apos;Close&apos;].max():.2f}&quot;)
print(f&quot;最低价: {df_stock[&apos;Close&apos;].min():.2f}&quot;)
print(f&quot;年化收益率: {df_stock[&apos;Returns&apos;].mean() * 252 * 100:.2f}%&quot;)
print(f&quot;年化波动率: {df_stock[&apos;Returns&apos;].std() * np.sqrt(252) * 100:.2f}%&quot;)
&lt;/code&gt;&lt;/pre&gt;
&lt;h3&gt;5.2 机器学习数据预处理&lt;/h3&gt;
&lt;pre&gt;&lt;code&gt;# 机器学习数据预处理示例
from sklearn.datasets import load_iris
from sklearn.preprocessing import StandardScaler
from sklearn.decomposition import PCA
import seaborn as sns

# 加载鸢尾花数据集
iris = load_iris()
df_iris = pd.DataFrame(iris.data, columns=iris.feature_names)
df_iris[&apos;target&apos;] = iris.target

# 数据探索
print(&quot;鸢尾花数据集信息:&quot;)
print(df_iris.info())
print(&quot;\n描述性统计:&quot;)
print(df_iris.describe())

# 数据可视化
fig, axes = plt.subplots(2, 2, figsize=(12, 10))

# 特征分布
for i, feature in enumerate(iris.feature_names):
    row, col = i // 2, i % 2
    for target in range(3):
        data = df_iris[df_iris[&apos;target&apos;] == target][feature]
        axes[row, col].hist(data, alpha=0.7, label=f&apos;类别 {target}&apos;)
    axes[row, col].set_title(f&apos;{feature} 分布&apos;)
    axes[row, col].legend()

plt.tight_layout()
plt.show()

# 相关性分析
plt.figure(figsize=(8, 6))
correlation_matrix = df_iris.drop(&apos;target&apos;, axis=1).corr()
sns.heatmap(correlation_matrix, annot=True, cmap=&apos;coolwarm&apos;, center=0)
plt.title(&apos;特征相关性热力图&apos;)
plt.show()

# 数据标准化
scaler = StandardScaler()
X_scaled = scaler.fit_transform(df_iris.drop(&apos;target&apos;, axis=1))
df_scaled = pd.DataFrame(X_scaled, columns=iris.feature_names)

# PCA降维
pca = PCA(n_components=2)
X_pca = pca.fit_transform(X_scaled)

# 可视化PCA结果
plt.figure(figsize=(10, 8))
for target in range(3):
    mask = df_iris[&apos;target&apos;] == target
    plt.scatter(X_pca[mask, 0], X_pca[mask, 1], 
                label=f&apos;类别 {target}&apos;, alpha=0.7)

plt.xlabel(&apos;主成分1&apos;)
plt.ylabel(&apos;主成分2&apos;)
plt.title(&apos;PCA降维结果&apos;)
plt.legend()
plt.grid(True, alpha=0.3)
plt.show()

print(f&quot;PCA解释方差比例: {pca.explained_variance_ratio_}&quot;)
print(f&quot;累计解释方差: {pca.explained_variance_ratio_.sum():.3f}&quot;)
&lt;/code&gt;&lt;/pre&gt;
&lt;hr /&gt;
&lt;h2&gt;6. 性能优化和最佳实践&lt;/h2&gt;
&lt;h3&gt;6.1 NumPy优化&lt;/h3&gt;
&lt;pre&gt;&lt;code&gt;# NumPy性能优化示例
import time

# 比较Python列表和NumPy数组的性能
n = 1000000

# Python列表
start_time = time.time()
python_list = list(range(n))
python_result = [x**2 for x in python_list]
python_time = time.time() - start_time

# NumPy数组
start_time = time.time()
numpy_array = np.arange(n)
numpy_result = numpy_array**2
numpy_time = time.time() - start_time

print(f&quot;Python列表时间: {python_time:.4f}秒&quot;)
print(f&quot;NumPy数组时间: {numpy_time:.4f}秒&quot;)
print(f&quot;性能提升: {python_time/numpy_time:.1f}倍&quot;)

# 向量化操作
# 低效方式
def slow_function(x):
    result = []
    for i in range(len(x)):
        if x[i] &amp;gt; 0:
            result.append(np.sqrt(x[i]))
        else:
            result.append(0)
    return np.array(result)

# 高效方式
def fast_function(x):
    return np.where(x &amp;gt; 0, np.sqrt(x), 0)

# 性能比较
x = np.random.randn(100000)
start_time = time.time()
result1 = slow_function(x)
slow_time = time.time() - start_time

start_time = time.time()
result2 = fast_function(x)
fast_time = time.time() - start_time

print(f&quot;慢速函数时间: {slow_time:.4f}秒&quot;)
print(f&quot;快速函数时间: {fast_time:.4f}秒&quot;)
print(f&quot;性能提升: {slow_time/fast_time:.1f}倍&quot;)
&lt;/code&gt;&lt;/pre&gt;
&lt;h3&gt;6.2 Pandas优化&lt;/h3&gt;
&lt;pre&gt;&lt;code&gt;# Pandas性能优化示例

# 1. 使用适当的数据类型
# 低效
df_inefficient = pd.DataFrame({
    &apos;category&apos;: [&apos;A&apos;, &apos;B&apos;, &apos;A&apos;, &apos;B&apos;] * 1000,
    &apos;value&apos;: [1, 2, 3, 4] * 1000
})

# 高效
df_efficient = pd.DataFrame({
    &apos;category&apos;: pd.Categorical([&apos;A&apos;, &apos;B&apos;, &apos;A&apos;, &apos;B&apos;] * 1000),
    &apos;value&apos;: [1, 2, 3, 4] * 1000
})

print(&quot;内存使用比较:&quot;)
print(f&quot;低效方式: {df_inefficient.memory_usage(deep=True).sum()} bytes&quot;)
print(f&quot;高效方式: {df_efficient.memory_usage(deep=True).sum()} bytes&quot;)

# 2. 使用向量化操作
# 低效方式
def slow_apply(df):
    return df[&apos;value&apos;].apply(lambda x: x * 2)

# 高效方式
def fast_apply(df):
    return df[&apos;value&apos;] * 2

# 性能比较
large_df = pd.DataFrame({&apos;value&apos;: range(100000)})

start_time = time.time()
result1 = slow_apply(large_df)
slow_time = time.time() - start_time

start_time = time.time()
result2 = fast_apply(large_df)
fast_time = time.time() - start_time

print(f&quot;apply方法时间: {slow_time:.4f}秒&quot;)
print(f&quot;向量化操作时间: {fast_time:.4f}秒&quot;)
print(f&quot;性能提升: {slow_time/fast_time:.1f}倍&quot;)
&lt;/code&gt;&lt;/pre&gt;
&lt;h3&gt;6.3 可视化最佳实践&lt;/h3&gt;
&lt;pre&gt;&lt;code&gt;# 可视化最佳实践示例

# 设置全局样式
plt.style.use(&apos;seaborn-v0_8&apos;)
plt.rcParams[&apos;figure.figsize&apos;] = (12, 8)
plt.rcParams[&apos;font.size&apos;] = 12

# 创建示例数据
np.random.seed(42)
categories = [&apos;A&apos;, &apos;B&apos;, &apos;C&apos;, &apos;D&apos;, &apos;E&apos;]
values1 = np.random.randint(10, 100, 5)
values2 = np.random.randint(10, 100, 5)

# 创建子图
fig, axes = plt.subplots(2, 2, figsize=(15, 12))

# 1. 清晰的柱状图
bars1 = axes[0, 0].bar(categories, values1, color=&apos;skyblue&apos;, alpha=0.8)
axes[0, 0].set_title(&apos;清晰的柱状图&apos;, fontsize=14, fontweight=&apos;bold&apos;)
axes[0, 0].set_xlabel(&apos;类别&apos;)
axes[0, 0].set_ylabel(&apos;数值&apos;)

# 添加数值标签
for bar, value in zip(bars1, values1):
    axes[0, 0].text(bar.get_x() + bar.get_width()/2, bar.get_height() + 1,
                    str(value), ha=&apos;center&apos;, va=&apos;bottom&apos;, fontweight=&apos;bold&apos;)

# 2. 对比柱状图
x = np.arange(len(categories))
width = 0.35

bars2 = axes[0, 1].bar(x - width/2, values1, width, label=&apos;组1&apos;, alpha=0.8)
bars3 = axes[0, 1].bar(x + width/2, values2, width, label=&apos;组2&apos;, alpha=0.8)

axes[0, 1].set_title(&apos;对比柱状图&apos;, fontsize=14, fontweight=&apos;bold&apos;)
axes[0, 1].set_xlabel(&apos;类别&apos;)
axes[0, 1].set_ylabel(&apos;数值&apos;)
axes[0, 1].set_xticks(x)
axes[0, 1].set_xticklabels(categories)
axes[0, 1].legend()

# 3. 散点图
x_scatter = np.random.randn(100)
y_scatter = np.random.randn(100)
colors = np.random.rand(100)

scatter = axes[1, 0].scatter(x_scatter, y_scatter, c=colors, alpha=0.6, cmap=&apos;viridis&apos;)
axes[1, 0].set_title(&apos;散点图&apos;, fontsize=14, fontweight=&apos;bold&apos;)
axes[1, 0].set_xlabel(&apos;X轴&apos;)
axes[1, 0].set_ylabel(&apos;Y轴&apos;)
plt.colorbar(scatter, ax=axes[1, 0])

# 4. 饼图
sizes = [30, 25, 20, 15, 10]
labels = [&apos;A&apos;, &apos;B&apos;, &apos;C&apos;, &apos;D&apos;, &apos;E&apos;]
colors_pie = [&apos;#ff9999&apos;, &apos;#66b3ff&apos;, &apos;#99ff99&apos;, &apos;#ffcc99&apos;, &apos;#ff99cc&apos;]

wedges, texts, autotexts = axes[1, 1].pie(sizes, labels=labels, colors=colors_pie,
                                          autopct=&apos;%1.1f%%&apos;, startangle=90)
axes[1, 1].set_title(&apos;饼图&apos;, fontsize=14, fontweight=&apos;bold&apos;)

# 美化文本
for autotext in autotexts:
    autotext.set_color(&apos;white&apos;)
    autotext.set_fontweight(&apos;bold&apos;)

plt.tight_layout()
plt.show()
&lt;/code&gt;&lt;/pre&gt;
&lt;hr /&gt;
&lt;h2&gt;总结&lt;/h2&gt;
&lt;h3&gt;核心要点&lt;/h3&gt;
&lt;ul&gt;
&lt;li&gt;&lt;strong&gt;NumPy&lt;/strong&gt;：提供高效的数组操作和数学计算基础&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Pandas&lt;/strong&gt;：强大的数据处理和分析工具&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Matplotlib&lt;/strong&gt;：灵活的数据可视化库&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;协同工作&lt;/strong&gt;：三个库相互配合，形成完整的数据科学工具链&lt;/li&gt;
&lt;/ul&gt;
&lt;h3&gt;学习建议&lt;/h3&gt;
&lt;ol&gt;
&lt;li&gt;&lt;strong&gt;循序渐进&lt;/strong&gt;：先掌握NumPy基础，再学习Pandas，最后深入Matplotlib&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;实践为主&lt;/strong&gt;：多动手编写代码，处理真实数据&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;性能意识&lt;/strong&gt;：注意代码效率，使用向量化操作&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;可视化思维&lt;/strong&gt;：学会用图表表达数据洞察&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;持续学习&lt;/strong&gt;：关注新特性和最佳实践&lt;/li&gt;
&lt;/ol&gt;
&lt;h3&gt;应用领域&lt;/h3&gt;
&lt;ul&gt;
&lt;li&gt;数据分析和探索&lt;/li&gt;
&lt;li&gt;机器学习数据预处理&lt;/li&gt;
&lt;li&gt;科学计算和数值分析&lt;/li&gt;
&lt;li&gt;商业智能和报表&lt;/li&gt;
&lt;li&gt;学术研究和论文写作&lt;/li&gt;
&lt;/ul&gt;
&lt;h2&gt;参考资料&lt;/h2&gt;
&lt;ul&gt;
&lt;li&gt;NumPy官方文档：https://numpy.org/doc/&lt;/li&gt;
&lt;li&gt;Pandas官方文档：https://pandas.pydata.org/docs/&lt;/li&gt;
&lt;li&gt;Matplotlib官方文档：https://matplotlib.org/&lt;/li&gt;
&lt;li&gt;Python数据科学手册&lt;/li&gt;
&lt;li&gt;数据科学实战指南&lt;/li&gt;
&lt;/ul&gt;
&lt;hr /&gt;
&lt;h2&gt;附录A：常用方法参数详解&lt;/h2&gt;
&lt;h3&gt;NumPy&lt;/h3&gt;
&lt;pre&gt;&lt;code&gt;np.array(object, dtype=None, copy=True, ndmin=0, order=None)
- object: 可迭代对象或嵌套序列
- dtype: 指定元素数据类型（如 np.int64, np.float32）
- copy: 是否拷贝数据
- ndmin: 指定最小维度
- order: 内存布局（&apos;C&apos; 行优先，&apos;F&apos; 列优先）

np.arange([start,] stop[, step], dtype=None)
- start: 起始值（默认0）
- stop: 结束值（不包含）
- step: 步长（默认1）
- dtype: 元素类型

np.linspace(start, stop, num=50, endpoint=True, retstep=False, dtype=None)
- start/stop: 起止值
- num: 点数量
- endpoint: 是否包含 stop
- retstep: 是否返回步长
- dtype: 元素类型

ndarray.reshape(newshape, order=&apos;C&apos;)
- newshape: 新形状（可用-1自适应）
- order: &apos;C&apos; 行优先，&apos;F&apos; 列优先

np.mean(a, axis=None, dtype=None, keepdims=False)
- a: 数组
- axis: 轴（None 为整体；0 按列；1 按行）
- dtype: 计算时使用的类型
- keepdims: 是否保留降维后的维度

np.linalg.inv(a)
- a: 可逆方阵
&lt;/code&gt;&lt;/pre&gt;
&lt;h3&gt;Pandas&lt;/h3&gt;
&lt;pre&gt;&lt;code&gt;pd.Series(data=None, index=None, dtype=None, name=None)
- data: 可迭代、ndarray、字典等
- index: 索引标签
- dtype: 数据类型
- name: 名称

pd.DataFrame(data=None, index=None, columns=None, dtype=None)
- data: 字典/二维数组/记录序列等
- index: 行索引
- columns: 列名
- dtype: 统一数据类型

pd.read_csv(filepath_or_buffer, sep=&apos;,&apos;, header=&apos;infer&apos;, names=None,
            index_col=None, usecols=None, dtype=None, parse_dates=False,
            na_values=None, encoding=None)
- filepath_or_buffer: 文件路径或缓冲
- sep: 分隔符
- header: 表头行号或 None
- names: 列名（与 header 搭配）
- index_col: 作为索引的列
- usecols: 读取的列集合
- dtype: 指定列类型
- parse_dates: 解析日期列
- na_values: 额外的缺失值标记
- encoding: 文件编码

DataFrame.groupby(by, axis=0, as_index=True, sort=True)
- by: 列名或映射/函数
- as_index: 分组键是否作索引
- sort: 是否对组键排序

pd.merge(left, right, how=&apos;inner&apos;, on=None, left_on=None, right_on=None,
         left_index=False, right_index=False, suffixes=(&apos;_x&apos;,&apos;_y&apos;))
- left/right: 左右 DataFrame
- how: 连接方式（&apos;left&apos;/&apos;right&apos;/&apos;inner&apos;/&apos;outer&apos;）
- on/left_on/right_on: 连接键
- left_index/right_index: 是否使用索引作为连接键
- suffixes: 重名列后缀

pd.concat(objs, axis=0, join=&apos;outer&apos;, ignore_index=False)
- objs: 序列或字典的对象集合
- axis: 0 纵向，1 横向
- join: &apos;outer&apos;/&apos;inner&apos;
- ignore_index: 是否重建索引
&lt;/code&gt;&lt;/pre&gt;
&lt;h3&gt;Matplotlib（pyplot）&lt;/h3&gt;
&lt;pre&gt;&lt;code&gt;plt.plot(x, y=None, fmt=&apos;&apos;, label=None, linewidth=None, color=None)
- x, y: 坐标序列（仅 x 时绘制 y=x 的索引）
- fmt: 格式字符串（&apos;b-&apos; 蓝色实线等）
- label: 图例标签
- linewidth/color: 线宽/颜色

plt.scatter(x, y, s=None, c=None, alpha=None, cmap=None, label=None)
- x, y: 点坐标
- s: 点大小
- c: 颜色或序列（可配合 cmap）
- alpha: 透明度
- cmap: 颜色映射

plt.bar(x, height, width=0.8, color=None, label=None)
- x: 类别或位置
- height: 高度
- width: 宽度
- color: 颜色

plt.hist(x, bins=10, range=None, density=False, color=None)
- x: 数据
- bins: 箱数或边界
- range: 范围
- density: 是否密度归一化

plt.pie(x, labels=None, colors=None, autopct=None, startangle=None)
- x: 扇区大小
- labels/colors: 标签/颜色
- autopct: 数值格式（如 &apos;%1.1f%%&apos;）
- startangle: 起始角度

plt.subplots(nrows=1, ncols=1, figsize=None, sharex=False, sharey=False)
- nrows/ncols: 子图行列
- figsize: 画布尺寸
- sharex/sharey: 共享坐标轴
&lt;/code&gt;&lt;/pre&gt;
&lt;hr /&gt;
&lt;h2&gt;附录B：关键代码输出示例&lt;/h2&gt;
&lt;h3&gt;NumPy 输出&lt;/h3&gt;
&lt;pre&gt;&lt;code&gt;# 来自 1.2 节
一维数组: [1 2 3 4 5]
二维数组:
 [[1 2 3]
  [4 5 6]]
零数组:
 [[0. 0. 0. 0.]
  [0. 0. 0. 0.]
  [0. 0. 0. 0.]]
一数组:
 [[1. 1. 1.]
  [1. 1. 1.]]
范围数组: [0 2 4 6 8]
线性空间数组: [0.   0.25 0.5  0.75 1.  ]

# 来自 1.3 节
数组形状: (2, 3)
数组维度: 2
数组大小: 6
数据类型: int64
重塑后:
 [[1 2]
  [3 4]
  [5 6]]
第一行: [1 2 3]
第一列: [1 4]
子数组: [[2 3]]
布尔掩码:
 [[False False False]
  [ True  True  True]]
条件选择: [4 5 6]

# 来自 1.4 节（数值可能略有差异）
加法: [ 6  8 10 12]
乘法: [ 5 12 21 32]
平方: [ 1  4  9 16]
平方根: [1.         1.41421356 1.73205081 2.        ]
平均值: 5.5
中位数: 5.5
标准差: 2.8722813232690143
方差: 8.25
最大值: 10
最小值: 1
矩阵:
 [[1 2]
  [3 4]]
行列式: -2.0000000000000004
逆矩阵:
 [[-2.   1. ]
  [ 1.5 -0.5]]
&lt;/code&gt;&lt;/pre&gt;
&lt;h3&gt;Pandas 输出&lt;/h3&gt;
&lt;pre&gt;&lt;code&gt;# 来自 2.3 节
DataFrame:
      name  age      city  salary
0    Alice   25  New York   50000
1      Bob   30    London   60000
2  Charlie   35     Paris   70000
3    David   40     Tokyo   80000

形状: (4, 4)
列名: Index([&apos;name&apos;, &apos;age&apos;, &apos;city&apos;, &apos;salary&apos;], dtype=&apos;object&apos;)
索引: RangeIndex(start=0, stop=4, step=1)
数据类型:
 name      object
 age        int64
 city     object
 salary    int64
dtype: object
前3行:
      name  age      city  salary
0    Alice   25  New York   50000
1      Bob   30    London   60000
2  Charlie   35     Paris   70000
描述性统计:
              age        salary
count   4.000000      4.000000
mean   32.500000  65000.000000
std     6.454972  12909.944487
min    25.000000  50000.000000
25%    28.750000  57500.000000
50%    32.500000  65000.000000
75%    36.250000  72500.000000
max    40.000000  80000.000000

# 来自 2.4 节
选择单列: 0      Alice
1        Bob
2    Charlie
3      David
Name: name, dtype: object
复合条件筛选:
      name  age   city  salary
2  Charlie   35  Paris   70000
3    David   40  Tokyo   80000

# 来自 2.5/2.6 节
按城市分组平均薪资:
 city
London      60000.0
New York    50000.0
Paris       70000.0
Tokyo       80000.0
Name: salary, dtype: float64
内连接:
    id     name  salary
0   1    Alice   50000
1   2      Bob   60000
2   3  Charlie   70000
外连接（示例）:
    id     name   salary
0   1    Alice  50000.0
1   2      Bob  60000.0
2   3  Charlie  70000.0
3   4    David      NaN
4   5      NaN  90000.0
&lt;/code&gt;&lt;/pre&gt;
&lt;h3&gt;Matplotlib 输出&lt;/h3&gt;
&lt;p&gt;由于图像以窗口/内嵌方式呈现，这里给出典型效果说明：&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;线图：标题“正弦函数”，蓝色实线，带网格与图例 label=sin(x)&lt;/li&gt;
&lt;li&gt;散点图：100 个点，透明度约 0.6，均匀分布在坐标系&lt;/li&gt;
&lt;li&gt;多子图：2x2 布局，分别为 sin、cos、随机散点、柱状&lt;/li&gt;
&lt;li&gt;柱状图：四类水果的高度柱，顶部有数值标注&lt;/li&gt;
&lt;li&gt;饼图：五个扇区，带百分比注记，圆形&lt;/li&gt;
&lt;li&gt;直方图：30 箱的正态分布直方图，浅蓝色，带网格&lt;/li&gt;
&lt;/ul&gt;
</content:encoded></item><item><title>pyecharts示例图</title><link>https://meteor-comet.github.io/posts/pyecharts/</link><guid isPermaLink="true">https://meteor-comet.github.io/posts/pyecharts/</guid><description>pyecharts各类图表代码示例与效果展示</description><pubDate>Thu, 11 Aug 2022 00:00:00 GMT</pubDate><content:encoded>&lt;h1&gt;pyecharts示例图&lt;/h1&gt;
&lt;h2&gt;渲染图片文件方法&lt;/h2&gt;
&lt;pre&gt;&lt;code&gt;from pyecharts.render import make_snapshot
from snapshot_selenium import snapshot

bar = (
    Bar()
    .add_xaxis([&quot;衬衫&quot;, &quot;羊毛衫&quot;, &quot;雪纺衫&quot;, &quot;裤子&quot;, &quot;高跟鞋&quot;, &quot;袜子&quot;])
    .add_yaxis(&apos;商家&apos;,[5, 20, 36, 10, 75, 90])
)
make_snapshot(snapshot,bar.render(),&apos;bar.png&apos;)
&lt;/code&gt;&lt;/pre&gt;
&lt;h2&gt;柱状图&lt;/h2&gt;
&lt;pre&gt;&lt;code&gt;from pyecharts.globals import CurrentConfig, NotebookType
CurrentConfig.NOTEBOOK_TYPE = NotebookType.JUPYTER_LAB
&lt;/code&gt;&lt;/pre&gt;
&lt;pre&gt;&lt;code&gt;from pyecharts.charts import Bar
bar = Bar()
bar.add_xaxis([&quot;衬衫&quot;, &quot;羊毛衫&quot;, &quot;雪纺衫&quot;, &quot;裤子&quot;, &quot;高跟鞋&quot;, &quot;袜子&quot;])
bar.add_yaxis(&apos;商家&apos;,[5, 20, 36, 10, 75, 90])
bar.load_javascript()
&lt;/code&gt;&lt;/pre&gt;
&lt;pre&gt;&lt;code&gt;bar.render_notebook()
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;&lt;img src=&quot;/images/in-post/post_pyecharts/bar.png&quot; alt=&quot;img1&quot; /&gt;&lt;/p&gt;
&lt;h2&gt;饼图&lt;/h2&gt;
&lt;pre&gt;&lt;code&gt;from pyecharts.charts import Pie
import pyecharts.options as opts
from pyecharts.faker import Faker 
&lt;/code&gt;&lt;/pre&gt;
&lt;pre&gt;&lt;code&gt;list(zip(Faker.choose(),Faker.values()))
&lt;/code&gt;&lt;/pre&gt;
&lt;pre&gt;&lt;code&gt;[(&apos;河马&apos;, 139),
 (&apos;蟒蛇&apos;, 41),
 (&apos;老虎&apos;, 100),
 (&apos;大象&apos;, 121),
 (&apos;兔子&apos;, 80),
 (&apos;熊猫&apos;, 47),
 (&apos;狮子&apos;, 147)]
&lt;/code&gt;&lt;/pre&gt;
&lt;pre&gt;&lt;code&gt;c = Pie()
c.add(&apos;&apos;,[list(x) for x in zip(Faker.choose(),Faker.values())])
c.render_notebook()
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;&lt;img src=&quot;/images/in-post/post_pyecharts/pie.png&quot; alt=&quot;img2&quot; /&gt;&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;c = (
    Pie()
    .add(&apos;数量&apos;,[list(x) for x in zip(Faker.choose(),Faker.values())])
    .set_colors([&apos;#313695&apos;,&apos;#4575b4&apos;,&apos;#74add1&apos;,&apos;#abd9e9&apos;,&apos;#e0f3f8&apos;,&apos;#ffffbf&apos;,&apos;#fee090&apos;,])
    .set_global_opts(
        title_opts=opts.TitleOpts(title=&apos;设置颜色&apos;,pos_top=0,pos_left=0),
        legend_opts=opts.LegendOpts(type_=&apos;scroll&apos;,pos_right=0,orient=&apos;vertical&apos;)
    )
    .set_series_opts(
        label_opts=opts.LabelOpts(formatter=&quot;{b}:{c}&quot;),
    )
)
c.render_notebook()
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;&lt;img src=&quot;/images/in-post/post_pyecharts/pie_2.png&quot; alt=&quot;img3&quot; /&gt;&lt;/p&gt;
&lt;h2&gt;玫瑰图&lt;/h2&gt;
&lt;pre&gt;&lt;code&gt;v = Faker.choose()
v
&lt;/code&gt;&lt;/pre&gt;
&lt;pre&gt;&lt;code&gt;[&apos;河马&apos;, &apos;蟒蛇&apos;, &apos;老虎&apos;, &apos;大象&apos;, &apos;兔子&apos;, &apos;熊猫&apos;, &apos;狮子&apos;]
&lt;/code&gt;&lt;/pre&gt;
&lt;pre&gt;&lt;code&gt;c = (
    Pie()
    .add(
        &apos;&apos;,
        [list(i) for i in zip(v,Faker.values())],
        radius=[&apos;20%&apos;,&apos;70%&apos;],
        center=[&apos;20%&apos;,&apos;50%&apos;],
        rosetype=&apos;radius&apos;,
        label_opts=opts.LabelOpts(is_show=False)
    )
    .add(
        &apos;&apos;,
        [list(i) for i in zip(v,Faker.values())],
        radius=[&apos;20%&apos;,&apos;70%&apos;],
        center=[&apos;80%&apos;,&apos;50%&apos;],
        rosetype=&apos;radius&apos;,
        label_opts=opts.LabelOpts(is_show=True)
    )
    .set_global_opts(title_opts=opts.TitleOpts(title=&apos;南丁格尔玫瑰图&apos;))
)
c.render_notebook()
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;&lt;img src=&quot;/images/in-post/post_pyecharts/rose.png&quot; alt=&quot;img4&quot; /&gt;&lt;/p&gt;
&lt;h2&gt;柱形图&lt;/h2&gt;
&lt;pre&gt;&lt;code&gt;from pyecharts.charts import Bar
&lt;/code&gt;&lt;/pre&gt;
&lt;pre&gt;&lt;code&gt;c=(
    Bar()
    .add_xaxis(Faker.choose())
    .add_yaxis(&apos;商家A&apos;,Faker.values())
    .add_yaxis(&apos;商家B&apos;,Faker.values())
    .set_global_opts(
        title_opts=opts.TitleOpts(title=&apos;销量&apos;,subtitle=&apos;副标题&apos;)
    )
)
c.render_notebook()
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;&lt;img src=&quot;/images/in-post/post_pyecharts/bar_2.png&quot; alt=&quot;img5&quot; /&gt;&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;c=(
    Bar(
        init_opts=opts.InitOpts(
            animation_opts=opts.AnimationOpts(
                animation_delay=100, #延时动画1000ms
                animation_easing=&apos;elasticOut&apos; #弹性动画
            )
        )
    )
    .add_xaxis(Faker.choose())
    .add_yaxis(&apos;商家A&apos;,Faker.values())
    .add_yaxis(&apos;商家B&apos;,Faker.values())
    .set_global_opts(
        title_opts=opts.TitleOpts(title=&apos;销量&apos;,subtitle=&apos;副标题&apos;),
    )
)
c.render_notebook()
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;&lt;img src=&quot;/images/in-post/post_pyecharts/bar_3.png&quot; alt=&quot;img6&quot; /&gt;&lt;/p&gt;
&lt;h2&gt;执行JS代码&lt;/h2&gt;
&lt;pre&gt;&lt;code&gt;from pyecharts.commons.utils import JsCode
&lt;/code&gt;&lt;/pre&gt;
&lt;pre&gt;&lt;code&gt;c=(
    Bar(
        init_opts=opts.InitOpts(
            bg_color={
                &apos;image&apos; :JsCode(&apos;img&apos;),
            }
        )
    )
    .add_xaxis(Faker.choose())
    .add_yaxis(&apos;商家A&apos;,Faker.values())
    .add_yaxis(&apos;商家B&apos;,Faker.values())
    .set_global_opts(
        title_opts=opts.TitleOpts(title=&apos;销量&apos;,subtitle=&apos;副标题&apos;),
    )
)

#添加js代码
c.add_js_funcs(
    &quot;&quot;&quot;
    var img = new Image();
    img.src = &quot;http://localhost:8888/files/pyecharts/%E3%81%82%E3%81%99%E3%81%8B.jpg?_xsrf=2%7C2b50c939%7Cd7ba3194aadfcc7497a9007df4693a2c%7C1722234893&quot;
    &quot;&quot;&quot;
)
c.render_notebook()
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;&lt;img src=&quot;/images/in-post/post_pyecharts/js.png&quot; alt=&quot;img7&quot; /&gt;&lt;/p&gt;
&lt;h2&gt;堆叠柱状图&lt;/h2&gt;
&lt;pre&gt;&lt;code&gt;c=(
    Bar()
    .add_xaxis(Faker.choose())
    .add_yaxis(&apos;商家A&apos;,Faker.values(),stack=&apos;abc&apos;)
    .add_yaxis(&apos;商家B&apos;,Faker.values(),stack=&apos;abc&apos;)
    .set_series_opts(
        label_opts=opts.LabelOpts(is_show=False)
    )
    .set_global_opts(
        title_opts=opts.TitleOpts(title=&apos;堆叠柱状图&apos;,subtitle=&apos;副标题&apos;),
        xaxis_opts=opts.AxisOpts(
            axislabel_opts=opts.LabelOpts(rotate=45)
        ),
        #缩放
        datazoom_opts=[
            opts.DataZoomOpts(),  #x轴拖拉缩放
            opts.DataZoomOpts(type_=&apos;inside&apos;) #滚轮缩放
        ]
        
    )
)

c.render_notebook()
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;&lt;img src=&quot;/images/in-post/post_pyecharts/stack_bar.png&quot; alt=&quot;img8&quot; /&gt;&lt;/p&gt;
&lt;h2&gt;条形图&lt;/h2&gt;
&lt;pre&gt;&lt;code&gt;c=(
    Bar()
    .add_xaxis(Faker.choose())
    .add_yaxis(&apos;商家A&apos;,Faker.values())
    .add_yaxis(&apos;商家B&apos;,Faker.values())
    .reversal_axis()  #反转轴 
    .set_series_opts(
        label_opts=opts.LabelOpts(is_show=True,position=&apos;right&apos;)
    )
    .set_global_opts(
        title_opts=opts.TitleOpts(title=&apos;条形图&apos;,subtitle=&apos;副标题&apos;),
    )
)

c.render_notebook()
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;&lt;img src=&quot;/images/in-post/post_pyecharts/bar_4.png&quot; alt=&quot;img9&quot; /&gt;&lt;/p&gt;
&lt;h2&gt;直方图&lt;/h2&gt;
&lt;p&gt;不同系列柱子之间的距离&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;c=(
    Bar()
    .add_xaxis(Faker.choose())
    .add_yaxis(&apos;商家A&apos;,Faker.values(),gap=&apos;0%&apos;)
    .add_yaxis(&apos;商家B&apos;,Faker.values(),gap=&apos;0%&apos;)
    .set_global_opts(
        title_opts=opts.TitleOpts(title=&apos;不同系列柱子之间距离&apos;),
    )
)

c.render_notebook()
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;&lt;img src=&quot;/images/in-post/post_pyecharts/bar_5.png&quot; alt=&quot;img10&quot; /&gt;&lt;/p&gt;
&lt;h2&gt;单系列柱子之间的间距&lt;/h2&gt;
&lt;pre&gt;&lt;code&gt;c=(
    Bar()
    .add_xaxis(Faker.choose())
    .add_yaxis(&apos;商家A&apos;,Faker.values(),category_gap=&apos;5%&apos;)
    .set_global_opts(
        title_opts=opts.TitleOpts(title=&apos;直方图&apos;),
    )
)

c.render_notebook()
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;&lt;img src=&quot;/images/in-post/post_pyecharts/bar_6.png&quot; alt=&quot;img11&quot; /&gt;&lt;/p&gt;
&lt;h2&gt;JsCode自定义柱颜色&lt;/h2&gt;
&lt;pre&gt;&lt;code&gt;color_func=&quot;&quot;&quot;
        function (params){
            if (params.value &amp;gt; 0 &amp;amp;&amp;amp; params.value &amp;lt; 50){
                return &apos;red&apos;;
            }
            else if (params.value &amp;gt; 50 &amp;amp;&amp;amp; params.value &amp;lt; 100){
                return &apos;green&apos;;
            }
            return &apos;blue&apos;;
        }
&quot;&quot;&quot;

c=(
    Bar()
    .add_xaxis(Faker.choose())
    .add_yaxis(
        &apos;商家A&apos;,Faker.values(),
        itemstyle_opts=opts.ItemStyleOpts(color=JsCode(color_func)),
        
    )
    .add_yaxis(
        &apos;商家B&apos;,Faker.values(),
        itemstyle_opts=opts.ItemStyleOpts(color=JsCode(color_func)),
        
    )
    .add_yaxis(
        &apos;商家C&apos;,Faker.values(),
        itemstyle_opts=opts.ItemStyleOpts(color=JsCode(color_func)),
        
    )
    .set_global_opts(
        title_opts=opts.TitleOpts(title=&apos;直方图&apos;),
    )
)

c.render_notebook()
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;&lt;img src=&quot;/images/in-post/post_pyecharts/js_2.png&quot; alt=&quot;img12&quot; /&gt;&lt;/p&gt;
&lt;h2&gt;象形柱状图&lt;/h2&gt;
&lt;pre&gt;&lt;code&gt;from pyecharts.charts import PictorialBar
from pyecharts.globals import SymbolType
&lt;/code&gt;&lt;/pre&gt;
&lt;pre&gt;&lt;code&gt;location = [&apos;山西&apos;,&apos;四川&apos;,&apos;西藏&apos;,&apos;北京&apos;,&apos;上海&apos;,&apos;内蒙古&apos;,&apos;云南&apos;,&apos;黑龙江&apos;,&apos;广东&apos;,&apos;福建&apos;]
values = [13,42,67,81,86,94,166,220,249,262]
c = (
    PictorialBar()
    .add_xaxis(location)
    .add_yaxis(
        &apos;&apos;,
        values,
        label_opts=opts.LabelOpts(is_show=True),
        symbol_repeat=&apos;fixed&apos;, #重复方式
        symbol=SymbolType.ROUND_RECT, #象形符号类型
        symbol_size=16, #符号裁剪
        is_symbol_clip=True
    )
    .reversal_axis()
    .set_global_opts(
        title_opts=opts.TitleOpts(title=&apos;象形柱状图&apos;),
        xaxis_opts=opts.AxisOpts(is_show=False), #不显示x轴
        yaxis_opts=opts.AxisOpts(
            axistick_opts=opts.AxisTickOpts(is_show=False), #不显示y轴刻度
            axisline_opts=opts.AxisLineOpts(is_show=False)
        ),
    )
    .set_series_opts(
        label_opts=opts.LabelOpts(position=&apos;right&apos;)
    )
)
c.render_notebook()
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;&lt;img src=&quot;/images/in-post/post_pyecharts/PictorialBar.png&quot; alt=&quot;img13&quot; /&gt;&lt;/p&gt;
&lt;h2&gt;雷达图&lt;/h2&gt;
&lt;pre&gt;&lt;code&gt;from pyecharts.charts import Radar
&lt;/code&gt;&lt;/pre&gt;
&lt;pre&gt;&lt;code&gt;v1 = [[4300,10000,28000,35000,50000,19000]]
v2 = [[5000,14000,28000,31000,42000,21000]]
&lt;/code&gt;&lt;/pre&gt;
&lt;pre&gt;&lt;code&gt;c=(
    Radar()
    .add_schema(
        schema=[
            opts.RadarIndicatorItem(name=&apos;项目1&apos;,max_=6000),
            opts.RadarIndicatorItem(name=&apos;项目2&apos;,max_=16000),
            opts.RadarIndicatorItem(name=&apos;项目3&apos;,max_=30000),
            opts.RadarIndicatorItem(name=&apos;项目4&apos;,max_=38000),
            opts.RadarIndicatorItem(name=&apos;项目5&apos;,max_=60000),
            opts.RadarIndicatorItem(name=&apos;项目6&apos;,max_=22000)
        ]
    )
    .add(&apos;数据1&apos;,v1,color=&apos;red&apos;)
    .add(&apos;数据2&apos;,v2)
    .set_series_opts(
        label_opts=opts.LabelOpts(is_show=False),
        linestyle_opts=opts.LineStyleOpts(width=5)
    )
)

c.render_notebook()
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;&lt;img src=&quot;/images/in-post/post_pyecharts/radar.png&quot; alt=&quot;img14&quot; /&gt;&lt;/p&gt;
&lt;h2&gt;折线图&lt;/h2&gt;
&lt;pre&gt;&lt;code&gt;from pyecharts.charts import Line
&lt;/code&gt;&lt;/pre&gt;
&lt;pre&gt;&lt;code&gt;c=(
    Line()
    .add_xaxis(Faker.choose())
    .add_yaxis(&apos;商家A&apos;,Faker.values())
    .add_yaxis(&apos;商家B&apos;,Faker.values(),is_smooth=True)  #平滑曲线
    .set_global_opts(
        title_opts=opts.TitleOpts(
            title=&apos;折线图&apos;
        ),
        tooltip_opts=opts.TooltipOpts(
            trigger=&apos;axis&apos;
        ),
        yaxis_opts=opts.AxisOpts(
            splitline_opts=opts.SplitLineOpts(is_show=False),
            is_show=True,
            axisline_opts=opts.AxisLineOpts(
                is_show=True
            )
        ),
        xaxis_opts=opts.AxisOpts(
            splitline_opts=opts.SplitLineOpts(is_show=False)
        ),
    )
)
c.render_notebook()
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;&lt;img src=&quot;/images/in-post/post_pyecharts/line.png&quot; alt=&quot;img15&quot; /&gt;&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;c=(
    Line(init_opts=opts.InitOpts(width=&apos;1000px&apos;,height=&apos;500px&apos;))
    .add_xaxis(xaxis_data=Faker.week)
    .add_yaxis(
        series_name=&apos;&apos;,
        y_axis=[120,200,150,80,70,110,130],
        symbol=&apos;triangle&apos;, #点类型
        symbol_size=20,
        #线条样式
        linestyle_opts=opts.LineStyleOpts(
            color=&apos;green&apos;,
            width=2,
            type_=&apos;dashed&apos;,
            
        ),
        label_opts=opts.LabelOpts(is_show=False),
        itemstyle_opts=opts.ItemStyleOpts(
            border_width=2,border_color=&apos;red&apos;,color=&apos;blue&apos;
        ),
        markpoint_opts=opts.MarkPointOpts(
            data=[
                opts.MarkPointItem(type_=&apos;max&apos;),
                opts.MarkPointItem(type_=&apos;min&apos;)
            ]
        ),
        markline_opts=opts.MarkLineOpts(
            data=[
                opts.MarkLineItem(type_=&apos;average&apos;)
            ],
        )
    )
    .set_global_opts(
        yaxis_opts=opts.AxisOpts(
            type_=&apos;value&apos;,
            splitline_opts=opts.SplitLineOpts(
                is_show=True
            )
        ),
        tooltip_opts=opts.TooltipOpts(trigger=&apos;axis&apos;)
    )
)

c.render_notebook()
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;&lt;img src=&quot;/images/in-post/post_pyecharts/line_2.png&quot; alt=&quot;img16&quot; /&gt;&lt;/p&gt;
&lt;h2&gt;面积图&lt;/h2&gt;
&lt;pre&gt;&lt;code&gt;c=(
    Line()
    .add_xaxis(Faker.week)
    .add_yaxis(
        &apos;&apos;,
        y_axis=[120,130,100,140,90,200,150],
        areastyle_opts=opts.AreaStyleOpts(opacity=0.5)
    )
    .set_global_opts(
        title_opts=opts.TitleOpts(title=&apos;面积图&apos;),
        tooltip_opts=opts.TooltipOpts(trigger=&apos;axis&apos;),
        xaxis_opts=opts.AxisOpts(type_=&apos;category&apos;,boundary_gap=False)
    )
)

c.render_notebook()
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;&lt;img src=&quot;/images/in-post/post_pyecharts/line_3.png&quot; alt=&quot;img17&quot; /&gt;&lt;/p&gt;
&lt;h2&gt;堆叠面积图&lt;/h2&gt;
&lt;pre&gt;&lt;code&gt;c=(
    Line()
    .add_xaxis(Faker.week,)
    .add_yaxis(
        &apos;广告&apos;,
        stack=&apos;堆叠&apos;,
        y_axis=[120,40,100,30,50,60,150],
        areastyle_opts=opts.AreaStyleOpts(opacity=0.5),
        label_opts=opts.LabelOpts(is_show=False) 
    )
    .add_yaxis(
        &apos;销售&apos;,
        stack=&apos;堆叠&apos;,
        y_axis=[120,40,100,30,50,60,150],
        areastyle_opts=opts.AreaStyleOpts(opacity=0.5),
        label_opts=opts.LabelOpts(is_show=False) 
    )
    .add_yaxis(
        &apos;流量&apos;,
        stack=&apos;堆叠&apos;,
        y_axis=[120,40,100,30,50,60,150],
        areastyle_opts=opts.AreaStyleOpts(opacity=0.5),
        label_opts=opts.LabelOpts(is_show=False) 
    )
    .add_yaxis(
        &apos;浏览&apos;,
        stack=&apos;堆叠&apos;,
        y_axis=[120,40,100,30,50,60,150],
        areastyle_opts=opts.AreaStyleOpts(opacity=0.5),
        label_opts=opts.LabelOpts(is_show=False) 
    )
    .set_global_opts(
        title_opts=opts.TitleOpts(title=&apos;堆叠面积图&apos;),
        xaxis_opts=opts.AxisOpts(type_=&apos;category&apos;,boundary_gap=False)
    )
)
c.render_notebook()
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;&lt;img src=&quot;/images/in-post/post_pyecharts/line_4.png&quot; alt=&quot;img18&quot; /&gt;&lt;/p&gt;
&lt;h2&gt;散点图&lt;/h2&gt;
&lt;pre&gt;&lt;code&gt;from pyecharts.charts import Scatter
&lt;/code&gt;&lt;/pre&gt;
&lt;pre&gt;&lt;code&gt;data=[
    [10.0,8.04],
    [8.0,6.95],
    [13.0,7.58],
    [9.0,8.81],
    [11.0,8.33],
    [14.0,9.96],
    [6.0,7.24],
    [4.0,4.26],
    [12.0,10.84],
    [7.0,4.82],
    [5.0,5.68]
]
&lt;/code&gt;&lt;/pre&gt;
&lt;pre&gt;&lt;code&gt;data.sort(key=lambda x: x[0])
&lt;/code&gt;&lt;/pre&gt;
&lt;pre&gt;&lt;code&gt;x_data = [d[0] for d in data]
y_data = [d[1] for d in data]
display(x_data,y_data)
&lt;/code&gt;&lt;/pre&gt;
&lt;pre&gt;&lt;code&gt;[4.0, 5.0, 6.0, 7.0, 8.0, 9.0, 10.0, 11.0, 12.0, 13.0, 14.0]



[4.26, 5.68, 7.24, 4.82, 6.95, 8.81, 8.04, 8.33, 10.84, 7.58, 9.96]
&lt;/code&gt;&lt;/pre&gt;
&lt;pre&gt;&lt;code&gt;c=(
    Scatter(
        init_opts=opts.InitOpts(width=&apos;800px&apos;,height=&apos;400px&apos;)
    )
    .add_xaxis(xaxis_data=x_data)
    .add_yaxis(
        &apos;&apos;,
        y_axis=y_data,
        symbol_size=20,
        label_opts=opts.LabelOpts(is_show=True,position=&apos;top&apos;)
    )
    .set_global_opts(
        xaxis_opts=opts.AxisOpts(
            min_=2,
            type_=&apos;value&apos;,
            
        )
    )
)
c.render_notebook()
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;&lt;img src=&quot;/images/in-post/post_pyecharts/scatter.png&quot; alt=&quot;img19&quot; /&gt;&lt;/p&gt;
&lt;h2&gt;涟漪散点图&lt;/h2&gt;
&lt;pre&gt;&lt;code&gt;from pyecharts.charts import EffectScatter
from pyecharts.globals import SymbolType
&lt;/code&gt;&lt;/pre&gt;
&lt;pre&gt;&lt;code&gt;c=(
    EffectScatter()
    .add_xaxis(Faker.choose())
    .add_yaxis(
        &apos;&apos;,
        Faker.values(),
        symbol=SymbolType.ARROW,
        symbol_size=25,
        color=&apos;red&apos;,
        label_opts=opts.LabelOpts(position=&apos;top&apos;)
    )
    .set_global_opts(
        title_opts=opts.TitleOpts(title=&apos;涟漪散点图&apos;),
    )
)
c.render_notebook()
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;&lt;img src=&quot;/images/in-post/post_pyecharts/scatter_2.png&quot; alt=&quot;img20&quot; /&gt;&lt;/p&gt;
&lt;h2&gt;热力图&lt;/h2&gt;
&lt;pre&gt;&lt;code&gt;from pyecharts.charts import HeatMap
import random
&lt;/code&gt;&lt;/pre&gt;
&lt;pre&gt;&lt;code&gt;value = [[i,j,random.randint(0,100)] for i in range(24) for j in range(7)]
&lt;/code&gt;&lt;/pre&gt;
&lt;pre&gt;&lt;code&gt;c=(
    HeatMap()
    .add_xaxis(Faker.clock)
    .add_yaxis(
        &apos;&apos;,
        Faker.week,
        value,
        label_opts=opts.LabelOpts(color=&apos;auto&apos;),
    )
)
c.render_notebook()
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;&lt;img src=&quot;/images/in-post/post_pyecharts/heat.png&quot; alt=&quot;img21&quot; /&gt;&lt;/p&gt;
&lt;h2&gt;日历图&lt;/h2&gt;
&lt;pre&gt;&lt;code&gt;from pyecharts.charts import Calendar
import datetime
&lt;/code&gt;&lt;/pre&gt;
&lt;pre&gt;&lt;code&gt;begin = datetime.date(2023,1,1)
end = datetime.date(2024,1,1)
&lt;/code&gt;&lt;/pre&gt;
&lt;pre&gt;&lt;code&gt;(end-begin).days
&lt;/code&gt;&lt;/pre&gt;
&lt;pre&gt;&lt;code&gt;365
&lt;/code&gt;&lt;/pre&gt;
&lt;pre&gt;&lt;code&gt;data = [[str(begin+datetime.timedelta(days=i)),random.randint(1000,25000)] 
        for i in range((end-begin).days)]
&lt;/code&gt;&lt;/pre&gt;
&lt;pre&gt;&lt;code&gt;data[0],data[-1]
&lt;/code&gt;&lt;/pre&gt;
&lt;pre&gt;&lt;code&gt;([&apos;2023-01-01&apos;, 4652], [&apos;2023-12-31&apos;, 15378])
&lt;/code&gt;&lt;/pre&gt;
&lt;pre&gt;&lt;code&gt;c = (
    Calendar(
        init_opts=opts.InitOpts(height=&apos;220px&apos;)
    )
    .add(
        &apos;&apos;,
        data,
        calendar_opts=opts.CalendarOpts(
            range_=&apos;2023&apos;,
            daylabel_opts=opts.CalendarDayLabelOpts(name_map=&apos;cn&apos;),
            monthlabel_opts=opts.CalendarMonthLabelOpts(name_map=&quot;cn&quot;)
        )
    )
    .set_global_opts(
        visualmap_opts=opts.VisualMapOpts(
            max_=25000,
            min_=1000,
            orient=&quot;horizontal&quot;,
            is_piecewise=True,
            pos_top=&quot;230px&quot;,
            pos_left=&quot;100px&quot;,
        )
    )
)
c.render_notebook()
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;&lt;img src=&quot;/images/in-post/post_pyecharts/calendar.png&quot; alt=&quot;img22&quot; /&gt;&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;from pyecharts.charts import Boxplot
&lt;/code&gt;&lt;/pre&gt;
&lt;pre&gt;&lt;code&gt;v1 = [
    [850,740,900,1070,930,850,950,980,980,880,1000,980],
    [960,940,960,940,880,800,850,880,900,840,830,790],
]
v2 = [
    [890,810,810,820,800,770,760,740,750,760,910,920],
    [890,840,780,810,760,810,790,810,820,850,870,870],
] 
&lt;/code&gt;&lt;/pre&gt;
&lt;h2&gt;箱型图&lt;/h2&gt;
&lt;pre&gt;&lt;code&gt;c=Boxplot()
c.add_xaxis([&apos;demo1&apos;,&apos;demo2&apos;])
c.add_yaxis(&apos;A&apos;,c.prepare_data(v1))
c.add_yaxis(&apos;B&apos;,c.prepare_data(v2))
c.render_notebook()
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;&lt;img src=&quot;/images/in-post/post_pyecharts/box.png&quot; alt=&quot;img23&quot; /&gt;&lt;/p&gt;
&lt;h2&gt;词云图&lt;/h2&gt;
&lt;pre&gt;&lt;code&gt;from pyecharts.charts import WordCloud
&lt;/code&gt;&lt;/pre&gt;
&lt;pre&gt;&lt;code&gt;# wordcloud = (
#     WordCloud()
#     .add(&quot;&quot;, data_pair = words, word_size_range=[6,60],textstyle_opts=opts.TextStyleOpts(font_family=&apos;Microsoft YaHei&apos;,font_weight=&apos;bold&apos;))
#     .set_global_opts(title_opts=opts.TitleOpts(title=&quot;《政府工作报告（2021）》新词&quot;,title_textstyle_opts = opts.TextStyleOpts(font_size = 25,color=&quot;midnightblue&quot;)))
# )
# wordcloud.render_notebook()
&lt;/code&gt;&lt;/pre&gt;
&lt;h2&gt;漏斗图&lt;/h2&gt;
&lt;pre&gt;&lt;code&gt;from pyecharts.charts import Funnel
&lt;/code&gt;&lt;/pre&gt;
&lt;pre&gt;&lt;code&gt;[list(i) for i in zip(Faker.choose(),Faker.values())]
&lt;/code&gt;&lt;/pre&gt;
&lt;pre&gt;&lt;code&gt;[[&apos;小米&apos;, 22],
 [&apos;三星&apos;, 81],
 [&apos;华为&apos;, 54],
 [&apos;苹果&apos;, 46],
 [&apos;魅族&apos;, 55],
 [&apos;VIVO&apos;, 124],
 [&apos;OPPO&apos;, 40]]
&lt;/code&gt;&lt;/pre&gt;
&lt;pre&gt;&lt;code&gt;c=(
    Funnel()
    .add(&apos;商品&apos;,[list(i) for i in zip(Faker.choose(),Faker.values())])
    .set_global_opts(
        title_opts=opts.TitleOpts(title=&apos;漏斗图&apos;)
    )
)
c.render_notebook()
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;&lt;img src=&quot;/images/in-post/post_pyecharts/funnel.png&quot; alt=&quot;img24&quot; /&gt;&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;x_data=[&apos;访问&apos;,&apos;点击&apos;,&apos;咨询&apos;,&apos;加购&apos;,&apos;下单&apos;]
y_data=[100,80,60,40,20]

data=[[x_data[i],y_data[i]] for i in range(len(x_data))]
&lt;/code&gt;&lt;/pre&gt;
&lt;pre&gt;&lt;code&gt;c=(
    Funnel(
        init_opts=opts.InitOpts(width=&apos;600px&apos;,height=&apos;400px&apos;)
    )
    .add(
        &apos;漏斗图&apos;,
        data_pair=data,
        gap=5,
        tooltip_opts=opts.TooltipOpts(
            trigger=&apos;item&apos;,
            formatter=&apos;{a}&amp;lt;br/&amp;gt; {b}:{c}&apos;
        ),
        label_opts=opts.LabelOpts(
            is_show=True,
            position=&apos;inside&apos;,
            font_size=16
        )
    )
    .set_global_opts(
        title_opts=opts.TitleOpts(title=&apos;漏斗图&apos;)
    )
)
c.render_notebook()
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;&lt;img src=&quot;/images/in-post/post_pyecharts/funnel_2.png&quot; alt=&quot;img25&quot; /&gt;&lt;/p&gt;
&lt;h2&gt;极坐标图&lt;/h2&gt;
&lt;pre&gt;&lt;code&gt;from pyecharts.charts import Polar
&lt;/code&gt;&lt;/pre&gt;
&lt;pre&gt;&lt;code&gt;data=[(i,random.randint(1,100)) for i in range(100)] 
&lt;/code&gt;&lt;/pre&gt;
&lt;pre&gt;&lt;code&gt;c=(
    Polar()
    .add(
        &apos;极坐标&apos;,
        data,
        type_=&apos;scatter&apos;,
        label_opts=opts.LabelOpts(
            is_show=False,
        ),
        symbol_size=10
        # effect_opts=opts.EffectOpts(
        #     scale=10
        # )
    )
    .set_global_opts(
        title_opts=opts.TitleOpts(
            title=&apos;极坐标图&apos;
        )
    )
)
c.render_notebook()
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;&lt;img src=&quot;/images/in-post/post_pyecharts/polar.png&quot; alt=&quot;img26&quot; /&gt;&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;c=(
    Polar()
    .add_schema(
        radiusaxis_opts=opts.RadiusAxisOpts(
            data=Faker.week,
            type_=&apos;category&apos;
        ),
        angleaxis_opts=opts.AngleAxisOpts(
            is_clockwise=True,
            max_=10
        )
    )
    .add(&apos;商品&apos;,[1,2,3,4,3,5,1],type_=&apos;bar&apos;)
)
c.render_notebook()
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;&lt;img src=&quot;/images/in-post/post_pyecharts/polar_2.png&quot; alt=&quot;img27&quot; /&gt;&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;c=(
    Polar()
    .add_schema(
        # radiusaxis_opts=opts.RadiusAxisOpts(
        #     data=Faker.week,
        #     type_=&apos;category&apos;
        # ),
        angleaxis_opts=opts.AngleAxisOpts(
            data=Faker.week,
            type_=&apos;category&apos;
        )
    )
    .add(&apos;商品A&apos;,[1,2,3,4,3,5,1],type_=&apos;bar&apos;,stack=&apos;abc&apos;)
    .add(&apos;商品B&apos;,[2,4,3,5,2,3,1],type_=&apos;bar&apos;,stack=&apos;abc&apos;)
    .add(&apos;商品C&apos;,[1,2,3,4,3,5,1],type_=&apos;bar&apos;,stack=&apos;abc&apos;)
    .set_global_opts(
        title_opts=opts.TitleOpts(
            title=&apos;极坐标图+堆叠柱形图&apos;
        )
    )
)
c.render_notebook()
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;&lt;img src=&quot;/images/in-post/post_pyecharts/polar_3.png&quot; alt=&quot;img28&quot; /&gt;&lt;/p&gt;
&lt;h2&gt;水球图&lt;/h2&gt;
&lt;pre&gt;&lt;code&gt;from pyecharts.charts import Liquid
&lt;/code&gt;&lt;/pre&gt;
&lt;pre&gt;&lt;code&gt;# c =(
#     Liquid()
#     .add(&apos;&apos;,[0.2,0.7]) #0.7水百分之七十，0.2显示数值
#     .set_global_opts(title_opts=opts.TitleOpts(title=&apos;水球图&apos;))
# )
# c.render_notebook()
&lt;/code&gt;&lt;/pre&gt;
&lt;h2&gt;桑基图&lt;/h2&gt;
&lt;pre&gt;&lt;code&gt;from pyecharts.charts import Sankey
&lt;/code&gt;&lt;/pre&gt;
&lt;pre&gt;&lt;code&gt;nodes = [
    {&apos;name&apos;:&apos;c1&apos;},
    {&apos;name&apos;:&apos;c2&apos;},
    {&apos;name&apos;:&apos;c3&apos;},
    {&apos;name&apos;:&apos;c4&apos;},
    {&apos;name&apos;:&apos;c5&apos;},
    {&apos;name&apos;:&apos;c6&apos;},
    {&apos;name&apos;:&apos;c7&apos;}
]

links=[
    {&apos;source&apos;:&apos;c1&apos;,&apos;target&apos;:&apos;c2&apos;,&apos;value&apos;:10},
    {&apos;source&apos;:&apos;c2&apos;,&apos;target&apos;:&apos;c3&apos;,&apos;value&apos;:20},
    {&apos;source&apos;:&apos;c3&apos;,&apos;target&apos;:&apos;c4&apos;,&apos;value&apos;:30},
    {&apos;source&apos;:&apos;c5&apos;,&apos;target&apos;:&apos;c6&apos;,&apos;value&apos;:50},
    {&apos;source&apos;:&apos;c6&apos;,&apos;target&apos;:&apos;c7&apos;,&apos;value&apos;:60},
]
&lt;/code&gt;&lt;/pre&gt;
&lt;pre&gt;&lt;code&gt;c=(
    Sankey()
    .add(
        &apos;&apos;,
        nodes,  #所有节点
        links,  #节点之间的链接关系
        linestyle_opt=opts.LineStyleOpts(
            opacity=0.2, #透明度
            curve=0.6,   #曲线幅度
            color=&apos;red&apos;
        ),
        # label_opts=opts.LabelOpts(position=&apos;right&apos;)
    )
    .set_global_opts(title_opts=opts.TitleOpts(title=&apos;桑基图&apos;))
)
c.render_notebook()
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;&lt;img src=&quot;/images/in-post/post_pyecharts/sankey.png&quot; alt=&quot;img29&quot; /&gt;&lt;/p&gt;
</content:encoded></item><item><title>pyecharts配置项</title><link>https://meteor-comet.github.io/posts/pyecharts-opts/</link><guid isPermaLink="true">https://meteor-comet.github.io/posts/pyecharts-opts/</guid><description>pyecharts图表配置参数完整指南</description><pubDate>Wed, 10 Aug 2022 00:00:00 GMT</pubDate><content:encoded>&lt;h2&gt;pyecharts配置项&lt;/h2&gt;
&lt;pre&gt;&lt;code&gt;from pyecharts.globals import CurrentConfig, NotebookType
CurrentConfig.NOTEBOOK_TYPE = NotebookType.JUPYTER_LAB
&lt;/code&gt;&lt;/pre&gt;
&lt;pre&gt;&lt;code&gt;from pyecharts.charts import Bar,Line
from pyecharts import options as opts

from pyecharts.faker import Faker
from pyecharts.globals import ThemeType,RenderType
&lt;/code&gt;&lt;/pre&gt;
&lt;pre&gt;&lt;code&gt;Faker.choose()
&lt;/code&gt;&lt;/pre&gt;
&lt;pre&gt;&lt;code&gt;[&apos;河马&apos;, &apos;蟒蛇&apos;, &apos;老虎&apos;, &apos;大象&apos;, &apos;兔子&apos;, &apos;熊猫&apos;, &apos;狮子&apos;]
&lt;/code&gt;&lt;/pre&gt;
&lt;h3&gt;全局配置项&lt;/h3&gt;
&lt;pre&gt;&lt;code&gt;c = (
    Bar(
        #初始化配置项
        init_opts=opts.InitOpts(
            width=&apos;900px&apos;,
            height=&apos;500px&apos;,
            renderer=RenderType.CANVAS, #渲染风格
            page_title=&apos;网页标题&apos;,
            theme=ThemeType.WHITE, #主题
            bg_color=&apos;white&apos;
        )
    )
    .add_xaxis(Faker.choose())
    .add_yaxis(&apos;商家A&apos;,Faker.values())
    .add_yaxis(&apos;商家B&apos;,Faker.values())
    
    #全局配置项
    .set_global_opts(
        title_opts=opts.TitleOpts(
            title=&apos;柱形图&apos;,
            title_link=&apos;https://www.baidu.com&apos;,
            title_target=&apos;blank&apos;, #self是在当前页面打开
            subtitle=&apos;副标题&apos;,
            subtitle_link=&apos;https://meteor-comet.github.io&apos;,
            subtitle_target=&apos;blank&apos;,
            pos_left=&apos;20%&apos;, #right/left/center/10px
            pos_top=0,
            #pos_bottom,pos_right
            padding=20, #内边距
            item_gap=5, #主标题和副标题之间间距
        ),
        
        #区域缩放配置项
        datazoom_opts=opts.DataZoomOpts(
            is_show=True, #是否显示组件
            type_=&apos;slider&apos;, #组件类型 inside/slider
            is_realtime=True, #拖动时是否实时更新图表
            range_start=20, #数据窗口起始百分比
            range_end=80,   #数据窗口结束百分比
            orient=&apos;horizontal&apos;,  #horizontal水平放，vertical垂直放(y轴数据份范围)
            is_zoom_lock=False, #是否锁定选择区域
        ),
        
        #图例配置项
        legend_opts=opts.LegendOpts(
            #图例类型: plain普通图例,scroll:可以滚动翻页的图例，用于图例较多的情况  
            type_=&apos;plain&apos;,
            is_show=True,
            pos_left=&apos;center&apos;, #图例位置同上
            orient=&apos;vertical&apos;, #horizontal水平放，vertical垂直放
            #选择模式 True:开启图例点击   False:关闭图例点击
            #         single:单选      multiple：多选
            selected_mode=&apos;multiple&apos;,
            #图表和文字的位置
            align=&apos;left&apos;,
            padding=15,
            item_gap=5,  #图例之间间距
            #图例的宽高
            item_width=30,
            item_height=15,
            inactive_color=&apos;#ccc&apos;,#图例关闭时显示的颜色
            #textstyle_opts=opts.TextStyleOpts(color=&apos;red&apos;), #图例字体颜色
            #Pyecharts常见图例
            legend_icon=&apos;roundRect&apos;,#circle,rect,roundRect,triangle,diamond,arrow
        ),
        
        #视觉映射配置项
        visualmap_opts=opts.VisualMapOpts(
            is_show=True,
            type_=&apos;color&apos;, # color/size
            min_=0, #最小值
            max_=150, #最大值
            range_opacity=0.8, #透明度
            range_text=[&apos;max&apos;,&apos;min&apos;],#两段文本
            #range_color=[&apos;#E7C2CA&apos;,&apos;#29AFD4&apos;,&apos;#E6755F&apos;], #过渡色
            orient=&apos;vertical&apos;,#horizontal水平放，vertical垂直放
            pos_right=0,
            pos_top=&apos;20%&apos;,
            is_piecewise=True, #是否分段
            # is_inverse=True, #是否反转
        ),
        
        #提示框配置项
        tooltip_opts=opts.TooltipOpts(
            is_show=True,
            #触发类型
            # item：数据项，一般用于：散点图，柱形图，饼图
            # axis： 坐标轴。提示线，主要用于条形图，折线图等
            trigger=&apos;item&apos;,
            #触发条件
            #mousemove,click,mousemove/click
            trigger_on=&apos;mousemove&apos;,
            is_show_content=True, #是否显示提示框浮层
            #标签内容的格式
            #字符串中的模板变量：
            # {a}:系列名series_name
            # {b}:数据名
            # {c}:值
            #formatter=&apos;{a}:{b}:{c}&apos;,
            #background_color=&apos;auto&apos;, #提示框背景色
            border_width=1,
            border_color=&apos;blue&apos;
        ),
        
        #AxisOpts: 坐标轴配置项
        xaxis_opts=opts.AxisOpts(
            is_show=True, #是否显示X轴
            #坐标轴类型：
            #value：数值轴，用于连续数据
            #category：类目轴，适用于离散数据，比如周一周二
            #time:时间轴，用于连续的时序数据
            type_=&apos;category&apos;,
        ),
        yaxis_opts=opts.AxisOpts(
            # is_show=False,
            #不显示Y轴的线
            axisline_opts=opts.AxisLineOpts(is_show=False),
            #不显示Y轴的刻度
            axistick_opts=opts.AxisTickOpts(is_show=False),
        ),
    )
)
c.load_javascript()
c.render_notebook()
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;&lt;img src=&quot;/images/in-post/post_pyecharts/bar_opt.png&quot; alt=&quot;img1&quot; /&gt;&lt;/p&gt;
&lt;h3&gt;系列配置项&lt;/h3&gt;
&lt;pre&gt;&lt;code&gt;d = (
    Line(
        #初始化配置项
        init_opts=opts.InitOpts(
            width=&apos;900px&apos;,
            height=&apos;500px&apos;,
            renderer=RenderType.CANVAS, #渲染风格
            page_title=&apos;网页标题&apos;,
            theme=ThemeType.WHITE, #主题
            bg_color=&apos;white&apos;
        )
    )
    .add_xaxis(Faker.choose())
    .add_yaxis(&apos;商家A&apos;,Faker.values())
    .add_yaxis(&apos;商家B&apos;,Faker.values())
    
    #全局配置项
    .set_global_opts(
        title_opts=opts.TitleOpts(title=&apos;折线图&apos;),
        #提示线
        tooltip_opts=opts.TooltipOpts(trigger=&apos;axis&apos;),
    )
    
    #系列配置项
    .set_series_opts(
        #图元样式配置项
        itemstyle_opts=opts.ItemStyleOpts(
            #图的颜色，使用纯色/RGB/RGBA/#ccc
            # color=&apos;red&apos;,
            opacity=0.8, #透明度
            # border_type=&apos;dotted&apos;,
            border_width=20
        ),
        
        #线样式配置项
        linestyle_opts=opts.LineStyleOpts(
            is_show=True,
            width=5,
            # color=&apos;blue&apos;,
            type_=&apos;solid&apos;, #solid,dashed,dotted
        ),
        
        #标签配置项
        label_opts=opts.LabelOpts(
            is_show=True,
            #数值在点的位置（top,left,right,bottom,inside,outside,insideleft）
            position=&apos;bottom&apos;, 
            color=&apos;auto&apos;,
            font_size=20,
            font_style=&apos;italic&apos;,
            font_weight=&apos;bold&apos;,
            #标签旋转(-90,90)
            rotate=20
        ),
        
        #标记点配置项
        markpoint_opts=opts.MarkPointOpts(
            data=[
                #type_:特殊标记类型,min,max,average
                #symbol:cirecle,pin,roundRect,rect...不建议改
                #symbol_size:标记点大小
                opts.MarkPointItem(type_=&apos;max&apos;,symbol=&apos;pin&apos;,symbol_size=60),
                opts.MarkPointItem(type_=&apos;min&apos;,symbol=&apos;pin&apos;,symbol_size=60)
            ]
        ),
        markline_opts=opts.MarkLineOpts(
            data = [
                opts.MarkLineItem(type_=&apos;average&apos;)
            ],
            label_opts=opts.LabelOpts(
                color=&apos;red&apos;
            )
        )
    )
)
d.render_notebook()
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;&lt;img src=&quot;/images/in-post/post_pyecharts/line_opt.png&quot; alt=&quot;img2&quot; /&gt;&lt;/p&gt;
</content:encoded></item><item><title>pyecharts图形在jupyter中不显示的解决办法</title><link>https://meteor-comet.github.io/posts/echarts-solve/</link><guid isPermaLink="true">https://meteor-comet.github.io/posts/echarts-solve/</guid><pubDate>Tue, 09 Aug 2022 00:00:00 GMT</pubDate><content:encoded>&lt;h3&gt;问题场景描述：在利用pyecharts库进行数据可视化学习过程中，遇到了在Jupyter Lab环境中执行bar.render_notebook()后图形未能正常显示的问题。&lt;/h3&gt;
&lt;pre&gt;&lt;code&gt;from pyecharts.charts import Bar
bar = Bar()
bar.add_xaxis([&quot;衬衫&quot;, &quot;羊毛衫&quot;, &quot;雪纺衫&quot;, &quot;裤子&quot;, &quot;高跟鞋&quot;, &quot;袜子&quot;])
bar.add_yaxis(&apos;商家&apos;,[5, 20, 36, 10, 75, 90])
bar.render_notebook()
&lt;/code&gt;&lt;/pre&gt;
&lt;h3&gt;问题解决策略：&lt;/h3&gt;
&lt;p&gt;经过查阅&amp;lt;a href=&quot;https://pyecharts.org/#/zh-cn/notebook?id=jupyter-lab&quot; target=&quot;_blank&quot;&amp;gt;官方文档&amp;lt;/a&amp;gt;发现问题源于Jupyter Lab特有的渲染机制，需按以下步骤进行调整：&lt;/p&gt;
&lt;h4&gt;1. 在顶部声明 Notebook 类型，必须在引入 pyecharts.charts 等模块前声明&lt;/h4&gt;
&lt;pre&gt;&lt;code&gt;from pyecharts.globals import CurrentConfig, NotebookType
CurrentConfig.NOTEBOOK_TYPE = NotebookType.JUPYTER_LAB
&lt;/code&gt;&lt;/pre&gt;
&lt;h4&gt;2. 在第一次渲染的时候调用 load_javascript() 会预先加载基本 JavaScript 文件到 Notebook 中。如若后面其他图形渲染不出来，则请开发者尝试再次调用，因为 load_javascript 只会预先加载最基本的 js 引用。而主题、地图等 js 文件需要再次按需加载。&lt;/h4&gt;
&lt;h4&gt;3. load_javascript() 和 render_notebook() 方法需要在不同的 cell 中调用，这是 Notebook 的内联机制，其实本质上我们是返回了带有 &lt;em&gt;html&lt;/em&gt;, &lt;em&gt;javascript&lt;/em&gt; 对象的 class。notebook 会自动去调用这些方法。&lt;/h4&gt;
&lt;pre&gt;&lt;code&gt;from pyecharts.charts import Bar
bar = Bar()
bar.add_xaxis([&quot;衬衫&quot;, &quot;羊毛衫&quot;, &quot;雪纺衫&quot;, &quot;裤子&quot;, &quot;高跟鞋&quot;, &quot;袜子&quot;])
bar.add_yaxis(&apos;商家&apos;,[5, 20, 36, 10, 75, 90])
bar.load_javascript()
bar.render_notebook()
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;成功运行&lt;/p&gt;
&lt;p&gt;Tips：其他Notebook渲染问题，如Jupyter Notebook、Nteract、Zeppelin可查看&amp;lt;a href=&quot;https://pyecharts.org/#/zh-cn/notebook&quot; target=&quot;:_blank&quot;&amp;gt;官方文档&amp;lt;/a&amp;gt;&lt;/p&gt;
</content:encoded></item><item><title>VPN节点搭建完整教程</title><link>https://meteor-comet.github.io/posts/vpn-node-setup/</link><guid isPermaLink="true">https://meteor-comet.github.io/posts/vpn-node-setup/</guid><description>从VPS购买到Netflix解锁</description><pubDate>Mon, 01 Aug 2022 00:00:00 GMT</pubDate><content:encoded>&lt;h2&gt;目录&lt;/h2&gt;
&lt;ol&gt;
&lt;li&gt;&lt;a href=&quot;#vps-purchase&quot;&gt;购买并初始化VPS&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;#server-connection&quot;&gt;连接服务器并进行基准测试&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;#install-xui&quot;&gt;安装X-UI面板&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;#create-node&quot;&gt;创建节点&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;#client-test&quot;&gt;客户端连接与基础测试&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;#warp-config&quot;&gt;配置WARP分流，解锁Netflix&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;#summary&quot;&gt;总结&lt;/a&gt;&lt;/li&gt;
&lt;/ol&gt;
&lt;h2&gt;VPN节点搭建完整教程&lt;/h2&gt;
&lt;h3&gt;第一步：购买并初始化你的VPS&lt;/h3&gt;
&lt;p&gt;以CloudCone (CC)的$15/年特价VPS为例：&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;strong&gt;选择配置&lt;/strong&gt;：2核CPU, 2GB内存, 120GB SSD&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;选择系统&lt;/strong&gt;：Ubuntu 22.04（兼容性最好的主流选择）&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;完成支付&lt;/strong&gt;：支持支付宝、PayPal、信用卡等，CC需要先充值到账户余额&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;获取信息&lt;/strong&gt;：创建成功后，获取服务器IP地址、用户名（通常是root）和初始密码&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;&lt;strong&gt;VPS推荐&lt;/strong&gt;：&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;10美元VPS：&lt;code&gt;https://app.cloudcone.com/?ref=13734&lt;/code&gt;&lt;/li&gt;
&lt;li&gt;住宅IP VPS：&lt;code&gt;https://ipraft.com/?i6c1ab5&lt;/code&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;h3&gt;第二步：连接服务器并进行基准测试&lt;/h3&gt;
&lt;ul&gt;
&lt;li&gt;使用SSH客户端（如Termius, Xshell, Putty）连接服务器&lt;/li&gt;
&lt;li&gt;运行综合性测试脚本，了解CPU、内存、硬盘性能及流媒体解锁能力：&lt;/li&gt;
&lt;/ul&gt;
&lt;pre&gt;&lt;code&gt;curl -L https://gitlab.com/spiritysdx/za/-/raw/main/ecs.sh -o ecs.sh &amp;amp;&amp;amp; chmod +x ecs.sh &amp;amp;&amp;amp; bash ecs.sh
&lt;/code&gt;&lt;/pre&gt;
&lt;h3&gt;第三步：使用一键脚本安装X-UI面板&lt;/h3&gt;
&lt;p&gt;使用“甬哥”一键脚本安装图形化X-UI面板：&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;bash &amp;lt;(wget -qO- https://raw.githubusercontent.com/yonggekkk/x-ui-yg/main/install.sh)
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;安装过程中：&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;选择1安装X-UI&lt;/li&gt;
&lt;li&gt;选择1关闭防火墙（新手推荐）&lt;/li&gt;
&lt;li&gt;设置面板登录名、密码和端口以及根路径&lt;/li&gt;
&lt;/ul&gt;
&lt;h3&gt;第四步：在X-UI中创建你的第一个节点&lt;/h3&gt;
&lt;ul&gt;
&lt;li&gt;通过&lt;code&gt;你的IP:你设置的端口&lt;/code&gt;访问X-UI网页后台&lt;/li&gt;
&lt;li&gt;进入“节点列表”，点击“添加节点”&lt;/li&gt;
&lt;li&gt;协议配置：选择VMess协议，传输协议选择ws (WebSocket)，其他保持默认&lt;/li&gt;
&lt;li&gt;点击添加，然后复制生成的分享链接&lt;/li&gt;
&lt;/ul&gt;
&lt;h3&gt;第五步：客户端连接与基础测试&lt;/h3&gt;
&lt;ul&gt;
&lt;li&gt;将分享链接导入客户端（如v2rayN, Clash等）&lt;/li&gt;
&lt;li&gt;连接后进行测速，尝试访问Google, YouTube等网站&lt;/li&gt;
&lt;li&gt;注意：此时访问Netflix可能只能观看自制剧，这是原生IP被限制&lt;/li&gt;
&lt;/ul&gt;
&lt;h3&gt;第六步：【进阶技巧】配置WARP分流，解锁Netflix&lt;/h3&gt;
&lt;p&gt;利用Cloudflare WARP为VPS“套上”更纯净的IP，只让Netflix流量走这个IP：&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;
&lt;p&gt;&lt;strong&gt;安装WARP SOCKS5代理&lt;/strong&gt;：&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;重新运行甬哥脚本&lt;/li&gt;
&lt;li&gt;选择11进入Cloudflare WARP相关功能&lt;/li&gt;
&lt;li&gt;选择3安装WARP SOCKS5代理，然后选择2 (IPv4)完成安装&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;&lt;strong&gt;配置X-UI出站分流&lt;/strong&gt;：&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;回到X-UI面板，进入“面板设置”-&amp;gt;“Xray相关设置”-&amp;gt;“出站设置”&lt;/li&gt;
&lt;li&gt;在routing的rules数组中添加以下规则：&lt;/li&gt;
&lt;/ul&gt;
&lt;pre&gt;&lt;code&gt;{ &quot;type&quot;: &quot;field&quot;, &quot;outboundTag&quot;: &quot;xray-socks5-warp-v4&quot;, &quot;domain&quot;: [&quot;geosite:netflix&quot;] }
&lt;/code&gt;&lt;/pre&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;&lt;strong&gt;保存并重启&lt;/strong&gt;：保存配置并重启X-UI面板&lt;/p&gt;
&lt;/li&gt;
&lt;/ol&gt;
&lt;h3&gt;总结&lt;/h3&gt;
&lt;p&gt;自建节点看似复杂，但借助强大的一键脚本，整个过程可以变得非常简单。通过基础的节点搭建和进阶的WARP分流，你不仅可以拥有一个稳定、私密的专属网络通道，还能享受到无限制的流媒体服务。&lt;/p&gt;
</content:encoded></item><item><title>日语学习日志</title><link>https://meteor-comet.github.io/posts/japanese-learning-log/</link><guid isPermaLink="true">https://meteor-comet.github.io/posts/japanese-learning-log/</guid><pubDate>Fri, 01 Jul 2022 00:00:00 GMT</pubDate><content:encoded>&lt;h1&gt;日语学习日志&lt;/h1&gt;
&lt;ul&gt;
&lt;li&gt;假名复习&lt;/li&gt;
&lt;li&gt;基础单词积累&lt;/li&gt;
&lt;li&gt;简单句型练习&lt;/li&gt;
&lt;/ul&gt;
&lt;blockquote&gt;
&lt;p&gt;每天进步一点点！&lt;/p&gt;
&lt;/blockquote&gt;
&lt;hr /&gt;
&lt;h2&gt;《新标日》初级上册 前三课语法与单词整理&lt;/h2&gt;
&lt;h3&gt;第一课：初次见面&lt;/h3&gt;
&lt;p&gt;&lt;strong&gt;语法要点：&lt;/strong&gt;&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;1. 名词は名词です。&lt;/strong&gt;&lt;/p&gt;
&lt;p&gt;用于说明主语的身份或属性，相当于中文的“……是……”。&lt;/p&gt;
&lt;p&gt;例句：&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;私は学生です。（我是学生。）&lt;/li&gt;
&lt;li&gt;彼は李さんです。（他是小李。）&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;&lt;strong&gt;2. 名词は名词ではありません。&lt;/strong&gt;&lt;/p&gt;
&lt;p&gt;表示否定判断，相当于中文的“……不是……”。&lt;/p&gt;
&lt;p&gt;例句：&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;私は先生ではありません。（我不是老师。）&lt;/li&gt;
&lt;li&gt;これは辞書ではありません。（这不是词典。）&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;&lt;strong&gt;3. ～か。&lt;/strong&gt;&lt;/p&gt;
&lt;p&gt;句末加上“か”构成疑问句，相当于中文的“……吗？”。&lt;/p&gt;
&lt;p&gt;例句：&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;あなたは王さんですか。（你是小王吗？）&lt;/li&gt;
&lt;li&gt;これは本ですか。（这是书吗？）&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;&lt;strong&gt;4. 疑问词：&lt;/strong&gt;&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;誰（だれ）：谁&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;例句：&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;あの方はどなたですか。（那位是谁？）&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;“どなた”是“谁”的礼貌说法。&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;5. 变形规则：&lt;/strong&gt;&lt;/p&gt;
&lt;p&gt;desu为敬体形，无需变形，但要注意否定形式是“ではありません”或口语中的“じゃありません”。&lt;/p&gt;
&lt;hr /&gt;
&lt;h3&gt;第二课：这是书&lt;/h3&gt;
&lt;p&gt;&lt;strong&gt;语法要点：&lt;/strong&gt;&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;1. これ・それ・あれ・どれ&lt;/strong&gt;&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;これ：这（离说话人近）&lt;/li&gt;
&lt;li&gt;それ：那（离听话人近）&lt;/li&gt;
&lt;li&gt;あれ：那（离双方都远）&lt;/li&gt;
&lt;li&gt;どれ：哪个（疑问词）&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;例句：&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;これは私の本です。（这是我的书。）&lt;/li&gt;
&lt;li&gt;あれは山です。（那是山。）&lt;/li&gt;
&lt;li&gt;それは何ですか。（那是什么？）&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;&lt;strong&gt;2. この・その・あの・どの＋名词&lt;/strong&gt;&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;この本：这本书&lt;/li&gt;
&lt;li&gt;その人：那个人&lt;/li&gt;
&lt;li&gt;あの車：那辆车&lt;/li&gt;
&lt;li&gt;どの方：哪位&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;例句：&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;この人は李さんです。（这位是小李。）&lt;/li&gt;
&lt;li&gt;どの本があなたのですか。（哪本书是你的？）&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;&lt;strong&gt;3. 名词1の名词2&lt;/strong&gt;&lt;/p&gt;
&lt;p&gt;表示名词1对名词2的修饰关系，可以表示所属、性质等。&lt;/p&gt;
&lt;p&gt;例句：&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;私の名前は王です。（我的名字是王。）&lt;/li&gt;
&lt;li&gt;日本の车です。（是日本的车。）&lt;/li&gt;
&lt;/ul&gt;
&lt;hr /&gt;
&lt;h3&gt;第三课：这里是大学&lt;/h3&gt;
&lt;p&gt;&lt;strong&gt;语法要点：&lt;/strong&gt;&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;1. ここ・そこ・あそこ・どこ&lt;/strong&gt;&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;ここ：这里（离说话人近）&lt;/li&gt;
&lt;li&gt;そこ：那里（离听话人近）&lt;/li&gt;
&lt;li&gt;あそこ：那里（离双方都远）&lt;/li&gt;
&lt;li&gt;どこ：哪里（疑问词）&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;例句：&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;ここは教室です。（这里是教室。）&lt;/li&gt;
&lt;li&gt;トイレはどこですか。（卫生间在哪里？）&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;&lt;strong&gt;2. 名词は名词（场所）です。&lt;/strong&gt;&lt;/p&gt;
&lt;p&gt;表示主语在某个场所。&lt;/p&gt;
&lt;p&gt;例句：&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;事務所はあそこです。（事务所在那里。）&lt;/li&gt;
&lt;li&gt;李さんはどこですか。（小李在哪里？）&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;&lt;strong&gt;3. お国はどちらですか。&lt;/strong&gt;&lt;/p&gt;
&lt;p&gt;询问对方的国家、公司等，更礼貌的说法。&lt;/p&gt;
&lt;p&gt;例句：&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;お国はどちらですか。（您是哪个国家的？）&lt;/li&gt;
&lt;li&gt;日本です。（日本。）&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;&lt;strong&gt;4. 名词も名词です。&lt;/strong&gt;&lt;/p&gt;
&lt;p&gt;表示并列，“……也是……”。&lt;/p&gt;
&lt;p&gt;例句：&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;私も学生です。（我也是学生。）&lt;/li&gt;
&lt;li&gt;彼も中国人です。（他也是中国人。）&lt;/li&gt;
&lt;/ul&gt;
&lt;hr /&gt;
&lt;h2&gt;第1课 单词表&lt;/h2&gt;
&lt;h4&gt;国籍/人物&lt;/h4&gt;
&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;日文&lt;/th&gt;
&lt;th&gt;假名&lt;/th&gt;
&lt;th&gt;词性&lt;/th&gt;
&lt;th&gt;中文释义&lt;/th&gt;
&lt;th&gt;例句&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;中国人&lt;/td&gt;
&lt;td&gt;ちゅうごくじん&lt;/td&gt;
&lt;td&gt;名&lt;/td&gt;
&lt;td&gt;中国人&lt;/td&gt;
&lt;td&gt;私は中国人です。&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;日本人&lt;/td&gt;
&lt;td&gt;にほんじん&lt;/td&gt;
&lt;td&gt;名&lt;/td&gt;
&lt;td&gt;日本人&lt;/td&gt;
&lt;td&gt;彼は日本人です。&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;韩国人&lt;/td&gt;
&lt;td&gt;かんこくじん&lt;/td&gt;
&lt;td&gt;名&lt;/td&gt;
&lt;td&gt;韩国人&lt;/td&gt;
&lt;td&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;美国人&lt;/td&gt;
&lt;td&gt;アメリカじん&lt;/td&gt;
&lt;td&gt;名&lt;/td&gt;
&lt;td&gt;美国人&lt;/td&gt;
&lt;td&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;法国人&lt;/td&gt;
&lt;td&gt;フランスじん&lt;/td&gt;
&lt;td&gt;名&lt;/td&gt;
&lt;td&gt;法国人&lt;/td&gt;
&lt;td&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;李&lt;/td&gt;
&lt;td&gt;り&lt;/td&gt;
&lt;td&gt;专&lt;/td&gt;
&lt;td&gt;李&lt;/td&gt;
&lt;td&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;王&lt;/td&gt;
&lt;td&gt;おう&lt;/td&gt;
&lt;td&gt;专&lt;/td&gt;
&lt;td&gt;王&lt;/td&gt;
&lt;td&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;张&lt;/td&gt;
&lt;td&gt;ちょう&lt;/td&gt;
&lt;td&gt;专&lt;/td&gt;
&lt;td&gt;张&lt;/td&gt;
&lt;td&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;森&lt;/td&gt;
&lt;td&gt;もり&lt;/td&gt;
&lt;td&gt;专&lt;/td&gt;
&lt;td&gt;森&lt;/td&gt;
&lt;td&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;林&lt;/td&gt;
&lt;td&gt;はやし&lt;/td&gt;
&lt;td&gt;专&lt;/td&gt;
&lt;td&gt;林&lt;/td&gt;
&lt;td&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;小野&lt;/td&gt;
&lt;td&gt;おの&lt;/td&gt;
&lt;td&gt;专&lt;/td&gt;
&lt;td&gt;小野&lt;/td&gt;
&lt;td&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;吉田&lt;/td&gt;
&lt;td&gt;よしだ&lt;/td&gt;
&lt;td&gt;专&lt;/td&gt;
&lt;td&gt;吉田&lt;/td&gt;
&lt;td&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;田中&lt;/td&gt;
&lt;td&gt;たなか&lt;/td&gt;
&lt;td&gt;专&lt;/td&gt;
&lt;td&gt;田中&lt;/td&gt;
&lt;td&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;中村&lt;/td&gt;
&lt;td&gt;なかむら&lt;/td&gt;
&lt;td&gt;专&lt;/td&gt;
&lt;td&gt;中村&lt;/td&gt;
&lt;td&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;太郎&lt;/td&gt;
&lt;td&gt;たろう&lt;/td&gt;
&lt;td&gt;专&lt;/td&gt;
&lt;td&gt;太郎&lt;/td&gt;
&lt;td&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;金&lt;/td&gt;
&lt;td&gt;キム&lt;/td&gt;
&lt;td&gt;专&lt;/td&gt;
&lt;td&gt;金&lt;/td&gt;
&lt;td&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;迪蓬&lt;/td&gt;
&lt;td&gt;デュポン&lt;/td&gt;
&lt;td&gt;专&lt;/td&gt;
&lt;td&gt;迪蓬&lt;/td&gt;
&lt;td&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;史密斯&lt;/td&gt;
&lt;td&gt;スミス&lt;/td&gt;
&lt;td&gt;专&lt;/td&gt;
&lt;td&gt;史密斯&lt;/td&gt;
&lt;td&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;约翰逊&lt;/td&gt;
&lt;td&gt;ジョンソン&lt;/td&gt;
&lt;td&gt;专&lt;/td&gt;
&lt;td&gt;约翰逊&lt;/td&gt;
&lt;td&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;中国&lt;/td&gt;
&lt;td&gt;ちゅうごく&lt;/td&gt;
&lt;td&gt;专&lt;/td&gt;
&lt;td&gt;中国&lt;/td&gt;
&lt;td&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;东京大学&lt;/td&gt;
&lt;td&gt;とうきょうだいがく&lt;/td&gt;
&lt;td&gt;专&lt;/td&gt;
&lt;td&gt;东京大学&lt;/td&gt;
&lt;td&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;北京大学&lt;/td&gt;
&lt;td&gt;ペキンだいがく&lt;/td&gt;
&lt;td&gt;专&lt;/td&gt;
&lt;td&gt;北京大学&lt;/td&gt;
&lt;td&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;JC策划公司&lt;/td&gt;
&lt;td&gt;ジェーシーきかく&lt;/td&gt;
&lt;td&gt;专&lt;/td&gt;
&lt;td&gt;JC策划公司&lt;/td&gt;
&lt;td&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;北京旅行社&lt;/td&gt;
&lt;td&gt;ペキンりょこうしゃ&lt;/td&gt;
&lt;td&gt;专&lt;/td&gt;
&lt;td&gt;北京旅行社&lt;/td&gt;
&lt;td&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;日中商社&lt;/td&gt;
&lt;td&gt;にっちゅうしょうじ&lt;/td&gt;
&lt;td&gt;专&lt;/td&gt;
&lt;td&gt;日中商社&lt;/td&gt;
&lt;td&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;
&lt;h4&gt;职业&lt;/h4&gt;
&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;日文&lt;/th&gt;
&lt;th&gt;假名&lt;/th&gt;
&lt;th&gt;词性&lt;/th&gt;
&lt;th&gt;中文释义&lt;/th&gt;
&lt;th&gt;例句&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;学生&lt;/td&gt;
&lt;td&gt;がくせい&lt;/td&gt;
&lt;td&gt;名&lt;/td&gt;
&lt;td&gt;（大）学生&lt;/td&gt;
&lt;td&gt;私は学生です。&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;先生&lt;/td&gt;
&lt;td&gt;せんせい&lt;/td&gt;
&lt;td&gt;名&lt;/td&gt;
&lt;td&gt;老师&lt;/td&gt;
&lt;td&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;留学生&lt;/td&gt;
&lt;td&gt;りゅうがくせい&lt;/td&gt;
&lt;td&gt;名&lt;/td&gt;
&lt;td&gt;留学生&lt;/td&gt;
&lt;td&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;教授&lt;/td&gt;
&lt;td&gt;きょうじゅ&lt;/td&gt;
&lt;td&gt;名&lt;/td&gt;
&lt;td&gt;教授&lt;/td&gt;
&lt;td&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;职员&lt;/td&gt;
&lt;td&gt;しゃいん&lt;/td&gt;
&lt;td&gt;名&lt;/td&gt;
&lt;td&gt;职员&lt;/td&gt;
&lt;td&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;公司职员&lt;/td&gt;
&lt;td&gt;かいしゃいん&lt;/td&gt;
&lt;td&gt;名&lt;/td&gt;
&lt;td&gt;公司职员&lt;/td&gt;
&lt;td&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;店员&lt;/td&gt;
&lt;td&gt;てんいん&lt;/td&gt;
&lt;td&gt;名&lt;/td&gt;
&lt;td&gt;店员&lt;/td&gt;
&lt;td&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;进修生&lt;/td&gt;
&lt;td&gt;けんしゅうせい&lt;/td&gt;
&lt;td&gt;名&lt;/td&gt;
&lt;td&gt;进修生&lt;/td&gt;
&lt;td&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;企业&lt;/td&gt;
&lt;td&gt;きぎょう&lt;/td&gt;
&lt;td&gt;名&lt;/td&gt;
&lt;td&gt;企业&lt;/td&gt;
&lt;td&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;大学&lt;/td&gt;
&lt;td&gt;だいがく&lt;/td&gt;
&lt;td&gt;名&lt;/td&gt;
&lt;td&gt;大学&lt;/td&gt;
&lt;td&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;科长&lt;/td&gt;
&lt;td&gt;かちょう&lt;/td&gt;
&lt;td&gt;名&lt;/td&gt;
&lt;td&gt;科长&lt;/td&gt;
&lt;td&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;总经理/社长&lt;/td&gt;
&lt;td&gt;しゃちょう&lt;/td&gt;
&lt;td&gt;名&lt;/td&gt;
&lt;td&gt;总经理，社长&lt;/td&gt;
&lt;td&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;父亲&lt;/td&gt;
&lt;td&gt;ちち&lt;/td&gt;
&lt;td&gt;名&lt;/td&gt;
&lt;td&gt;（我）父亲&lt;/td&gt;
&lt;td&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;迎接&lt;/td&gt;
&lt;td&gt;でむかえ&lt;/td&gt;
&lt;td&gt;名&lt;/td&gt;
&lt;td&gt;迎接&lt;/td&gt;
&lt;td&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;那个人&lt;/td&gt;
&lt;td&gt;あのひと&lt;/td&gt;
&lt;td&gt;名&lt;/td&gt;
&lt;td&gt;那个人&lt;/td&gt;
&lt;td&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;我&lt;/td&gt;
&lt;td&gt;をたし&lt;/td&gt;
&lt;td&gt;代&lt;/td&gt;
&lt;td&gt;我&lt;/td&gt;
&lt;td&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;你&lt;/td&gt;
&lt;td&gt;あなた&lt;/td&gt;
&lt;td&gt;代&lt;/td&gt;
&lt;td&gt;你&lt;/td&gt;
&lt;td&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;
&lt;h4&gt;常用表达&lt;/h4&gt;
&lt;ul&gt;
&lt;li&gt;&lt;strong&gt;こんにちは&lt;/strong&gt;：你好&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;すみません&lt;/strong&gt;：对不起，请问&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;どうぞ&lt;/strong&gt;：请&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;よろしくおねがいします&lt;/strong&gt;（～お願いします）：请多关照&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;はじめまして&lt;/strong&gt;：初次见面&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;こちらこそ&lt;/strong&gt;：我才要（请您～）&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;そうです&lt;/strong&gt;：是（这样）&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;ちがいます&lt;/strong&gt;：不是&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;わかりません&lt;/strong&gt;（分かりません）：不知道&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;どうもすみません&lt;/strong&gt;：实在对不起&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;～さん∕～ちゅん∕～君くん&lt;/strong&gt;：称呼后缀&lt;/li&gt;
&lt;/ul&gt;
&lt;hr /&gt;
&lt;h2&gt;第2课 单词表&lt;/h2&gt;
&lt;h4&gt;物品&lt;/h4&gt;
&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;日文&lt;/th&gt;
&lt;th&gt;假名&lt;/th&gt;
&lt;th&gt;词性&lt;/th&gt;
&lt;th&gt;中文释义&lt;/th&gt;
&lt;th&gt;例句&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;书&lt;/td&gt;
&lt;td&gt;ほん&lt;/td&gt;
&lt;td&gt;名&lt;/td&gt;
&lt;td&gt;书&lt;/td&gt;
&lt;td&gt;これは本です。&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;包&lt;/td&gt;
&lt;td&gt;かばん&lt;/td&gt;
&lt;td&gt;名&lt;/td&gt;
&lt;td&gt;包，公文包&lt;/td&gt;
&lt;td&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;笔记本&lt;/td&gt;
&lt;td&gt;ノート&lt;/td&gt;
&lt;td&gt;名&lt;/td&gt;
&lt;td&gt;笔记本，本子&lt;/td&gt;
&lt;td&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;铅笔&lt;/td&gt;
&lt;td&gt;えんぴつ&lt;/td&gt;
&lt;td&gt;名&lt;/td&gt;
&lt;td&gt;铅笔&lt;/td&gt;
&lt;td&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;伞&lt;/td&gt;
&lt;td&gt;かさ&lt;/td&gt;
&lt;td&gt;名&lt;/td&gt;
&lt;td&gt;伞&lt;/td&gt;
&lt;td&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;鞋&lt;/td&gt;
&lt;td&gt;くつ&lt;/td&gt;
&lt;td&gt;名&lt;/td&gt;
&lt;td&gt;鞋&lt;/td&gt;
&lt;td&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;报纸&lt;/td&gt;
&lt;td&gt;しんぶん&lt;/td&gt;
&lt;td&gt;名&lt;/td&gt;
&lt;td&gt;报纸&lt;/td&gt;
&lt;td&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;杂志&lt;/td&gt;
&lt;td&gt;ざっし&lt;/td&gt;
&lt;td&gt;名&lt;/td&gt;
&lt;td&gt;杂志&lt;/td&gt;
&lt;td&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;词典&lt;/td&gt;
&lt;td&gt;じしょ&lt;/td&gt;
&lt;td&gt;名&lt;/td&gt;
&lt;td&gt;词典&lt;/td&gt;
&lt;td&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;照相机&lt;/td&gt;
&lt;td&gt;カメラ&lt;/td&gt;
&lt;td&gt;名&lt;/td&gt;
&lt;td&gt;照相机&lt;/td&gt;
&lt;td&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;电视机&lt;/td&gt;
&lt;td&gt;テレビ&lt;/td&gt;
&lt;td&gt;名&lt;/td&gt;
&lt;td&gt;电视机&lt;/td&gt;
&lt;td&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;个人电脑&lt;/td&gt;
&lt;td&gt;パソコン&lt;/td&gt;
&lt;td&gt;名&lt;/td&gt;
&lt;td&gt;个人电脑&lt;/td&gt;
&lt;td&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;收音机&lt;/td&gt;
&lt;td&gt;ラジオ&lt;/td&gt;
&lt;td&gt;名&lt;/td&gt;
&lt;td&gt;收音机&lt;/td&gt;
&lt;td&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;电话&lt;/td&gt;
&lt;td&gt;でんわ&lt;/td&gt;
&lt;td&gt;名&lt;/td&gt;
&lt;td&gt;电话&lt;/td&gt;
&lt;td&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;桌子&lt;/td&gt;
&lt;td&gt;つくえ&lt;/td&gt;
&lt;td&gt;名&lt;/td&gt;
&lt;td&gt;桌子，书桌&lt;/td&gt;
&lt;td&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;椅子&lt;/td&gt;
&lt;td&gt;いす&lt;/td&gt;
&lt;td&gt;名&lt;/td&gt;
&lt;td&gt;椅子&lt;/td&gt;
&lt;td&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;钥匙&lt;/td&gt;
&lt;td&gt;かぎ&lt;/td&gt;
&lt;td&gt;名&lt;/td&gt;
&lt;td&gt;钥匙，锁&lt;/td&gt;
&lt;td&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;钟/表&lt;/td&gt;
&lt;td&gt;とけい&lt;/td&gt;
&lt;td&gt;名&lt;/td&gt;
&lt;td&gt;钟，表&lt;/td&gt;
&lt;td&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;记事本&lt;/td&gt;
&lt;td&gt;てちょう&lt;/td&gt;
&lt;td&gt;名&lt;/td&gt;
&lt;td&gt;记事本&lt;/td&gt;
&lt;td&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;照片&lt;/td&gt;
&lt;td&gt;しゃしん&lt;/td&gt;
&lt;td&gt;名&lt;/td&gt;
&lt;td&gt;照片&lt;/td&gt;
&lt;td&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;车&lt;/td&gt;
&lt;td&gt;くるま&lt;/td&gt;
&lt;td&gt;名&lt;/td&gt;
&lt;td&gt;车&lt;/td&gt;
&lt;td&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;自行车&lt;/td&gt;
&lt;td&gt;じてんしゃ&lt;/td&gt;
&lt;td&gt;名&lt;/td&gt;
&lt;td&gt;自行车&lt;/td&gt;
&lt;td&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;礼物&lt;/td&gt;
&lt;td&gt;おみやげ&lt;/td&gt;
&lt;td&gt;名&lt;/td&gt;
&lt;td&gt;礼物&lt;/td&gt;
&lt;td&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;名产&lt;/td&gt;
&lt;td&gt;めいさんひん&lt;/td&gt;
&lt;td&gt;名&lt;/td&gt;
&lt;td&gt;特产，名产&lt;/td&gt;
&lt;td&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;丝绸&lt;/td&gt;
&lt;td&gt;シルク&lt;/td&gt;
&lt;td&gt;名&lt;/td&gt;
&lt;td&gt;丝绸&lt;/td&gt;
&lt;td&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;手绢&lt;/td&gt;
&lt;td&gt;ハンカチ&lt;/td&gt;
&lt;td&gt;名&lt;/td&gt;
&lt;td&gt;手绢&lt;/td&gt;
&lt;td&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;
&lt;h4&gt;人物/家庭&lt;/h4&gt;
&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;日文&lt;/th&gt;
&lt;th&gt;假名&lt;/th&gt;
&lt;th&gt;词性&lt;/th&gt;
&lt;th&gt;中文释义&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;公司&lt;/td&gt;
&lt;td&gt;かいしゃ&lt;/td&gt;
&lt;td&gt;名&lt;/td&gt;
&lt;td&gt;公司&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;方（敬称）&lt;/td&gt;
&lt;td&gt;かた&lt;/td&gt;
&lt;td&gt;名&lt;/td&gt;
&lt;td&gt;（敬称）位，人&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;人&lt;/td&gt;
&lt;td&gt;ひと&lt;/td&gt;
&lt;td&gt;名&lt;/td&gt;
&lt;td&gt;人&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;家族&lt;/td&gt;
&lt;td&gt;かぞく&lt;/td&gt;
&lt;td&gt;名&lt;/td&gt;
&lt;td&gt;家人，家属&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;母&lt;/td&gt;
&lt;td&gt;はは&lt;/td&gt;
&lt;td&gt;名&lt;/td&gt;
&lt;td&gt;（我）母亲&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;母亲&lt;/td&gt;
&lt;td&gt;おかあさん&lt;/td&gt;
&lt;td&gt;名&lt;/td&gt;
&lt;td&gt;母亲&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;
&lt;h4&gt;语言&lt;/h4&gt;
&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;日文&lt;/th&gt;
&lt;th&gt;假名&lt;/th&gt;
&lt;th&gt;词性&lt;/th&gt;
&lt;th&gt;中文释义&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;日语&lt;/td&gt;
&lt;td&gt;にほんご&lt;/td&gt;
&lt;td&gt;名&lt;/td&gt;
&lt;td&gt;日语&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;汉语&lt;/td&gt;
&lt;td&gt;ちゅうごくご&lt;/td&gt;
&lt;td&gt;名&lt;/td&gt;
&lt;td&gt;汉语，中文&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;
&lt;h4&gt;指示/疑问/连体词&lt;/h4&gt;
&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;日文&lt;/th&gt;
&lt;th&gt;假名&lt;/th&gt;
&lt;th&gt;词性&lt;/th&gt;
&lt;th&gt;中文释义&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;这&lt;/td&gt;
&lt;td&gt;これ&lt;/td&gt;
&lt;td&gt;代&lt;/td&gt;
&lt;td&gt;这，这个&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;那&lt;/td&gt;
&lt;td&gt;それ&lt;/td&gt;
&lt;td&gt;代&lt;/td&gt;
&lt;td&gt;那，那个&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;那&lt;/td&gt;
&lt;td&gt;あれ&lt;/td&gt;
&lt;td&gt;代&lt;/td&gt;
&lt;td&gt;那，那个&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;哪个&lt;/td&gt;
&lt;td&gt;どれ&lt;/td&gt;
&lt;td&gt;疑&lt;/td&gt;
&lt;td&gt;哪个&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;什么&lt;/td&gt;
&lt;td&gt;なん&lt;/td&gt;
&lt;td&gt;疑&lt;/td&gt;
&lt;td&gt;什么&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;谁&lt;/td&gt;
&lt;td&gt;だれ&lt;/td&gt;
&lt;td&gt;疑&lt;/td&gt;
&lt;td&gt;谁&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;哪位&lt;/td&gt;
&lt;td&gt;どなた&lt;/td&gt;
&lt;td&gt;疑&lt;/td&gt;
&lt;td&gt;哪位&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;这（连体）&lt;/td&gt;
&lt;td&gt;この&lt;/td&gt;
&lt;td&gt;连体&lt;/td&gt;
&lt;td&gt;这，这个&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;那（连体）&lt;/td&gt;
&lt;td&gt;その&lt;/td&gt;
&lt;td&gt;连体&lt;/td&gt;
&lt;td&gt;那，那个&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;那（连体）&lt;/td&gt;
&lt;td&gt;あの&lt;/td&gt;
&lt;td&gt;连体&lt;/td&gt;
&lt;td&gt;那，那个&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;哪个（连体）&lt;/td&gt;
&lt;td&gt;どの&lt;/td&gt;
&lt;td&gt;连体&lt;/td&gt;
&lt;td&gt;哪个&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;
&lt;h4&gt;其他表达/专有名词&lt;/h4&gt;
&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;日文&lt;/th&gt;
&lt;th&gt;假名&lt;/th&gt;
&lt;th&gt;词性&lt;/th&gt;
&lt;th&gt;中文释义&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;啊&lt;/td&gt;
&lt;td&gt;えっ&lt;/td&gt;
&lt;td&gt;叹&lt;/td&gt;
&lt;td&gt;啊&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;哇&lt;/td&gt;
&lt;td&gt;わあ&lt;/td&gt;
&lt;td&gt;叹&lt;/td&gt;
&lt;td&gt;哇&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;嗯，是&lt;/td&gt;
&lt;td&gt;ええ&lt;/td&gt;
&lt;td&gt;叹&lt;/td&gt;
&lt;td&gt;（应答）嗯，是&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;长岛&lt;/td&gt;
&lt;td&gt;ながしま&lt;/td&gt;
&lt;td&gt;专&lt;/td&gt;
&lt;td&gt;长岛&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;日本&lt;/td&gt;
&lt;td&gt;にほん&lt;/td&gt;
&lt;td&gt;专&lt;/td&gt;
&lt;td&gt;日本&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;汕头&lt;/td&gt;
&lt;td&gt;スワトウ&lt;/td&gt;
&lt;td&gt;专&lt;/td&gt;
&lt;td&gt;汕头&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;伦敦&lt;/td&gt;
&lt;td&gt;ロンドン&lt;/td&gt;
&lt;td&gt;专&lt;/td&gt;
&lt;td&gt;伦敦&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;
&lt;h4&gt;常用表达&lt;/h4&gt;
&lt;ul&gt;
&lt;li&gt;&lt;strong&gt;ありがとうございます&lt;/strong&gt;：谢谢&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;おいくつ&lt;/strong&gt;：多大&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;何なん～∕～歳さい&lt;/strong&gt;：几岁&lt;/li&gt;
&lt;/ul&gt;
&lt;hr /&gt;
&lt;h2&gt;第3课 单词表&lt;/h2&gt;
&lt;h4&gt;场所/建筑&lt;/h4&gt;
&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;日文&lt;/th&gt;
&lt;th&gt;假名&lt;/th&gt;
&lt;th&gt;词性&lt;/th&gt;
&lt;th&gt;中文释义&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;百货商店&lt;/td&gt;
&lt;td&gt;デパート&lt;/td&gt;
&lt;td&gt;名&lt;/td&gt;
&lt;td&gt;百货商店&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;食堂&lt;/td&gt;
&lt;td&gt;しょくどう&lt;/td&gt;
&lt;td&gt;名&lt;/td&gt;
&lt;td&gt;食堂&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;邮局&lt;/td&gt;
&lt;td&gt;ゆうびんきょく&lt;/td&gt;
&lt;td&gt;名&lt;/td&gt;
&lt;td&gt;邮局&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;银行&lt;/td&gt;
&lt;td&gt;ぎんこう&lt;/td&gt;
&lt;td&gt;名&lt;/td&gt;
&lt;td&gt;银行&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;图书馆&lt;/td&gt;
&lt;td&gt;としょかん&lt;/td&gt;
&lt;td&gt;名&lt;/td&gt;
&lt;td&gt;图书馆&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;（高级）公寓&lt;/td&gt;
&lt;td&gt;マンション&lt;/td&gt;
&lt;td&gt;名&lt;/td&gt;
&lt;td&gt;（高级）公寓&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;宾馆&lt;/td&gt;
&lt;td&gt;ホテル&lt;/td&gt;
&lt;td&gt;名&lt;/td&gt;
&lt;td&gt;宾馆&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;便利店&lt;/td&gt;
&lt;td&gt;コンビニ&lt;/td&gt;
&lt;td&gt;名&lt;/td&gt;
&lt;td&gt;便利店&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;咖啡馆&lt;/td&gt;
&lt;td&gt;きっさてん&lt;/td&gt;
&lt;td&gt;名&lt;/td&gt;
&lt;td&gt;咖啡馆&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;医院&lt;/td&gt;
&lt;td&gt;びょういん&lt;/td&gt;
&lt;td&gt;名&lt;/td&gt;
&lt;td&gt;医院&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;书店&lt;/td&gt;
&lt;td&gt;ほんや&lt;/td&gt;
&lt;td&gt;名&lt;/td&gt;
&lt;td&gt;书店&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;餐馆&lt;/td&gt;
&lt;td&gt;レストラン&lt;/td&gt;
&lt;td&gt;名&lt;/td&gt;
&lt;td&gt;餐馆，西餐馆&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;大楼&lt;/td&gt;
&lt;td&gt;ビル&lt;/td&gt;
&lt;td&gt;名&lt;/td&gt;
&lt;td&gt;大楼，大厦&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;建筑物&lt;/td&gt;
&lt;td&gt;たてもの&lt;/td&gt;
&lt;td&gt;名&lt;/td&gt;
&lt;td&gt;大楼，建筑物&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;柜台&lt;/td&gt;
&lt;td&gt;うりば&lt;/td&gt;
&lt;td&gt;名&lt;/td&gt;
&lt;td&gt;柜台，出售处&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;厕所&lt;/td&gt;
&lt;td&gt;トイレ&lt;/td&gt;
&lt;td&gt;名&lt;/td&gt;
&lt;td&gt;厕所，盥洗室&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;入口&lt;/td&gt;
&lt;td&gt;いりぐち&lt;/td&gt;
&lt;td&gt;名&lt;/td&gt;
&lt;td&gt;入口&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;事务所&lt;/td&gt;
&lt;td&gt;じむしょ&lt;/td&gt;
&lt;td&gt;名&lt;/td&gt;
&lt;td&gt;事务所，办事处&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;接待处&lt;/td&gt;
&lt;td&gt;うけつけ&lt;/td&gt;
&lt;td&gt;名&lt;/td&gt;
&lt;td&gt;接待处&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;大卖场&lt;/td&gt;
&lt;td&gt;バーゲンかいじょう&lt;/td&gt;
&lt;td&gt;名&lt;/td&gt;
&lt;td&gt;降价处理大卖场&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;自动扶梯&lt;/td&gt;
&lt;td&gt;エスカレーター&lt;/td&gt;
&lt;td&gt;名&lt;/td&gt;
&lt;td&gt;自动扶梯&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;
&lt;h4&gt;物品/生活&lt;/h4&gt;
&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;日文&lt;/th&gt;
&lt;th&gt;假名&lt;/th&gt;
&lt;th&gt;词性&lt;/th&gt;
&lt;th&gt;中文释义&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;衣服&lt;/td&gt;
&lt;td&gt;ふく&lt;/td&gt;
&lt;td&gt;名&lt;/td&gt;
&lt;td&gt;衣服&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;风衣/大衣&lt;/td&gt;
&lt;td&gt;コート&lt;/td&gt;
&lt;td&gt;名&lt;/td&gt;
&lt;td&gt;风衣，大衣&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;数码相机&lt;/td&gt;
&lt;td&gt;デジカメ&lt;/td&gt;
&lt;td&gt;名&lt;/td&gt;
&lt;td&gt;数码相机&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;国&lt;/td&gt;
&lt;td&gt;くに&lt;/td&gt;
&lt;td&gt;名&lt;/td&gt;
&lt;td&gt;国，国家&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;地图&lt;/td&gt;
&lt;td&gt;ちず&lt;/td&gt;
&lt;td&gt;名&lt;/td&gt;
&lt;td&gt;地图&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;旁边&lt;/td&gt;
&lt;td&gt;となり&lt;/td&gt;
&lt;td&gt;名&lt;/td&gt;
&lt;td&gt;旁边&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;周边&lt;/td&gt;
&lt;td&gt;しゅうへん&lt;/td&gt;
&lt;td&gt;名&lt;/td&gt;
&lt;td&gt;附近，周边&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;今天&lt;/td&gt;
&lt;td&gt;きょう&lt;/td&gt;
&lt;td&gt;名&lt;/td&gt;
&lt;td&gt;今天&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;星期三&lt;/td&gt;
&lt;td&gt;すいようび&lt;/td&gt;
&lt;td&gt;名&lt;/td&gt;
&lt;td&gt;星期三&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;星期四&lt;/td&gt;
&lt;td&gt;もくようび&lt;/td&gt;
&lt;td&gt;名&lt;/td&gt;
&lt;td&gt;星期四&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;
&lt;h4&gt;指示/疑问/代词&lt;/h4&gt;
&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;日文&lt;/th&gt;
&lt;th&gt;假名&lt;/th&gt;
&lt;th&gt;词性&lt;/th&gt;
&lt;th&gt;中文释义&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;这里&lt;/td&gt;
&lt;td&gt;ここ&lt;/td&gt;
&lt;td&gt;代&lt;/td&gt;
&lt;td&gt;这里，这儿&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;那里&lt;/td&gt;
&lt;td&gt;そこ&lt;/td&gt;
&lt;td&gt;代&lt;/td&gt;
&lt;td&gt;那里，那儿&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;那里&lt;/td&gt;
&lt;td&gt;あそこ&lt;/td&gt;
&lt;td&gt;代&lt;/td&gt;
&lt;td&gt;那里，那儿&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;这边&lt;/td&gt;
&lt;td&gt;こちら&lt;/td&gt;
&lt;td&gt;代&lt;/td&gt;
&lt;td&gt;这儿，这边&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;那边&lt;/td&gt;
&lt;td&gt;そちら&lt;/td&gt;
&lt;td&gt;代&lt;/td&gt;
&lt;td&gt;那儿，那边&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;那边&lt;/td&gt;
&lt;td&gt;あちら&lt;/td&gt;
&lt;td&gt;代&lt;/td&gt;
&lt;td&gt;那儿，那边&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;哪里&lt;/td&gt;
&lt;td&gt;どこ&lt;/td&gt;
&lt;td&gt;疑&lt;/td&gt;
&lt;td&gt;哪里，哪儿&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;哪边&lt;/td&gt;
&lt;td&gt;どちら&lt;/td&gt;
&lt;td&gt;疑&lt;/td&gt;
&lt;td&gt;哪儿，哪边&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;
&lt;h4&gt;其他表达/专有名词&lt;/h4&gt;
&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;日文&lt;/th&gt;
&lt;th&gt;假名&lt;/th&gt;
&lt;th&gt;词性&lt;/th&gt;
&lt;th&gt;中文释义&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;请问/对不起&lt;/td&gt;
&lt;td&gt;あのう&lt;/td&gt;
&lt;td&gt;叹&lt;/td&gt;
&lt;td&gt;请问，对不起&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;上海&lt;/td&gt;
&lt;td&gt;シャンハイ&lt;/td&gt;
&lt;td&gt;专&lt;/td&gt;
&lt;td&gt;上海&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;东京&lt;/td&gt;
&lt;td&gt;とうきょう&lt;/td&gt;
&lt;td&gt;专&lt;/td&gt;
&lt;td&gt;东京&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;
&lt;h4&gt;常用表达&lt;/h4&gt;
&lt;ul&gt;
&lt;li&gt;&lt;strong&gt;いくら&lt;/strong&gt;：多少钱&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;お～∕～階かい∕～円えん∕～曜日ようび&lt;/strong&gt;：楼层/日元/星期等表达&lt;/li&gt;
&lt;/ul&gt;
&lt;hr /&gt;
&lt;h2&gt;第4课 单词表&lt;/h2&gt;
&lt;h4&gt;房间/家具/家居&lt;/h4&gt;
&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;日文&lt;/th&gt;
&lt;th&gt;假名&lt;/th&gt;
&lt;th&gt;词性&lt;/th&gt;
&lt;th&gt;中文释义&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;房间&lt;/td&gt;
&lt;td&gt;へや&lt;/td&gt;
&lt;td&gt;名&lt;/td&gt;
&lt;td&gt;房间，屋子&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;院子&lt;/td&gt;
&lt;td&gt;にわ&lt;/td&gt;
&lt;td&gt;名&lt;/td&gt;
&lt;td&gt;院子&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;家&lt;/td&gt;
&lt;td&gt;いえ&lt;/td&gt;
&lt;td&gt;名&lt;/td&gt;
&lt;td&gt;家&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;起居室&lt;/td&gt;
&lt;td&gt;いま&lt;/td&gt;
&lt;td&gt;名&lt;/td&gt;
&lt;td&gt;起居室&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;冰箱&lt;/td&gt;
&lt;td&gt;れいぞうこ&lt;/td&gt;
&lt;td&gt;名&lt;/td&gt;
&lt;td&gt;冰箱&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;墙壁&lt;/td&gt;
&lt;td&gt;かべ&lt;/td&gt;
&lt;td&gt;名&lt;/td&gt;
&lt;td&gt;墙壁&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;开关&lt;/td&gt;
&lt;td&gt;スイッチ&lt;/td&gt;
&lt;td&gt;名&lt;/td&gt;
&lt;td&gt;开关&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;书架&lt;/td&gt;
&lt;td&gt;ほんだな&lt;/td&gt;
&lt;td&gt;名&lt;/td&gt;
&lt;td&gt;书架&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;床&lt;/td&gt;
&lt;td&gt;ベッド&lt;/td&gt;
&lt;td&gt;名&lt;/td&gt;
&lt;td&gt;床&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;盒子/箱子&lt;/td&gt;
&lt;td&gt;はこ&lt;/td&gt;
&lt;td&gt;名&lt;/td&gt;
&lt;td&gt;盒子，箱子&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;
&lt;h4&gt;生活用品/物品&lt;/h4&gt;
&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;日文&lt;/th&gt;
&lt;th&gt;假名&lt;/th&gt;
&lt;th&gt;词性&lt;/th&gt;
&lt;th&gt;中文释义&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;眼镜&lt;/td&gt;
&lt;td&gt;めがね&lt;/td&gt;
&lt;td&gt;名&lt;/td&gt;
&lt;td&gt;眼镜&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;录像机&lt;/td&gt;
&lt;td&gt;ビデオ&lt;/td&gt;
&lt;td&gt;名&lt;/td&gt;
&lt;td&gt;录像机&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;足球&lt;/td&gt;
&lt;td&gt;サッカーボール&lt;/td&gt;
&lt;td&gt;名&lt;/td&gt;
&lt;td&gt;足球&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;啤酒&lt;/td&gt;
&lt;td&gt;ビール&lt;/td&gt;
&lt;td&gt;名&lt;/td&gt;
&lt;td&gt;啤酒&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;威士忌&lt;/td&gt;
&lt;td&gt;ウイスキー&lt;/td&gt;
&lt;td&gt;名&lt;/td&gt;
&lt;td&gt;威士忌&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;
&lt;h4&gt;人/动物/家庭成员&lt;/h4&gt;
&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;日文&lt;/th&gt;
&lt;th&gt;假名&lt;/th&gt;
&lt;th&gt;词性&lt;/th&gt;
&lt;th&gt;中文释义&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;孩子&lt;/td&gt;
&lt;td&gt;こども&lt;/td&gt;
&lt;td&gt;名&lt;/td&gt;
&lt;td&gt;孩子，小孩&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;兄弟姐妹&lt;/td&gt;
&lt;td&gt;きょうだい&lt;/td&gt;
&lt;td&gt;名&lt;/td&gt;
&lt;td&gt;兄弟姐妹&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;父母&lt;/td&gt;
&lt;td&gt;りょうしん&lt;/td&gt;
&lt;td&gt;名&lt;/td&gt;
&lt;td&gt;父母，双亲&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;妹妹&lt;/td&gt;
&lt;td&gt;いもうと&lt;/td&gt;
&lt;td&gt;名&lt;/td&gt;
&lt;td&gt;妹妹&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;男&lt;/td&gt;
&lt;td&gt;おとこ&lt;/td&gt;
&lt;td&gt;名&lt;/td&gt;
&lt;td&gt;男&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;女&lt;/td&gt;
&lt;td&gt;おんな&lt;/td&gt;
&lt;td&gt;名&lt;/td&gt;
&lt;td&gt;女&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;猫&lt;/td&gt;
&lt;td&gt;ねこ&lt;/td&gt;
&lt;td&gt;名&lt;/td&gt;
&lt;td&gt;猫&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;狗&lt;/td&gt;
&lt;td&gt;いぬ&lt;/td&gt;
&lt;td&gt;名&lt;/td&gt;
&lt;td&gt;狗&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;学生&lt;/td&gt;
&lt;td&gt;せいと&lt;/td&gt;
&lt;td&gt;名&lt;/td&gt;
&lt;td&gt;学生&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;
&lt;h4&gt;方位/场所&lt;/h4&gt;
&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;日文&lt;/th&gt;
&lt;th&gt;假名&lt;/th&gt;
&lt;th&gt;词性&lt;/th&gt;
&lt;th&gt;中文释义&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;上面&lt;/td&gt;
&lt;td&gt;うえ&lt;/td&gt;
&lt;td&gt;名&lt;/td&gt;
&lt;td&gt;上面&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;外面&lt;/td&gt;
&lt;td&gt;そと&lt;/td&gt;
&lt;td&gt;名&lt;/td&gt;
&lt;td&gt;外面&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;里面/中间&lt;/td&gt;
&lt;td&gt;なか&lt;/td&gt;
&lt;td&gt;名&lt;/td&gt;
&lt;td&gt;里面，内部，中间&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;下面&lt;/td&gt;
&lt;td&gt;した&lt;/td&gt;
&lt;td&gt;名&lt;/td&gt;
&lt;td&gt;下面&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;前面&lt;/td&gt;
&lt;td&gt;まえ&lt;/td&gt;
&lt;td&gt;名&lt;/td&gt;
&lt;td&gt;前，前面&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;后面&lt;/td&gt;
&lt;td&gt;うしろ&lt;/td&gt;
&lt;td&gt;名&lt;/td&gt;
&lt;td&gt;后，后面&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;附近&lt;/td&gt;
&lt;td&gt;ちかく&lt;/td&gt;
&lt;td&gt;名&lt;/td&gt;
&lt;td&gt;附近，近旁&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;场所&lt;/td&gt;
&lt;td&gt;ばしょ&lt;/td&gt;
&lt;td&gt;名&lt;/td&gt;
&lt;td&gt;所在地，地方，场所&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;教室&lt;/td&gt;
&lt;td&gt;きょうしつ&lt;/td&gt;
&lt;td&gt;名&lt;/td&gt;
&lt;td&gt;教室&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;会议室&lt;/td&gt;
&lt;td&gt;かいぎしつ&lt;/td&gt;
&lt;td&gt;名&lt;/td&gt;
&lt;td&gt;会议室&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;图书室&lt;/td&gt;
&lt;td&gt;としょしつ&lt;/td&gt;
&lt;td&gt;名&lt;/td&gt;
&lt;td&gt;图书室&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;公园&lt;/td&gt;
&lt;td&gt;こうえん&lt;/td&gt;
&lt;td&gt;名&lt;/td&gt;
&lt;td&gt;公园&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;花店&lt;/td&gt;
&lt;td&gt;はなや&lt;/td&gt;
&lt;td&gt;名&lt;/td&gt;
&lt;td&gt;花店&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;小卖部&lt;/td&gt;
&lt;td&gt;ばいてん&lt;/td&gt;
&lt;td&gt;名&lt;/td&gt;
&lt;td&gt;小卖部，售货亭&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;车站&lt;/td&gt;
&lt;td&gt;えき&lt;/td&gt;
&lt;td&gt;名&lt;/td&gt;
&lt;td&gt;车站&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;地铁&lt;/td&gt;
&lt;td&gt;ちかてつ&lt;/td&gt;
&lt;td&gt;名&lt;/td&gt;
&lt;td&gt;地铁&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;树&lt;/td&gt;
&lt;td&gt;き&lt;/td&gt;
&lt;td&gt;名&lt;/td&gt;
&lt;td&gt;树，树木&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;
&lt;h4&gt;生活/表达/专有名词&lt;/h4&gt;
&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;日文&lt;/th&gt;
&lt;th&gt;假名&lt;/th&gt;
&lt;th&gt;词性&lt;/th&gt;
&lt;th&gt;中文释义&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;单身生活&lt;/td&gt;
&lt;td&gt;ひとりぐらし&lt;/td&gt;
&lt;td&gt;名&lt;/td&gt;
&lt;td&gt;单身生活&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;有，在（物）&lt;/td&gt;
&lt;td&gt;あります&lt;/td&gt;
&lt;td&gt;动1&lt;/td&gt;
&lt;td&gt;有，在（非意志者）&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;有，在（人）&lt;/td&gt;
&lt;td&gt;います&lt;/td&gt;
&lt;td&gt;动2&lt;/td&gt;
&lt;td&gt;有，在（具意志者）&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;啊，嗯&lt;/td&gt;
&lt;td&gt;ええと&lt;/td&gt;
&lt;td&gt;叹&lt;/td&gt;
&lt;td&gt;啊，嗯&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;横滨&lt;/td&gt;
&lt;td&gt;よこはま&lt;/td&gt;
&lt;td&gt;专&lt;/td&gt;
&lt;td&gt;横滨&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;名古屋&lt;/td&gt;
&lt;td&gt;なごや&lt;/td&gt;
&lt;td&gt;专&lt;/td&gt;
&lt;td&gt;名古屋&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;大阪&lt;/td&gt;
&lt;td&gt;おおさか&lt;/td&gt;
&lt;td&gt;专&lt;/td&gt;
&lt;td&gt;大阪&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;JR&lt;/td&gt;
&lt;td&gt;ジェーアール&lt;/td&gt;
&lt;td&gt;专&lt;/td&gt;
&lt;td&gt;JR&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;
&lt;hr /&gt;
&lt;ul&gt;
&lt;li&gt;复习了第1~4课的所有单词和1~3课语法点，巩固记忆，查漏补缺。&lt;/li&gt;
&lt;li&gt;重点回顾了各类名词、指示词、疑问词、方位表达、家庭成员、常用表达等。&lt;/li&gt;
&lt;li&gt;反复朗读和默写单词，尝试用所学语法造句。&lt;/li&gt;
&lt;li&gt;对易混淆的表达和敬体/简体用法进行了整理。&lt;/li&gt;
&lt;/ul&gt;
&lt;blockquote&gt;
&lt;p&gt;复习是进步的阶梯，坚持就是胜利！&lt;/p&gt;
&lt;/blockquote&gt;
</content:encoded></item><item><title>如何使用SSH连接GitHub和本地以方便git操作</title><link>https://meteor-comet.github.io/posts/ssh-github-git-2022/</link><guid isPermaLink="true">https://meteor-comet.github.io/posts/ssh-github-git-2022/</guid><description>GitHub SSH密钥配置完整指南</description><pubDate>Fri, 10 Jun 2022 00:00:00 GMT</pubDate><content:encoded>&lt;h2&gt;学习目标&lt;/h2&gt;
&lt;ul&gt;
&lt;li&gt;了解SSH密钥的工作原理&lt;/li&gt;
&lt;li&gt;掌握生成SSH密钥的方法&lt;/li&gt;
&lt;li&gt;学会配置GitHub SSH连接&lt;/li&gt;
&lt;li&gt;掌握SSH连接的优势和使用技巧&lt;/li&gt;
&lt;/ul&gt;
&lt;h2&gt;学习计划&lt;/h2&gt;
&lt;ol&gt;
&lt;li&gt;&lt;strong&gt;SSH密钥基础知识&lt;/strong&gt;&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;生成SSH密钥对&lt;/strong&gt;&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;配置GitHub SSH密钥&lt;/strong&gt;&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;测试SSH连接&lt;/strong&gt;&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;配置Git使用SSH&lt;/strong&gt;&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;常见问题和解决方案&lt;/strong&gt;&lt;/li&gt;
&lt;/ol&gt;
&lt;hr /&gt;
&lt;h2&gt;1. SSH密钥基础知识&lt;/h2&gt;
&lt;h3&gt;1.1 SSH密钥的作用&lt;/h3&gt;
&lt;p&gt;SSH密钥是一种安全的身份验证方式，比密码更安全，可以避免每次输入密码的麻烦。&lt;/p&gt;
&lt;h3&gt;1.2 SSH密钥的工作原理&lt;/h3&gt;
&lt;ul&gt;
&lt;li&gt;&lt;strong&gt;公钥&lt;/strong&gt;：存储在GitHub服务器上，用于验证身份&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;私钥&lt;/strong&gt;：存储在本地计算机上，用于身份验证&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;非对称加密&lt;/strong&gt;：公钥加密，私钥解密&lt;/li&gt;
&lt;/ul&gt;
&lt;h3&gt;1.3 SSH连接的优势&lt;/h3&gt;
&lt;ul&gt;
&lt;li&gt;&lt;strong&gt;安全性高&lt;/strong&gt;：比密码更安全&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;无需密码&lt;/strong&gt;：配置后无需每次输入密码&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;自动化友好&lt;/strong&gt;：适合脚本和自动化工具&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;多设备支持&lt;/strong&gt;：可以在多台设备上使用&lt;/li&gt;
&lt;/ul&gt;
&lt;hr /&gt;
&lt;h2&gt;2. 生成SSH密钥对&lt;/h2&gt;
&lt;h3&gt;2.1 检查现有SSH密钥&lt;/h3&gt;
&lt;pre&gt;&lt;code&gt;# 检查是否已有SSH密钥
ls -la ~/.ssh
# 输出:
# total 20
# drwx------ 2 user user 4096 Jun 10 10:00 .
# drwx------ 3 user user 4096 Jun 10 10:00 ..
# -rw------- 1 user user 1679 Jun 10 10:00 id_rsa
# -rw-r--r-- 1 user user  399 Jun 10 10:00 id_rsa.pub
&lt;/code&gt;&lt;/pre&gt;
&lt;h3&gt;2.2 生成新的SSH密钥&lt;/h3&gt;
&lt;pre&gt;&lt;code&gt;# 生成SSH密钥对
ssh-keygen -t rsa -b 4096 -C &quot;your_email@example.com&quot;

# 输出:
# Generating public/private rsa key pair.
# Enter file in which to save the key (/home/user/.ssh/id_rsa): [按回车使用默认路径]
# Enter passphrase (empty for no passphrase): [可以设置密码短语，也可以直接回车]
# Enter same passphrase again: [再次输入密码短语]
# Your identification has been saved in /home/user/.ssh/id_rsa.
# Your public key has been saved in /home/user/.ssh/id_rsa.pub.
# The key fingerprint is:
# SHA256:abcdef1234567890abcdef1234567890abcdef12 your_email@example.com
# The key&apos;s randomart image is:
# +---[RSA 4096]----+
# |                 |
# |                 |
# |                 |
# |                 |
# |                 |
# |                 |
# |                 |
# |                 |
# |                 |
# +----[SHA256]-----+
&lt;/code&gt;&lt;/pre&gt;
&lt;h3&gt;2.3 启动SSH代理&lt;/h3&gt;
&lt;pre&gt;&lt;code&gt;# 启动SSH代理
eval &quot;$(ssh-agent -s)&quot;
# 输出: Agent pid 12345

# 添加SSH密钥到代理
ssh-add ~/.ssh/id_rsa
# 输出: Identity added: /home/user/.ssh/id_rsa (your_email@example.com)
&lt;/code&gt;&lt;/pre&gt;
&lt;hr /&gt;
&lt;h2&gt;3. 配置GitHub SSH密钥&lt;/h2&gt;
&lt;h3&gt;3.1 复制公钥内容&lt;/h3&gt;
&lt;pre&gt;&lt;code&gt;# 查看公钥内容
cat ~/.ssh/id_rsa.pub
# 输出:
# ssh-rsa AAAAB3NzaC1yc2EAAAADAQABAAACAQC... your_email@example.com

# 或者使用clipboard命令复制（Windows）
clip &amp;lt; ~/.ssh/id_rsa.pub

# 或者使用pbcopy命令复制（macOS）
pbcopy &amp;lt; ~/.ssh/id_rsa.pub

# 或者使用xclip命令复制（Linux）
xclip -sel clip &amp;lt; ~/.ssh/id_rsa.pub
&lt;/code&gt;&lt;/pre&gt;
&lt;h3&gt;3.2 在GitHub中添加SSH密钥&lt;/h3&gt;
&lt;ol&gt;
&lt;li&gt;&lt;strong&gt;登录GitHub&lt;/strong&gt;&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;点击右上角头像 → Settings&lt;/strong&gt;&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;左侧菜单选择 &quot;SSH and GPG keys&quot;&lt;/strong&gt;&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;点击 &quot;New SSH key&quot;&lt;/strong&gt;&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;填写信息：&lt;/strong&gt;
&lt;ul&gt;
&lt;li&gt;&lt;strong&gt;Title&lt;/strong&gt;: 给密钥起个名字（如：My Laptop）&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Key&lt;/strong&gt;: 粘贴刚才复制的公钥内容&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;点击 &quot;Add SSH key&quot;&lt;/strong&gt;&lt;/li&gt;
&lt;/ol&gt;
&lt;h3&gt;3.3 验证SSH密钥添加&lt;/h3&gt;
&lt;pre&gt;&lt;code&gt;# 测试SSH连接
ssh -T git@github.com

# 首次连接会看到：
# The authenticity of host &apos;github.com (140.82.112.4)&apos; can&apos;t be established.
# RSA key fingerprint is SHA256:nThbg6kXUpJWGl7E1IGOCspRomTxdCARLviKw6E5SY8.
# Are you sure you want to continue connecting (yes/no)? yes

# 成功后会看到：
# Hi username! You&apos;ve successfully authenticated, but GitHub does not provide shell access.
&lt;/code&gt;&lt;/pre&gt;
&lt;hr /&gt;
&lt;h2&gt;4. 测试SSH连接&lt;/h2&gt;
&lt;h3&gt;4.1 测试连接状态&lt;/h3&gt;
&lt;pre&gt;&lt;code&gt;# 测试SSH连接
ssh -T git@github.com
# 输出: Hi username! You&apos;ve successfully authenticated, but GitHub does not provide shell access.

# 测试连接详细信息
ssh -vT git@github.com
# 输出详细的连接信息，包括密钥验证过程
&lt;/code&gt;&lt;/pre&gt;
&lt;h3&gt;4.2 检查SSH配置&lt;/h3&gt;
&lt;pre&gt;&lt;code&gt;# 查看SSH配置文件
cat ~/.ssh/config

# 如果没有配置文件，创建一个
touch ~/.ssh/config
chmod 600 ~/.ssh/config
&lt;/code&gt;&lt;/pre&gt;
&lt;h3&gt;4.3 配置SSH配置文件&lt;/h3&gt;
&lt;pre&gt;&lt;code&gt;# 编辑SSH配置文件
nano ~/.ssh/config

# 添加以下内容：
# Host github.com
#     HostName github.com
#     User git
#     IdentityFile ~/.ssh/id_rsa
#     IdentitiesOnly yes
&lt;/code&gt;&lt;/pre&gt;
&lt;hr /&gt;
&lt;h2&gt;5. 配置Git使用SSH&lt;/h2&gt;
&lt;h3&gt;5.1 配置Git用户信息&lt;/h3&gt;
&lt;pre&gt;&lt;code&gt;# 设置Git用户名和邮箱
git config --global user.name &quot;Your Name&quot;
git config --global user.email &quot;your_email@example.com&quot;

# 验证配置
git config --global user.name
# 输出: Your Name
git config --global user.email
# 输出: your_email@example.com
&lt;/code&gt;&lt;/pre&gt;
&lt;h3&gt;5.2 克隆仓库使用SSH&lt;/h3&gt;
&lt;pre&gt;&lt;code&gt;# 使用SSH URL克隆仓库
git clone git@github.com:username/repository.git

# 输出:
# Cloning into &apos;repository&apos;...
# remote: Enumerating objects: 100, done.
# remote: Counting objects: 100% (100/100), done.
# remote: Compressing objects: 100% (80/80), done.
# remote: Total 100 (delta 20), reused 100 (delta 20), pack-reused 0
# Receiving objects: 100% (100/100), 1.23 MiB | 2.45 MiB/s, done.
# Resolving deltas: 100% (20/20), done.
&lt;/code&gt;&lt;/pre&gt;
&lt;h3&gt;5.3 修改现有仓库的远程URL&lt;/h3&gt;
&lt;pre&gt;&lt;code&gt;# 查看当前远程URL
git remote -v
# 输出:
# origin  https://github.com/username/repository.git (fetch)
# origin  https://github.com/username/repository.git (push)

# 修改为SSH URL
git remote set-url origin git@github.com:username/repository.git

# 验证修改
git remote -v
# 输出:
# origin  git@github.com:username/repository.git (fetch)
# origin  git@github.com:username/repository.git (push)
&lt;/code&gt;&lt;/pre&gt;
&lt;h3&gt;5.4 测试Git操作&lt;/h3&gt;
&lt;pre&gt;&lt;code&gt;# 测试推送
git push origin main
# 输出:
# Enumerating objects: 5, done.
# Counting objects: 100% (5/5), done.
# Delta compression using up to 8 threads
# Compressing objects: 100% (3/3), done.
# Writing objects: 100% (3/3), 300 bytes | 300.00 KiB/s, done.
# Total 3 (delta 0), reused 0 (delta 0), pack-reused 0
# To github.com:username/repository.git
#    abc1234..def5678  main -&amp;gt; main

# 测试拉取
git pull origin main
# 输出:
# Already up to date.
&lt;/code&gt;&lt;/pre&gt;
&lt;hr /&gt;
&lt;h2&gt;6. 常见问题和解决方案&lt;/h2&gt;
&lt;h3&gt;6.1 问题1：SSH密钥权限错误&lt;/h3&gt;
&lt;pre&gt;&lt;code&gt;# 错误信息
# Permissions 0644 for &apos;/home/user/.ssh/id_rsa&apos; are too open.

# 解决方案
chmod 600 ~/.ssh/id_rsa
chmod 644 ~/.ssh/id_rsa.pub
chmod 700 ~/.ssh
&lt;/code&gt;&lt;/pre&gt;
&lt;h3&gt;6.2 问题2：SSH代理未启动&lt;/h3&gt;
&lt;pre&gt;&lt;code&gt;# 错误信息
# Could not open a connection to your authentication agent.

# 解决方案
eval &quot;$(ssh-agent -s)&quot;
ssh-add ~/.ssh/id_rsa
&lt;/code&gt;&lt;/pre&gt;
&lt;h3&gt;6.3 问题3：多个SSH密钥&lt;/h3&gt;
&lt;pre&gt;&lt;code&gt;# 创建SSH配置文件
nano ~/.ssh/config

# 添加多个密钥配置
# Host github.com
#     HostName github.com
#     User git
#     IdentityFile ~/.ssh/id_rsa_github
#     IdentitiesOnly yes
# 
# Host gitlab.com
#     HostName gitlab.com
#     User git
#     IdentityFile ~/.ssh/id_rsa_gitlab
#     IdentitiesOnly yes
&lt;/code&gt;&lt;/pre&gt;
&lt;h3&gt;6.4 问题4：连接超时&lt;/h3&gt;
&lt;pre&gt;&lt;code&gt;# 测试网络连接
ping github.com

# 检查防火墙设置
# Windows: 检查Windows防火墙
# macOS: 检查系统偏好设置
# Linux: 检查iptables或ufw

# 使用代理（如果需要）
# 在SSH配置中添加代理设置
# Host github.com
#     HostName github.com
#     User git
#     ProxyCommand nc -X connect -x proxy.example.com:8080 %h %p
&lt;/code&gt;&lt;/pre&gt;
&lt;h3&gt;6.5 问题5：密钥被拒绝&lt;/h3&gt;
&lt;pre&gt;&lt;code&gt;# 检查密钥是否正确添加
ssh -T git@github.com

# 重新生成密钥
ssh-keygen -t rsa -b 4096 -C &quot;your_email@example.com&quot;

# 重新添加到GitHub
# 1. 复制新公钥
cat ~/.ssh/id_rsa.pub
# 2. 在GitHub中删除旧密钥，添加新密钥
&lt;/code&gt;&lt;/pre&gt;
&lt;hr /&gt;
&lt;h2&gt;7. 高级配置和技巧&lt;/h2&gt;
&lt;h3&gt;7.1 配置SSH密钥密码短语&lt;/h3&gt;
&lt;pre&gt;&lt;code&gt;# 生成带密码短语的密钥
ssh-keygen -t rsa -b 4096 -C &quot;your_email@example.com&quot;

# 设置密码短语后，每次使用需要输入密码
# 可以使用ssh-add避免重复输入
ssh-add ~/.ssh/id_rsa
# 输入密码短语后，会话期间无需重复输入
&lt;/code&gt;&lt;/pre&gt;
&lt;h3&gt;7.2 使用SSH配置文件管理多个账户&lt;/h3&gt;
&lt;pre&gt;&lt;code&gt;# 编辑SSH配置文件
nano ~/.ssh/config

# 配置多个GitHub账户
# Host github-personal
#     HostName github.com
#     User git
#     IdentityFile ~/.ssh/id_rsa_personal
#     IdentitiesOnly yes
# 
# Host github-work
#     HostName github.com
#     User git
#     IdentityFile ~/.ssh/id_rsa_work
#     IdentitiesOnly yes

# 使用不同账户克隆仓库
git clone git@github-personal:username/repository.git
git clone git@github-work:company/repository.git
&lt;/code&gt;&lt;/pre&gt;
&lt;h3&gt;7.3 自动化SSH密钥管理&lt;/h3&gt;
&lt;pre&gt;&lt;code&gt;# 创建SSH密钥管理脚本
nano ~/scripts/ssh-setup.sh

# 脚本内容：
#!/bin/bash
# 启动SSH代理
eval &quot;$(ssh-agent -s)&quot;

# 添加所有SSH密钥
for key in ~/.ssh/id_rsa*; do
    if [[ -f &quot;$key&quot; &amp;amp;&amp;amp; ! &quot;$key&quot; =~ \.pub$ ]]; then
        ssh-add &quot;$key&quot;
    fi
done

# 测试GitHub连接
ssh -T git@github.com

# 设置脚本权限
chmod +x ~/scripts/ssh-setup.sh

# 添加到启动脚本
echo &quot;~/scripts/ssh-setup.sh&quot; &amp;gt;&amp;gt; ~/.bashrc
&lt;/code&gt;&lt;/pre&gt;
&lt;h3&gt;7.4 使用SSH密钥进行部署&lt;/h3&gt;
&lt;pre&gt;&lt;code&gt;# 在服务器上配置SSH密钥
# 1. 生成服务器密钥
ssh-keygen -t rsa -b 4096 -C &quot;server@example.com&quot;

# 2. 将服务器公钥添加到GitHub
cat ~/.ssh/id_rsa.pub

# 3. 在GitHub仓库设置中添加Deploy Keys

# 4. 在服务器上克隆仓库
git clone git@github.com:username/repository.git
&lt;/code&gt;&lt;/pre&gt;
&lt;hr /&gt;
&lt;h2&gt;8. 总结&lt;/h2&gt;
&lt;h3&gt;8.1 关键要点&lt;/h3&gt;
&lt;ol&gt;
&lt;li&gt;&lt;strong&gt;SSH密钥生成&lt;/strong&gt;：使用&lt;code&gt;ssh-keygen&lt;/code&gt;生成密钥对&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;GitHub配置&lt;/strong&gt;：将公钥添加到GitHub账户&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;SSH代理&lt;/strong&gt;：使用&lt;code&gt;ssh-agent&lt;/code&gt;管理密钥&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Git配置&lt;/strong&gt;：使用SSH URL进行Git操作&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;权限设置&lt;/strong&gt;：正确设置SSH密钥文件权限&lt;/li&gt;
&lt;/ol&gt;
&lt;h3&gt;8.2 操作流程总结&lt;/h3&gt;
&lt;ol&gt;
&lt;li&gt;&lt;strong&gt;生成SSH密钥&lt;/strong&gt;：&lt;code&gt;ssh-keygen -t rsa -b 4096 -C &quot;email&quot;&lt;/code&gt;&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;启动SSH代理&lt;/strong&gt;：&lt;code&gt;eval &quot;$(ssh-agent -s)&quot;&lt;/code&gt;&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;添加密钥到代理&lt;/strong&gt;：&lt;code&gt;ssh-add ~/.ssh/id_rsa&lt;/code&gt;&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;复制公钥&lt;/strong&gt;：&lt;code&gt;cat ~/.ssh/id_rsa.pub&lt;/code&gt;&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;添加到GitHub&lt;/strong&gt;：在GitHub设置中添加SSH密钥&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;测试连接&lt;/strong&gt;：&lt;code&gt;ssh -T git@github.com&lt;/code&gt;&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;配置Git&lt;/strong&gt;：使用SSH URL克隆或修改远程URL&lt;/li&gt;
&lt;/ol&gt;
&lt;h3&gt;8.3 最佳实践&lt;/h3&gt;
&lt;ul&gt;
&lt;li&gt;定期更新SSH密钥&lt;/li&gt;
&lt;li&gt;为不同用途使用不同密钥&lt;/li&gt;
&lt;li&gt;设置强密码短语&lt;/li&gt;
&lt;li&gt;定期检查SSH配置&lt;/li&gt;
&lt;li&gt;备份SSH密钥&lt;/li&gt;
&lt;/ul&gt;
&lt;h3&gt;8.4 学习收获&lt;/h3&gt;
&lt;p&gt;通过这次学习，我掌握了：&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;SSH密钥的生成和配置方法&lt;/li&gt;
&lt;li&gt;GitHub SSH连接的完整流程&lt;/li&gt;
&lt;li&gt;Git使用SSH的优势和技巧&lt;/li&gt;
&lt;li&gt;多账户SSH配置方法&lt;/li&gt;
&lt;li&gt;SSH连接故障排查技能&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;这些知识对于开发者来说非常重要，能够提高Git操作的效率和安全性，是日常开发工作的必备技能。&lt;/p&gt;
&lt;h3&gt;8.5 注意事项&lt;/h3&gt;
&lt;ul&gt;
&lt;li&gt;私钥必须保密，不要分享给他人&lt;/li&gt;
&lt;li&gt;定期更换SSH密钥&lt;/li&gt;
&lt;li&gt;在不同设备上需要重新配置&lt;/li&gt;
&lt;li&gt;生产环境使用更严格的权限设置&lt;/li&gt;
&lt;li&gt;备份重要的SSH密钥&lt;/li&gt;
&lt;/ul&gt;
</content:encoded></item><item><title>使用Charles对移动端环境进行抓包实录</title><link>https://meteor-comet.github.io/posts/charles-solve/</link><guid isPermaLink="true">https://meteor-comet.github.io/posts/charles-solve/</guid><description>Charles代理配置与移动端网络调试完整指南</description><pubDate>Mon, 21 Feb 2022 00:00:00 GMT</pubDate><content:encoded>&lt;h2&gt;问题概述：&lt;/h2&gt;
&lt;p&gt;在尝试利用Charles工具对应用程序（以雷电模拟器中的APP为例）的网络通信进行抓包时，遭遇了开启代理后移动设备无法访问互联网的问题。&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;Charles作为一款强大的HTTP代理服务器工具，能够拦截和分析客户端与服务器间的通信数据。&lt;/p&gt;
&lt;/blockquote&gt;
&lt;h2&gt;解决策略概览：&lt;/h2&gt;
&lt;blockquote&gt;
&lt;p&gt;本文参照&amp;lt;a href=&quot;https://blog.csdn.net/qq_53631388/article/details/134706062&quot;&amp;gt;IOS/安卓+charles实现抓包（主要解决证书网站无法打开问题）&amp;lt;/a&amp;gt;
提供了一套详细的操作步骤，以确保在安装Charles SSL证书后，移动端不仅能够成功代理，还能保持正常的网络连接。&lt;/p&gt;
&lt;/blockquote&gt;
&lt;h3&gt;实施步骤详解&lt;/h3&gt;
&lt;h4&gt;1. Charles基础代理配置&lt;/h4&gt;
&lt;ul&gt;
&lt;li&gt;&lt;strong&gt;识别代理服务器IP&lt;/strong&gt;：首先进入Charles的Help &amp;gt; SSL Proxying &amp;gt; Install Charles Root Certificate on a Mobile Device or Remote Browser，记录显示的IP地址。&amp;lt;br&amp;gt;
&lt;img src=&quot;/images/in-post/post_charles/Charles_1.png&quot; alt=&quot;Charles_1&quot; /&gt;
这时会弹出弹窗，记下IP地址，然后打开模拟器的WLAN设置
&lt;img src=&quot;/images/in-post/post_charles/Charles_2.png&quot; alt=&quot;Charles_2&quot; /&gt;&lt;/li&gt;
&lt;li&gt;模拟器网络设置：在雷电模拟器的WLAN设置中，手动配置代理，输入刚才记录的IP及Charles的默认端口（通常为8888）。
&lt;img src=&quot;/images/in-post/post_charles/Charles_3.png&quot; alt=&quot;Charles_3&quot; /&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;h4&gt;2. SSL证书下载与安装&lt;/h4&gt;
&lt;p&gt;默认情况下，charles不能解析https协议的接口，里面的请求和响应数据都是乱码格式，所以我们需要下载ssl证书，来获取里面的数据，我们先从Charles下载证书&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;strong&gt;下载Charles根证书&lt;/strong&gt;：在Charles菜单选择Help &amp;gt; SSL Proxying &amp;gt; Save Charles Root Certificate...，保存为PEM格式。
&lt;img src=&quot;/images/in-post/post_charles/Charles_5.png&quot; alt=&quot;Charles_5&quot; /&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;这里下载证书需要下载到系统目录，一般来说我们是没有权限的，所以需要打开模拟器的abd和root权限，方便后续连接
&lt;img src=&quot;/images/in-post/post_charles/Charles_4.png&quot; alt=&quot;Charles_4&quot; /&gt;&lt;/p&gt;
&lt;h4&gt;3. ADB与OpenSSL工具准备&lt;/h4&gt;
&lt;blockquote&gt;
&lt;p&gt;ADB:ADB 全称为 Android Debug Bridge，起到调试桥的作用，是一个客户端-服务器端程序。其中客户端是用来操作的电脑，服务端是 Android 设备。&amp;lt;/br&amp;gt;
压缩包下载：
&lt;a href=&quot;https://dl.google.com/android/repository/platform-tools-latest-windows.zip?_blank&quot;&gt;Windows版本&lt;/a&gt;;
&lt;a href=&quot;https://dl.google.com/android/repository/platform-tools-latest-darwin.zip&quot;&gt;Mac版本&lt;/a&gt;;
&lt;a href=&quot;https://dl.google.com/android/repository/platform-tools-latest-linux.zip&quot;&gt;Linux版本&lt;/a&gt;&lt;/p&gt;
&lt;/blockquote&gt;
&lt;blockquote&gt;
&lt;p&gt;OpenSSL:一个安全套接字层密码库，囊括主要的密码算法、常用密钥、证书封装管理功能及实现ssl协议。&lt;a href=&quot;https://blog.csdn.net/loveryunz/article/details/136739887&quot;&gt;保姆级OpenSSL下载及安装教程&lt;/a&gt;&lt;/p&gt;
&lt;/blockquote&gt;
&lt;h4&gt;4. 查看证书哈希值并重命名上传证书到移动端环境&lt;/h4&gt;
&lt;p&gt;在安装OpenSSL工具后，打开CMD，运行命令获取计算后的证书哈希值&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;openssl x509 -subject_hash_old -in {charles.pem(下载的证书)的绝对路径}
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;得到证书哈希值
接下来我们需要将证书重命名为{哈希值}.0
并使用adb工具进行上传&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;strong&gt;命令行操作&lt;/strong&gt;：使用ADB进入root模式，使能文件系统重新挂载，以获得写权限，随后执行证书的推送与重命名操作。&lt;/li&gt;
&lt;/ul&gt;
&lt;pre&gt;&lt;code&gt;adb root
adb remount
adb push {证书绝对路径} /system/etc/security/cacerts
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;这里其实在上传后会发现证书名字缺少后半部分以及后缀.0，所以我们需要使用adb shell进入到证书目录手动重命名
输入&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;strong&gt;验证与调整&lt;/strong&gt;：通过ADB Shell进入目标目录检查并手动完成证书名称的最终调整，确保格式为.0结尾的完整哈希值命名。&lt;/li&gt;
&lt;/ul&gt;
&lt;pre&gt;&lt;code&gt;adb shell
cd /system/etc/security/cacerts
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;进入证书目录
&lt;img src=&quot;/images/in-post/post_charles/Charles_6.png&quot; alt=&quot;Charles_6&quot; /&gt;&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;这里由于我已经进行过上传以及重命名，仅做演示&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;找到我们上传的证书，使用&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;mv {命名不完整证书名} {完整哈希值.0}
&lt;/code&gt;&lt;/pre&gt;
&lt;h3&gt;成功标志&lt;/h3&gt;
&lt;p&gt;完成上述步骤后，重新启动移动设备或模拟器上的网络服务，此时应当能够正常连接互联网，同时Charles能够成功捕获到HTTPS通信的数据包，实现对移动端网络流量的有效监控与分析。&lt;/p&gt;
</content:encoded></item><item><title>DJango5学习日志</title><link>https://meteor-comet.github.io/posts/django5-learn/</link><guid isPermaLink="true">https://meteor-comet.github.io/posts/django5-learn/</guid><description>Django5 Web框架完整教程</description><pubDate>Tue, 15 Feb 2022 00:00:00 GMT</pubDate><content:encoded>&lt;h2&gt;Django5介绍及安装&lt;/h2&gt;
&lt;h3&gt;Django5简介&lt;/h3&gt;
&lt;p&gt;Django(&lt;em&gt;发音:[`dʒæŋɡəʊ]&lt;/em&gt;)    也有的小伙伴读成  “酱狗”，&quot;贱狗&quot;，&quot;进狗&quot;，&quot;撞狗&quot;，甚至还有读成&quot;打狗&quot;。&lt;/p&gt;
&lt;p&gt;官方：https://www.djangoproject.com/&lt;/p&gt;
&lt;p&gt;Django是一个高级的Python Web框架，可以快速开发安全和可维护的网站。由经验丰富的开发者构建，Django负责处理网站开发中麻烦的部分，可以专注于编写应用程序，而无需重新开发。它是免费和开源的，有活跃繁荣的社区，丰富的文档，以及很多免费和付费的解决方案。目前最新版本：5.0.1&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;/images/in-post/post_django/image-20240131210528300.png&quot; alt=&quot;image-20240131210528300&quot; /&gt;&lt;/p&gt;
&lt;h3&gt;Django5安装&lt;/h3&gt;
&lt;p&gt;pip安装：&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;pip install Django==5.0.1 -i https://pypi.tuna.tsinghua.edu.cn/simple
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;执行安装完成后，在python目录的Scripts下，会多出一个diango-admin.exe 这个是django项目创建工具&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;/images/in-post/post_django/image-20240131220327128.png&quot; alt=&quot;image-20240131220327128&quot; /&gt;&lt;/p&gt;
&lt;p&gt;当然同时Lib下的site-packages下，也会有一个django目录，这个是后面我们开发项目会用到的django开发包。&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;/images/in-post/post_django/image-20240131220452238.png&quot; alt=&quot;image-20240131220452238&quot; /&gt;&lt;/p&gt;
&lt;h2&gt;Django5项目创建与项目配置&lt;/h2&gt;
&lt;h3&gt;Django5创建项目(用命令方式)&lt;/h3&gt;
&lt;p&gt;首先cmd，进入命令提示符终端&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;/images/in-post/post_django/image-20240202122717635.png&quot; alt=&quot;image-20240202122717635&quot; /&gt;&lt;/p&gt;
&lt;p&gt;我这边是准备把项目创建在 D:\python\django5 这个目录下。&lt;/p&gt;
&lt;p&gt;cd 切换到你需要建项目的目录：&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;cd /d D:\python\django5
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;&lt;img src=&quot;/images/in-post/post_django/image-20240202130345896.png&quot; alt=&quot;image-20240202130345896&quot; /&gt;&lt;/p&gt;
&lt;p&gt;接下啦 借助 django-admin 工具，创建项目，命令如下：&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;django-admin startproject 项目名称
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;&lt;img src=&quot;/images/in-post/post_django/image-20240202134335721.png&quot; alt=&quot;image-20240202134335721&quot; /&gt;&lt;/p&gt;
&lt;p&gt;执行后，我们去看下 项目目录：&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;/images/in-post/post_django/image-20240202134401394.png&quot; alt=&quot;image-20240202134401394&quot; /&gt;&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;/images/in-post/post_django/image-20240202134408937.png&quot; alt=&quot;image-20240202134408937&quot; /&gt;&lt;/p&gt;
&lt;p&gt;说明项目创建成功。&lt;/p&gt;
&lt;h3&gt;Django5创建项目(用PyCharm工具)&lt;/h3&gt;
&lt;p&gt;除了在命令提示符窗口创建项目之外，还可以在 PyCharm中创建项目。PyCharm必须为专业版才能创建与调试 Django项目，社区版是不支持此功能的。打开PyCharm并在左上方单击File→New Project，选择第一个Django，创建新项目，如下图：&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;/images/in-post/post_django/image-20240202205117982.png&quot; alt=&quot;image-20240202205117982&quot; /&gt;&lt;/p&gt;
&lt;p&gt;相对于用命令方式，PyCharm创建的项目，多了templates目录（用来放html模版文件）,以及settings.py，多了 &lt;code&gt;BASE_DIR / &apos;templates&apos;&lt;/code&gt;&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;/images/in-post/post_django/image-20240202205750355.png&quot; alt=&quot;image-20240202205750355&quot; /&gt;&lt;/p&gt;
&lt;p&gt;这里介绍下默认创建的文件&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;  python222_site1
    │  manage.py
    │
    └─python222_site1
            asgi.py
            settings.py
            urls.py
            wsgi.py
            __init__.py
&lt;/code&gt;&lt;/pre&gt;
&lt;ul&gt;
&lt;li&gt;&lt;code&gt;manage.py&lt;/code&gt; :项目管理命令行工具，内置多种方式与项目进行交互，包括启动项目，创建app，数据管理等。在命令提示符窗口下，将路径切换到python222_site1项目并输入python manage.py help，可以查看该工具的指令信息；【不用修改】&lt;/li&gt;
&lt;li&gt;&lt;code&gt; __init__.py&lt;/code&gt;：初始化文件,一般情况下无须修改；&lt;/li&gt;
&lt;li&gt;&lt;code&gt;settings.py&lt;/code&gt;：项目的配置文件，项目的所有功能都需要在该文件中进行配置；&lt;/li&gt;
&lt;li&gt;&lt;code&gt;urls.py&lt;/code&gt;：项目的路由设置，设置网站的具体网址内容；&lt;/li&gt;
&lt;li&gt;&lt;code&gt;wsgi.py&lt;/code&gt;：全 称 为 Python Web Server Gateway Interface，即Python服务器⽹关接⼝，是Python应⽤与Web服务器之间的接⼝，⽤于Django项⽬在服务器上的部署和上线；【不用修改】&lt;/li&gt;
&lt;li&gt;&lt;code&gt;asgi.py&lt;/code&gt;：开启⼀个ASGI服务，ASGI是异步⽹关协议接⼝；【不用修改】&lt;/li&gt;
&lt;/ul&gt;
&lt;h2&gt;Django5应用创建与应用配置&lt;/h2&gt;
&lt;h3&gt;Django5操作命令&lt;/h3&gt;
&lt;p&gt;我们掌握了如何在命令提示符或PyCharm下创建Django项目和项目应用，无论是创建项目还是创建项目应用，都需要输入相关的指令才能得以实现，这些都是Django内置的操作指令。&lt;/p&gt;
&lt;p&gt;在PyCharm的Terminal中输入指令 &lt;code&gt;python manage.py help&lt;/code&gt; 并按回车键，即可看到相关的指令信息，如下图所示。&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;/images/in-post/post_django/image-20240203062507128.png&quot; alt=&quot;image-20240203062507128&quot; /&gt;&lt;/p&gt;
&lt;p&gt;Django5 的操作指令共有31条，每条指令的说明以表格形式展示。&lt;/p&gt;
&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;指令&lt;/th&gt;
&lt;th&gt;说明&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;changepassword&lt;/td&gt;
&lt;td&gt;修改内置用户表的用户密码&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;createsuperuser&lt;/td&gt;
&lt;td&gt;为内置用户表创建超级管理员账号&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;remove_stale_contenttypes&lt;/td&gt;
&lt;td&gt;删除数据库中已不使用的数据表&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;check&lt;/td&gt;
&lt;td&gt;检测整个项目是否存在异常问题&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;compilemessages&lt;/td&gt;
&lt;td&gt;编译语言文件，用于项目的区域语言设置&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;createcachetable&lt;/td&gt;
&lt;td&gt;创建缓存数据表，为内置的缓存机制提供存储功能&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;dbshell&lt;/td&gt;
&lt;td&gt;进入Django配置的数据库，可以执行数据库的SOL语句&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;diffsettings&lt;/td&gt;
&lt;td&gt;显示当前settings.py的配置信息与默认配置的差异&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;dumpdata&lt;/td&gt;
&lt;td&gt;导出数据表的数据并以JSON格式存储，如 python manage.py dumpdata index &amp;gt;data.json，这是index的模型所对应的数据导出，并保存在 data.json文件中&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;flush&lt;/td&gt;
&lt;td&gt;清空数据表的数据信息&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;inspectdb&lt;/td&gt;
&lt;td&gt;获取项目所有模型的定义过程&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;loaddata&lt;/td&gt;
&lt;td&gt;将数据文件导入数据表，如 python manage.py loaddatadata.,json&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;makemessages&lt;/td&gt;
&lt;td&gt;创建语言文件，用于项目的区域语言设置&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;makemigrations&lt;/td&gt;
&lt;td&gt;从模型对象创建数据迁移文件并保存在App 的migrations文件夹&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;migrate&lt;/td&gt;
&lt;td&gt;根据迁移文件的内容，在数据库里生成相应的数据表&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;sendtestemail&lt;/td&gt;
&lt;td&gt;向指定的收件人发送测试的电子邮件&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;shell&lt;/td&gt;
&lt;td&gt;进入Django的Shell模式,用于调试项目功能&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;showmigrations&lt;/td&gt;
&lt;td&gt;查看当前项目的所有迁移文件&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;sqlflush&lt;/td&gt;
&lt;td&gt;查看清空数据库的SOL语句脚本&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;sqlmigrate&lt;/td&gt;
&lt;td&gt;根据迁移文件内容输出相应的SQL语句&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;sqlsequencereset&lt;/td&gt;
&lt;td&gt;重置数据表递增字段的索引值&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;squashmigrations&lt;/td&gt;
&lt;td&gt;对迁移文件进行压缩处理&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;startapp&lt;/td&gt;
&lt;td&gt;创建项目应用App&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;optimizemigration&lt;/td&gt;
&lt;td&gt;允许优化迁移操作&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;startproject&lt;/td&gt;
&lt;td&gt;创建新的Django项目&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;test&lt;/td&gt;
&lt;td&gt;运行App里面的测试程序&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;testserver&lt;/td&gt;
&lt;td&gt;新建测试数据库并使用该数据库运行项目&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;clearsessions&lt;/td&gt;
&lt;td&gt;清除会话Session数据&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;collectstatic&lt;/td&gt;
&lt;td&gt;收集所有的静态文件&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;findstatic&lt;/td&gt;
&lt;td&gt;查找静态文件的路径信息&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;runserver&lt;/td&gt;
&lt;td&gt;在本地计算机上启动Django项目&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;
&lt;p&gt;小技巧，前面每次执行命令都要在Terminal终端输入 &lt;code&gt;python manage.py 命令&lt;/code&gt;&lt;/p&gt;
&lt;p&gt;比较繁琐，我们借助PyCharm开发工具，在菜单Tools里，有个 &lt;code&gt;Run manage.py Task...&lt;/code&gt;,直接点击&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;/images/in-post/post_django/image-20240203064746201.png&quot; alt=&quot;image-20240203064746201&quot; /&gt;&lt;/p&gt;
&lt;p&gt;直接输入命令，比如 &lt;code&gt;help&lt;/code&gt;，结果就出来，是不是很方便，以后我们就用这种简便方式，来提高工作效率。&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;/images/in-post/post_django/image-20240203064907048.png&quot; alt=&quot;image-20240203064907048&quot; /&gt;&lt;/p&gt;
&lt;h3&gt;Django5应用创建&lt;/h3&gt;
&lt;p&gt;前面我们创建的是一个项目，一个项目是由于一个或者多个应用组成(我们一般开发，一个项目里就创建一个应用即可)。&lt;/p&gt;
&lt;p&gt;项目里的每个应用 都是独立的，可以拥有独立的数据库，模版代码，业务代码。&lt;/p&gt;
&lt;p&gt;比如，一个网站，可以有前台用户应用和后台管理员应用；&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;/images/in-post/post_django/image-20240203065945159.png&quot; alt=&quot;image-20240203065945159&quot; /&gt;&lt;/p&gt;
&lt;p&gt;复杂的电商项目，后台甚至可以拆分用户应用，商品应用，订单应用，支付应用，积分应用，优惠券应用等。&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;/images/in-post/post_django/image-20240203070100404.png&quot; alt=&quot;image-20240203070100404&quot; /&gt;&lt;/p&gt;
&lt;p&gt;前面我们学过Django5的操作命令，有一个命令是 &lt;code&gt;startapp&lt;/code&gt;，就是用来创建项目应用APP的。&lt;/p&gt;
&lt;p&gt;我们执行 &lt;code&gt;startapp app01&lt;/code&gt; 创建名字为app01的应用，执行完毕后，多出一个app01目录(目录里的生成文件，我们下一讲解释)&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;/images/in-post/post_django/image-20240203070451378.png&quot; alt=&quot;image-20240203070451378&quot; /&gt;&lt;/p&gt;
&lt;p&gt;我们还可以继续执行 &lt;code&gt;startapp app02&lt;/code&gt;,&lt;code&gt;startapp app03&lt;/code&gt;&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;/images/in-post/post_django/image-20240203070628402.png&quot; alt=&quot;image-20240203070628402&quot; /&gt;&lt;/p&gt;
&lt;p&gt;一个项目可以拥有一个或者多个应用，所以理论上，我们可以创建无数应用。但是还是强调下，一般情况，我们一个项目就之需要创建一个应用即可。&lt;/p&gt;
&lt;h3&gt;Django5应用配置&lt;/h3&gt;
&lt;p&gt;为了更好的理解Django5的应用配置，我们先来学习下Django的MTV模型。&lt;/p&gt;
&lt;p&gt;Django的MTV分别代表：&lt;/p&gt;
&lt;p&gt;Model(模型)：业务对象与数据库的对象(ORM)&lt;/p&gt;
&lt;p&gt;Template(模版)：负责如何把页面展示给用户&lt;/p&gt;
&lt;p&gt;View(视图)：负责业务逻辑，并在适当的时候调用Model和Template&lt;/p&gt;
&lt;p&gt;此外，Django还有一个urls分发器，它的作用是将一个URI的页面请求分发给不同的view处理，view再调用相应的Model和Template。 Django WEB框架示意图如下所示:&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;/images/in-post/post_django/image-20240203073932302.png&quot; alt=&quot;image-20240203073932302&quot; /&gt;&lt;/p&gt;
&lt;p&gt;前面生成应用结构如下：&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;+---app01
|   |   admin.py
|   |   apps.py
|   |   models.py
|   |   tests.py
|   |   views.py
|   |   __init__.py
|   |
|   \---migrations
|           __init__.py
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;我们来解释下这些生成的python文件。&lt;/p&gt;
&lt;p&gt;&lt;code&gt;__init__.py&lt;/code&gt;：说明目录是一个python模块&lt;/p&gt;
&lt;p&gt;&lt;code&gt;migrations.py目录&lt;/code&gt;：用于存放数据库迁移历史文件&lt;/p&gt;
&lt;p&gt;&lt;code&gt;models.py&lt;/code&gt;: 用于应用操作数据库的模型&lt;/p&gt;
&lt;p&gt;&lt;code&gt;views.py&lt;/code&gt;: 用于编写Web应用视图，接收数据，处理数据，与Model(模型)，Template(模版)进行交互，返回应答&lt;/p&gt;
&lt;p&gt;&lt;code&gt;apps.py&lt;/code&gt;：应用配置文件。&lt;/p&gt;
&lt;p&gt;&lt;code&gt;tests.py&lt;/code&gt;：做单元测试。&lt;/p&gt;
&lt;p&gt;&lt;code&gt;admin.py&lt;/code&gt;：默认提供了admin后台管理，用作网站的后台管理站点配置相关&lt;/p&gt;
&lt;h3&gt;Django5 Hello World编写&lt;/h3&gt;
&lt;p&gt;前面对应用创建和应用配置掌握后，我们来编写第一个Hello World应用吧。体验一把Django5的项目开发过程。&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;第一步：创建Hello World应用&lt;/strong&gt;&lt;/p&gt;
&lt;p&gt;直接执行&lt;code&gt;startapp helloWorld&lt;/code&gt;命令创建应用。&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;/images/in-post/post_django/image-20240203074654173.png&quot; alt=&quot;image-20240203074654173&quot; /&gt;&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;第二步：注册应用到项目的settings.py&lt;/strong&gt;&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;/images/in-post/post_django/image-20240203074900684.png&quot; alt=&quot;image-20240203074900684&quot; /&gt;&lt;/p&gt;
&lt;p&gt;把helloWorld应用的apps.py里的HelloworldConfig类注册到settings.py里去&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;/images/in-post/post_django/image-20240203075050181.png&quot; alt=&quot;image-20240203075050181&quot; /&gt;&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;第三步：编写模版网页代码index.html&lt;/strong&gt;&lt;/p&gt;
&lt;p&gt;在templates目录下，新建index.html文件&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;/images/in-post/post_django/image-20240203075648286.png&quot; alt=&quot;image-20240203075648286&quot; /&gt;&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;&amp;lt;!DOCTYPE html&amp;gt;
&amp;lt;html lang=&quot;en&quot;&amp;gt;
&amp;lt;head&amp;gt;
    &amp;lt;meta charset=&quot;UTF-8&quot;&amp;gt;
    &amp;lt;title&amp;gt;Title&amp;lt;/title&amp;gt;
&amp;lt;/head&amp;gt;
&amp;lt;body&amp;gt;
&amp;lt;p&amp;gt;Django5大爷你好！&amp;lt;/p&amp;gt;
&amp;lt;a href=&quot;http://python222.com/post/7&quot; target=&quot;_blank&quot;&amp;gt;Python学习路线图&amp;lt;/a&amp;gt;
&amp;lt;/body&amp;gt;
&amp;lt;/html&amp;gt;
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;&lt;strong&gt;第四步：编写视图处理请求层代码&lt;/strong&gt;&lt;/p&gt;
&lt;p&gt;在应用的views.py里编写index方法,request是客户端请求对象,render是渲染方法，可以携带数据渲染到指定页面&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;def index(request):
    return render(request,&apos;index.html&apos;)
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;&lt;strong&gt;第五步：编写请求映射函数配置&lt;/strong&gt;&lt;/p&gt;
&lt;p&gt;在项目的urls.py里编写应用的index/请求，执行我们上面应用定义的请求处理代码，也就是写一个映射关系代码。&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;/images/in-post/post_django/image-20240203081154387.png&quot; alt=&quot;image-20240203081154387&quot; /&gt;&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;import helloWorld.views

urlpatterns = [
    path(&apos;admin/&apos;, admin.site.urls),
    path(&apos;index/&apos;, helloWorld.views.index,

]
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;&lt;strong&gt;第五步，启动项目，测试&lt;/strong&gt;&lt;/p&gt;
&lt;p&gt;我们可以用前面讲的Django5的操作命令 &lt;code&gt;runserver&lt;/code&gt;  启动&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;/images/in-post/post_django/image-20240203081216218.png&quot; alt=&quot;image-20240203081216218&quot; /&gt;&lt;/p&gt;
&lt;p&gt;默认端口 8000&lt;/p&gt;
&lt;p&gt;当然我们还有更简单的方式。直接用PyCharm启动。直接点击绿色小三角即可。&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;/images/in-post/post_django/image-20240203081416777.png&quot; alt=&quot;image-20240203081416777&quot; /&gt;&lt;/p&gt;
&lt;p&gt;启动后，浏览器输入，因为我们项目urls.py里配置的请求地址就是index/ 所以请求如下&lt;/p&gt;
&lt;pre&gt;&lt;code&gt; http://127.0.0.1:8000/index/
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;&lt;img src=&quot;/images/in-post/post_django/image-20240203081619080.png&quot; alt=&quot;image-20240203081619080&quot; /&gt;&lt;/p&gt;
&lt;p&gt;运行测试成功。&lt;/p&gt;
&lt;p&gt;执行过程：客户端请求index/ - &amp;gt; 经过django url请求分发器 - &amp;gt; 执行到应用的views.py的index方法 - &amp;gt; index方法再render渲染到index.html模版代码 - &amp;gt; 最终显示到用户浏览器终端。&lt;/p&gt;
&lt;h3&gt;Django5项目配置settings.py文件&lt;/h3&gt;
&lt;h4&gt;基本配置&lt;/h4&gt;
&lt;p&gt;Django 的配置文件 settings.py用于配置整个网站的环境和功能，核心配置必须有项目路径、密钥配置、域名访问权限、App列表、中间件、资源文件、模板配置、数据库的连接方式。&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;# 项目路径
# Build paths inside the project like this: BASE_DIR / &apos;subdir&apos;.
BASE_DIR = Path(__file__).resolve().parent.parent


# Quick-start development settings - unsuitable for production
# See https://docs.djangoproject.com/en/5.0/howto/deployment/checklist/

# 密钥配置
# SECURITY WARNING: keep the secret key used in production secret!
SECRET_KEY = &apos;django-insecure-^+$)&amp;amp;&amp;amp;p^atz-o)&amp;amp;ytg&amp;amp;8%6dq!!ujgh7t2w#2n^i_f#r^#*vyqh&apos;

# 调试模式
# SECURITY WARNING: don&apos;t run with debug turned on in production!
DEBUG = True

# 域名访问权限
ALLOWED_HOSTS = []


# Application definition
# APP列表
INSTALLED_APPS = [
    &apos;django.contrib.admin&apos;,
    &apos;django.contrib.auth&apos;,
    &apos;django.contrib.contenttypes&apos;,
    &apos;django.contrib.sessions&apos;,
    &apos;django.contrib.messages&apos;,
    &apos;django.contrib.staticfiles&apos;,
    &apos;helloWorld.apps.HelloworldConfig&apos;
]
&lt;/code&gt;&lt;/pre&gt;
&lt;ul&gt;
&lt;li&gt;&lt;code&gt;BASE_DIR&lt;/code&gt;项目路径：主要通过os模块读取当前项目在计算机系统的具体路径，该代码在创建项目时自动生成，一般情况下无须修改。&lt;/li&gt;
&lt;li&gt;&lt;code&gt;SECRET_KEY&lt;/code&gt;密钥配置：密钥配置SECRET_KEY:这是一个随机值，在项目创建的时候自动生成，一般情况下无须修改。主要用于重要数据的加密处理，提高项目的安全性，避免遭到攻击者恶意破坏。密钥主要用于用户密码、CSRF机制和会话Session等数据加密。
&lt;ul&gt;
&lt;li&gt;用户密码: Django 内置一套Auth认证系统，该系统具有用户认证和存储用户信息等功能，在创建用户的时候，将用户密码通过密钥进行加密处理，保证用户的安全性。&lt;/li&gt;
&lt;li&gt;CSRF机制:该机制主要用于表单提交，防止窃取网站的用户信息来制造恶意请求。&lt;/li&gt;
&lt;li&gt;会话Session: Session的信息存放在Cookie中，以一串随机的字符串表示，用于标识当前访问网站的用户身份，记录相关用户信息。&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;&lt;code&gt;DEBUG&lt;/code&gt;调试模式：该值为布尔类型。如果在开发调试阶段，那么应设置为True，在开发调试过程中会自动检测代码是否发生更改，根据检测结果执行是否刷新重启系统。如果项目部署上线，那么应将其改为False，否则会泄漏项目的相关信息。&lt;/li&gt;
&lt;li&gt;&lt;code&gt;ALLOWED_HOSTS&lt;/code&gt;域名访问权限：设置可访问的域名,默认值为空列表。当DEBUG为True并且 ALLOWED_HOSTS为空列表时，项目只允许以localhost或127.0.0.1在浏览器上访问。当DEBUG为False时，ALLOWED_HOSTS为必填项，否则程序无法启动，如果想允许所有域名访问，可设置ALLOW_HOSTS=[&apos;*&apos;]。&lt;/li&gt;
&lt;li&gt;&lt;code&gt;INSTALLED_APPS&lt;/code&gt;APP列表：告诉Django有哪些App。在项目创建时已有admin、auth和sessions 等配置信息，这些都是Django内置的应用功能，各个功能说明如下。
&lt;ol&gt;
&lt;li&gt;admin:内置的后台管理系统。&lt;/li&gt;
&lt;li&gt;auth:内置的用户认证系统。&lt;/li&gt;
&lt;li&gt;contenttypes:记录项目中所有model元数据( Django 的ORM框架)。&lt;/li&gt;
&lt;li&gt;sessions: Session会话功能，用于标识当前访问网站的用户身份，记录相关用户信息。&lt;/li&gt;
&lt;li&gt;messages:消息提示功能。&lt;/li&gt;
&lt;li&gt;staticfiles:查找静态资源路径。&lt;/li&gt;
&lt;/ol&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;如果在项目中创建了App，就必须在App列表&lt;code&gt;INSTALLED_APPS&lt;/code&gt;添加App类&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;/images/in-post/post_django/image-20240204152115633.png&quot; alt=&quot;image-20240204152115633&quot; /&gt;&lt;/p&gt;
&lt;h4&gt;资源文件配置&lt;/h4&gt;
&lt;p&gt;资源文件配置分为静态资源和媒体资源。静态资源的配置方式由配置属性STATIC_URL、STATICFILES DIRS和STATIC_ROOT进行设置;媒体资源的配置方式由配置属性MEDIA_URL和MEDIA ROOT决定。&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;静态资源配置=STATIC_URL&lt;/strong&gt;&lt;/p&gt;
&lt;p&gt;静态资源指的是网站中不会改变的文件。在一般的应用程序中，静态资源包括CSS文件、JavaScript文件以及图片等资源文件。&lt;/p&gt;
&lt;p&gt;默认配置，app下的static目录为静态资源，可以直接访问。其他目录不行。&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;STATIC_URL = &apos;static/&apos;
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;我们在app下新建static目录，再放一个logo.png图片。&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;/images/in-post/post_django/image-20240204154907810.png&quot; alt=&quot;image-20240204154907810&quot; /&gt;&lt;/p&gt;
&lt;p&gt;同时在app目录下再新建一个images目录，放一个qq.jpg头像图片&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;/images/in-post/post_django/image-20240204155057595.png&quot; alt=&quot;image-20240204155057595&quot; /&gt;&lt;/p&gt;
&lt;p&gt;最后再项目目录下新建一个static，放一个pig.jpg，也试试看是否可以访问；&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;/images/in-post/post_django/image-20240204155505474.png&quot; alt=&quot;image-20240204155505474&quot; /&gt;&lt;/p&gt;
&lt;p&gt;我们启动项目测试：&lt;/p&gt;
&lt;p&gt;先测试app下的static目录下的logo.png，能显示没问题。&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;http://127.0.0.1:8000/static/logo.png
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;&lt;img src=&quot;/images/in-post/post_django/image-20240204155620143.png&quot; alt=&quot;image-20240204155620143&quot; /&gt;&lt;/p&gt;
&lt;p&gt;再试试app下的images目录下的qq.jpg&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;http://127.0.0.1:8000/static/qq.jpg
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;&lt;img src=&quot;/images/in-post/post_django/image-20240204163444518.png&quot; alt=&quot;image-20240204163444518&quot; /&gt;&lt;/p&gt;
&lt;p&gt;404不存在&lt;/p&gt;
&lt;p&gt;最后再测试下项目目录下的static下的pig.jpg&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;http://127.0.0.1:8000/static/pig.jpg
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;&lt;img src=&quot;/images/in-post/post_django/image-20240204155805759.png&quot; alt=&quot;image-20240204155805759&quot; /&gt;&lt;/p&gt;
&lt;p&gt;也是404不存在。&lt;/p&gt;
&lt;p&gt;通过测试说明，也就app下的static目录下的静态资源才能访问。&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;静态资源集合配置-STATICFILES DIRS&lt;/strong&gt;&lt;/p&gt;
&lt;p&gt;由于STATIC_URL的特殊性，在开发中会造成诸多不便，比如将静态文件夹存放在项目的根目录以及定义多个静态文件夹等。我们可以通过配置STATICFILES DIRS实现多个目录下的静态资源可以访问。&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;# 设置静态资源文件集合
STATICFILES_DIRS = [BASE_DIR / &quot;static&quot;, BASE_DIR / &quot;helloWorld/images&quot;]
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;我们再测试下：&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;http://127.0.0.1:8000/static/pig.jpg
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;&lt;img src=&quot;/images/in-post/post_django/image-20240204163323694.png&quot; alt=&quot;image-20240204163323694&quot; /&gt;&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;http://127.0.0.1:8000/static/qq.jpg
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;&lt;img src=&quot;/images/in-post/post_django/image-20240204163405106.png&quot; alt=&quot;image-20240204163405106&quot; /&gt;&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;静态资源部署配置-STATIC_ROOT&lt;/strong&gt;&lt;/p&gt;
&lt;p&gt;静态资源配置还有STATIC_ROOT，其作用是在服务器上部署项目，实现服务器和项目之间的映射。STATIC_ROOT 主要收集整个项目的静态资源并存放在一个新的文件夹，然后由该文件夹与服务器之间构建映射关系。STATIC_ROOT配置如下:&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;# 静态资源部署
STATIC_ROOT = BASE_DIR / &apos;static&apos;
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;当项目的配置属性 DEBUG 设为True的时候，Django 会自动提供静态文件代理服务，此时整个项目处于开发阶段，因此无须使用STATIC_ROOT。当配置属性DEBUG 设为False的时候，意味着项目进入生产环境，Django不再提供静态文件代理服务，此时需要在项目的配置文件中设置STATIC_ROOT。
设置STATIC_ROOT需要使用 Django操作指令collectstatic来收集所有静态资源，这些静态资源都会保存在STATIC_ROOT所设置的文件夹里。&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;媒体资源配置-MEDIA&lt;/strong&gt;&lt;/p&gt;
&lt;p&gt;一般情况下，STATIC_URL是设置静态文件的路由地址，如CSS样式文件、JavaScript文件以及常用图片等。对于一些经常变动的资源，通常将其存放在媒体资源文件夹，如用户头像、歌曲文件等。&lt;/p&gt;
&lt;p&gt;媒体资源和静态资源是可以同时存在的，而且两者可以独立运行，互不影响，而媒体资源只有配置属性MEDIA_URL和 MEDIA_ROOT。&lt;/p&gt;
&lt;p&gt;我们在项目目录下新建media目录，里面再放一个boy.jpg图片。&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;/images/in-post/post_django/image-20240204172234364.png&quot; alt=&quot;image-20240204172234364&quot; /&gt;&lt;/p&gt;
&lt;p&gt;然后在配置文件settings.py里设置配置属性MEDIA_URL和 MEDIA_ROOT，MEDIA_URL用于设置媒体资源的路由地址，MEDIA_ROOT用于获取 media文件夹在计算机系统的完整路径信息，如下所示：&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;# 设置媒体路由
MEDIA_URL = &apos;media/&apos;
# 设置media目录的完整路径
MEDIA_ROOT = BASE_DIR / &apos;media&apos;
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;配置属性设置后，还需要将media文件夹注册到 Django里，让 Django知道如何找到媒体文件，否则无法在浏览器上访问该文件夹的文件信息。打开项目文件夹的urls.py文件，为媒体文件夹media添加相应的路由地址，代码如下:&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;from django.conf import settings
from django.contrib import admin
from django.urls import path, re_path
from django.views.static import serve

import helloWorld.views

urlpatterns = [
    path(&apos;admin/&apos;, admin.site.urls),
    path(&apos;index/&apos;, helloWorld.views.index),
    # 配置媒体文件的路由地址
    re_path(&apos;media/(?P&amp;lt;path&amp;gt;.*)&apos;, serve, {&apos;document_root&apos;: settings.MEDIA_ROOT}, name=&apos;media&apos;)
]

&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;我们来测试下：&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;http://127.0.0.1:8000/media/boy.jpg
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;&lt;img src=&quot;/images/in-post/post_django/image-20240204173014299.png&quot; alt=&quot;image-20240204173014299&quot; /&gt;&lt;/p&gt;
&lt;h4&gt;模版配置&lt;/h4&gt;
&lt;p&gt;在 Web开发中，模板是一种较为特殊的HTML文档。这个HTML文档嵌入了一些能够让Django识别的变量和指令，然后由Django的模板引擎解析这些变量和指令，生成完整的HTML网页并返回给用户浏览。模板是Django里面的MTV框架模式的T部分，配置模板路径是告诉Django在解析模板时，如何找到模板所在的位置。创建项目时，Django已有初始的模板配置信息，如下所示:&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;TEMPLATES = [
    {
        &apos;BACKEND&apos;: &apos;django.template.backends.django.DjangoTemplates&apos;,
        &apos;DIRS&apos;: [BASE_DIR / &apos;templates&apos;]
        ,
        &apos;APP_DIRS&apos;: True,
        &apos;OPTIONS&apos;: {
            &apos;context_processors&apos;: [
                &apos;django.template.context_processors.debug&apos;,
                &apos;django.template.context_processors.request&apos;,
                &apos;django.contrib.auth.context_processors.auth&apos;,
                &apos;django.contrib.messages.context_processors.messages&apos;,
            ],
        },
    },
]
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;模板配置是以列表格式呈现的，每个元素具有不同的含义，其含义说明如下。&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;BACKEND:定义模板引擎，用于识别模板里面的变量和指令。内置的模板引擎有 DjangoTemplates 和 jinja2.Jinja2，每个模板引擎都有自己的变量和指令语法。&lt;/li&gt;
&lt;li&gt;DIRS:设置模板所在路径，告诉Django在哪个地方查找模板的位置，默认为空列表。&lt;/li&gt;
&lt;li&gt;APP_DIRS:是否在App里查找模板文件。&lt;/li&gt;
&lt;li&gt;OPTIONS:用于填充在RequestContext 的上下文（模板里面的变量和指令)，一般情况下不做任何修改。&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;我们是可以在应用里新建templates，供自己的应用使用。在templates下新建index2.html模版文件&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;/images/in-post/post_django/image-20240205121557430.png&quot; alt=&quot;image-20240205121557430&quot; /&gt;&lt;/p&gt;
&lt;p&gt;views.py里面把index.html改成index2.html&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;/images/in-post/post_django/image-20240205121919651.png&quot; alt=&quot;image-20240205121919651&quot; /&gt;&lt;/p&gt;
&lt;p&gt;最后就是在DIRS里面加上应用的模版路径即可。&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;/images/in-post/post_django/image-20240205121940320.png&quot; alt=&quot;image-20240205121940320&quot; /&gt;&lt;/p&gt;
&lt;p&gt;启动测试：&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;http://127.0.0.1:8000/index/
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;&lt;img src=&quot;/images/in-post/post_django/image-20240205122020903.png&quot; alt=&quot;image-20240205122020903&quot; /&gt;&lt;/p&gt;
&lt;p&gt;但是我们这里有个疑问，如果说应用里的模版和项目里的模版名字一样，起冲突了。这时候，会选择哪个呢，或者说哪个优先级高？&lt;/p&gt;
&lt;p&gt;我们测试下吧。把应用里的index2.html改成index.html，以及views.py里面也改下。&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;/images/in-post/post_django/image-20240205122253035.png&quot; alt=&quot;image-20240205122253035&quot; /&gt;&lt;/p&gt;
&lt;p&gt;然后我们重新运行测试：运行结果显示的是项目里的模版。&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;/images/in-post/post_django/image-20240205122520607.png&quot; alt=&quot;image-20240205122520607&quot; /&gt;&lt;/p&gt;
&lt;p&gt;锋哥经过查看Django底层源码，其实优先级顺序是根据模版配置的目录顺序来定的，我们前面项目模版在前面，所以就显示项目模版。&lt;/p&gt;
&lt;p&gt;如果我们把应用模版配置路径放前面&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;/images/in-post/post_django/image-20240205122706802.png&quot; alt=&quot;image-20240205122706802&quot; /&gt;&lt;/p&gt;
&lt;p&gt;运行测试下：&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;/images/in-post/post_django/image-20240205122719231.png&quot; alt=&quot;image-20240205122719231&quot; /&gt;&lt;/p&gt;
&lt;p&gt;结果就是应用模版了。&lt;/p&gt;
&lt;h4&gt;数据库配置&lt;/h4&gt;
&lt;p&gt;数据库配置是选择项目所使用的数据库的类型，不同的数据库需要设置不同的数据库引擎，数据库引擎用于实现项目与数据库的连接，Django提供4种数据库引擎:
&apos;django.db.backends.postgresql&apos;
&apos;django.db.backends.mysql&apos;
&apos;django.db.backends.sqlite3&apos;
&apos;django.db.backends.oracle&apos;&lt;/p&gt;
&lt;p&gt;项目创建时默认使用Sqlite3数据库，这是一款轻型的数据库，常用于嵌入式系统开发，而且占用的资源非常少。Sqlite3数据库配置信息如下:&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;DATABASES = {
    &apos;default&apos;: {
        &apos;ENGINE&apos;: &apos;django.db.backends.sqlite3&apos;,
        &apos;NAME&apos;: BASE_DIR / &apos;db.sqlite3&apos;,
    }
}
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;如果要把上述的连接信息改成MySQL数据库，首先需要安装MySQL连接模块 mysqlclient&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;pip install mysqlclient -i https://pypi.tuna.tsinghua.edu.cn/simple
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;mysqlclient模块安装后，在项目的配置文件settings.py中配置MySQL数据库连接信息&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;DATABASES = {
    &apos;default&apos;: {
        &apos;ENGINE&apos;: &apos;django.db.backends.mysql&apos;,
        &apos;NAME&apos;: &apos;db_python222&apos;,
        &apos;USER&apos;: &apos;root&apos;,
        &apos;PASSWORD&apos;: &apos;123456&apos;,
        &apos;HOST&apos;: &apos;localhost&apos;,
        &apos;PORT&apos;: &apos;3306&apos;
    }
}
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;（django5至少需要MySQL 8.0.11版本）&lt;/p&gt;
&lt;p&gt;我们来测试下数据库连接；&lt;/p&gt;
&lt;p&gt;我们首先在mysql&lt;code&gt;里创建数据库db_python222&lt;/code&gt;&lt;/p&gt;
&lt;p&gt;然后我们用Django5 manage.py 提供的内置命令 migrate 来创建Django内置功能的数据表；&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;/images/in-post/post_django/image-20240212192654562.png&quot; alt=&quot;image-20240212192654562&quot; /&gt;&lt;/p&gt;
&lt;p&gt;刷新数据库表：&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;/images/in-post/post_django/image-20240212192803613.png&quot; alt=&quot;image-20240212192803613&quot; /&gt;&lt;/p&gt;
&lt;p&gt;这些是Django内置自带的Admin后台管理系统，Auth用户系统以及会话机制等功能需要用到的表。&lt;/p&gt;
&lt;p&gt;注意：django也支持pymysql,mysqldb等，但是用的时候会有点小问题，所以建议大家还是用mysqlclient，比较稳定。&lt;/p&gt;
&lt;p&gt;同时django支持多数据库；&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;DATABASES = {
    &apos;default&apos;: {
        &apos;ENGINE&apos;: &apos;django.db.backends.mysql&apos;,
        &apos;NAME&apos;: &apos;db_python222&apos;,
        &apos;USER&apos;: &apos;root&apos;,
        &apos;PASSWORD&apos;: &apos;123456&apos;,
        &apos;HOST&apos;: &apos;localhost&apos;,
        &apos;PORT&apos;: &apos;3308&apos;
    },
    &apos;mySqlite3&apos;: {
        &apos;ENGINE&apos;: &apos;django.db.backends.sqlite3&apos;,
        &apos;NAME&apos;: BASE_DIR / &apos;db.sqlite3&apos;,
    },
    &apos;mySql3&apos;: {
        &apos;ENGINE&apos;: &apos;django.db.backends.mysql&apos;,
        &apos;NAME&apos;: &apos;db_django&apos;,
        &apos;USER&apos;: &apos;root&apos;,
        &apos;PASSWORD&apos;: &apos;123&apos;,
        &apos;HOST&apos;: &apos;localhost&apos;,
        &apos;PORT&apos;: &apos;3306&apos;
    }
}
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;例如上面，我们定义了三个数据库，两个mysql，一个sqlite；配置属性DATABASES设有3个键值对，分别是：&apos;default&apos;，&apos;mySqlite3&apos;，&apos;mySql3&apos;，每个键值对代表Django连接了某个数据库。&lt;/p&gt;
&lt;p&gt;若项目中连接了多个数据库，则数据库之间的使用需要遵从一定的规则和设置。比如项目中定义了多个模型，每个模型所对应的数据表可以选择在某个数据库中生成，如果模型没有指向某个数据库，模型就会在key为default的数据库里生成。&lt;/p&gt;
&lt;h4&gt;中间件&lt;/h4&gt;
&lt;p&gt;中间件(Middleware）是一个用来处理 Django 的请求(Request）和响应（Response）的框架级别的钩子，它是一个轻量、低级别的插件系统，用于在全局范围内改变 Django的输入和输出。
当用户在网站中进行某个操作时，这个过程是用户向网站发送HTTP请求(Request);而网站会根据用户的操作返回相关的网页内容，这个过程称为响应处理(Response)。从请求到响应的过程中，当 Django接收到用户请求时，首先经过中间件处理请求信息，执行相关的处理，然后将处理结果返回给用户。&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;/images/in-post/post_django/image-20240213114551921.png&quot; alt=&quot;image-20240213114551921&quot; /&gt;&lt;/p&gt;
&lt;p&gt;django默认的中间配置如下：&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;MIDDLEWARE = [
    &apos;django.middleware.security.SecurityMiddleware&apos;,
    &apos;django.contrib.sessions.middleware.SessionMiddleware&apos;,
    &apos;django.middleware.common.CommonMiddleware&apos;,
    &apos;django.middleware.csrf.CsrfViewMiddleware&apos;,
    &apos;django.contrib.auth.middleware.AuthenticationMiddleware&apos;,
    &apos;django.contrib.messages.middleware.MessageMiddleware&apos;,
    &apos;django.middleware.clickjacking.XFrameOptionsMiddleware&apos;,
]
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;django自带的中间件有：&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;SecurityMiddleware:内置的安全机制，保护用户与网站的通信安全。&lt;/li&gt;
&lt;li&gt;SessionMiddleware:会话Session功能。&lt;/li&gt;
&lt;li&gt;LocaleMiddleware:国际化和本地化功能。&lt;/li&gt;
&lt;li&gt;CommonMiddleware:处理请求信息，规范化请求内容。&lt;/li&gt;
&lt;li&gt;CsrfViewMiddleware:开启CSRF防护功能。&lt;/li&gt;
&lt;li&gt;AuthenticationMiddleware:开启内置的用户认证系统。&lt;/li&gt;
&lt;li&gt;MessageMiddleware:开启内置的信息提示功能。&lt;/li&gt;
&lt;li&gt;XFrameOptionsMiddleware:防止恶意程序单击劫持。&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;我们也可以自定义中间件：&lt;/p&gt;
&lt;p&gt;中间件可以定义五个方法，分别是：（主要的是process_request和process_response），在自己定义中间件时，必须继承MiddlewareMixin&lt;/p&gt;
&lt;p&gt;process_request(self,request) 请求views方法之前会执行。
process_view(self, request, callback, callback_args, callback_kwargs)  Django会在调用视图函数之前调用process_view方法。
process_template_response(self,request,response) 该方法对视图函数返回值有要求，必须是一个含有render方法类的对象，才会执行此方法
process_exception(self, request, exception)  这个方法只有在视图函数中出现异常了才执行
process_response(self, request, response)  请求执行完成，返回页面前会执行&lt;/p&gt;
&lt;p&gt;新建Md1自定义中间件类，继承MiddlewareMixin，实现process_request和process_response方法。&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;/images/in-post/post_django/image-20240213121211702.png&quot; alt=&quot;image-20240213121211702&quot; /&gt;&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;&quot;&quot;&quot;
    自定义中间件
    作者 : 小锋老师
    官网 : www.python222.com
&quot;&quot;&quot;
from django.utils.deprecation import MiddlewareMixin


class Md1(MiddlewareMixin):
    def process_request(self, request):
        print(&quot;request请求来了&quot;)

    def process_response(self, request, response):
        print(&quot;请求处理完毕，将返回到页面&quot;)
        return response

&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;setting.py里配置自定义中间件。&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;/images/in-post/post_django/image-20240213121340027.png&quot; alt=&quot;image-20240213121340027&quot; /&gt;&lt;/p&gt;
&lt;p&gt;views.py的index请求处理方法，我们加一句打印。&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;/images/in-post/post_django/image-20240213121408003.png&quot; alt=&quot;image-20240213121408003&quot; /&gt;&lt;/p&gt;
&lt;p&gt;最后我们运行测试：&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;http://127.0.0.1:8000/index/
&lt;/code&gt;&lt;/pre&gt;
&lt;pre&gt;&lt;code&gt;request请求来了
页面请求处理中
请求处理完毕，将返回到页面
&lt;/code&gt;&lt;/pre&gt;
&lt;h4&gt;其他配置&lt;/h4&gt;
&lt;p&gt;还有一些其他settings.py配置我们了解下&lt;/p&gt;
&lt;p&gt;ROOT_URLCONF = &apos;python222_site1_pc.urls&apos;   它指定了当前项目的根 URL，是 Django 路由系统的入口。&lt;/p&gt;
&lt;p&gt;WSGI_APPLICATION = &apos;python222_site1_pc.wsgi.application&apos;  项目部署时，Django 的内置服务器将使用的 WSGI 应用程序对象的完整 Python 路径。&lt;/p&gt;
&lt;p&gt;AUTH_PASSWORD_VALIDATORS  这是一个支持插拔的密码验证器，且可以一次性配置多个，Django 通过这些内置组件来避免用户设置的密码等级不足的问题。&lt;/p&gt;
&lt;p&gt;LANGUAGE_CODE = &apos;en-us&apos;   TIME_ZONE = &apos;UTC&apos;&lt;/p&gt;
&lt;p&gt;分别代表语言配置项和当前服务端时区的配置项，我们常用的配置如下所示：&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;LANGUAGE_CODE 取值是英文：&apos;en-us&apos;或者中文：&apos;zh-Hans&apos;；&lt;/li&gt;
&lt;li&gt;TIME_ZONE 取值是世界时区 &apos;UTC&apos; 或中国时区 &apos;Asia/Shanghai&apos;。&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;USE_I18N = True 项目开发完成后，可以选择向不同国家的用户提供服务，那么就需要支持国际化和本地化。&lt;/p&gt;
&lt;p&gt;USE_TZ = True  它指对时区的处理方式，当设置为 True 的时候，存储到数据库的时间是世界时间 &apos;UTC&apos;。&lt;/p&gt;
&lt;p&gt;DEFAULT_AUTO_FIELD = &apos;django.db.models.BigAutoField&apos;  默认主键自增类型&lt;/p&gt;
&lt;h2&gt;Django5路由定义与使用&lt;/h2&gt;
&lt;h3&gt;Django5路由定义&lt;/h3&gt;
&lt;p&gt;一个完整的路由包含:路由地址、视图函数（或者视图类)、可选变量和路由命名。&lt;/p&gt;
&lt;p&gt;路由称为URL (Uniform Resource Locator，统一资源定位符），也可以称为URLconf，是对可以从互联网上得到的资源位置和访问方法的一种简洁的表示，是互联网上标准资源的地址。互联网上的每个文件都有一个唯一的路由，用于指出网站文件的路径位置。简单地说，路由可视为我们常说的网址，每个网址代表不同的网页。&lt;/p&gt;
&lt;p&gt;前面的Hello World项目。我们请求的地址：&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;http://127.0.0.1:8000/index/
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;就是一个路由地址。&lt;/p&gt;
&lt;p&gt;&lt;code&gt;index/&lt;/code&gt;请求地址，根据urls.py配置文件，找到对应的helloWorld views下的index视图函数；&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;/images/in-post/post_django/image-20240214095458811.png&quot; alt=&quot;image-20240214095458811&quot; /&gt;&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;/images/in-post/post_django/image-20240214095529865.png&quot; alt=&quot;image-20240214095529865&quot; /&gt;&lt;/p&gt;
&lt;p&gt;然后最终执行index视图函数，然后到index.html页面。&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;/images/in-post/post_django/image-20240214095615429.png&quot; alt=&quot;image-20240214095615429&quot; /&gt;&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;/images/in-post/post_django/image-20240214095555412.png&quot; alt=&quot;image-20240214095555412&quot; /&gt;&lt;/p&gt;
&lt;h3&gt;Django5路由变量&lt;/h3&gt;
&lt;p&gt;在平时开发中，有时候一个路由可以代表多个不同的页面，比如博客系统里面，有1千个博客页面，按照前面学习的方式，需要写1千个路由才能实现，这种做法显然不可取，维护也麻烦。我们可以通过路由变量，来实现一个路由代表多个页面。&lt;/p&gt;
&lt;p&gt;路由的变量类型有字符类型、整型、slug 和 uuid，最为常用的是字符类型和整型。各个类型说明如下。&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;字符类型:匹配任何非空字符串，但不含斜杠。如果没有指定类型，就默认使用该类型。&lt;/li&gt;
&lt;li&gt;整型:匹配О和正整数。&lt;/li&gt;
&lt;li&gt;slug:可理解为注释、后缀或附属等概念，常作为路由的解释性字符。可匹配任何ASCII字符以及连接符和下画线，能使路由更加清晰易懂。比如网页的标题是“15岁的孩子”，其路由地址可以设置为“15-sui-de-hai-zi”。&lt;/li&gt;
&lt;li&gt;uuid:匹配一个uuid格式的对象。为了防止冲突，规定必须使用“”并且所有字母必须小写，例如175194d3-6885-437e-a8a8-6c231e272f00。&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;下面列举实例一 博客帖子请求：&lt;/p&gt;
&lt;p&gt;首先urls.py里定义路由映射：&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;path(&apos;blog/&amp;lt;int:id&amp;gt;&apos;, helloWorld.views.blog)
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;&lt;img src=&quot;/images/in-post/post_django/image-20240214101929403.png&quot; alt=&quot;image-20240214101929403&quot; /&gt;&lt;/p&gt;
&lt;p&gt;views.py里再定义blog函数实现：&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;def blog(request, id):
    return HttpResponse(&apos;id是&apos; + str(id) + &quot;的博客页面&quot;)
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;&lt;img src=&quot;/images/in-post/post_django/image-20240214102016747.png&quot; alt=&quot;image-20240214102016747&quot; /&gt;&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;/images/in-post/post_django/image-20240214102024852.png&quot; alt=&quot;image-20240214102024852&quot; /&gt;&lt;/p&gt;
&lt;p&gt;这样，我们就实现了一个带变量的路由的多个博客页面的实现。&lt;/p&gt;
&lt;p&gt;当然我们也可以带多个路由变量。让博客的路由地址，在带上年月日变量。&lt;/p&gt;
&lt;p&gt;urls.py修改&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;path(&apos;blog2/&amp;lt;int:year&amp;gt;/&amp;lt;int:month&amp;gt;/&amp;lt;int:day&amp;gt;/&amp;lt;int:id&amp;gt;&apos;, helloWorld.views.blog2)
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;views.py修改&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;def blog2(request, year, month, day, id):
    return HttpResponse(str(year) + &apos;/&apos; + str(month) + &apos;/&apos; + str(day) + &apos;/&apos; + &apos;  id是&apos; + str(id) + &quot;的博客页面&quot;)
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;运行测试：&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;/images/in-post/post_django/image-20240214102933139.png&quot; alt=&quot;image-20240214102933139&quot; /&gt;&lt;/p&gt;
&lt;h3&gt;Django5正则路由&lt;/h3&gt;
&lt;p&gt;有时候我们为了更好的进行路由匹配，可以用正则表达式。&lt;/p&gt;
&lt;p&gt;比如前面讲的日期的路由变量，其实是有点问题的。&lt;/p&gt;
&lt;p&gt;我们月份输入333，也是满足条件的。但是不符合实际，实际情况月份最多两位数。&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;/images/in-post/post_django/image-20240214110103791.png&quot; alt=&quot;image-20240214110103791&quot; /&gt;&lt;/p&gt;
&lt;p&gt;所以这时候，我们可以用正则表达式来限制。&lt;/p&gt;
&lt;p&gt;urls.py修改：&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;re_path(&apos;blog3/(?P&amp;lt;year&amp;gt;[0-9]{4})/(?P&amp;lt;month&amp;gt;[0-9]{2})/(?P&amp;lt;day&amp;gt;[0-9]{2})&apos;, helloWorld.views.blog3)
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;views.py添加blogs方法：&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;def blog3(request, year, month, day):
    return HttpResponse(str(year) + &apos;/&apos; + str(month) + &apos;/&apos; + str(day) + &apos;的博客页面&apos;)
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;这里有几个正则的注意点：&lt;/p&gt;
&lt;p&gt;第一：正则urls匹配，必须用re_path方法；&lt;/p&gt;
&lt;p&gt;第二：正则表达式?P开头是固定格式；&lt;/p&gt;
&lt;p&gt;运行测试：&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;/images/in-post/post_django/image-20240214111820848.png&quot; alt=&quot;image-20240214111820848&quot; /&gt;&lt;/p&gt;
&lt;h3&gt;Django5路由重定向&lt;/h3&gt;
&lt;p&gt;重定向称为HTTP协议重定向，也可以称为网页跳转，它对应的HTTP状态码为301、302、303、307、308。简单来说，网页重定向就是在浏览器访问某个网页的时候，这个网页不提供响应内容，而是自动跳转到其他网址，由其他网址来生成响应内容。&lt;/p&gt;
&lt;p&gt;Django的网页重定向有两种方式:&lt;/p&gt;
&lt;p&gt;第一种方式是路由重定向;&lt;/p&gt;
&lt;p&gt;第二种方式是自定义视图的重定向。&lt;/p&gt;
&lt;p&gt;两种重定向方式各有优点，前者是使用Django内置的视图类RedirectView实现的，默认支持HTTP的GET请求;后者是在自定义视图的响应状态设置重定向，能让开发者实现多方面的开发需求。&lt;/p&gt;
&lt;p&gt;我们分别用实例来演示下这两种方式：&lt;/p&gt;
&lt;h4&gt;路由重定向&lt;/h4&gt;
&lt;p&gt;路由重定向方式，我们用RedirectView实现，在urls.py里面，我们再加一个路由代码：&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;path(&apos;redirectTo&apos;, RedirectView.as_view(url=&quot;index/&quot;))
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;请求redirectTo，直接重定向到 index/ 地址&lt;/p&gt;
&lt;p&gt;运行测试，请求：&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;http://127.0.0.1:8000/redirectTo
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;&lt;img src=&quot;/images/in-post/post_django/image-20240214203510469.png&quot; alt=&quot;image-20240214203510469&quot; /&gt;&lt;/p&gt;
&lt;p&gt;自动重定向到index，状态是302。&lt;/p&gt;
&lt;h4&gt;自定义视图重定向&lt;/h4&gt;
&lt;p&gt;更多的情况，我们平时开发用的是自定义视图重定向，视图代码里，通过逻辑判断，通过redirect方法来实现具体的页面重定向，使用更加灵活。&lt;/p&gt;
&lt;p&gt;我们改造下前面的views.py下的blog函数：假如id是0，重定向到错误静态页面。&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;def blog(request, id):
    if id == 0:
        return redirect(&quot;/static/error.html&quot;)
    else:
        return HttpResponse(&apos;id是&apos; + str(id) + &quot;的博客页面&quot;)
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;&lt;img src=&quot;/images/in-post/post_django/image-20240214205436954.png&quot; alt=&quot;image-20240214205436954&quot; /&gt;&lt;/p&gt;
&lt;p&gt;static目录下新建一个error.html文件&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;&amp;lt;!DOCTYPE html&amp;gt;
&amp;lt;html lang=&quot;en&quot;&amp;gt;
&amp;lt;head&amp;gt;
    &amp;lt;meta charset=&quot;UTF-8&quot;&amp;gt;
    &amp;lt;title&amp;gt;Title&amp;lt;/title&amp;gt;
&amp;lt;/head&amp;gt;
&amp;lt;body&amp;gt;
系统运行有问题！
&amp;lt;/body&amp;gt;
&amp;lt;/html&amp;gt;
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;当然你也可以项目根目录下再新建一个目录，比如common,然后 STATICFILES_DIRS 静态资源文件集合里，加下 BASE_DIR / &quot;common&quot;，把error.html放到common目录下，我们也是可以通过static/请求地址访问的。当然如果你觉得static/请求名称不好，也可以修改 STATIC_URL 参数 比如 改成 common/ 也行，你就可以通过 common/ 也访问你的静态资源文件。&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;/images/in-post/post_django/image-20240214205539794.png&quot; alt=&quot;image-20240214205539794&quot; /&gt;&lt;/p&gt;
&lt;p&gt;我们访问&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;http://127.0.0.1:8000/blog/0
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;&lt;img src=&quot;/images/in-post/post_django/image-20240214210117230.png&quot; alt=&quot;image-20240214210117230&quot; /&gt;&lt;/p&gt;
&lt;p&gt;302状态，自动跳转到了error.html错误页面。&lt;/p&gt;
&lt;p&gt;访问其他id是正常的。&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;/images/in-post/post_django/image-20240214210210839.png&quot; alt=&quot;image-20240214210210839&quot; /&gt;&lt;/p&gt;
&lt;h3&gt;Django5命名空间namespace&lt;/h3&gt;
&lt;p&gt;当我们网站项目规模越来越多，子项目很多的时候，为了方便管理路由地址，我们可以采用命名空间namespace来对路由地址根据子项目分类。&lt;/p&gt;
&lt;p&gt;我们通过django manage.py自带的startapp命令新建两个项目，分别是user和order&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;/images/in-post/post_django/image-20240215182312654.png&quot; alt=&quot;image-20240215182312654&quot; /&gt;&lt;/p&gt;
&lt;p&gt;我们分别添加urls.py到user和order项目里去。&lt;/p&gt;
&lt;p&gt;以及加下代码：&lt;/p&gt;
&lt;p&gt;user项目的urls.py:&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;from django.contrib import admin
from django.urls import path

from user import views

urlpatterns = [
    path(&apos;admin/&apos;, admin.site.urls),
    path(&apos;index/&apos;, views.index),
]
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;user项目的views.py&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;def index(request):
    return HttpResponse(&quot;用户信息&quot;)
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;order项目的urls.py:&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;from django.contrib import admin
from django.urls import path

from order import views

urlpatterns = [
    path(&apos;admin/&apos;, admin.site.urls),
    path(&apos;index/&apos;, views.index),
    path(&apos;list/&apos;, views.list),
]
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;order项目的views.py：&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;from django.http import HttpResponse


# Create your views here.
def index(request):
    return HttpResponse(&quot;订单信息&quot;)


def list(request):
    return HttpResponse(&quot;订单列表&quot;)
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;接下来，我们在主项目里，加下映射：&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;/images/in-post/post_django/image-20240215183407545.png&quot; alt=&quot;image-20240215183407545&quot; /&gt;&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;path(&apos;user/&apos;, include((&apos;user.urls&apos;, &apos;user&apos;), namespace=&apos;user&apos;)),
    path(&apos;order/&apos;, include((&apos;order.urls&apos;, &apos;order&apos;), namespace=&apos;order&apos;))
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;说明下：&lt;/p&gt;
&lt;p&gt;include((&apos;user.urls&apos;, &apos;user&apos;) 相当于找到user项目的urls.py文件。&lt;/p&gt;
&lt;p&gt;namespace=&apos;user&apos; 给这个映射取名是user，一般是根据项目名称来取。&lt;/p&gt;
&lt;p&gt;第一个参数 &apos;user/&apos; 标识 user/开头的请求，都由user项目的urls.py去管理处理映射关系。&lt;/p&gt;
&lt;p&gt;通过这种命名空间，我们可以把复杂项目的路由映射拆分，升级维护会方便很多。&lt;/p&gt;
&lt;h3&gt;Django5路由命名与反向解析reverse与resolve&lt;/h3&gt;
&lt;p&gt;我们在urls.py里定义的路由信息，有时候需要动态获取路由信息，然后进行一些处理，统计，日志等操作，这时候我们需要在其他代码里用到路由信息，比如views.py，后面要学到的模型models.py，Admin系统等，因此我们引入路由反向解析reverse与resolve方法，再使用这两个方法前，我们还需要给路由取名，否则我们无法找到我们需要的那个路由的信息。reverse方法根据路由名称得到路由地址，resolve方法根据路由地址得到路由所有信息。&lt;/p&gt;
&lt;p&gt;我们先举一个简单例子来体会下吧。&lt;/p&gt;
&lt;p&gt;在order项目的urls.py里，我们对index/和list/请求路由分别取名index和list&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;/images/in-post/post_django/image-20240215192246072.png&quot; alt=&quot;image-20240215192246072&quot; /&gt;&lt;/p&gt;
&lt;p&gt;然后修改views.py的index方法：&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;def index(request):
    route_url = reverse(&apos;order:index&apos;)
    print(&quot;reverse反向解析得到路由地址：&quot; + route_url)
    result = resolve(route_url)
    print(&quot;resolve通过路由地址得到路由信息：&quot; + str(result))
    return HttpResponse(&quot;订单信息&quot;)
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;我们运行请求：&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;http://127.0.0.1:8000/order/index/
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;控制台输出：&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;reverse反向解析得到路由地址：/order/index/
resolve通过路由地址得到路由信息：ResolverMatch(func=order.views.index, args=(), kwargs={}, url_name=&apos;index&apos;, app_names=[&apos;order&apos;], namespaces=[&apos;order&apos;], route=&apos;order/index/&apos;)
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;resolve返回对象属性介绍：&lt;/p&gt;
&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;函数方法&lt;/th&gt;
&lt;th&gt;说明&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;func&lt;/td&gt;
&lt;td&gt;路由的视图函数对象或视图类对象&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;args&lt;/td&gt;
&lt;td&gt;以列表格式获取路由的变量信息&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;kwargs&lt;/td&gt;
&lt;td&gt;以字典格式获取路由的变量信息&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;url_name&lt;/td&gt;
&lt;td&gt;获取路由命名name&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;app names&lt;/td&gt;
&lt;td&gt;与app name功能一致，但以列表格式表示&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;namespaces&lt;/td&gt;
&lt;td&gt;与namespace功能一致,但以列表格式表示&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;route&lt;/td&gt;
&lt;td&gt;获取整个路由的名称，包括命名空间&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;
&lt;p&gt;这里我们在修改下项目，来讲下参数的运用。&lt;/p&gt;
&lt;p&gt;order的urls.py的list请求加下年月日路由变量&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;/images/in-post/post_django/image-20240215193854795.png&quot; alt=&quot;image-20240215193854795&quot; /&gt;&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;path(&apos;list/&amp;lt;int:year&amp;gt;/&amp;lt;int:month&amp;gt;/&amp;lt;int:day&amp;gt;/&apos;, views.list, name=&quot;list&quot;)
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;对应的views.py的list方法我们也进行修改，要加上三个路由变量&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;def list(request, year, month, day):
    kwargs = {&apos;year&apos;: year - 1, &apos;month&apos;: month + 1, &apos;day&apos;: day}
    args = [year, month, day]
    # route_url = reverse(&apos;order:list&apos;, args=args)
    route_url = reverse(&apos;order:list&apos;, kwargs=kwargs)
    print(&quot;reverse反向解析得到路由地址：&quot; + route_url)
    result = resolve(route_url)
    print(&quot;resolve通过路由地址得到路由信息：&quot; + str(result))
    return HttpResponse(&quot;订单列表&quot;)
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;进行反向解析路由的时候，我们也可以带上路由实参，可以通过kwargs字典键值对，也可以通过args元组；&lt;/p&gt;
&lt;p&gt;测试请求地址：&lt;code&gt;http://127.0.0.1:8000/order/list/2010/11/11/&lt;/code&gt;&lt;/p&gt;
&lt;p&gt;控制台输出：&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;reverse反向解析得到路由地址：/order/list/2009/12/11/
resolve通过路由地址得到路由信息：ResolverMatch(func=order.views.list, args=(), kwargs={&apos;year&apos;: 2009, &apos;month&apos;: 12, &apos;day&apos;: 11}, url_name=&apos;list&apos;, app_names=[&apos;order&apos;], namespaces=[&apos;order&apos;], route=&apos;order/list/&amp;lt;int:year&amp;gt;/&amp;lt;int:month&amp;gt;/&amp;lt;int:day&amp;gt;/&apos;, captured_kwargs={&apos;year&apos;: 2009, &apos;month&apos;: 12, &apos;day&apos;: 11})
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;点开reverse方法：&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;/images/in-post/post_django/image-20240215195534704.png&quot; alt=&quot;image-20240215195534704&quot; /&gt;&lt;/p&gt;
&lt;p&gt;必须参数viewname，以及一些可选参数：&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;viewname:代表路由命名或可调用视图对象，一般情况下是以路由命名name来生成路由地址的。&lt;/li&gt;
&lt;li&gt;urlconf:设置反向解析的URLconf模块。默认情况下，使用配置文件 settings.py 的ROOT_URLCONF属性( 主项目文件夹的urls.py ).&lt;/li&gt;
&lt;li&gt;args:以列表方式传递路由地址变量，列表元素顺序和数量应与路由地址变量的顺序和数量一致。&lt;/li&gt;
&lt;li&gt;kwargs:以字典方式传递路由地址变量，字典的键必须对应路由地址变量名，字典的键值对数量与变量的数量一致。&lt;/li&gt;
&lt;li&gt;current app:提示当前正在执行的视图所在的项目应用，主要起到提示作用，在功能上并无实质的作用。&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;点开resolve方法：&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;/images/in-post/post_django/image-20240215195754140.png&quot; alt=&quot;image-20240215195754140&quot; /&gt;&lt;/p&gt;
&lt;p&gt;就两个参数：&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;path:代表路由地址，通过路由地址来获取对应的路由对象信息。&lt;/li&gt;
&lt;li&gt;urlconf:设置反向解析的_URLconf模块。默认情况下，使用配置文件 settings.py 的ROOT_URLCONF属性( 主项目文件夹的urls.py ).&lt;/li&gt;
&lt;/ul&gt;
&lt;h2&gt;Django5视图定义与使用&lt;/h2&gt;
&lt;p&gt;视图(Views）是 Django的 MTV架构模式的V部分，主要负责处理用户请求和生成相应的响应内容,然后在页面或其他类型文档中显示。&lt;/p&gt;
&lt;p&gt;Django的MTV分别代表：&lt;/p&gt;
&lt;p&gt;Model(模型)：业务对象与数据库的对象(ORM)&lt;/p&gt;
&lt;p&gt;Template(模版)：负责如何把页面展示给用户&lt;/p&gt;
&lt;p&gt;View(视图)：负责业务逻辑，并在适当的时候调用Model和Template&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;/images/in-post/post_django/image-20240216110307884.png&quot; alt=&quot;image-20240216110307884&quot; /&gt;&lt;/p&gt;
&lt;h3&gt;Django5设置视图响应状态&lt;/h3&gt;
&lt;p&gt;客户端请求后端服务，在view.py视图层方法最终return 返回视图响应。Python内置提供了响应类型，来实现不同的返回不同的http状态码；&lt;/p&gt;
&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;响应类型&lt;/th&gt;
&lt;th&gt;解释说明&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;HttpResponse(&apos;Hello world&apos;&quot;)&lt;/td&gt;
&lt;td&gt;状态码200，请求已成功被服务器接收&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;HttpResponseRedirect(&apos;/&apos;)&lt;/td&gt;
&lt;td&gt;状态码302，重定向首页地址&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;HttpResponsePermanentRedirect(&apos;/&apos;)&lt;/td&gt;
&lt;td&gt;状态码301，永久重定向首页地址&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;HttpResponseBadRequest(&quot;&apos;400&apos;)&lt;/td&gt;
&lt;td&gt;状态码400，访问的页面不存在或请求错误&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;HttpResponseNotFound(&apos;404&quot;)&lt;/td&gt;
&lt;td&gt;状态码404，网页不存在或网页的URL失效&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;HttpResponseForbidden(&apos;403&apos;)&lt;/td&gt;
&lt;td&gt;状态码403，没有访问权限&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;HttpResponseNotAllowed(&apos;405&apos;)&lt;/td&gt;
&lt;td&gt;状态码405，不允许使用该请求方式&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;HttpResponseServerError(&apos;500&apos;&quot;)&lt;/td&gt;
&lt;td&gt;状态码500，服务器内容错误&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;JsonResponse( {&apos;foo&apos; : &apos;bar&apos;})&lt;/td&gt;
&lt;td&gt;默认状态码200，响应内容为JSON数据&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;StreamingHttpResponse()&lt;/td&gt;
&lt;td&gt;默认状态码200，响应内容以流式输出&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;
&lt;p&gt;下面我们举几个例子来实操下视图响应状态应用；&lt;/p&gt;
&lt;p&gt;举例一：HttpResponse&lt;/p&gt;
&lt;p&gt;修改helloWorld的views.py的index函数：&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;def index(request):
    html = &quot;&amp;lt;font color=&apos;red&apos;&amp;gt;学Python，上www.python222.com&amp;lt;/font&amp;gt;&quot;
    return HttpResponse(html, status=200)
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;&lt;img src=&quot;/images/in-post/post_django/image-20240216113028234.png&quot; alt=&quot;image-20240216113028234&quot; /&gt;&lt;/p&gt;
&lt;p&gt;请求测试，状态码200，返回网页信息。status=200不写的话默认也是200.&lt;/p&gt;
&lt;p&gt;举例二：HttpResponseNotFound  404&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;def index(request):
    return HttpResponseNotFound()
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;&lt;img src=&quot;/images/in-post/post_django/image-20240216113425405.png&quot; alt=&quot;image-20240216113425405&quot; /&gt;&lt;/p&gt;
&lt;p&gt;请求测试，状态码404。&lt;/p&gt;
&lt;p&gt;举例三：JsonResponse 响应json数据&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;def index(request):
    return JsonResponse({&apos;foo&apos;: &apos;bar&apos;})
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;&lt;img src=&quot;/images/in-post/post_django/image-20240216113717071.png&quot; alt=&quot;image-20240216113717071&quot; /&gt;&lt;/p&gt;
&lt;p&gt;请求测试，状态码200，返回json格式数据。&lt;/p&gt;
&lt;p&gt;我们第一个实例用到的是HttpResponse，简单网页我们直接可以响应到页面，但是假如是复杂网页，就会增加视图函数的代码量。所以我们引入模版，通过django提供的render方法渲染数据到模版，然后再响应到页面。&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;def index(request):
    return render(request, &apos;index.html&apos;)
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;这个是我们前面的的HelloWorld代码，我们ctrl点进去render方法，看下源码：&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;/images/in-post/post_django/image-20240216122419239.png&quot; alt=&quot;image-20240216122419239&quot; /&gt;&lt;/p&gt;
&lt;p&gt;经过模版渲染后得到content网页内容，依然返回的是HttpResponse对象。&lt;/p&gt;
&lt;p&gt;render方法定义：&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;def render(
    request, template_name, context=None, content_type=None, status=None, using=None
):
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;request和template_name是必须的参数。其他参数可选。&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;request:浏览器向服务器发送的请求对象，包含用户信息、请求内容和请求方式等。&lt;/li&gt;
&lt;li&gt;template_name:设置模板文件名，用于生成网页内容。&lt;/li&gt;
&lt;li&gt;context:对模板上下文（模板变量）赋值，以字典格式表示，默认情况下是一个空字典。&lt;/li&gt;
&lt;li&gt;content_type:响应内容的数据格式，一般情况下使用默认值即可。&lt;/li&gt;
&lt;li&gt;status: HTTP状态码，默认为200.&lt;/li&gt;
&lt;li&gt;using:设置模板引擎，用于解析模板文件，生成网页内容。&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;我们再写一个带字典参数的render渲染模版实例：&lt;/p&gt;
&lt;p&gt;views.py改写下：&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;def index(request):
    content_value = {&quot;msg&quot;: &apos;学Python，上www.python222.com&apos;}
    return render(request, &apos;index.html&apos;, context=content_value)
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;模版代码改写下，模版里取值语法 {{ 字典的key值 }}&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;&amp;lt;!DOCTYPE html&amp;gt;
&amp;lt;html lang=&quot;en&quot;&amp;gt;
&amp;lt;head&amp;gt;
    &amp;lt;meta charset=&quot;UTF-8&quot;&amp;gt;
    &amp;lt;title&amp;gt;Title&amp;lt;/title&amp;gt;
&amp;lt;/head&amp;gt;
&amp;lt;body&amp;gt;
模版取值： {{ msg }}
&amp;lt;/body&amp;gt;
&amp;lt;/html&amp;gt;
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;请求测试：&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;/images/in-post/post_django/image-20240216123224384.png&quot; alt=&quot;image-20240216123224384&quot; /&gt;&lt;/p&gt;
&lt;h3&gt;Django5设置重定向响应&lt;/h3&gt;
&lt;p&gt;Django重定向我们前面讲过一种urls.py里使用RedirectView实现&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;path(&apos;redirectTo&apos;, RedirectView.as_view(url=&quot;index/&quot;))
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;这种方便书写简单，但是不灵活，我们实际开发，为了能否实现业务上的判断，来进行业务重定向跳转，还是需要用redirect方法。&lt;/p&gt;
&lt;p&gt;重定向的状态码分为301和302，前者是永久性跳转的，后者是临时跳转的，两者的区别在于搜索引擎的网页抓取。301重定向是永久的重定向，搜索引擎在抓取新内容的同时会将旧的网址替换为重定向之后的网址。302跳转是暂时的跳转，搜索引擎会抓取新内容而保留旧的网址。因为服务器返回302代码，所以搜索引擎认为新的网址只是暂时的。&lt;/p&gt;
&lt;p&gt;Django内置提供了重定向类HttpResponseRedirect和HttpResponsePermanentRedirect分别代表HTTP状态码302和301&lt;/p&gt;
&lt;p&gt;![image-20240219205454516](D:\开发\Python\Django\2024版 一周掌握Django5视频教程 python web开发.assets\image-20240219205454516.png)&lt;/p&gt;
&lt;p&gt;我们通过一个实例来测试下重定向&lt;/p&gt;
&lt;p&gt;我们static下面新建一个新页面new.html&lt;/p&gt;
&lt;p&gt;![image-20240219205608640](D:\开发\Python\Django\2024版 一周掌握Django5视频教程 python web开发.assets\image-20240219205608640.png)&lt;/p&gt;
&lt;p&gt;改写helleWorld里得index方法：&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;def index(request):
    return redirect(&quot;/static/new.html&quot;)
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;我们测试下：请求  &lt;code&gt;http://127.0.0.1:8000/index/&lt;/code&gt; 302跳转到了new.html页面 （最好用谷歌浏览器得无痕浏览，这样没有缓存干扰测试）&lt;/p&gt;
&lt;p&gt;![image-20240219205626919](D:\开发\Python\Django\2024版 一周掌握Django5视频教程 python web开发.assets\image-20240219205626919.png)&lt;/p&gt;
&lt;p&gt;我们加上 &lt;code&gt;permanent=True&lt;/code&gt;参数；&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;def index(request):
    return redirect(&quot;/static/new.html&quot;, permanent=True)
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;再测试下，就变成301永久跳转了。&lt;/p&gt;
&lt;p&gt;![image-20240219205715484](D:\开发\Python\Django\2024版 一周掌握Django5视频教程 python web开发.assets\image-20240219205715484.png)&lt;/p&gt;
&lt;p&gt;我们点进去看下redirect方法得源码实现：&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;def redirect(to, *args, permanent=False, **kwargs):
    &quot;&quot;&quot;
    Return an HttpResponseRedirect to the appropriate URL for the arguments
    passed.

    The arguments could be:

        * A model: the model&apos;s `get_absolute_url()` function will be called.

        * A view name, possibly with arguments: `urls.reverse()` will be used
          to reverse-resolve the name.

        * A URL, which will be used as-is for the redirect location.

    Issues a temporary redirect by default; pass permanent=True to issue a
    permanent redirect.
    &quot;&quot;&quot;
    redirect_class = (
        HttpResponsePermanentRedirect if permanent else HttpResponseRedirect
    )
    return redirect_class(resolve_url(to, *args, **kwargs))
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;第一个跳转参数支持模型，视图路由名称，还有最常用得url地址；第二个参数就是是否永久跳转，默认Flase；&lt;/p&gt;
&lt;p&gt;redirect_class通过permanent判断，true返回HttpResponsePermanentRedirect，false返回HttpResponseRedirect;&lt;/p&gt;
&lt;h3&gt;Django5二进制文件下载响应&lt;/h3&gt;
&lt;p&gt;响应内容除了返回网页信息外，还可以实现文件下载功能，是网站常用的功能之一。&lt;/p&gt;
&lt;p&gt;Django提供三种方式实现文件下载功能，分别是HttpResponse、StreamingHttpResponse和 FileResponse,三者的说明如下:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;HttpResponse是所有响应过程的核心类，它的底层功能类是HttpResponseBase。&lt;/li&gt;
&lt;li&gt;StreamingHttpResponse是在 HttpResponseBase的基础上进行继承与重写的，它实现流式响应输出（流式响应输出是使用Python的迭代器将数据进行分段处理并传输的)，适用于大规模数据响应和文件传输响应。&lt;/li&gt;
&lt;li&gt;FileResponse是在StreamingHttpResponse 的基础上进行继承与重写的，它实现文件的流式响应输出，只适用于文件传输响应。&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;我们通过实例来看下如何应用：&lt;/p&gt;
&lt;p&gt;我们准备一个文件，这里我们用一个exe二进制文件。放D盘根目录。&lt;/p&gt;
&lt;p&gt;![image-20240219205729549](D:\开发\Python\Django\2024版 一周掌握Django5视频教程 python web开发.assets\image-20240219205729549.png)&lt;/p&gt;
&lt;p&gt;views.py里写方法实现方法：&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;# 定义文件路径
file_path = &quot;D:\\360zip_setup.exe&quot;


def download_file1(request):
    file = open(file_path, &apos;rb&apos;)  # 打开文件
    response = HttpResponse(file)  # 创建HttpResponse对象
    response[&apos;Content_Type&apos;] = &apos;application/octet-stream&apos;
    response[&apos;Content-Disposition&apos;] = &apos;attachment;filename=file1.exe&apos;
    return response


def download_file2(request):
    file = open(file_path, &apos;rb&apos;)  # 打开文件
    response = StreamingHttpResponse(file)  # 创建StreamingHttpResponse对象
    response[&apos;Content_Type&apos;] = &apos;application/octet-stream&apos;
    response[&apos;Content-Disposition&apos;] = &apos;attachment;filename=file2.exe&apos;
    return response


def download_file3(request):
    file = open(file_path, &apos;rb&apos;)  # 打开文件
    response = FileResponse(file)  # 创建FileResponse对象
    response[&apos;Content_Type&apos;] = &apos;application/octet-stream&apos;
    response[&apos;Content-Disposition&apos;] = &apos;attachment;filename=file3.exe&apos;
    return response
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;urls.py里定义下映射：&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;path(&apos;download1&apos;, helloWorld.views.download_file1),
    path(&apos;download2&apos;, helloWorld.views.download_file2),
    path(&apos;download3&apos;, helloWorld.views.download_file3)
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;为了方便测试，我们static目录下新建一个download.html静态文件：&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;&amp;lt;!DOCTYPE html&amp;gt;
&amp;lt;html lang=&quot;en&quot;&amp;gt;
&amp;lt;head&amp;gt;
    &amp;lt;meta charset=&quot;UTF-8&quot;&amp;gt;
    &amp;lt;title&amp;gt;下载测试&amp;lt;/title&amp;gt;
&amp;lt;/head&amp;gt;
&amp;lt;body&amp;gt;
&amp;lt;a href=&quot;/download1&quot;&amp;gt;下载测试一：HttpResponse&amp;lt;/a&amp;gt;&amp;lt;br&amp;gt;
&amp;lt;a href=&quot;/download2&quot;&amp;gt;下载测试二：StreamingHttpResponse&amp;lt;/a&amp;gt;&amp;lt;br&amp;gt;
&amp;lt;a href=&quot;/download3&quot;&amp;gt;下载测试三：FileResponse&amp;lt;/a&amp;gt;
&amp;lt;/body&amp;gt;
&amp;lt;/html&amp;gt;
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;页面输入：&lt;code&gt;http://127.0.0.1:8000/static/download.html&lt;/code&gt; 测试：&lt;/p&gt;
&lt;p&gt;![image-20240219205740833](D:\开发\Python\Django\2024版 一周掌握Django5视频教程 python web开发.assets\image-20240219205740833.png)&lt;/p&gt;
&lt;p&gt;分别点击下载测试：&lt;/p&gt;
&lt;p&gt;![image-20240219205750450](D:\开发\Python\Django\2024版 一周掌握Django5视频教程 python web开发.assets\image-20240219205750450.png)&lt;/p&gt;
&lt;h3&gt;Http请求&amp;amp;HttpRequest请求类&lt;/h3&gt;
&lt;p&gt;超文本传输协议（Hypertext Transfer Protocol，HTTP）是一个简单的请求-响应协议，它通常运行在TCP之上。它指定了客户端可能发送给服务器什么样的消息以及得到什么样的响应。&lt;/p&gt;
&lt;p&gt;当在浏览器上访问某个网址时，其实质是向网站发送一个HTTP请求，HTTP请求分为8种请求方式，每种请求方式的说明如下：&lt;/p&gt;
&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;请求方式&lt;/th&gt;
&lt;th&gt;说明&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;OPTIONS&lt;/td&gt;
&lt;td&gt;返回服务器针对特定资源所支持的请求方法&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;GET&lt;/td&gt;
&lt;td&gt;向特定资源发出请求（访问网页）&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;POST&lt;/td&gt;
&lt;td&gt;向指定资源提交数据处理请求（提交表单、上传文件)&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;PUT&lt;/td&gt;
&lt;td&gt;向指定资源位置上传数据内容&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;DELETE&lt;/td&gt;
&lt;td&gt;请求服务器删除request-URL所标示的资源&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;HEAD&lt;/td&gt;
&lt;td&gt;与GET请求类似，返回的响应中没有具体内容，用于获取报头&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;TRACE&lt;/td&gt;
&lt;td&gt;回复和显示服务器收到的请求，用于测试和诊断&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;CONNECT&lt;/td&gt;
&lt;td&gt;HTTP/1.1协议中能够将连接改为管道方式的代理服务器&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;
&lt;p&gt;在上述的HTTP请求方式里，最基本的是GET请求和POST 请求，网站开发者关心的也只有GET请求和POST请求。GET请求和 POST请求是可以设置请求参数的，两者的设置方式如下:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;GET请求的请求参数是在路由地址后添加“?”和参数内容，参数内容以key=value 形式表示，等号前面的是参数名，后面的是参数值，如果涉及多个参数，每个参数之间就使用“&amp;amp;”隔开，如127.0.0.1:8000/?name=python222&amp;amp;pw=123456。&lt;/li&gt;
&lt;li&gt;POST请求的请求参数一般以表单的形式传递，常见的表单使用HTML的 form标签，并且form标签的method 属性设为POST.&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;再Django5中，Http请求信息都被封装到了HttpRequest类中。&lt;/p&gt;
&lt;p&gt;HttpRequest类的常用属性如下：&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;COOKIE:获取客户端（浏览器）的Cookie信息，以字典形式表示，并且键值对都是字符串类型。&lt;/li&gt;
&lt;li&gt;FILES: django.http.request.QueryDict对象，包含所有的文件上传信息。&lt;/li&gt;
&lt;li&gt;GET:获取GET请求的请求参数，它是django.http.request.QueryDict对象，操作起来类似于字典。&lt;/li&gt;
&lt;li&gt;POST:获取POST请求的请求参数，它是django.http.request.QueryDict对象，操作起来类似于字典。&lt;/li&gt;
&lt;li&gt;META:获取客户端（浏览器）的请求头信息，以字典形式存储。&lt;/li&gt;
&lt;li&gt;method:获取当前请求的请求方式(GET请求或POST请求).&lt;/li&gt;
&lt;li&gt;path:获取当前请求的路由地址。&lt;/li&gt;
&lt;li&gt;session:一个类似于字典的对象，用来操作服务器的会话信息，可临时存放用户信息。&lt;/li&gt;
&lt;li&gt;user:当 Django启用AuthenticationMiddleware中间件时才可用。它的值是内置数据模型User的对象，表示当前登录的用户。如果用户当前没有登录，那么user将设为django.contrib.auth.models.AnonymousUser的一个实例。&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;HttpRequest类常用方法如下：&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;is_secure():是否是采用HTTPS协议。&lt;/li&gt;
&lt;li&gt;get_host():获取服务器的域名。如果在访问的时候设有端口，就会加上端口号，如127.0.0.1:8000。&lt;/li&gt;
&lt;li&gt;get_full path():返回路由地址。如果该请求为GET请求并且设有请求参数，返回路由地址就会将请求参数返回，如/?name=python222&amp;amp;pw=123456。&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;我们搞个实例来测试下吧：&lt;/p&gt;
&lt;p&gt;先views.py里定义两个方法，分别测试get和post：&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;def get_test(request):
    &quot;&quot;&quot;
    get请求测试
    :param request:
    :return:
    &quot;&quot;&quot;
    print(request.method)  # 请求方式
    # 常用属性
    print(request.content_type)
    print(request.content_params)
    print(request.COOKIES)
    print(request.scheme)
    # 常用方法
    print(request.is_secure())
    print(request.get_host())
    print(request.get_full_path())

    print(request.GET.get(&quot;name&quot;))
    print(request.GET.get(&quot;pwd&quot;))
    print(request.GET.get(&quot;aaa&quot;, &quot;666&quot;))
    return HttpResponse(&quot;http get ok&quot;)


def post_test(request):
    &quot;&quot;&quot;
    post请求测试
    :param request:
    :return:
    &quot;&quot;&quot;
    print(request.method)  # 请求方式
    print(request.POST.get(&quot;name&quot;))
    print(request.POST.get(&quot;pwd&quot;))
    print(request.POST.get(&quot;aaa&quot;, &quot;666&quot;))
    return HttpResponse(&quot;http post ok&quot;)
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;urls.py里定义下映射：&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;path(&apos;get&apos;, helloWorld.views.get_test),
    path(&apos;post&apos;, helloWorld.views.post_test)
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;模版里新建http.html，这里我们不能用静态页面，需要一个csrf安全机制token 需要后端server来提供，所以只能用模版&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;&amp;lt;!DOCTYPE html&amp;gt;
&amp;lt;html lang=&quot;en&quot;&amp;gt;
&amp;lt;head&amp;gt;
    &amp;lt;meta charset=&quot;UTF-8&quot;&amp;gt;
    &amp;lt;title&amp;gt;Title&amp;lt;/title&amp;gt;
&amp;lt;/head&amp;gt;
&amp;lt;body&amp;gt;
&amp;lt;a href=&quot;/get?name=python222&amp;amp;pwd=123456&quot; target=&quot;_blank&quot;&amp;gt;http get请求测试&amp;lt;/a&amp;gt;&amp;lt;br&amp;gt;&amp;lt;br&amp;gt;
&amp;lt;form action=&quot;/post&quot; method=&quot;post&quot;&amp;gt;
    {百分号 csrf_token %}
    name:&amp;lt;input type=&quot;text&quot; name=&quot;name&quot;&amp;gt;&amp;lt;br&amp;gt;
    pwd:&amp;lt;input type=&quot;text&quot; name=&quot;pwd&quot;&amp;gt;
    &amp;lt;input type=&quot;submit&quot; value=&quot;提交&quot;&amp;gt;&amp;lt;/input&amp;gt;
&amp;lt;/form&amp;gt;
&amp;lt;/body&amp;gt;
&amp;lt;/html&amp;gt;
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;请求index跳转到http.html&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;def index(request):
    return render(request, &apos;http.html&apos;)
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;浏览器输入 &lt;code&gt;http://127.0.0.1:8000/index/&lt;/code&gt;测试：&lt;/p&gt;
&lt;p&gt;![image-20240220170942882](D:\开发\Python\Django\2024版 一周掌握Django5视频教程 python web开发.assets\image-20240220170942882.png)&lt;/p&gt;
&lt;p&gt;点击get链接地址：&lt;/p&gt;
&lt;p&gt;后台输出：&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;GET
text/plain
{}
{&apos;csrftoken&apos;: &apos;GDgfE2kvXwRYS6WMejaYNE9ij9KPodHi&apos;}
http
False
127.0.0.1:8000
/get?name=python222&amp;amp;pwd=123456
python222
123456
666
请求处理完毕，将返回到页面
[20/Feb/2024 17:09:52] &quot;GET /get?name=python222&amp;amp;pwd=123456 HTTP/1.1&quot; 200 11
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;再post表单测试：&lt;/p&gt;
&lt;p&gt;后台输出：&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;POST
python222
123456
666
&lt;/code&gt;&lt;/pre&gt;
&lt;h3&gt;会话管理（Cookies&amp;amp;Session）&lt;/h3&gt;
&lt;p&gt;HTTP是一种无状态协议，每次客户端访问web页面时，客户端打开一个单独的浏览器窗口连接到web服务器，由于服务器不会自动保存之前客户端请求的相关信息，所有无法识别一个HTTP请求是否为第一次访问。这就引进了web客户端和服务器端之间的会话，这就是会话管理。&lt;/p&gt;
&lt;p&gt;常用的会话跟踪技术是Cookie与Session。Cookie通过在客户端记录信息确定用户身份，Session通过在服务器端记录信息确定用户身份。&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;一、关于Cookie&lt;/strong&gt;&lt;/p&gt;
&lt;p&gt;cookie是某些网站为了辨别用户身份，进行Session跟踪而储存在用户本地终端上的数据（通常经过加密），由用户客户端计算机暂时或永久保存的信息。&lt;/p&gt;
&lt;p&gt;Cookie定义了一些HTTP请求头和HTTP响应头，通过这些HTTP头信息使服务器可以与客户进行状态交互。&lt;/p&gt;
&lt;p&gt;客户端请求服务器后，如果服务器需要记录用户状态，服务器会在响应信息中包含一个Set-Cookie的响应头，客户端会根据这个响应头存储Cookie信息。再次请求服务器时，客户端会在请求信息中包含一个Cookie请求头，而服务器会根据这个请求头进行用户身份、状态等较验。&lt;/p&gt;
&lt;p&gt;![image-20240221103150293](D:\开发\Python\Django\2024版 一周掌握Django5视频教程 python web开发.assets\image-20240221103150293.png)&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;二、关于session&lt;/strong&gt;&lt;/p&gt;
&lt;p&gt;Session是另一种记录客户状态的机制，不同的是Cookie保存在客户端浏览器中，而Session保存在服务器上。客户端浏览器访问服务器的时候，服务器把客户端信息以某种形式记录在服务器上。这就是Session。客户端浏览器再次访问时只需要从该Session中查找该客户的状态就可以了。&lt;/p&gt;
&lt;p&gt;当程序需要为某个客户端的请求创建一个session的时候，服务器首先检查这个客户端的请求里是否已包含了一个session标识，称为session id，如果已包含一个session id则说明以前已经为此客户端创建过session，服务器就按照session id把这个session检索出来使用（如果检索不到，可能会新建一个），如果客户端请求不包含session id，则为此客户端创建一个session并且生成一个与此session相关联的session id，session id的值应该是一个既不会重复，又不容易被找到规律以仿造的字符串，这个session id将被在本次响应中返回给客户端保存。&lt;/p&gt;
&lt;p&gt;![image-20240221104053204](D:\开发\Python\Django\2024版 一周掌握Django5视频教程 python web开发.assets\image-20240221104053204.png)&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;三，Session和Cookie的区别&lt;/strong&gt;&lt;/p&gt;
&lt;p&gt;1、数据存储位置：cookie 数据存放在客户的浏览器上，session 数据放在服务器上。&lt;/p&gt;
&lt;p&gt;2、安全性：cookie不是很安全，别人可以分析存放在本地的cookie并进行cookie欺骗，考虑到安全应当使用session。&lt;/p&gt;
&lt;p&gt;3、服务器性能：session会在一定时间内保存在服务器上。当访问增多，会比较占用你服务器的性能，考虑到减轻服务器性
能方面，应当使用cookie。&lt;/p&gt;
&lt;p&gt;4、数据大小：单个cookie保存的数据不能超过4K，很多浏览器都限制一个站点最多保存20个cookie。&lt;/p&gt;
&lt;p&gt;5、信息重要程度：可以考虑将用户信息等重要信息存放为session，其他信息如果需要保留，可以放在cookie中。&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;四，Cookies&amp;amp;Session再Django中使用实例&lt;/strong&gt;&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;获取 cookie&lt;/strong&gt;&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;request.COOKIES[&apos;key&apos;]
request.COOKIES.get(&apos;key&apos;)
这俩个方法可以获取指定键名的cookie

request.get_signed_cookie(key, default=RAISE_ERROR, salt=&apos;&apos;, max_age=None)

default: 默认值
salt: 	 加密盐
max_age: 后台控制过期时间，默认是秒数
expires: 专门针对IE浏览器设置超时时间
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;&lt;strong&gt;设置 cookie&lt;/strong&gt;&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;# 获取HttpResponse对象
# rep = HttpResponse(...)
rep ＝ render(request, ...)
# 设置 cookie
rep.set_cookie(key,value,...)
rep.set_signed_cookie(key,value,salt=&apos;加密盐&apos;, max_age=None, ...)

参数

key, 	  	   键
value=&apos;&apos;, 	   值
max_age=None,  超时时间
expires=None,  超时时间(IE requires expires, so set it if hasn&apos;t been already.)
path=&apos;/&apos;, 	   Cookie生效的路径，/ 表示根路径，特殊的：根路径的cookie可以被任何url的页面访问
domain=None,   Cookie生效的域名
secure=False,  https传输
httponly=False 只能http协议传输，无法被JavaScript获取（不是绝对，底层抓包可以获取到也可以被覆盖）
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;&lt;strong&gt;删除 cookie&lt;/strong&gt;&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;# 获取HttpResponse对象
# rep = HttpResponse(...)
rep ＝ render(request, ...)
# 删除 cookie
rep.delete_cookie(key)

此方法会删除用户浏览器上之前设置的cookie值
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;&lt;strong&gt;Django 操作 session&lt;/strong&gt;&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;1. 获取、设置、删除Session中数据

		request.session[&apos;k1&apos;] 					# 没有值会报错				
		request.session.get(&apos;k1&apos;,None)			# 可以获取多组
		request.session[&apos;k1&apos;] = 123				# 可以设置多组
		request.session.setdefault(&apos;k1&apos;,123) 	# 存在则不设置
		del request.session[&apos;k1&apos;]


2. 所有 键、值、键值对

		request.session.keys()
		request.session.values()
		request.session.items()
		request.session.iterkeys()
		request.session.itervalues()
		request.session.iteritems()

4. 会话session的key
		
		request.session.session_key

5. 将所有Session失效日期小于当前日期的数据删除
		
		request.session.clear_expired()

6. 检查会话session的key在数据库中是否存在
		
		request.session.exists(&quot;session_key&quot;)

7. 删除当前会话的所有Session数据
		
		request.session.delete()	# 只删客户端
　　
8. 删除当前的会话数据并删除会话的Cookie。
		
		request.session.flush() 	# 服务端、客户端都删
	    这用于确保前面的会话数据不可以再次被用户的浏览器访问
	    例如，django.contrib.auth.logout() 函数中就会调用它。

9. 设置会话Session和Cookie的超时时间
		
		&apos;django默认的session失效时间是14天&apos;
		request.session.set_expiry(value)
	    * 如果value是个整数，session会在些秒数后失效。
	    * 如果value是个datatime或timedelta，session就会在这个时间后失效。
	    * 如果value是0,用户关闭浏览器session就会失效。
	    * 如果value是None,session会依赖全局session失效策略。
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;下面通过一个具体Django事例来深入体验下Django项目里的Cookie&amp;amp;Session操作&lt;/p&gt;
&lt;p&gt;views.py里定义两个方法，分别是登录页面跳转，以及登录逻辑处理&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;def to_login(request):
    &quot;&quot;&quot;
    跳转登录页面
    :param request:
    :return:
    &quot;&quot;&quot;
    return render(request, &apos;login.html&apos;)


def login(request):
    &quot;&quot;&quot;
    登录
    :param request:
    :return:
    &quot;&quot;&quot;
    user_name = request.POST.get(&quot;user_name&quot;)
    pwd = request.POST.get(&quot;pwd&quot;)
    if user_name == &apos;python222&apos; and pwd == &apos;123456&apos;:
        request.session[&apos;currentUserName&apos;] = user_name  # session中存一个用户名
        print(&apos;session获取&apos;, request.session[&apos;currentUserName&apos;])
        response = render(request, &apos;main.html&apos;)  # 获取HttpResponse
        response.set_cookie(&quot;remember_me&quot;, True)  # 设置cookie
        return response
    else:
        content_value = {&quot;error_info&quot;: &apos;用户名或者密码错误！&apos;}
        return render(request, &apos;login.html&apos;, context=content_value)
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;urls.py里定义映射：&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;    path(&apos;toLogin/&apos;, helloWorld.views.to_login),
    path(&apos;login&apos;, helloWorld.views.login)
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;templates下新建login.html和main.html&lt;/p&gt;
&lt;p&gt;login.html&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;&amp;lt;!DOCTYPE html&amp;gt;
&amp;lt;html lang=&quot;en&quot;&amp;gt;
&amp;lt;head&amp;gt;
    &amp;lt;meta charset=&quot;UTF-8&quot;&amp;gt;
    &amp;lt;title&amp;gt;登录页面&amp;lt;/title&amp;gt;
&amp;lt;/head&amp;gt;
&amp;lt;body&amp;gt;
&amp;lt;form action=&quot;/login&quot; method=&quot;post&quot;&amp;gt;
    {百分号 csrf_token %}
    &amp;lt;table&amp;gt;
        &amp;lt;tr&amp;gt;
            &amp;lt;th&amp;gt;用户登录&amp;lt;/th&amp;gt;
        &amp;lt;/tr&amp;gt;
        &amp;lt;tr&amp;gt;
            &amp;lt;td&amp;gt;用户名：&amp;lt;/td&amp;gt;
            &amp;lt;td&amp;gt;&amp;lt;input type=&quot;text&quot; name=&quot;user_name&quot;&amp;gt;&amp;lt;/td&amp;gt;
        &amp;lt;/tr&amp;gt;
        &amp;lt;tr&amp;gt;
            &amp;lt;td&amp;gt;密码：&amp;lt;/td&amp;gt;
            &amp;lt;td&amp;gt;&amp;lt;input type=&quot;password&quot; name=&quot;pwd&quot;&amp;gt;&amp;lt;/td&amp;gt;
        &amp;lt;/tr&amp;gt;
        &amp;lt;tr&amp;gt;
            &amp;lt;td&amp;gt;
                &amp;lt;input type=&quot;submit&quot; value=&quot;提交&quot;&amp;gt;
            &amp;lt;/td&amp;gt;
        &amp;lt;/tr&amp;gt;
        &amp;lt;tr&amp;gt;
            &amp;lt;td colspan=&quot;2&quot;&amp;gt;&amp;lt;font color=&quot;red&quot;&amp;gt;{{ error_info }}&amp;lt;/font&amp;gt;&amp;lt;/td&amp;gt;
        &amp;lt;/tr&amp;gt;
    &amp;lt;/table&amp;gt;
&amp;lt;/form&amp;gt;
&amp;lt;/body&amp;gt;
&amp;lt;/html&amp;gt;
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;main.html&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;&amp;lt;!DOCTYPE html&amp;gt;
&amp;lt;html lang=&quot;en&quot;&amp;gt;
&amp;lt;head&amp;gt;
    &amp;lt;meta charset=&quot;UTF-8&quot;&amp;gt;
    &amp;lt;title&amp;gt;主页面&amp;lt;/title&amp;gt;
&amp;lt;/head&amp;gt;
&amp;lt;body&amp;gt;
欢迎：{{ request.session.currentUserName }}
&amp;lt;/body&amp;gt;
&amp;lt;/html&amp;gt;
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;测试运行，浏览器输入：&lt;code&gt;http://127.0.0.1:8000/toLogin/&lt;/code&gt;&lt;/p&gt;
&lt;p&gt;![image-20240221124210193](D:\开发\Python\Django\2024版 一周掌握Django5视频教程 python web开发.assets\image-20240221124210193.png)&lt;/p&gt;
&lt;p&gt;转发到login.html&lt;/p&gt;
&lt;p&gt;我们先输入一个错误的用户名和密码：&lt;/p&gt;
&lt;p&gt;![image-20240221124240889](D:\开发\Python\Django\2024版 一周掌握Django5视频教程 python web开发.assets\image-20240221124240889.png)&lt;/p&gt;
&lt;p&gt;![image-20240221124303198](D:\开发\Python\Django\2024版 一周掌握Django5视频教程 python web开发.assets\image-20240221124303198.png)&lt;/p&gt;
&lt;p&gt;携带错误信息参数，转发到登录页面，页面提示错误信息。&lt;/p&gt;
&lt;p&gt;我们在输入一个正确的用户名和密码：，则转发到main.html主页面。&lt;/p&gt;
&lt;p&gt;![image-20240221124524413](D:\开发\Python\Django\2024版 一周掌握Django5视频教程 python web开发.assets\image-20240221124524413.png)&lt;/p&gt;
&lt;p&gt;![image-20240221124639172](D:\开发\Python\Django\2024版 一周掌握Django5视频教程 python web开发.assets\image-20240221124639172.png)&lt;/p&gt;
&lt;p&gt;同时服务器返回set-cookies信息，包括内置的sessionid以及我们自己设置的remember_me。&lt;/p&gt;
&lt;p&gt;![image-20240221124744537](D:\开发\Python\Django\2024版 一周掌握Django5视频教程 python web开发.assets\image-20240221124744537.png)&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;Django 中的 Session 配置&lt;/strong&gt;&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;1. 数据库Session
SESSION_ENGINE = &apos;django.contrib.sessions.backends.db&apos;   # 引擎（默认）

2. 缓存Session
SESSION_ENGINE = &apos;django.contrib.sessions.backends.cache&apos;  # 引擎
SESSION_CACHE_ALIAS = &apos;default&apos;                            # 使用的缓存别名（默认内存缓存，也可以是memcache），此处别名依赖缓存的设置

3. 文件Session
SESSION_ENGINE = &apos;django.contrib.sessions.backends.file&apos;    # 引擎
SESSION_FILE_PATH = None                                    # 缓存文件路径，如果为None，则使用tempfile模块获取一个临时地址tempfile.gettempdir() 

4. 缓存+数据库
SESSION_ENGINE = &apos;django.contrib.sessions.backends.cached_db&apos;        # 引擎

5. 加密Cookie Session
SESSION_ENGINE = &apos;django.contrib.sessions.backends.signed_cookies&apos;   # 引擎

其他公用设置项：
SESSION_COOKIE_NAME ＝ &quot;sessionid&quot;        # Session的cookie保存在浏览器上时的key，即：sessionid＝随机字符串（默认）
SESSION_COOKIE_PATH ＝ &quot;/&quot;                # Session的cookie保存的路径（默认）
SESSION_COOKIE_DOMAIN = None              # Session的cookie保存的域名（默认）
SESSION_COOKIE_SECURE = False             # 是否Https传输cookie（默认）
SESSION_COOKIE_HTTPONLY = True            # 是否Session的cookie只支持http传输（默认）
SESSION_COOKIE_AGE = 1209600              # Session的cookie失效日期（2周）（默认）
SESSION_EXPIRE_AT_BROWSER_CLOSE = False   # 是否关闭浏览器使得Session过期（默认）
SESSION_SAVE_EVERY_REQUEST = False        # 是否每次请求都保存Session，默认修改之后才保存（默认）
&lt;/code&gt;&lt;/pre&gt;
&lt;h3&gt;Django5文件上传实现&lt;/h3&gt;
&lt;p&gt;文件上传功能是网站开发或者业务系统常见的功能之一，比如上传图片（用户头像或文章配图）和导入文件（压缩包，视频，音乐)。无论上传的文件是什么格式的，其上传原理都是将文件以二进制的数据格式读取并写入网站或者业务系统指定的目录里。&lt;/p&gt;
&lt;p&gt;我们通过一个实例来深入体验学习下文件上传：&lt;/p&gt;
&lt;p&gt;首先templates下新建upload.html ，前端上传文件模版页面&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;&amp;lt;!DOCTYPE html&amp;gt;
&amp;lt;html lang=&quot;en&quot;&amp;gt;
&amp;lt;head&amp;gt;
    &amp;lt;meta charset=&quot;UTF-8&quot;&amp;gt;
    &amp;lt;title&amp;gt;文件上传&amp;lt;/title&amp;gt;
&amp;lt;/head&amp;gt;
&amp;lt;body&amp;gt;
&amp;lt;form action=&quot;/upload&quot; enctype=&quot;multipart/form-data&quot; method=&quot;post&quot;&amp;gt;
    {百分号 csrf_token %}
    &amp;lt;input type=&quot;file&quot; name=&quot;myfile&quot;&amp;gt;&amp;lt;br&amp;gt;&amp;lt;br&amp;gt;
    &amp;lt;input type=&quot;submit&quot; value=&quot;上传文件&quot;&amp;gt;
&amp;lt;/form&amp;gt;
&amp;lt;/body&amp;gt;
&amp;lt;/html&amp;gt;
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;views.py里定义to_upload和upload两个方法，分别是跳转文件页面，和文件上传处理&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;def to_upload(request):
    &quot;&quot;&quot;
    跳转文件上传页面
    :param request:
    :return:
    &quot;&quot;&quot;
    return render(request, &apos;upload.html&apos;)


def upload(request):
    &quot;&quot;&quot;
    文件上传
    :param request:
    :return:
    &quot;&quot;&quot;
    # 获取上传的文件，如果没有文件，就默认为None
    myFile = request.FILES.get(&quot;myfile&quot;, None)
    if myFile:
        # 打开特定的文件进行二进制的写操作
        f = open(os.path.join(&quot;D:\\myFile&quot;, myFile.name), &quot;wb+&quot;)
        # 分块写入文件
        for chunk in myFile.chunks():
            f.write(chunk)
        f.close()
        return HttpResponse(&quot;文件上传成功！&quot;)
    else:
        return HttpResponse(&quot;没发现文件！&quot;)
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;最后urls.py里，定义下映射：&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;    path(&apos;toUpload/&apos;, helloWorld.views.to_upload),
    path(&apos;upload&apos;, helloWorld.views.upload)
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;运行测试：浏览器输入 &lt;code&gt;http://127.0.0.1:8000/toUpload/&lt;/code&gt;，进入文件上传页面：&lt;/p&gt;
&lt;p&gt;![image-20240224173658650](D:\开发\Python\Django\2024版 一周掌握Django5视频教程 python web开发.assets\image-20240224173658650.png)&lt;/p&gt;
&lt;p&gt;![image-20240224173709825](D:\开发\Python\Django\2024版 一周掌握Django5视频教程 python web开发.assets\image-20240224173709825.png)&lt;/p&gt;
&lt;p&gt;测试两个文件，一个压缩包，一个图片，选择文件，点击上传文件，则上传到指定目录：&lt;/p&gt;
&lt;p&gt;![image-20240224173743748](D:\开发\Python\Django\2024版 一周掌握Django5视频教程 python web开发.assets\image-20240224173743748.png)&lt;/p&gt;
&lt;p&gt;文件对象myFile提供一下属性来获取文件信息：&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;myFile.name:获取上传文件的文件名，包含文件后缀名。&lt;/li&gt;
&lt;li&gt;myFile.size:获取上传文件的文件大小。&lt;/li&gt;
&lt;li&gt;myFile.content_type:获取文件类型，通过后续名判断文件类型。&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;从文件对象myFile获取文件内容，Django提供了以下读取方式，每种方式说明如下。&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;myFile.read():从文件对象里读取整个文件上传的数据，这个方法只适合小文件。&lt;/li&gt;
&lt;li&gt;myFile.chunks():按流式响应方式读取文件，在for 循环中进行迭代，将大文件分块写入服务器所指定的保存位置。&lt;/li&gt;
&lt;li&gt;myFile.multiple_chunks():判断文件对象的文件大小,返回True或者False,当文件大于2.5MB（默认值为2.5MB）时，该方法返回True，否则返回False。因此，可以根据该方法来选择选用read方法读取还是采用chunks方法。&lt;/li&gt;
&lt;/ul&gt;
&lt;h3&gt;Django5列表视图ListView&lt;/h3&gt;
&lt;p&gt;为了实现快速开发，Django提供了视图类功能，封装了视图开发常用的代码，这种基于类实现的响应与请求称为CBV （ Class Base Views）,我们先介绍列表视图ListView，该视图类可以将数据库表的数据以列表的形式显示到页面，常用于数据的查询和展示。&lt;/p&gt;
&lt;p&gt;首先为了得到数据库数据，我们先定义模型，来映射数据库表；&lt;/p&gt;
&lt;p&gt;models.py里定义StudentInfo类：&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;from django.db import models


# Create your models here.

class StudentInfo(models.Model):
    id = models.AutoField(primary_key=True)
    name = models.CharField(max_length=20)
    age = models.IntegerField()

    class Meta:
        db_table = &quot;t_student&quot;
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;然后我们执行： &lt;code&gt;python manage.py makemigrations&lt;/code&gt; 生成数据库迁移文件&lt;/p&gt;
&lt;p&gt;所谓的迁移文件, 是类似模型类的迁移类,主要是描述了数据表结构的类文件；&lt;/p&gt;
&lt;p&gt;再执行：&lt;code&gt;python manage.py migrate&lt;/code&gt; 执行迁移文件，同步到数据库中&lt;/p&gt;
&lt;p&gt;注意：生成的表名默认为：app名_定义的表名，可通过db_table 指明数据库表名。&lt;/p&gt;
&lt;p&gt;![image-20240225125157804](D:\开发\Python\Django\2024版 一周掌握Django5视频教程 python web开发.assets\image-20240225125157804.png)&lt;/p&gt;
&lt;p&gt;![image-20240225125230435](D:\开发\Python\Django\2024版 一周掌握Django5视频教程 python web开发.assets\image-20240225125230435.png)&lt;/p&gt;
&lt;p&gt;我们会看到 数据库t_student自动生成了：&lt;/p&gt;
&lt;p&gt;![image-20240225125307454](D:\开发\Python\Django\2024版 一周掌握Django5视频教程 python web开发.assets\image-20240225125307454.png)&lt;/p&gt;
&lt;p&gt;我们输入一些测试数据：&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;insert into t_student VALUES(null,&apos;张三1&apos;,20);
insert into t_student VALUES(null,&apos;张三2&apos;,21);
insert into t_student VALUES(null,&apos;张三3&apos;,22);
insert into t_student VALUES(null,&apos;张三4&apos;,23);
insert into t_student VALUES(null,&apos;张三5&apos;,24);
insert into t_student VALUES(null,&apos;张三6&apos;,25);
insert into t_student VALUES(null,&apos;张三7&apos;,26);
insert into t_student VALUES(null,&apos;张三8&apos;,27);
insert into t_student VALUES(null,&apos;张三9&apos;,28);
insert into t_student VALUES(null,&apos;张三10&apos;,29);
insert into t_student VALUES(null,&apos;张三11&apos;,30);
insert into t_student VALUES(null,&apos;张三12&apos;,31);
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;要使用 ListView，需要继承它并设置一些属性。以下属性是最常用的：&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;code&gt;model&lt;/code&gt;：指定要使用的模型。&lt;/li&gt;
&lt;li&gt;&lt;code&gt;template_name&lt;/code&gt;：指定要使用的模板名称。&lt;/li&gt;
&lt;li&gt;&lt;code&gt;context_object_name&lt;/code&gt;：指定上下文变量名称，默认为 object_list。&lt;/li&gt;
&lt;li&gt;&lt;code&gt;paginate_by&lt;/code&gt;：指定分页大小。&lt;/li&gt;
&lt;li&gt;&lt;code&gt;extra_context&lt;/code&gt;：设置模型外的数据&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;在views.py里，我们可以定义List类：&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;class List(ListView):
    # 设置模版文件
    template_name = &apos;student/list.html&apos;
    # 设置模型外的数据
    extra_context = {&apos;title&apos;: &apos;学生信息列表&apos;}
    # 查询结果集
    queryset = StudentInfo.objects.all()
    # 每页展示5条数据
    paginate_by = 5
    # 设置上下文对象名称
    context_object_name = &apos;student_list&apos;
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;除了设置属性之外，还可以重写 ListView 中的方法以进行自定义。以下是一些常见的方法：&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;code&gt;get_queryset()&lt;/code&gt;：返回要在视图中使用的查询集合。这里可以对查询集合进行筛选、排序等操作。&lt;/li&gt;
&lt;li&gt;&lt;code&gt;get_context_data()&lt;/code&gt;：返回要在模板上下文中使用的变量。这里可以添加额外的变量，如表单、过滤器等。&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;urls.py里，我们定义映射：&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;path(&apos;student/list&apos;, helloWorld.views.List.as_view())
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;在模版页面，Django 给我们提供了分页的功能：&lt;code&gt;Paginator&lt;/code&gt;和&lt;code&gt;Page&lt;/code&gt;类都是用来做分页的。&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;# Paginator常用属性和方法
1.`count`： 总共有多少条数据。
2.`num_pages`： 总共有多少页。
3.`page_range`：页面的区间。比如有三页，那么就是```range``(``1``,``4``)`。
&lt;/code&gt;&lt;/pre&gt;
&lt;pre&gt;&lt;code&gt;# Page常用属性和方法：
1.`has_next`: 是否还有下一页。
2.`has_previous`: 是否还有上一页。
3.`next_page_number`: 下一页的页码。
4.`previous_page_number`: 上一页的页码。
5.`number`: 当前页。
6.`start_index`: 当前页的第一条数据的索引值。
7.`end_index`: 当前页的最后一条数据的索引值。
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;我们在templates下新建student目录，再新建list.html&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;&amp;lt;!DOCTYPE html&amp;gt;
&amp;lt;html lang=&quot;en&quot;&amp;gt;
&amp;lt;head&amp;gt;
    &amp;lt;meta charset=&quot;UTF-8&quot;&amp;gt;
    &amp;lt;title&amp;gt;{{ title }}&amp;lt;/title&amp;gt;
&amp;lt;/head&amp;gt;
&amp;lt;body&amp;gt;
&amp;lt;h3&amp;gt;{{ title }}&amp;lt;/h3&amp;gt;
&amp;lt;table border=&quot;1&quot;&amp;gt;
    &amp;lt;tr&amp;gt;
        &amp;lt;th&amp;gt;编号&amp;lt;/th&amp;gt;
        &amp;lt;th&amp;gt;姓名&amp;lt;/th&amp;gt;
        &amp;lt;th&amp;gt;年龄&amp;lt;/th&amp;gt;
    &amp;lt;/tr&amp;gt;
    {百分号 for student in student_list %}
        &amp;lt;tr&amp;gt;
            &amp;lt;td&amp;gt;{{ student.id }}&amp;lt;/td&amp;gt;
            &amp;lt;td&amp;gt;{{ student.name }}&amp;lt;/td&amp;gt;
            &amp;lt;td&amp;gt;{{ student.age }}&amp;lt;/td&amp;gt;
        &amp;lt;/tr&amp;gt;
    {百分号 endfor %}
&amp;lt;/table&amp;gt;
&amp;lt;br&amp;gt;
{百分号 if is_paginated %}
    {百分号 if page_obj.has_previous %}
        &amp;lt;a href=&quot;/student/list?page={{ page_obj.previous_page_number }}&quot;&amp;gt;上一页&amp;lt;/a&amp;gt;
    {百分号 endif %}
    {百分号 for current in paginator.page_range %}
        {百分号 if current == page_obj.number %}
            &amp;lt;a href=&quot;/student/list?page={{ current }}&quot;&amp;gt;&amp;lt;b&amp;gt;&amp;lt;font color=&quot;blue&quot;&amp;gt;{{ current }}&amp;lt;/font&amp;gt;&amp;lt;/b&amp;gt;&amp;lt;/a&amp;gt;
        {百分号 else %}
            &amp;lt;a href=&quot;/student/list?page={{ current }}&quot;&amp;gt;{{ current }}&amp;lt;/a&amp;gt;
        {百分号 endif %}
    {百分号 endfor %}
    {百分号 if page_obj.has_next %}
        &amp;lt;a href=&quot;/student/list?page={{ page_obj.next_page_number }}&quot;&amp;gt;下一页&amp;lt;/a&amp;gt;
    {百分号 endif %}
{百分号 endif %}
&amp;lt;/body&amp;gt;
&amp;lt;/html&amp;gt;
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;测试，浏览器输入：&lt;code&gt;http://127.0.0.1:8000/student/list&lt;/code&gt;&lt;/p&gt;
&lt;p&gt;![image-20240226165014815](D:\开发\Python\Django\2024版 一周掌握Django5视频教程 python web开发.assets\image-20240226165014815.png)&lt;/p&gt;
&lt;p&gt;点击下一页：&lt;/p&gt;
&lt;p&gt;![image-20240226165026594](D:\开发\Python\Django\2024版 一周掌握Django5视频教程 python web开发.assets\image-20240226165026594.png)&lt;/p&gt;
&lt;p&gt;运用ListView列表视图开发，是不是非常容易，方便。&lt;/p&gt;
&lt;h3&gt;Django5详细视图DetailView&lt;/h3&gt;
&lt;p&gt;DetailView多用于展示某一个具体数据对象的详细信息的页面。&lt;/p&gt;
&lt;p&gt;使用DetailView，你只需要指定要使用的模型和对象的唯一标识符，并可以自定义其他一些属性，例如模型名称、模板名称、上下文数据等。&lt;/p&gt;
&lt;p&gt;以下是DetailView的一些常见属性和方法：&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;
&lt;p&gt;model：指定要使用的模型。&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;queryset：指定要使用的查询集，用于获取对象。如果未指定，则将使用模型的默认查询集。&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;pk_url_kwarg：指定URL中用于获取对象的唯一标识符的参数名称，默认为’pk’。&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;context_object_name：指定将对象传递给模板时的上下文变量名称，默认为’model’。&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;template_name：指定要使用的模板的名称。&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;get_object(queryset=None)：获取要展示的对象。可以重写这个方法来自定义获取对象的逻辑。&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;get_context_data(kwargs)：返回要传递给模板的上下文数据。你可以重写这个方法来自定义上下文数据。&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;get()：处理GET请求的方法，根据配置的对象获取规则执行对象获取和展示逻辑。&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;dispatch(request, *args, **kwargs)：处理请求的入口方法，根据请求的不同方法（GET、POST等）执行相应的处理逻辑。&lt;/p&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;通过继承DetailView，并根据自己的需求重写这些方法，你可以创建自定义的展示单个对象详细信息的视图，并实现你想要的功能。&lt;/p&gt;
&lt;p&gt;总之，DetailView是Django框架中的一个便捷的通用视图，用于展示单个对象的详细信息，并提供了一些有用的属性和方法来简化对象展示逻辑。&lt;/p&gt;
&lt;p&gt;通过重新设置model属性来指定需要获取的Model类，默认对象名称为object,也可以通过重新设置context_object_name属性来更改这个名字。&lt;/p&gt;
&lt;p&gt;下面我们通过实例来体验下吧：&lt;/p&gt;
&lt;p&gt;views.py里新建Detail，继承DetailView&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;class Detail(DetailView):
    # 设置模版文件
    template_name = &apos;student/detail.html&apos;
    # 设置模型外的数据
    extra_context = {&apos;title&apos;: &apos;学生信息详情&apos;}
    # 设置查询模型
    model = StudentInfo
    # 设置上下文对象名称
    context_object_name = &apos;student&apos;
    # 指定URL中用于获取对象的唯一标识符的参数名称，默认为’pk’。
    # pk_url_kwarg = &apos;id&apos;
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;templates下的student目录下新建detail.html&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;&amp;lt;!DOCTYPE html&amp;gt;
&amp;lt;html lang=&quot;en&quot;&amp;gt;
&amp;lt;head&amp;gt;
    &amp;lt;meta charset=&quot;UTF-8&quot;&amp;gt;
    &amp;lt;title&amp;gt;{{ title }}&amp;lt;/title&amp;gt;
&amp;lt;/head&amp;gt;
&amp;lt;body&amp;gt;
&amp;lt;h3&amp;gt;{{ title }}&amp;lt;/h3&amp;gt;
编号：{{ student.id }}&amp;lt;br/&amp;gt;
姓名：{{ student.name }}&amp;lt;br/&amp;gt;
年龄：{{ student.age }}
&amp;lt;/body&amp;gt;
&amp;lt;/html&amp;gt;
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;urls.py里加一个映射：&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;    path(&apos;student/&amp;lt;int:pk&amp;gt;&apos;, helloWorld.views.Detail.as_view()),
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;list.html里，加一个操作项-查看详情：&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;&amp;lt;!DOCTYPE html&amp;gt;
&amp;lt;html lang=&quot;en&quot;&amp;gt;
&amp;lt;head&amp;gt;
    &amp;lt;meta charset=&quot;UTF-8&quot;&amp;gt;
    &amp;lt;title&amp;gt;{{ title }}&amp;lt;/title&amp;gt;
&amp;lt;/head&amp;gt;
&amp;lt;body&amp;gt;
&amp;lt;h3&amp;gt;{{ title }}&amp;lt;/h3&amp;gt;
&amp;lt;table border=&quot;1&quot;&amp;gt;
    &amp;lt;tr&amp;gt;
        &amp;lt;th&amp;gt;编号&amp;lt;/th&amp;gt;
        &amp;lt;th&amp;gt;姓名&amp;lt;/th&amp;gt;
        &amp;lt;th&amp;gt;年龄&amp;lt;/th&amp;gt;
        &amp;lt;th&amp;gt;操作&amp;lt;/th&amp;gt;
    &amp;lt;/tr&amp;gt;
    {百分号 for student in student_list %}
        &amp;lt;tr&amp;gt;
            &amp;lt;td&amp;gt;{{ student.id }}&amp;lt;/td&amp;gt;
            &amp;lt;td&amp;gt;{{ student.name }}&amp;lt;/td&amp;gt;
            &amp;lt;td&amp;gt;{{ student.age }}&amp;lt;/td&amp;gt;
            &amp;lt;td&amp;gt;
                &amp;lt;a href=&quot;/student/{{ student.id }}&quot;&amp;gt;查看详情&amp;lt;/a&amp;gt;
            &amp;lt;/td&amp;gt;
        &amp;lt;/tr&amp;gt;
    {百分号 endfor %}
&amp;lt;/table&amp;gt;
&amp;lt;br&amp;gt;
{百分号 if is_paginated %}
    {百分号 if page_obj.has_previous %}
        &amp;lt;a href=&quot;/student/list?page={{ page_obj.previous_page_number }}&quot;&amp;gt;上一页&amp;lt;/a&amp;gt;
    {百分号 endif %}
    {百分号 for current in paginator.page_range %}
        {百分号 if current == page_obj.number %}
            &amp;lt;a href=&quot;/student/list?page={{ current }}&quot;&amp;gt;&amp;lt;b&amp;gt;&amp;lt;font color=&quot;blue&quot;&amp;gt;{{ current }}&amp;lt;/font&amp;gt;&amp;lt;/b&amp;gt;&amp;lt;/a&amp;gt;
        {百分号 else %}
            &amp;lt;a href=&quot;/student/list?page={{ current }}&quot;&amp;gt;{{ current }}&amp;lt;/a&amp;gt;
        {百分号 endif %}
    {百分号 endfor %}
    {百分号 if page_obj.has_next %}
        &amp;lt;a href=&quot;/student/list?page={{ page_obj.next_page_number }}&quot;&amp;gt;下一页&amp;lt;/a&amp;gt;
    {百分号 endif %}
{百分号 endif %}
&amp;lt;/body&amp;gt;
&amp;lt;/html&amp;gt;
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;运行测试，浏览器输入：&lt;code&gt;http://127.0.0.1:8000/student/list&lt;/code&gt;，点击“查看详情”&lt;/p&gt;
&lt;p&gt;![image-20240301204822453](D:\开发\Python\Django\2024版 一周掌握Django5视频教程 python web开发.assets\image-20240301204822453.png)&lt;/p&gt;
&lt;p&gt;![image-20240301204900339](D:\开发\Python\Django\2024版 一周掌握Django5视频教程 python web开发.assets\image-20240301204900339.png)&lt;/p&gt;
&lt;p&gt;即可查询出学生详情；&lt;/p&gt;
&lt;h3&gt;Django5新增视图CreateView&lt;/h3&gt;
&lt;p&gt;视图类CreateView是对模型新增数据的视图类，它是在表单视图类FormView 的基础上加以封装的。简单来说，就是在视图类FormView的基础上加入数据新增的功能。&lt;/p&gt;
&lt;p&gt;所有涉及到表单视图的功能开发，都需要定义form表单类：&lt;/p&gt;
&lt;p&gt;我们新建forms.py，里面新建StudentForm&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;from django import forms
from django.forms import ModelForm

from helloWorld.models import StudentInfo


# 定义学生form表单
class StudentForm(ModelForm):
    # 配置中心
    class Meta:
        model = StudentInfo  # 导入model
        # fields = &apos;__all__&apos;  # 代表所有字段
        fields = [&apos;name&apos;, &apos;age&apos;]  # 指定字段
        widgets = {  # 定义控件
            &apos;name&apos;: forms.TextInput(attrs={&apos;id&apos;: &apos;name&apos;, &apos;class&apos;: &apos;inputClass&apos;}),
            &apos;age&apos;: forms.NumberInput(attrs={&apos;id&apos;: &apos;age&apos;})
        }

        labels = {  # 指定标签
            &apos;name&apos;: &apos;姓名&apos;,
            &apos;age&apos;: &apos;年龄&apos;
        }

&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;views.py里新建Create类，继承CreateView&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;class Create(CreateView):
    # 设置模版文件
    template_name = &apos;student/create.html&apos;
    # 设置模型外的数据
    extra_context = {&apos;title&apos;: &apos;学生信息添加&apos;}
    # 指定form
    form_class = StudentForm
    # 执行成功后跳转地址
    success_url = &apos;/student/list&apos;
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;student目录下新建create.html&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;&amp;lt;!DOCTYPE html&amp;gt;
&amp;lt;html lang=&quot;en&quot;&amp;gt;
&amp;lt;head&amp;gt;
    &amp;lt;meta charset=&quot;UTF-8&quot;&amp;gt;
    &amp;lt;title&amp;gt;{{ title }}&amp;lt;/title&amp;gt;
    &amp;lt;style&amp;gt;
        .inputClass {
            width: 200px;
        }
    &amp;lt;/style&amp;gt;
&amp;lt;/head&amp;gt;
&amp;lt;body&amp;gt;
&amp;lt;h3&amp;gt;{{ title }}&amp;lt;/h3&amp;gt;
&amp;lt;form method=&quot;post&quot;&amp;gt;
    {百分号 csrf_token %}
    {{ form.as_p }}
    &amp;lt;input type=&quot;submit&quot; value=&quot;确定&quot;&amp;gt;
&amp;lt;/form&amp;gt;
&amp;lt;/body&amp;gt;
&amp;lt;/html&amp;gt;
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;urls.py里加一个映射：&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;    path(&apos;student/create&apos;, helloWorld.views.Create.as_view()),
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;list.html页面，加一个新增学生链接&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;&amp;lt;!DOCTYPE html&amp;gt;
&amp;lt;html lang=&quot;en&quot;&amp;gt;
&amp;lt;head&amp;gt;
    &amp;lt;meta charset=&quot;UTF-8&quot;&amp;gt;
    &amp;lt;title&amp;gt;{{ title }}&amp;lt;/title&amp;gt;
&amp;lt;/head&amp;gt;
&amp;lt;body&amp;gt;
&amp;lt;h3&amp;gt;{{ title }}&amp;lt;/h3&amp;gt;
&amp;lt;a href=&quot;/student/create&quot;&amp;gt;新增学生&amp;lt;/a&amp;gt;
&amp;lt;table border=&quot;1&quot;&amp;gt;
    &amp;lt;tr&amp;gt;
        &amp;lt;th&amp;gt;编号&amp;lt;/th&amp;gt;
        &amp;lt;th&amp;gt;姓名&amp;lt;/th&amp;gt;
        &amp;lt;th&amp;gt;年龄&amp;lt;/th&amp;gt;
        &amp;lt;th&amp;gt;操作&amp;lt;/th&amp;gt;
    &amp;lt;/tr&amp;gt;
    {百分号 for student in student_list %}
        &amp;lt;tr&amp;gt;
            &amp;lt;td&amp;gt;{{ student.id }}&amp;lt;/td&amp;gt;
            &amp;lt;td&amp;gt;{{ student.name }}&amp;lt;/td&amp;gt;
            &amp;lt;td&amp;gt;{{ student.age }}&amp;lt;/td&amp;gt;
            &amp;lt;td&amp;gt;
                &amp;lt;a href=&quot;/student/{{ student.id }}&quot;&amp;gt;查看详情&amp;lt;/a&amp;gt;
            &amp;lt;/td&amp;gt;
        &amp;lt;/tr&amp;gt;
    {百分号 endfor %}
&amp;lt;/table&amp;gt;
&amp;lt;br&amp;gt;
{百分号 if is_paginated %}
    {百分号 if page_obj.has_previous %}
        &amp;lt;a href=&quot;/student/list?page={{ page_obj.previous_page_number }}&quot;&amp;gt;上一页&amp;lt;/a&amp;gt;
    {百分号 endif %}
    {百分号 for current in paginator.page_range %}
        {百分号 if current == page_obj.number %}
            &amp;lt;a href=&quot;/student/list?page={{ current }}&quot;&amp;gt;&amp;lt;b&amp;gt;&amp;lt;font color=&quot;blue&quot;&amp;gt;{{ current }}&amp;lt;/font&amp;gt;&amp;lt;/b&amp;gt;&amp;lt;/a&amp;gt;
        {百分号 else %}
            &amp;lt;a href=&quot;/student/list?page={{ current }}&quot;&amp;gt;{{ current }}&amp;lt;/a&amp;gt;
        {百分号 endif %}
    {百分号 endfor %}
    {百分号 if page_obj.has_next %}
        &amp;lt;a href=&quot;/student/list?page={{ page_obj.next_page_number }}&quot;&amp;gt;下一页&amp;lt;/a&amp;gt;
    {百分号 endif %}
{百分号 endif %}
&amp;lt;/body&amp;gt;
&amp;lt;/html&amp;gt;
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;测试，浏览器输入： &lt;code&gt;http://127.0.0.1:8000/student/list&lt;/code&gt;&lt;/p&gt;
&lt;p&gt;![image-20240304203332197](D:\开发\Python\Django\2024版 一周掌握Django5视频教程 python web开发.assets\image-20240304203332197.png)&lt;/p&gt;
&lt;p&gt;点击“新增学生“链接&lt;/p&gt;
&lt;p&gt;![image-20240304203359073](D:\开发\Python\Django\2024版 一周掌握Django5视频教程 python web开发.assets\image-20240304203359073.png)&lt;/p&gt;
&lt;p&gt;输入表单信息&lt;/p&gt;
&lt;p&gt;![image-20240304203419715](D:\开发\Python\Django\2024版 一周掌握Django5视频教程 python web开发.assets\image-20240304203419715.png)&lt;/p&gt;
&lt;p&gt;点击确定，则跳转到学生信息列表页面&lt;/p&gt;
&lt;p&gt;![image-20240304203705345](D:\开发\Python\Django\2024版 一周掌握Django5视频教程 python web开发.assets\image-20240304203705345.png)&lt;/p&gt;
&lt;h3&gt;Django5修改视图UpdateView&lt;/h3&gt;
&lt;p&gt;视图类UpdateView是在视图类FormView和视图类DetailView的基础上实现的，它首先使用视图类 DetailView的功能（功能核心类是SingleObjectMixin)，通过路由变量查询数据表某条数据并显示在网页上，然后在视图类FormView的基础上，通过表单方式实现数据修改。&lt;/p&gt;
&lt;p&gt;views.py里新建Update类：&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;class Update(UpdateView):
    # 设置模版文件
    template_name = &apos;student/update.html&apos;
    # 设置模型外的数据
    extra_context = {&apos;title&apos;: &apos;学生信息编辑&apos;}
    # 设置查询模型
    model = StudentInfo
    # 指定form
    form_class = StudentForm
    # 执行成功后跳转地址
    success_url = &apos;/student/list&apos;
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;student下新建update.html&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;&amp;lt;!DOCTYPE html&amp;gt;
&amp;lt;html lang=&quot;en&quot;&amp;gt;
&amp;lt;head&amp;gt;
    &amp;lt;meta charset=&quot;UTF-8&quot;&amp;gt;
    &amp;lt;title&amp;gt;{{ title }}&amp;lt;/title&amp;gt;
    &amp;lt;style&amp;gt;
        .inputClass {
            width: 200px;
        }
    &amp;lt;/style&amp;gt;
&amp;lt;/head&amp;gt;
&amp;lt;body&amp;gt;
&amp;lt;h3&amp;gt;{{ title }}&amp;lt;/h3&amp;gt;
&amp;lt;form method=&quot;post&quot;&amp;gt;
    {百分号 csrf_token %}
    {{ form.as_p }}
    &amp;lt;input type=&quot;submit&quot; value=&quot;确定&quot;&amp;gt;
&amp;lt;/form&amp;gt;
&amp;lt;/body&amp;gt;
&amp;lt;/html&amp;gt;
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;urls.py里加一个映射：&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;    path(&apos;student/update/&amp;lt;int:pk&amp;gt;&apos;, helloWorld.views.Update.as_view()),
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;list.html里加一个&lt;/p&gt;
&lt;p&gt;&lt;code&gt;&amp;lt;a href=&quot;/student/update/{{ student.id }}&quot;&amp;gt;修改&amp;lt;/a&amp;gt;&lt;/code&gt;&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;&amp;lt;!DOCTYPE html&amp;gt;
&amp;lt;html lang=&quot;en&quot;&amp;gt;
&amp;lt;head&amp;gt;
    &amp;lt;meta charset=&quot;UTF-8&quot;&amp;gt;
    &amp;lt;title&amp;gt;{{ title }}&amp;lt;/title&amp;gt;
&amp;lt;/head&amp;gt;
&amp;lt;body&amp;gt;
&amp;lt;h3&amp;gt;{{ title }}&amp;lt;/h3&amp;gt;
&amp;lt;a href=&quot;/student/create&quot;&amp;gt;新增学生&amp;lt;/a&amp;gt;
&amp;lt;table border=&quot;1&quot;&amp;gt;
    &amp;lt;tr&amp;gt;
        &amp;lt;th&amp;gt;编号&amp;lt;/th&amp;gt;
        &amp;lt;th&amp;gt;姓名&amp;lt;/th&amp;gt;
        &amp;lt;th&amp;gt;年龄&amp;lt;/th&amp;gt;
        &amp;lt;th&amp;gt;操作&amp;lt;/th&amp;gt;
    &amp;lt;/tr&amp;gt;
    {百分号 for student in student_list %}
        &amp;lt;tr&amp;gt;
            &amp;lt;td&amp;gt;{{ student.id }}&amp;lt;/td&amp;gt;
            &amp;lt;td&amp;gt;{{ student.name }}&amp;lt;/td&amp;gt;
            &amp;lt;td&amp;gt;{{ student.age }}&amp;lt;/td&amp;gt;
            &amp;lt;td&amp;gt;
                &amp;lt;a href=&quot;/student/{{ student.id }}&quot;&amp;gt;查看详情&amp;lt;/a&amp;gt;
                &amp;lt;a href=&quot;/student/update/{{ student.id }}&quot;&amp;gt;修改&amp;lt;/a&amp;gt;
            &amp;lt;/td&amp;gt;
        &amp;lt;/tr&amp;gt;
    {百分号 endfor %}
&amp;lt;/table&amp;gt;
&amp;lt;br&amp;gt;
{百分号 if is_paginated %}
    {百分号 if page_obj.has_previous %}
        &amp;lt;a href=&quot;/student/list?page={{ page_obj.previous_page_number }}&quot;&amp;gt;上一页&amp;lt;/a&amp;gt;
    {百分号 endif %}
    {百分号 for current in paginator.page_range %}
        {百分号 if current == page_obj.number %}
            &amp;lt;a href=&quot;/student/list?page={{ current }}&quot;&amp;gt;&amp;lt;b&amp;gt;&amp;lt;font color=&quot;blue&quot;&amp;gt;{{ current }}&amp;lt;/font&amp;gt;&amp;lt;/b&amp;gt;&amp;lt;/a&amp;gt;
        {百分号 else %}
            &amp;lt;a href=&quot;/student/list?page={{ current }}&quot;&amp;gt;{{ current }}&amp;lt;/a&amp;gt;
        {百分号 endif %}
    {百分号 endfor %}
    {百分号 if page_obj.has_next %}
        &amp;lt;a href=&quot;/student/list?page={{ page_obj.next_page_number }}&quot;&amp;gt;下一页&amp;lt;/a&amp;gt;
    {百分号 endif %}
{百分号 endif %}
&amp;lt;/body&amp;gt;
&amp;lt;/html&amp;gt;
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;运行测试：浏览器输入 &lt;code&gt;http://127.0.0.1:8000/student/list&lt;/code&gt;&lt;/p&gt;
&lt;p&gt;![image-20240306202723079](D:\开发\Python\Django\2024版 一周掌握Django5视频教程 python web开发.assets\image-20240306202723079.png)&lt;/p&gt;
&lt;p&gt;点击修改，进入修改页面，我们发现，django自动帮我获取了数据，并且填充到了表单&lt;/p&gt;
&lt;p&gt;![image-20240306202732436](D:\开发\Python\Django\2024版 一周掌握Django5视频教程 python web开发.assets\image-20240306202732436.png)&lt;/p&gt;
&lt;p&gt;我们修改，数据：&lt;/p&gt;
&lt;p&gt;![image-20240306202817950](D:\开发\Python\Django\2024版 一周掌握Django5视频教程 python web开发.assets\image-20240306202817950.png)&lt;/p&gt;
&lt;p&gt;点击确定提交，则django给我们做了数据库修改操作，然后准发到列表页面。是不是非常的方便。&lt;/p&gt;
&lt;p&gt;![image-20240306202849625](D:\开发\Python\Django\2024版 一周掌握Django5视频教程 python web开发.assets\image-20240306202849625.png)&lt;/p&gt;
&lt;h3&gt;Django5删除视图DeleteView&lt;/h3&gt;
&lt;p&gt;视图类DeleteView的使用方法与视图类UpdateView类似，视图类DeleteView只能删除单条数据，路由变量为模型主键提供查询范围，因为模型主键具有唯一性，所以通过主键查询能精准到某条数据。查询出来的数据通过POST 请求实现数据删除。&lt;/p&gt;
&lt;p&gt;views.py里面，我们新建Delete类，继承DeleteView&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;class Delete(DeleteView):
    # 设置模版文件
    template_name = &apos;student/delete.html&apos;
    # 设置模型外的数据
    extra_context = {&apos;title&apos;: &apos;学生信息删除&apos;}
    # 设置上下文对象名称
    context_object_name = &apos;student&apos;
    # 设置查询模型
    model = StudentInfo
    # 执行成功后跳转地址
    success_url = &apos;/student/list&apos;
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;urls.py里加下映射：&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;path(&apos;student/delete/&amp;lt;int:pk&amp;gt;&apos;, helloWorld.views.Delete.as_view()),
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;student下新建delete.html&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;&amp;lt;!DOCTYPE html&amp;gt;
&amp;lt;html lang=&quot;en&quot;&amp;gt;
&amp;lt;head&amp;gt;
    &amp;lt;meta charset=&quot;UTF-8&quot;&amp;gt;
    &amp;lt;title&amp;gt;{{ title }}&amp;lt;/title&amp;gt;
&amp;lt;/head&amp;gt;
&amp;lt;body&amp;gt;
&amp;lt;h3&amp;gt;{{ title }}&amp;lt;/h3&amp;gt;
&amp;lt;form method=&quot;post&quot;&amp;gt;
    {百分号 csrf_token %}
    您确定更要删除 id:{{ student.id }} name:{{ student.name }} age:{{ student.age }} 的记录吗 ？
    &amp;lt;input type=&quot;submit&quot; value=&quot;确定&quot;&amp;gt;
&amp;lt;/form&amp;gt;
&amp;lt;/body&amp;gt;
&amp;lt;/html&amp;gt;
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;list.hml加下 &lt;code&gt;&amp;lt;a href=&quot;/student/delete/{{ student.id }}&quot;&amp;gt;删除&amp;lt;/a&amp;gt;&lt;/code&gt;&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;&amp;lt;!DOCTYPE html&amp;gt;
&amp;lt;html lang=&quot;en&quot;&amp;gt;
&amp;lt;head&amp;gt;
    &amp;lt;meta charset=&quot;UTF-8&quot;&amp;gt;
    &amp;lt;title&amp;gt;{{ title }}&amp;lt;/title&amp;gt;
&amp;lt;/head&amp;gt;
&amp;lt;body&amp;gt;
&amp;lt;h3&amp;gt;{{ title }}&amp;lt;/h3&amp;gt;
&amp;lt;a href=&quot;/student/create&quot;&amp;gt;新增学生&amp;lt;/a&amp;gt;
&amp;lt;table border=&quot;1&quot;&amp;gt;
    &amp;lt;tr&amp;gt;
        &amp;lt;th&amp;gt;编号&amp;lt;/th&amp;gt;
        &amp;lt;th&amp;gt;姓名&amp;lt;/th&amp;gt;
        &amp;lt;th&amp;gt;年龄&amp;lt;/th&amp;gt;
        &amp;lt;th&amp;gt;操作&amp;lt;/th&amp;gt;
    &amp;lt;/tr&amp;gt;
    {百分号 for student in student_list %}
        &amp;lt;tr&amp;gt;
            &amp;lt;td&amp;gt;{{ student.id }}&amp;lt;/td&amp;gt;
            &amp;lt;td&amp;gt;{{ student.name }}&amp;lt;/td&amp;gt;
            &amp;lt;td&amp;gt;{{ student.age }}&amp;lt;/td&amp;gt;
            &amp;lt;td&amp;gt;
                &amp;lt;a href=&quot;/student/{{ student.id }}&quot;&amp;gt;查看详情&amp;lt;/a&amp;gt;
                &amp;lt;a href=&quot;/student/update/{{ student.id }}&quot;&amp;gt;修改&amp;lt;/a&amp;gt;
                &amp;lt;a href=&quot;/student/delete/{{ student.id }}&quot;&amp;gt;删除&amp;lt;/a&amp;gt;
            &amp;lt;/td&amp;gt;
        &amp;lt;/tr&amp;gt;
    {百分号 endfor %}
&amp;lt;/table&amp;gt;
&amp;lt;br&amp;gt;
{百分号 if is_paginated %}
    {百分号 if page_obj.has_previous %}
        &amp;lt;a href=&quot;/student/list?page={{ page_obj.previous_page_number }}&quot;&amp;gt;上一页&amp;lt;/a&amp;gt;
    {百分号 endif %}
    {百分号 for current in paginator.page_range %}
        {百分号 if current == page_obj.number %}
            &amp;lt;a href=&quot;/student/list?page={{ current }}&quot;&amp;gt;&amp;lt;b&amp;gt;&amp;lt;font color=&quot;blue&quot;&amp;gt;{{ current }}&amp;lt;/font&amp;gt;&amp;lt;/b&amp;gt;&amp;lt;/a&amp;gt;
        {百分号 else %}
            &amp;lt;a href=&quot;/student/list?page={{ current }}&quot;&amp;gt;{{ current }}&amp;lt;/a&amp;gt;
        {百分号 endif %}
    {百分号 endfor %}
    {百分号 if page_obj.has_next %}
        &amp;lt;a href=&quot;/student/list?page={{ page_obj.next_page_number }}&quot;&amp;gt;下一页&amp;lt;/a&amp;gt;
    {百分号 endif %}
{百分号 endif %}
&amp;lt;/body&amp;gt;
&amp;lt;/html&amp;gt;
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;运行测试：浏览器输入：&lt;code&gt;http://127.0.0.1:8000/student/list&lt;/code&gt;&lt;/p&gt;
&lt;p&gt;![image-20240307162905922](D:\开发\Python\Django\2024版 一周掌握Django5视频教程 python web开发.assets\image-20240307162905922.png)&lt;/p&gt;
&lt;p&gt;点击删除，进入删除确定页面：&lt;/p&gt;
&lt;p&gt;![image-20240307162931621](D:\开发\Python\Django\2024版 一周掌握Django5视频教程 python web开发.assets\image-20240307162931621.png)&lt;/p&gt;
&lt;p&gt;点击 确定，django帮我删除数据后，转发到列表页面：&lt;/p&gt;
&lt;p&gt;![image-20240307162956503](D:\开发\Python\Django\2024版 一周掌握Django5视频教程 python web开发.assets\image-20240307162956503.png)&lt;/p&gt;
&lt;h2&gt;Django5模板引擎&lt;/h2&gt;
&lt;p&gt;Django 作为 Web框架，需要一种很便利的方法动态地生成HTML 网页，因此有了模板这个概念。模板包含所需HTML 的部分代码以及一些特殊语法，特殊语法用于描述如何将视图传递的数据动态插入HTML 网页中。
Django可以配置一个或多个模板引擎(甚至是О个，如前后端分离，Django只提供API接口，无须使用模板引擎)，模板引擎有Django模板语言（Django Template Language，DTL)和Jinja3。Django模板语言是Django 内置的功能之一，Jinja3是当前Python流行的模板语言。本章分别讲述Django模板语言和Jinja3的使用方法。&lt;/p&gt;
&lt;h3&gt;Django5内置模板引擎&lt;/h3&gt;
&lt;p&gt;Django 内置的模板引擎包含模板上下文（亦可称为模板变量)、标签和过滤器，各个功能说明如下:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;模板上下文是以变量的形式写入模板文件里面，变量值由视图函数或视图类传递所得。&lt;/li&gt;
&lt;li&gt;标签是对模板上下文进行控制输出，比如模板上下文的判断和循环控制等。&lt;/li&gt;
&lt;li&gt;模板继承隶属于标签，它是将每个模板文件重复的代码抽取出来并写在一个共用的模板文件中，其他模板文件通过继承共用模板文件来实现完整的网页输出。&lt;/li&gt;
&lt;li&gt;过滤器是对模板上下文进行操作处理，比如模板上下文的内容截取、替换或格式转换等。&lt;/li&gt;
&lt;/ul&gt;
&lt;h4&gt;模板上下文&lt;/h4&gt;
&lt;p&gt;模板上下文是模板中基本的组成单位，上下文的数据由视图函数或视图类传递。它以{{ variable }}表示，variable是上下文的名称，它支持 Python 所有的数据类型，如字典、列表、元组、字符串、整型或实例化对象等。上下文的数据格式不同，在模板里的使用方式也有所差异。&lt;/p&gt;
&lt;p&gt;使用变量的一些注意点如下：&lt;/p&gt;
&lt;p&gt;&amp;lt;blockquote&amp;gt;
&amp;lt;ul&amp;gt;&amp;lt;li&amp;gt;当模板引擎遇到一个变量，将计算这个变量，然后输出结果&amp;lt;/li&amp;gt;
&amp;lt;li&amp;gt;变量名必须由字母、数字、下划线、点组成，不能由数字和下划线开头&amp;lt;/li&amp;gt;
&amp;lt;/ul&amp;gt;&amp;lt;ul&amp;gt;&amp;lt;li&amp;gt;当模板引擎遇到 “ . ” 的时候，按以下顺序进行解析
&amp;lt;ul&amp;gt;&amp;lt;li&amp;gt;按照 dict 解析    var[key]&amp;lt;/li&amp;gt;
&amp;lt;li&amp;gt;按照对象的属性或方法解析    var.var/func&amp;lt;/li&amp;gt;
&amp;lt;li&amp;gt;按照索引解析    var[index]&amp;lt;/li&amp;gt;
&amp;lt;/ul&amp;gt;&amp;lt;/li&amp;gt;
&amp;lt;li&amp;gt;如果变量不存在，不会引发异常，模板会插入空字符串 &apos;&apos;&amp;lt;/li&amp;gt;
&amp;lt;li&amp;gt;在模板中使用变量或方法时，不能出现 ()、[]、{}&amp;lt;/li&amp;gt;
&amp;lt;li&amp;gt;调用方法时，不能传递参数&amp;lt;/li&amp;gt;
&amp;lt;/ul&amp;gt;&amp;lt;/blockquote&amp;gt;&lt;/p&gt;
&lt;p&gt;我们通过一个实例来学习下：&lt;/p&gt;
&lt;p&gt;views.py，index方法：&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;# 定义人类
class Person:
    # 属性 姓名
    name = None
    # 属性 年龄
    age = None

    def __init__(self, name, age):
        self.name = name
        self.age = age
        
def index(request):
    str = &quot;模板变量&quot;
    myDict = {&quot;tom&quot;: &apos;666&apos;, &apos;cat&apos;: &apos;999&apos;, &apos;wzw&apos;: &apos;333&apos;}
    # 创建一个对象 zhangsan
    zhangsan = Person(&quot;张三&quot;, 21)
    myList = [&quot;java&quot;, &quot;python&quot;, &quot;c&quot;]
    myTuple = (&quot;python&quot;, 222, 3.14, False)
    content_value = {&quot;msg&quot;: str, &quot;msg2&quot;: myDict, &quot;msg3&quot;: zhangsan, &quot;msg4&quot;: myList, &quot;msg5&quot;: myTuple}
    return render(request, &apos;index.html&apos;, context=content_value)
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;index.html:&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;&amp;lt;!DOCTYPE html&amp;gt;
&amp;lt;html lang=&quot;en&quot;&amp;gt;
&amp;lt;head&amp;gt;
    &amp;lt;meta charset=&quot;UTF-8&quot;&amp;gt;
    &amp;lt;title&amp;gt;Title&amp;lt;/title&amp;gt;
&amp;lt;/head&amp;gt;
&amp;lt;body&amp;gt;
字符串：{{ msg }}&amp;lt;br&amp;gt;
字典类型：{{ msg2.tom }},{{ msg2.cat }},{{ msg2.wzw }}&amp;lt;br&amp;gt;
对象：{{ msg3.name }},{{ msg3.age }}&amp;lt;br&amp;gt;
列表：{{ msg4.0 }},{{ msg4.1 }},{{ msg4.3 }},{{ msg4.2 }}&amp;lt;br&amp;gt;
元组：{{ msg5.0 }},{{ msg5.4 }},{{ msg5.1 }},{{ msg5.2 }},{{ msg5.3 }}
&amp;lt;/body&amp;gt;
&amp;lt;/html&amp;gt;
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;测试，浏览器输入：&lt;code&gt;http://127.0.0.1:8000/index/&lt;/code&gt;&lt;/p&gt;
&lt;p&gt;![image-20240308213417406](D:\开发\Python\Django\2024版 一周掌握Django5视频教程 python web开发.assets\image-20240308213417406.png)&lt;/p&gt;
&lt;h4&gt;模板标签&lt;/h4&gt;
&lt;p&gt;标签是对模板上下文进行控制输出，它是以{百分号 tag %}表示的，其中 tag是标签的名称，Django内置了许多模板标签，比如{百分号 if %}（判断标签）、{百分号 for %}(循环标签）或{百分号 url %}(路由标签）等。&lt;/p&gt;
&lt;p&gt;常用内置标签如下：&lt;/p&gt;
&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;标签&lt;/th&gt;
&lt;th&gt;描述&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;{百分号 for %}&lt;/td&gt;
&lt;td&gt;遍历输出上下文的内容&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;{百分号 if %}&lt;/td&gt;
&lt;td&gt;对上下文进行条件判断&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;{百分号 csrf_token %}&lt;/td&gt;
&lt;td&gt;生成csrf token的标签，用于防护跨站请求伪造攻击&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;{百分号 url %}&lt;/td&gt;
&lt;td&gt;引用路由配置的地址，生成相应的路由地址&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;{百分号 with %}&lt;/td&gt;
&lt;td&gt;将上下文名重新命名&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;{百分号 load %}&lt;/td&gt;
&lt;td&gt;加载导入 Django的标签库&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;{百分号 static %}&lt;/td&gt;
&lt;td&gt;读取静态资源的文件内容&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;{百分号 extends xxx %}&lt;/td&gt;
&lt;td&gt;模板继承，xxx为模板文件名，使当前模板继承xxx模板&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;{百分号 block xxx %}&lt;/td&gt;
&lt;td&gt;重写父类模板的代码&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;
&lt;p&gt;在for标签中，模板还提供了一些特殊的变量来获取for标签的循环信息，变量说明如下：&lt;/p&gt;
&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;变量&lt;/th&gt;
&lt;th&gt;描述&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;forloop.counter&lt;/td&gt;
&lt;td&gt;获取当前循环的索引，从1开始计算&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;forloop.counter0&lt;/td&gt;
&lt;td&gt;获取当前循环的索引，从0开始计算&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;forloop.revcounter&lt;/td&gt;
&lt;td&gt;索引从最大数开始递减，直到索引到1位置&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;forloop.revcounter0&lt;/td&gt;
&lt;td&gt;索引从最大数开始递减，直到索引到0位置&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;forloop.first&lt;/td&gt;
&lt;td&gt;当遍历的元素为第一项时为真&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;forloop.last&lt;/td&gt;
&lt;td&gt;当遍历的元素为最后一项时为真&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;forloop.parentloop&lt;/td&gt;
&lt;td&gt;在嵌套的for循环中，获取上层for循环的forloop&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;
&lt;p&gt;我们修改index.html：&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;&amp;lt;!DOCTYPE html&amp;gt;
&amp;lt;html lang=&quot;en&quot;&amp;gt;
&amp;lt;head&amp;gt;
    &amp;lt;meta charset=&quot;UTF-8&quot;&amp;gt;
    &amp;lt;title&amp;gt;Title&amp;lt;/title&amp;gt;
&amp;lt;/head&amp;gt;
&amp;lt;body&amp;gt;
字符串：{{ msg }}&amp;lt;br&amp;gt;
字典类型：{{ msg2.tom }},{{ msg2.cat }},{{ msg2.wzw }}&amp;lt;br&amp;gt;
对象：{{ msg3.name }},{{ msg3.age }}&amp;lt;br&amp;gt;
列表：{{ msg4.0 }},{{ msg4.1 }},{{ msg4.3 }},{{ msg4.2 }}&amp;lt;br&amp;gt;
元组：{{ msg5.0 }},{{ msg5.4 }},{{ msg5.1 }},{{ msg5.2 }},{{ msg5.3 }}

&amp;lt;h3&amp;gt;模板标签&amp;lt;/h3&amp;gt;
&amp;lt;p&amp;gt;遍历for标签：&amp;lt;/p&amp;gt;
{百分号 for item in msg4 %}
    &amp;lt;p&amp;gt;这个是第{{ forloop.counter }}次循环&amp;lt;/p&amp;gt;
    {百分号 if forloop.first %}
        &amp;lt;p&amp;gt;这个是第一项：{{ item }}&amp;lt;/p&amp;gt;
    {百分号 elif forloop.last %}
        &amp;lt;p&amp;gt;这个是最后一项：{{ item }}&amp;lt;/p&amp;gt;
    {百分号 endif %}
{百分号 endfor %}
&amp;lt;p&amp;gt;判断if标签：&amp;lt;/p&amp;gt;
{百分号 if msg == &apos;模板变量&apos; %}
    &amp;lt;p&amp;gt;模板变量&amp;lt;/p&amp;gt;
{百分号 elif msg == &apos;模板变量2&apos; %}
    &amp;lt;p&amp;gt;模板变量2&amp;lt;/p&amp;gt;
{百分号 else %}
    &amp;lt;p&amp;gt;其他&amp;lt;/p&amp;gt;
{百分号 endif %}
&amp;lt;p&amp;gt;url标签&amp;lt;/p&amp;gt;
&amp;lt;a href=&quot;{百分号 url &apos;index&apos; %}&quot;&amp;gt;请求index&amp;lt;/a&amp;gt;
&amp;lt;p&amp;gt;with标签&amp;lt;/p&amp;gt;
{百分号 with info=msg %}
    {{ info }}
{百分号 endwith %}

&amp;lt;/body&amp;gt;
&amp;lt;/html&amp;gt;
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;用url标签的时候 第二个参数是路由名称，所以urls.py里，修改下：&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;path(&apos;index/&apos;, helloWorld.views.index, name=&quot;index&quot;),
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;测试，浏览器输入：&lt;code&gt;http://127.0.0.1:8000/index/&lt;/code&gt;&lt;/p&gt;
&lt;p&gt;![image-20240309122305103](D:\开发\Python\Django\2024版 一周掌握Django5视频教程 python web开发.assets\image-20240309122305103.png)&lt;/p&gt;
&lt;h4&gt;模板继承&lt;/h4&gt;
&lt;p&gt;Django模板继承是一个强大的工具，可以将通用页面元素（例如页眉、页脚、侧边栏等）分离出来，并在多个页面之间共享他们。&lt;/p&gt;
&lt;p&gt;模板继承和 Python 语言中类的继承含义是一样的，在 Django 中模板只是一个文本文件，如 HTML。&lt;/p&gt;
&lt;p&gt;模板继承是 Django 模板语言中最强大的部分。模板继承使你可以构建基本的“骨架”模板，将通用的功能或者属性写在基础模板中，也叫基类模板或者父模板。子模板可以继承父类模板，子模板继承后将自动拥有父类中的属性和方法，我们还可以在子模板中对父模板进行重写，即重写父模板中方法或者属性，从而实现子模板的定制。模板继承大大提高了代码的可重用性，减轻开发人员的工作量。&lt;/p&gt;
&lt;p&gt;在模板继承中最常用了标签就是 {百分号 block %} 与 {百分号 extends %} 标签，其中 {百分号 block% } 标签与 {百分号 endblock %} 标签成对出现&lt;/p&gt;
&lt;p&gt;我们新建一个基础模版base.html&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;&amp;lt;!DOCTYPE html&amp;gt;
&amp;lt;html lang=&quot;en&quot;&amp;gt;
&amp;lt;head&amp;gt;
    &amp;lt;meta charset=&quot;UTF-8&quot;&amp;gt;
    &amp;lt;title&amp;gt;
        {百分号 block title %}
            Python222学院
        {百分号 endblock %}
    &amp;lt;/title&amp;gt;
&amp;lt;/head&amp;gt;
&amp;lt;body&amp;gt;
&amp;lt;div id=&quot;head&quot;&amp;gt;
    &amp;lt;img src=&quot;http://127.0.0.1:8000/static/logo.png&quot;/&amp;gt;
&amp;lt;/div&amp;gt;
&amp;lt;div id=&quot;content&quot;&amp;gt;
    {百分号 block content %}
        欢迎进入Python222学院
    {百分号 endblock %}
&amp;lt;/div&amp;gt;
&amp;lt;div id=&quot;footer&quot;&amp;gt;
    版权所有 www.python222.com
&amp;lt;/div&amp;gt;
&amp;lt;/body&amp;gt;
&amp;lt;/html&amp;gt;
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;再写一个course.html，继承base.html&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;{百分号 extends &apos;base.html&apos; %}
&amp;lt;!-- 重写title --&amp;gt;
{百分号 block title %}
    课程页面-Python222
{百分号 endblock %}
&amp;lt;!-- 重写content --&amp;gt;
{百分号 block content %}
    Django5课程-模板引擎章节
{百分号 endblock %}
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;我们来测试下吧。&lt;/p&gt;
&lt;p&gt;views.py里新建一个to_course方法：&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;def to_course(request):
    &quot;&quot;&quot;
    跳转课程页面
    :param request:
    :return:
    &quot;&quot;&quot;
    return render(request, &apos;course.html&apos;)
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;urls.py里加一个映射：&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;path(&apos;toCourse/&apos;, helloWorld.views.to_course)
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;浏览器输入：&lt;code&gt;http://127.0.0.1:8000/toCourse/&lt;/code&gt;&lt;/p&gt;
&lt;p&gt;![image-20240310230510647](D:\开发\Python\Django\2024版 一周掌握Django5视频教程 python web开发.assets\image-20240310230510647.png)&lt;/p&gt;
&lt;p&gt;我们发现模板里的标题和内容被course页面修改了，其他的没变。&lt;/p&gt;
&lt;p&gt;这里我们再优化下，直接写死静态路径是不是很不好啊。&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;&amp;lt;div id=&quot;head&quot;&amp;gt;
    &amp;lt;img src=&quot;http://127.0.0.1:8000/static/logo.png&quot;/&amp;gt;
&amp;lt;/div&amp;gt;
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;这时候我们就能用上 &lt;code&gt;{百分号 load static %}&lt;/code&gt;，加载项目中的静态文件，包括图片，css，js文件，字体文件等。&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;&amp;lt;img src=&quot;{百分号 static &apos;logo.png&apos; %}&quot;/&amp;gt;
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;完整base.html：&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;&amp;lt;!DOCTYPE html&amp;gt;
&amp;lt;html lang=&quot;en&quot;&amp;gt;
&amp;lt;head&amp;gt;
    &amp;lt;meta charset=&quot;UTF-8&quot;&amp;gt;
    &amp;lt;title&amp;gt;
        {百分号 block title %}
            Python222学院
        {百分号 endblock %}
    &amp;lt;/title&amp;gt;
&amp;lt;/head&amp;gt;
{百分号 load static %}
&amp;lt;body&amp;gt;
&amp;lt;div id=&quot;head&quot;&amp;gt;
    &amp;lt;img src=&quot;{百分号 static &apos;logo.png&apos; %}&quot;/&amp;gt;
&amp;lt;/div&amp;gt;
&amp;lt;div id=&quot;content&quot;&amp;gt;
    {百分号 block content %}
        欢迎进入Python222学院
    {百分号 endblock %}
&amp;lt;/div&amp;gt;
&amp;lt;div id=&quot;footer&quot;&amp;gt;
    版权所有 www.python222.com
&amp;lt;/div&amp;gt;
&amp;lt;/body&amp;gt;
&amp;lt;/html&amp;gt;
&lt;/code&gt;&lt;/pre&gt;
&lt;h4&gt;过滤器&lt;/h4&gt;
&lt;p&gt;Django过滤器是一种用于在Django模板中处理数据的技术。过滤器的作用是可以对模板中的变量进行加工、过滤或格式化，返回一个新的值供模板使用。&lt;/p&gt;
&lt;p&gt;过滤器作用是在变量输出时，对输出的变量值做进一步的处理。
我们可以使用过滤器来更改变量的输出显示。
过滤器跟模板标签一样，也是在模板中对函数进行调用
对输出的日期进行格式化处理，或者转换大小写字母等，这些都有对应的过滤器去处理它们。&lt;/p&gt;
&lt;p&gt;过滤器的语法格式如下：
{{ 变量 | 过滤器1:参数值1 | 过滤器2:参数值2 ... }}&lt;/p&gt;
&lt;p&gt;常用内置过滤器如下：&lt;/p&gt;
&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;过滤器&lt;/th&gt;
&lt;th&gt;说明&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;add&lt;/td&gt;
&lt;td&gt;加法&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;addslashes&lt;/td&gt;
&lt;td&gt;添加斜杠&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;capfirst&lt;/td&gt;
&lt;td&gt;首字母大写&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;center&lt;/td&gt;
&lt;td&gt;文本居中&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;cut&lt;/td&gt;
&lt;td&gt;切除字符&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;date&lt;/td&gt;
&lt;td&gt;日期格式化&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;default&lt;/td&gt;
&lt;td&gt;设置默认值&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;default_if_none&lt;/td&gt;
&lt;td&gt;为None设置默认值&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;dictsort&lt;/td&gt;
&lt;td&gt;字典排序&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;dictsortreversed&lt;/td&gt;
&lt;td&gt;字典反向排序&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;divisibleby&lt;/td&gt;
&lt;td&gt;整除判断&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;escape&lt;/td&gt;
&lt;td&gt;转义&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;escapejs&lt;/td&gt;
&lt;td&gt;转义js代码&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;filesizeformat&lt;/td&gt;
&lt;td&gt;文件尺寸人性化显示&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;first&lt;/td&gt;
&lt;td&gt;第一个元素&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;floatformat&lt;/td&gt;
&lt;td&gt;浮点数格式化&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;force_escape&lt;/td&gt;
&lt;td&gt;强制立刻转义&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;get_digit&lt;/td&gt;
&lt;td&gt;获取数字&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;iriencode&lt;/td&gt;
&lt;td&gt;转换IRI&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;join&lt;/td&gt;
&lt;td&gt;字符列表链接&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;last&lt;/td&gt;
&lt;td&gt;最后一个&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;length&lt;/td&gt;
&lt;td&gt;长度&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;length_is&lt;/td&gt;
&lt;td&gt;长度等于&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;linebreaks&lt;/td&gt;
&lt;td&gt;行转换&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;linebreaksbr&lt;/td&gt;
&lt;td&gt;行转换&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;linenumbers&lt;/td&gt;
&lt;td&gt;行号&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;ljust&lt;/td&gt;
&lt;td&gt;左对齐&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;lower&lt;/td&gt;
&lt;td&gt;小写&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;make_list&lt;/td&gt;
&lt;td&gt;分割成字符列表&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;phone2numeric&lt;/td&gt;
&lt;td&gt;电话号码&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;pluralize&lt;/td&gt;
&lt;td&gt;复数形式&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;pprint&lt;/td&gt;
&lt;td&gt;调试&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;random&lt;/td&gt;
&lt;td&gt;随机获取&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;rjust&lt;/td&gt;
&lt;td&gt;右对齐&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;safe&lt;/td&gt;
&lt;td&gt;安全确认&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;safeseq&lt;/td&gt;
&lt;td&gt;列表安全确认&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;slice&lt;/td&gt;
&lt;td&gt;切片&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;slugify&lt;/td&gt;
&lt;td&gt;转换成ASCII&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;stringformat&lt;/td&gt;
&lt;td&gt;字符串格式化&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;striptags&lt;/td&gt;
&lt;td&gt;去除HTML中的标签&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;time&lt;/td&gt;
&lt;td&gt;时间格式化&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;timesince&lt;/td&gt;
&lt;td&gt;从何时开始&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;timeuntil&lt;/td&gt;
&lt;td&gt;到何时多久&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;title&lt;/td&gt;
&lt;td&gt;所有单词首字母大写&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;truncatechars&lt;/td&gt;
&lt;td&gt;截断字符&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;truncatechars_html&lt;/td&gt;
&lt;td&gt;截断字符&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;truncatewords&lt;/td&gt;
&lt;td&gt;截断单词&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;truncatewords_html&lt;/td&gt;
&lt;td&gt;截断单词&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;unordered_list&lt;/td&gt;
&lt;td&gt;无序列表&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;upper&lt;/td&gt;
&lt;td&gt;大写&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;urlencode&lt;/td&gt;
&lt;td&gt;转义url&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;urlize&lt;/td&gt;
&lt;td&gt;url转成可点击的链接&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;urlizetrunc&lt;/td&gt;
&lt;td&gt;urlize的截断方式&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;wordcount&lt;/td&gt;
&lt;td&gt;单词计数&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;wordwrap&lt;/td&gt;
&lt;td&gt;单词包裹&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;yesno&lt;/td&gt;
&lt;td&gt;将True，False和None，映射成字符串‘yes’，‘no’，‘maybe’&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;
&lt;p&gt;根据给定的格式格式化日期&lt;/p&gt;
&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;格式字符&lt;/th&gt;
&lt;th&gt;描述&lt;/th&gt;
&lt;th&gt;示例输出&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;a&lt;/td&gt;
&lt;td&gt;‘a.m.’ or ‘p.m.’&lt;/td&gt;
&lt;td&gt;‘a.m.’&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;A&lt;/td&gt;
&lt;td&gt;‘AM’ or ‘PM’&lt;/td&gt;
&lt;td&gt;‘AM’&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;b&lt;/td&gt;
&lt;td&gt;月份，文字形式，3个字幕库，小写&lt;/td&gt;
&lt;td&gt;&apos;jan&apos;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;B&lt;/td&gt;
&lt;td&gt;未实现&lt;/td&gt;
&lt;td&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;c&lt;/td&gt;
&lt;td&gt;ISO 8601格式&lt;/td&gt;
&lt;td&gt;2008-01-02T10:30:00.000123+02:00&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;d&lt;/td&gt;
&lt;td&gt;月的日子，带前导零的2位数字。&lt;/td&gt;
&lt;td&gt;01&apos;到&apos;31&apos;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;D&lt;/td&gt;
&lt;td&gt;周几的文字表述形式，3个字母。&lt;/td&gt;
&lt;td&gt;&apos;Fri&apos;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;e&lt;/td&gt;
&lt;td&gt;时区名称&lt;/td&gt;
&lt;td&gt;&quot;，&apos;GMT,&apos;-500&apos;，US/Eastern&apos;等&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;E&lt;/td&gt;
&lt;td&gt;月份，分地区。&lt;/td&gt;
&lt;td&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;f&lt;/td&gt;
&lt;td&gt;时间&lt;/td&gt;
&lt;td&gt;1&apos;，1:30&apos;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;g&lt;/td&gt;
&lt;td&gt;12小时格式，无前导零。&lt;/td&gt;
&lt;td&gt;&quot;1&apos;到&apos;12&apos;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;G&lt;/td&gt;
&lt;td&gt;24小时格式，无前导零。&lt;/td&gt;
&lt;td&gt;0&apos;到&apos;23&apos;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;h&lt;/td&gt;
&lt;td&gt;12小时格式。&lt;/td&gt;
&lt;td&gt;&apos;01&apos;到&apos;12&apos;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;H&lt;/td&gt;
&lt;td&gt;24小时格式。&lt;/td&gt;
&lt;td&gt;&apos;00&apos;到23&apos;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;i&lt;/td&gt;
&lt;td&gt;分钟&lt;/td&gt;
&lt;td&gt;00&apos;到59&apos;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;I&lt;/td&gt;
&lt;td&gt;夏令时间，无论是否生效。&lt;/td&gt;
&lt;td&gt;&apos;1&apos;或0&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;j&lt;/td&gt;
&lt;td&gt;没有前导零的月份的日子。&lt;/td&gt;
&lt;td&gt;&apos;1&apos;到&quot;31&apos;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;l&lt;/td&gt;
&lt;td&gt;星期几,完整英文名&lt;/td&gt;
&lt;td&gt;&apos;Friday&apos;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;L&lt;/td&gt;
&lt;td&gt;布尔值是否是—个闰年。&lt;/td&gt;
&lt;td&gt;True或False&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;m&lt;/td&gt;
&lt;td&gt;月，2位数字带前导零。&lt;/td&gt;
&lt;td&gt;&apos;01&apos;到&apos;12&apos;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;M&lt;/td&gt;
&lt;td&gt;月，文字，3个字母。&lt;/td&gt;
&lt;td&gt;&quot;Jan”&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;n&lt;/td&gt;
&lt;td&gt;月无前导零。&lt;/td&gt;
&lt;td&gt;&apos;1&apos;到&apos;12&apos;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;N&lt;/td&gt;
&lt;td&gt;美联社风格的月份缩写。&lt;/td&gt;
&lt;td&gt;&apos;Jan.&apos; ,&apos;Feb.&apos;,&apos;March&apos;,&apos;May&apos;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;o&lt;/td&gt;
&lt;td&gt;ISO-8601周编号&lt;/td&gt;
&lt;td&gt;&apos;1999&apos;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;O&lt;/td&gt;
&lt;td&gt;与格林威治时间的差，单位小时。&lt;/td&gt;
&lt;td&gt;&apos;+0200&apos;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;P&lt;/td&gt;
&lt;td&gt;时间为12小时&lt;/td&gt;
&lt;td&gt;1:30 p.m.’ , ‘midnight’ , ‘noon’ , ‘12:30 p.m.’&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;r&lt;/td&gt;
&lt;td&gt;RFC 5322格式化日期。&lt;/td&gt;
&lt;td&gt;&apos;Thu,21 Dec 2000 16:01:07+0200&apos;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;s&lt;/td&gt;
&lt;td&gt;秒，带前导零的2位数字。&lt;/td&gt;
&lt;td&gt;&apos;00&apos;到59&apos;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;S&lt;/td&gt;
&lt;td&gt;一个月的英文序数后缀，2个字符。&lt;/td&gt;
&lt;td&gt;&apos;st&apos; ,&apos;nd&apos;, &apos;rd&apos;或&apos;th&apos;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;t&lt;/td&gt;
&lt;td&gt;给定月份的天数。&lt;/td&gt;
&lt;td&gt;28 to 31&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;u&lt;/td&gt;
&lt;td&gt;微秒。&lt;/td&gt;
&lt;td&gt;000000 to 999999&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;U&lt;/td&gt;
&lt;td&gt;自Unix Epoch以来的秒数(1970年1月1日00:00:00 UTC).&lt;/td&gt;
&lt;td&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;w&lt;/td&gt;
&lt;td&gt;星期几,数字无前导零。&lt;/td&gt;
&lt;td&gt;&apos;O&apos;（星期日)至&apos;6’(星期六)&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;W&lt;/td&gt;
&lt;td&gt;ISO-8601周数，周数从星期一开始。&lt;/td&gt;
&lt;td&gt;1，53&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;y&lt;/td&gt;
&lt;td&gt;年份，2位数字。&lt;/td&gt;
&lt;td&gt;99&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Y&lt;/td&gt;
&lt;td&gt;年，4位数。&lt;/td&gt;
&lt;td&gt;&apos;1999&apos;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;z&lt;/td&gt;
&lt;td&gt;—年中的日子&lt;/td&gt;
&lt;td&gt;0到365&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Z&lt;/td&gt;
&lt;td&gt;时区偏移量，单位为秒。&lt;/td&gt;
&lt;td&gt;-43200到43200&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;
&lt;p&gt;views.py index函数我们修改下：str改成&quot;hello&quot;，再定义一个日期对象&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;def index(request):
    str = &quot;hello&quot;
    date = datetime.datetime.now()
    myDict = {&quot;tom&quot;: &apos;666&apos;, &apos;cat&apos;: &apos;999&apos;, &apos;wzw&apos;: &apos;333&apos;}
    # 创建一个对象 zhangsan
    zhangsan = Person(&quot;张三&quot;, 21)
    myList = [&quot;java&quot;, &quot;python&quot;, &quot;c&quot;]
    myTuple = (&quot;python&quot;, 222, 3.14, False)
    content_value = {&quot;msg&quot;: str, &quot;msg2&quot;: myDict, &quot;msg3&quot;: zhangsan, &quot;msg4&quot;: myList, &quot;msg5&quot;: myTuple, &quot;date&quot;: date}
    return render(request, &apos;index.html&apos;, context=content_value)
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;index.html加下：&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;&amp;lt;p&amp;gt;内置过滤器&amp;lt;/p&amp;gt;
capfirst:{{ msg | capfirst }}&amp;lt;br&amp;gt;
length:{{ msg | length }}&amp;lt;br&amp;gt;
date:{{ date }} - &amp;gt;&amp;gt; {{ date | date:&apos;Y-m-d H:i:s&apos; }}
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;运行测试：&lt;/p&gt;
&lt;p&gt;![image-20240311202716156](D:\开发\Python\Django\2024版 一周掌握Django5视频教程 python web开发.assets\image-20240311202716156.png)&lt;/p&gt;
&lt;h3&gt;Jinja3模版引擎&lt;/h3&gt;
&lt;p&gt;Jinja是Python里面被广泛应用的模板引擎，最新版本3.1.3 它的设计思想来源于Django的模板引擎，并扩展了其语法和一系列强大的功能。其中最显著的是增加了沙箱执行功能和可选的自动转义功能，这对大多数应用的安全性来说是非常重要。此外，它还具备以下特性:
·沙箱执行模式，模板的每个部分都在引擎的监督之下执行，模板将会被明确地标记在白名单或黑名单内，这样对于那些不信任的模板也可以执行。
强大的自动HTML转义系统，可以有效地阻止跨站脚本攻击。
模板继承机制，此机制可以使得所有模板具有相似一致的布局，也方便开发人员对模板进行修改和管理。
高效的执行效率，Jinja3引擎在模板第一次加载时就把源码转换成Python字节码，加快模板执行时间。
调试系统融合了标准的Python的TrackBack功能，使得模板编译和运行期间的错误能及时被发现和调试。
语法配置，可以重新配置Jinja3，使得它更好地适应LaTeX或JavaScript 的输出。官方文档手册，此手册指导设计人员更好地使用Jinja3引擎的各种方法。
Django支持 Jinja3模板引擎的使用，由于Jinja3的设计思想来源于Django 的模板引擎，因此Jinja3的使用方法与 Django 的模板语法有相似之处。&lt;/p&gt;
&lt;p&gt;开源主页：&lt;code&gt;https://github.com/pallets/jinja&lt;/code&gt;&lt;/p&gt;
&lt;p&gt;官方文档：&lt;code&gt;https://jinja.palletsprojects.com/en/3.1.x/&lt;/code&gt;&lt;/p&gt;
&lt;h4&gt;Jinja3安装与配置&lt;/h4&gt;
&lt;p&gt;我们用pip命令安装Jinja3&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;pip install Jinja2 -i https://pypi.tuna.tsinghua.edu.cn/simple
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Jinja3安装成功后，接着在 Django里配置Jinja3模板。由于 Django的内置功能是使用 Django的模板引擎，如果将整个项目都改为Jinja3模板引擎，就会导致内置功能无法正常使用。在这种情况下，既要保证内置功能能够正常使用,又要使用Jinja3模板引擎，只能将两个模板引擎共存在同一个项目里。&lt;/p&gt;
&lt;p&gt;首先我们在helloWorld项目库里新建Jinja3.py，用来定义环境参数；&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;from django.contrib.staticfiles.storage import staticfiles_storage
from django.urls import reverse
from jinja2 import Environment


def environment(**options):
    env = Environment(**options)
    env.globals.update(
        {
            &apos;static&apos;: staticfiles_storage.url,
            &apos;url&apos;: reverse
        }
    )
    return env
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;然后我们找到项目配置settings.py&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;TEMPLATES = [
    {
        &apos;BACKEND&apos;: &apos;django.template.backends.jinja2.Jinja2&apos;,
        &apos;DIRS&apos;: [BASE_DIR / &apos;templates&apos;]
        ,
        &apos;APP_DIRS&apos;: True,
        &apos;OPTIONS&apos;: {
            &apos;environment&apos;: &apos;helloWorld.Jinja3.environment&apos;
        },
    },
    {
        &apos;BACKEND&apos;: &apos;django.template.backends.django.DjangoTemplates&apos;,
        &apos;DIRS&apos;: [BASE_DIR / &apos;helloWorld/templates&apos;, BASE_DIR / &apos;templates&apos;]
        ,
        &apos;APP_DIRS&apos;: True,
        &apos;OPTIONS&apos;: {
            &apos;context_processors&apos;: [
                &apos;django.template.context_processors.debug&apos;,
                &apos;django.template.context_processors.request&apos;,
                &apos;django.contrib.auth.context_processors.auth&apos;,
                &apos;django.contrib.messages.context_processors.messages&apos;,
            ],
        },
    },
]
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;我们找到项目目录下templates的index.html，修改下Jinja3支持的模板语法：&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;&amp;lt;!DOCTYPE html&amp;gt;
&amp;lt;html lang=&quot;en&quot;&amp;gt;
&amp;lt;head&amp;gt;
    &amp;lt;meta charset=&quot;UTF-8&quot;&amp;gt;
    &amp;lt;title&amp;gt;Title&amp;lt;/title&amp;gt;
&amp;lt;/head&amp;gt;
&amp;lt;body&amp;gt;
{{ msg2[&apos;cat&apos;] }}
&amp;lt;/body&amp;gt;
&amp;lt;/html&amp;gt;
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;运行测试：&lt;/p&gt;
&lt;p&gt;![image-20240313083415508](D:\开发\Python\Django\2024版 一周掌握Django5视频教程 python web开发.assets\image-20240313083415508.png)&lt;/p&gt;
&lt;h4&gt;Jinja3模板语法&lt;/h4&gt;
&lt;p&gt;尽管Jinja3的设计思想来源于Django 的模板引擎，但在功能和使用细节上，Jinja3比Django的模板引擎更为完善，而且Jinja3的模板语法在使用上与 Django的模板引擎存在一定的差异。
由于Jinja3有模板设计人员帮助手册（官方文档: &lt;code&gt;https://jinja.palletsprojects.com/en/3.1.x/&lt;/code&gt;)，并且官方文档对模板语法的使用说明较为详细，因此这里只讲述Jinja3与 Django模板语言的使用差异。&lt;/p&gt;
&lt;p&gt;我们把helloworld子项目下templates下的index.html复制到父项目下的templates下，然后进行修改：&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;&amp;lt;!DOCTYPE html&amp;gt;
&amp;lt;html lang=&quot;en&quot;&amp;gt;
&amp;lt;head&amp;gt;
    &amp;lt;meta charset=&quot;UTF-8&quot;&amp;gt;
    &amp;lt;title&amp;gt;Title&amp;lt;/title&amp;gt;
&amp;lt;/head&amp;gt;
&amp;lt;body&amp;gt;
字符串：{{ msg }}&amp;lt;br&amp;gt;
字典类型：{{ msg2.tom }},{{ msg2.cat }},{{ msg2.wzw }}&amp;lt;br&amp;gt;
对象：{{ msg3.name }},{{ msg3.age }}&amp;lt;br&amp;gt;
列表：{{ msg4.0 }},{{ msg4.1 }},{{ msg4.3 }},{{ msg4.2 }}&amp;lt;br&amp;gt;
元组：{{ msg5.0 }},{{ msg5.4 }},{{ msg5.1 }},{{ msg5.2 }},{{ msg5.3 }}

&amp;lt;h3&amp;gt;模板标签&amp;lt;/h3&amp;gt;
&amp;lt;p&amp;gt;遍历for标签：&amp;lt;/p&amp;gt;
{百分号 for item in msg4 %}
    &amp;lt;p&amp;gt;这个是第{{ loop.length }}次循环&amp;lt;/p&amp;gt;
    {百分号 if loop.first %}
        &amp;lt;p&amp;gt;这个是第一项：{{ item }}&amp;lt;/p&amp;gt;
    {百分号 elif loop.last %}
        &amp;lt;p&amp;gt;这个是最后一项：{{ item }}&amp;lt;/p&amp;gt;
    {百分号 endif %}
{百分号 endfor %}
&amp;lt;p&amp;gt;判断if标签：&amp;lt;/p&amp;gt;
{百分号 if msg == &apos;模板变量&apos; %}
    &amp;lt;p&amp;gt;模板变量&amp;lt;/p&amp;gt;
{百分号 elif msg == &apos;模板变量2&apos; %}
    &amp;lt;p&amp;gt;模板变量2&amp;lt;/p&amp;gt;
{百分号 else %}
    &amp;lt;p&amp;gt;其他&amp;lt;/p&amp;gt;
{百分号 endif %}
&amp;lt;p&amp;gt;url标签&amp;lt;/p&amp;gt;
{#&amp;lt;a href=&quot;{百分号 url &apos;index&apos; %}&quot;&amp;gt;请求index&amp;lt;/a&amp;gt;#}
&amp;lt;a href=&quot;{{ url(&apos;index&apos;) }}&quot;&amp;gt;请求index&amp;lt;/a&amp;gt;
&amp;lt;p&amp;gt;with标签&amp;lt;/p&amp;gt;
{百分号 with info=msg %}
    {{ info }}
{百分号 endwith %}

&amp;lt;/body&amp;gt;
&amp;lt;/html&amp;gt;
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;运行测试：&lt;/p&gt;
&lt;p&gt;![image-20240314205429022](D:\开发\Python\Django\2024版 一周掌握Django5视频教程 python web开发.assets\image-20240314205429022.png)&lt;/p&gt;
&lt;p&gt;在遍历对象，列表，元组的时候，假如元素或者属性不存在，Jinja3会返回具体的报错信息：no such element&lt;/p&gt;
&lt;p&gt;以及url函数用法不一样；在遍历for标签上，属性页不一样，内置的对象是loop&lt;/p&gt;
&lt;p&gt;for函数模板变量：&lt;/p&gt;
&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Variable&lt;/th&gt;
&lt;th&gt;Description&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;loop.index&lt;/td&gt;
&lt;td&gt;循环的当前迭代（索引从1开始)&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;loop.index0&lt;/td&gt;
&lt;td&gt;循环的当前迭代（索引从0开始）&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;loop.revindex&lt;/td&gt;
&lt;td&gt;循环结束时的迭代次数(索引从1开始)&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;loop.revindex0&lt;/td&gt;
&lt;td&gt;循环结束时的迭代次数(索引从0开始)&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;loop.first&lt;/td&gt;
&lt;td&gt;如果是第一次迭代，就为True&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;loop.last&lt;/td&gt;
&lt;td&gt;如果是最后一次迭代，就为True&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;loop.length&lt;/td&gt;
&lt;td&gt;序列中的项目数，即循环总次&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;loop.cycle&lt;/td&gt;
&lt;td&gt;辅助函数,用于在序列列表之间循环&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;loop.depth&lt;/td&gt;
&lt;td&gt;当前递归循环的深度，从1级开始&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;loop.depth0&lt;/td&gt;
&lt;td&gt;当前递归循环的深度，从0级开始&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;loop.previtem&lt;/td&gt;
&lt;td&gt;上一次迭代中的对象&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;loop.nextitem&lt;/td&gt;
&lt;td&gt;下一次迭代中的对象&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;loop.changed(*val)&lt;/td&gt;
&lt;td&gt;若上次迭代的值与当前迭代的值不同，则返回True&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;
&lt;p&gt;我们把base.html和course.html也复制一份到父项目templates下；&lt;/p&gt;
&lt;p&gt;base.html需要修改下：&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;&amp;lt;!DOCTYPE html&amp;gt;
&amp;lt;html lang=&quot;en&quot;&amp;gt;
&amp;lt;head&amp;gt;
    &amp;lt;meta charset=&quot;UTF-8&quot;&amp;gt;
    &amp;lt;title&amp;gt;
        {百分号 block title %}
            Python222学院
        {百分号 endblock %}
    &amp;lt;/title&amp;gt;
&amp;lt;/head&amp;gt;
{# load不支持，要去掉 #}
{#{百分号 load static %}#}
&amp;lt;body&amp;gt;
&amp;lt;div id=&quot;head&quot;&amp;gt;
{#  Django语法  #}
{#    &amp;lt;img src=&quot;{百分号 static &apos;logo.png&apos; %}&quot;/&amp;gt;#}
{#  Jinja3语法  #}
    &amp;lt;img src=&quot;{{ static(&apos;logo.png&apos;) }}&quot;/&amp;gt;
&amp;lt;/div&amp;gt;
&amp;lt;div id=&quot;content&quot;&amp;gt;
    {百分号 block content %}
        欢迎进入Python222学院
    {百分号 endblock %}
&amp;lt;/div&amp;gt;
&amp;lt;div id=&quot;footer&quot;&amp;gt;
    版权所有 www.python222.com
&amp;lt;/div&amp;gt;
&amp;lt;/body&amp;gt;
&amp;lt;/html&amp;gt;
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;运行测试:&lt;/p&gt;
&lt;p&gt;![image-20240314211924703](D:\开发\Python\Django\2024版 一周掌握Django5视频教程 python web开发.assets\image-20240314211924703.png)&lt;/p&gt;
&lt;p&gt;相比较Django，Jinja3在static函数用法上也有区别，模版继承用法基本一致。&lt;/p&gt;
&lt;h4&gt;Jinja3过滤器&lt;/h4&gt;
&lt;p&gt;Jinja3的过滤器与 Django内置过滤器的使用方法有相似之处，也是由管道符号“|”连接模板上下文和过滤器，但是两者的过滤器名称是不同的，而且过滤器的参数设置方式也不同。Jinja3常用过滤器如下表格：&lt;/p&gt;
&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;过滤器&lt;/th&gt;
&lt;th&gt;说明&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;abs&lt;/td&gt;
&lt;td&gt;设置数值的绝对值&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;default&lt;/td&gt;
&lt;td&gt;设置默认值&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;escape&lt;/td&gt;
&lt;td&gt;转义字符，转成HTML的语法&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;first&lt;/td&gt;
&lt;td&gt;获取上下文的第一个元素&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;last&lt;/td&gt;
&lt;td&gt;获取上下文的最后一个元素&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;length&lt;/td&gt;
&lt;td&gt;获取上下文的长度&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;join&lt;/td&gt;
&lt;td&gt;功能与Python的join语法一致&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;safe&lt;/td&gt;
&lt;td&gt;将上下文转义处理&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;int&lt;/td&gt;
&lt;td&gt;将上下文转换为int类型&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;float&lt;/td&gt;
&lt;td&gt;将上下文转换为float类型&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;lower&lt;/td&gt;
&lt;td&gt;将字符串转换为小写&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;upper&lt;/td&gt;
&lt;td&gt;将字符串转换为大写&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;replace&lt;/td&gt;
&lt;td&gt;字符串的替换&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;truncate&lt;/td&gt;
&lt;td&gt;字符串的截断&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;striptags&lt;/td&gt;
&lt;td&gt;删除字符串中所有的HTML标签&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;trim&lt;/td&gt;
&lt;td&gt;截取字符串前面和后面的空白字符&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;string&lt;/td&gt;
&lt;td&gt;将上下文转换成字符串&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;wordcount&lt;/td&gt;
&lt;td&gt;计算长字符串的单词个数&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;
&lt;p&gt;具体使用可以参考下Jinja3官方文档( &lt;code&gt;https://jinja.palletsprojects.com/en/3.1.x/templates/#filters&lt;/code&gt; )&lt;/p&gt;
&lt;p&gt;下面我们通过实例来体检下Jinja3的用法吧：&lt;/p&gt;
&lt;p&gt;修改index.html：&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;&amp;lt;p&amp;gt;Jinja3过滤器&amp;lt;/p&amp;gt;
first: {{ msg | first }}&amp;lt;br&amp;gt;
last:{{ msg | last }}&amp;lt;br&amp;gt;
length:{{ msg | length }}&amp;lt;br&amp;gt;
upper:{{ msg | upper }}
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;运行结果：&lt;/p&gt;
&lt;p&gt;![image-20240315161534830](D:\开发\Python\Django\2024版 一周掌握Django5视频教程 python web开发.assets\image-20240315161534830.png)&lt;/p&gt;
&lt;h2&gt;Django5模型定义与使用&lt;/h2&gt;
&lt;p&gt;Django5对各种数据库提供了很好的支持，包括PostgreSQL、MySQL、SQLite和 Oracle，而且为这些数据库提供了统一的API方法，这些API统称为ORM框架。通过使用Django5内置的ORM框架可以实现数据库连接和读写操作。&lt;/p&gt;
&lt;h3&gt;Django5模型定义&lt;/h3&gt;
&lt;p&gt;ORM框架是一种程序技术，用于实现面向对象编程语言中不同类型系统的数据之间的转换。
从效果上说，它创建了一个可在编程语言中使用的“虚拟对象数据库”，通过对虚拟对象数据库的操作从而实现对目标数据库的操作，虚拟对象数据库与目标数据库是相互对应的。在 Django5中，虚拟对象数据库也称为模型，通过模型实现对目标数据库的读写操作，实现方法如下:&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;配置目标数据库，在settings.py中设置配置属性&lt;/li&gt;
&lt;li&gt;构建虚拟对象数据库，在App 的models.py文件中以类的形式定义模型。&lt;/li&gt;
&lt;li&gt;通过模型在目标数据库中创建相应的数据表。&lt;/li&gt;
&lt;li&gt;在其他模块（如视图函数）里使用模型来实现目标数据库的读写操作。&lt;/li&gt;
&lt;/ol&gt;
&lt;p&gt;settings.py下我们配置mysql数据库：&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;DATABASES = {
    &apos;default&apos;: {
        &apos;ENGINE&apos;: &apos;django.db.backends.mysql&apos;,
        &apos;NAME&apos;: &apos;db_python222&apos;,
        &apos;USER&apos;: &apos;root&apos;,
        &apos;PASSWORD&apos;: &apos;123456&apos;,
        &apos;HOST&apos;: &apos;localhost&apos;,
        &apos;PORT&apos;: &apos;3308&apos;
    }
}
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;然后我们在models.py里新建两个模型类，分别是图书模型BookInfo和图书类别模型BookTypeInfo，他们是多对一的关系；&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;class BookTypeInfo(models.Model):
    id = models.AutoField(primary_key=True)
    bookTypeName = models.CharField(max_length=20)

    class Meta:
        db_table = &quot;t_bookType&quot;
        verbose_name = &quot;图书类别信息&quot;  # 给模型取个直观的名字


class BookInfo(models.Model):
    id = models.AutoField(primary_key=True)
    bookName = models.CharField(max_length=20)
    price = models.FloatField()
    publishDate = models.DateField()
    bookType = models.ForeignKey(BookTypeInfo, on_delete=models.PROTECT)

    class Meta:
        db_table = &quot;t_book&quot;
        verbose_name = &quot;图书信息&quot;  # 给模型取个直观的名字
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;模型字段类型如下：&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;AutoField:自增长类型，数据表的字段类型为整数，长度为11位。&lt;/li&gt;
&lt;li&gt;BigAutoField:自增长类型，数据表的字段类型为bigint，长度为20位。&lt;/li&gt;
&lt;li&gt;CharField:字符类型。&lt;/li&gt;
&lt;li&gt;BooleanField:布尔类型。&lt;/li&gt;
&lt;li&gt;CommaSeparatedIntegerField:用逗号分割的整数类型。DateField:日期( Date）类型。&lt;/li&gt;
&lt;li&gt;DateTimeField:日期时间( Datetime)类型。Decimal:十进制小数类型。&lt;/li&gt;
&lt;li&gt;EmailField:字符类型,存储邮箱格式的字符串。&lt;/li&gt;
&lt;li&gt;FloatField:浮点数类型，数据表的字段类型变成Double类型。IntegerField:整数类型，数据表的字段类型为11位的整数。&lt;/li&gt;
&lt;li&gt;BigIntegerField:长整数类型。&lt;/li&gt;
&lt;li&gt;IPAddressField:字符类型，存储Ipv4地址的字符串。&lt;/li&gt;
&lt;li&gt;GenericIPAddressField:字符类型，存储Ipv4和Ipv6地址的字符串。NullBooleanField:允许为空的布尔类型。&lt;/li&gt;
&lt;li&gt;PositiveIntegerFiel:正整数的整数类型。&lt;/li&gt;
&lt;li&gt;PositiveSmallIntegerField:小正整数类型，取值范围为0~32767。SlugField:字符类型，包含字母、数字、下画线和连字符的字符串。&lt;/li&gt;
&lt;li&gt;SmallIntegerField:小整数类型，取值范围为-32,768~+32,767。&lt;/li&gt;
&lt;li&gt;TextField:长文本类型。&lt;/li&gt;
&lt;li&gt;TimeField:时间类型，显示时分秒HH:MM[ :ss[.uuuuuu]]。URLField:字符类型，存储路由格式的字符串。&lt;/li&gt;
&lt;li&gt;BinaryField:二进制数据类型。&lt;/li&gt;
&lt;li&gt;FileField:字符类型，存储文件路径的字符串。ImageField:字符类型，存储图片路径的字符串。&lt;/li&gt;
&lt;li&gt;FilePathField:字符类型，从特定的文件目录选择某个文件。&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;模型字段参数如下：&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;
&lt;p&gt;verbose_name:默认为None，在 Admin站点管理设置字段的显示名称。&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;primary_key:默认为False，若为True，则将字段设置成主键。&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;max_length:默认为None，设置字段的最大长度。&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;unique:默认为False，若为True，则设置字段的唯一属性。&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;blank:默认为False，若为True，则字段允许为空值，数据库将存储空字符串。null:默认为False，若为True，则字段允许为空值，数据库表现为NULL。&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;db_index:默认为False，若为True，则以此字段来创建数据库索引。default:默认为NOT_PROVIDED对象，设置字段的默认值。&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;editable:默认为True，允许字段可编辑，用于设置Admin的新增数据的字段。serialize:默认为True，允许字段序列化，可将数据转化为JSON格式。&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;unique_for_date:默认为None，设置日期字段的唯一性。&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;unique_for_month:默认为None，设置日期字段月份的唯一性。unique_for_year:默认为None，设置日期字段年份的唯一性。choices:默认为空列表，设置字段的可选值。&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;help_text:默认为空字符串，用于设置表单的提示信息。&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;db_column:默认为None，设置数据表的列名称，若不设置，则将字段名作为数据表的列名。&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;db_tablespace:默认为None，如果字段已创建索引，那么数据库的表空间名称将作为该字段的索引名。注意:部分数据库不支持表空间。&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;auto_created:默认为False，若为True，则自动创建字段，用于一对一的关系模型。validators:默认为空列表,设置字段内容的验证函数。&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;error_messages:默认为None，设置错误提示。&lt;/p&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;ForeignKey方法参数如下：&lt;/p&gt;
&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;参数名&lt;/th&gt;
&lt;th&gt;参数说明&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;to&lt;/td&gt;
&lt;td&gt;指定关联的目标模型类。可以使用字符串表示模型类的路径，也可以直接使用模型类的引用。&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;on_delete&lt;/td&gt;
&lt;td&gt;指定当关联对象被删除时的行为。CASCADE、PROTECT、SET_NULL、SET_DEFAULT、SET0、DO_NOTHING。&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;related_name&lt;/td&gt;
&lt;td&gt;指定反向关联的名称，默认为模型类名_set。&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;to_field&lt;/td&gt;
&lt;td&gt;指定关联的目标模型类中用于关联的字段名称。默认为主键字段。&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;db_index&lt;/td&gt;
&lt;td&gt;如果为True，则在目标模型的关联字段上创建索引。&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;null&lt;/td&gt;
&lt;td&gt;指定关联字段是否可以为空。如果 null=True，则数据库中该字段将允许 NULL值。&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;blank&lt;/td&gt;
&lt;td&gt;指定关联字段是否可以为空。如果blank=True，则表单中该字段可以为空。&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;limit_choices_to&lt;/td&gt;
&lt;td&gt;指定关联对象的过滤条件。可以是一个字典、一个 QuerySet或一个函数。&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;verbose_name&lt;/td&gt;
&lt;td&gt;用于在 Django Admin后台中显示字段名称。&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;help_text&lt;/td&gt;
&lt;td&gt;用于在 Django Admin后台中显示帮助文本。&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;
&lt;p&gt;on_delete的models属性有下面设置选项；&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;CASCADE:这就是默认的选项，级联删除，你无需显性指定它。&lt;/li&gt;
&lt;li&gt;PROTECT: 保护模式，如果采用该选项，删除的时候，会抛出ProtectedError错误。&lt;/li&gt;
&lt;li&gt;SET_NULL: 置空模式，删除的时候，外键字段被设置为空，前提就是blank=True, null=True,定义该字段的时候，允许为空。&lt;/li&gt;
&lt;li&gt;SET_DEFAULT: 置默认值，删除的时候，外键字段设置为默认值，所以定义外键的时候注意加上一个默认值。&lt;/li&gt;
&lt;li&gt;SET(): 自定义一个值，该值当然只能是对应的实体了&lt;/li&gt;
&lt;/ul&gt;
&lt;h3&gt;Django5数据迁移&lt;/h3&gt;
&lt;p&gt;然后我们执行： &lt;code&gt;python manage.py makemigrations&lt;/code&gt; 生成数据库迁移文件&lt;/p&gt;
&lt;p&gt;所谓的迁移文件, 是类似模型类的迁移类,主要是描述了数据表结构的类文件；&lt;/p&gt;
&lt;p&gt;这个生成的迁移文件在migrations目录下；每执行一次，都会生成一个新文件。&lt;/p&gt;
&lt;p&gt;![image-20240324173810936](D:\开发\Python\Django\2024版 一周掌握Django5视频教程 python web开发.assets\image-20240324173810936.png)&lt;/p&gt;
&lt;p&gt;再执行：&lt;code&gt;python manage.py migrate&lt;/code&gt; 执行迁移文件，同步到数据库中；&lt;/p&gt;
&lt;p&gt;数据库里就会生成t_book和t_bookType两个表；&lt;/p&gt;
&lt;p&gt;![image-20240324173950279](D:\开发\Python\Django\2024版 一周掌握Django5视频教程 python web开发.assets\image-20240324173950279.png)&lt;/p&gt;
&lt;p&gt;最后我们再搞一些测试数据；&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;INSERT INTO `t_booktype` VALUES (1, &apos;计算机类&apos;);
INSERT INTO `t_booktype` VALUES (2, &apos;数学类&apos;);
&lt;/code&gt;&lt;/pre&gt;
&lt;pre&gt;&lt;code&gt;INSERT INTO `t_book` VALUES (1, &apos;Java编程思想&apos;, 100, &apos;2004-03-16&apos;, 1);
INSERT INTO `t_book` VALUES (2, &apos;Head First设计模式&apos;, 88, &apos;2020-03-16&apos;, 1);
INSERT INTO `t_book` VALUES (3, &apos;数学的秘密&apos;, 50, &apos;2019-03-06&apos;, 2);
&lt;/code&gt;&lt;/pre&gt;
&lt;h3&gt;Django5模型查询(上)&lt;/h3&gt;
&lt;p&gt;我们知道数据库设有多种数据查询方式，如单表查询、多表查询、子查询和联合查询等，而 Django 的ORM框架对不同的查询方式定义了相应的API方法。下面我们通过实例来深入学习下；&lt;/p&gt;
&lt;p&gt;我们来实现下图书信息的查询，顺便通过外键关联配置，把图书类别信息也级联查询出来。我们通过all()方法查询出所有图书信息；&lt;/p&gt;
&lt;p&gt;views.py里我们加下bookList方法：&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;def bookList(request):
    &quot;&quot;&quot;
    图书列表查询
    &quot;&quot;&quot;
    # 查询所有信息
    bookList = BookInfo.objects.all()
    print(bookList)
    content_value = {&quot;title&quot;: &quot;图书列表&quot;, &quot;bookList&quot;: bookList}
    return render(request, &apos;book/list.html&apos;, context=content_value)
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;urls.py里加下映射配置：&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;path(&apos;book/list&apos;, helloWorld.views.bookList)
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;templates下新建book目录，book目录下新建list.html&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;&amp;lt;!DOCTYPE html&amp;gt;
&amp;lt;html lang=&quot;en&quot;&amp;gt;
&amp;lt;head&amp;gt;
    &amp;lt;meta charset=&quot;UTF-8&quot;&amp;gt;
    &amp;lt;title&amp;gt;{{ title }}&amp;lt;/title&amp;gt;
&amp;lt;/head&amp;gt;
&amp;lt;body&amp;gt;
&amp;lt;h3&amp;gt;{{ title }}&amp;lt;/h3&amp;gt;
&amp;lt;table border=&quot;1&quot;&amp;gt;
    &amp;lt;tr&amp;gt;
        &amp;lt;th&amp;gt;编号&amp;lt;/th&amp;gt;
        &amp;lt;th&amp;gt;图书名称&amp;lt;/th&amp;gt;
        &amp;lt;th&amp;gt;价格&amp;lt;/th&amp;gt;
        &amp;lt;th&amp;gt;出版日期&amp;lt;/th&amp;gt;
        &amp;lt;th&amp;gt;图书类别&amp;lt;/th&amp;gt;
    &amp;lt;/tr&amp;gt;
    {百分号 for book in bookList %}
        &amp;lt;tr&amp;gt;
            &amp;lt;td&amp;gt;{{ book.id }}&amp;lt;/td&amp;gt;
            &amp;lt;td&amp;gt;{{ book.bookName }}&amp;lt;/td&amp;gt;
            &amp;lt;td&amp;gt;{{ book.price }}&amp;lt;/td&amp;gt;
            &amp;lt;td&amp;gt;{{ book.publishDate | date:&apos;Y-m-d&apos; }}&amp;lt;/td&amp;gt;
            &amp;lt;td&amp;gt;{{ book.bookType.bookTypeName }}&amp;lt;/td&amp;gt;
        &amp;lt;/tr&amp;gt;
    {百分号 endfor %}
&amp;lt;/table&amp;gt;
&amp;lt;/body&amp;gt;
&amp;lt;/html&amp;gt;
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;测试运行，浏览器输入：&lt;code&gt;http://127.0.0.1:8000/book/list&lt;/code&gt;&lt;/p&gt;
&lt;p&gt;![image-20240325160429583](D:\开发\Python\Django\2024版 一周掌握Django5视频教程 python web开发.assets\image-20240325160429583.png)&lt;/p&gt;
&lt;p&gt;查询出了所有的图书信息；&lt;/p&gt;
&lt;h3&gt;Django5模型查询(下)&lt;/h3&gt;
&lt;p&gt;前面实例我们用了ORM框架提供的all方法，查询所有数据。&lt;/p&gt;
&lt;p&gt;![image-20240327074851936](D:\开发\Python\Django\2024版 一周掌握Django5视频教程 python web开发.assets\image-20240327074851936.png)&lt;/p&gt;
&lt;p&gt;下面我们继续学习下ORM框架给我们提供的一些其他常用方法；&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;# 查询所有信息
bookList = BookInfo.objects.all()
# 获取数据集的第一条数据的bookName属性值
print(bookList[0].bookName) 
# 返回前2条数据 select * from t_book limit 2
bookList = BookInfo.objects.all()[:2]
# 查询指定字段
bookList = BookInfo.objects.values(&quot;bookName&quot;, &quot;price&quot;)
# 查询指定字段 数据以列表方式返回，列表元素以元组表示
bookList = BookInfo.objects.values_list(&quot;bookName&quot;, &quot;price&quot;)
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;ORM框架提供了get()方法，返回满足条件的单个数据：&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;# 获取单个对象，一般是根据id查询
book = BookInfo.objects.get(id=2)
print(book.bookName)
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;ORM框架提供了filter()方法，返回满足条件的数据：&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;# 返回满足条件id=2的数据，返回类型是列表
bookList = BookInfo.objects.filter(id=2)
bookList = BookInfo.objects.filter(price=100, id=1)
# filter的查询条件可以设置成字典格式
d = dict(price=100, id=1)
bookList = BookInfo.objects.filter(**d)
# SQL的or查询，需要引入Q，from django.db.models import Q
# 语法格式：Q(field=value)|Q(field=value) 多个Q之间用&quot;|&quot;隔开
bookList = BookInfo.objects.filter(Q(id=1) | Q(price=88))
# SQL的不等于查询，在Q查询中用“~”即可
# SQL select * from t_book where not (id=1)
bookList = BookInfo.objects.filter(~Q(id=1))
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;ORM框架提供了exclude()方法，返回不满足条件的数据：&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;# 也可以使用exclude 返回满足条件之外的数据 实现不等于查询
bookList = BookInfo.objects.exclude(id=1)
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;ORM框架提供了count()方法，返回满足查询条件后的数据量：&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;# 使用count()方法，返回满足查询条件后的数据量
t = BookInfo.objects.filter(id=2).count()
print(t)
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;ORM框架提供了distinct()方法，返回去重后的数据：&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;# distinct()方法，返回去重后的数据
bookList = BookInfo.objects.values(&apos;bookName&apos;).distinct()
print(bookList)
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;ORM框架提供了order_by()方法，对结果进行排序；默认是升序；如果需要降序，只需要在字段前面加“-”即可；&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;# 使用order_by设置排序
# bookList = BookInfo.objects.order_by(&quot;price&quot;)
bookList = BookInfo.objects.order_by(&quot;-id&quot;)
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;ORM框架提供了annotate方法来实现聚合查询，比如数据值求和，求平均值等。&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;# annotate类似于SQL里面的GROUP BY方法
# 如果不设置values，默认对主键进行GROUIP BY分组
# SQL: select bookType_id，SUM(price) AS &apos;price_sum&apos; from t_book GROUP BY bookType_id
r = BookInfo.objects.values(&apos;bookType&apos;).annotate(Sum(&apos;price&apos;))
# SQL: select bookType_id，AVG(price) AS &apos;price_sum&apos; from t_book GROUP BY bookType_id
r2 = BookInfo.objects.values(&apos;bookType&apos;).annotate(Avg(&apos;price&apos;))
&lt;/code&gt;&lt;/pre&gt;
&lt;h3&gt;Django5模型分页查询&lt;/h3&gt;
&lt;p&gt;在Django中实现分页通常使用&lt;code&gt;Paginator&lt;/code&gt;类。以下是一个简单的示例，展示了如何在Django视图中实现分页功能：&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;bookList = BookInfo.objects.all()
# Paginator(object_list ,per_page)
# object_list   结果集/列表
# per_page  每页多少条记录
p = Paginator(bookList, 2)
# 获取第几页的数据
bookListPage = p.page(2)
print(&quot;总记录数：&quot;, BookInfo.objects.count())
&lt;/code&gt;&lt;/pre&gt;
&lt;h3&gt;Django5高级查询匹配符&lt;/h3&gt;
&lt;p&gt;前面讲了开发中常用的数据查询方法，但有时需要设置不同的查询条件来满足多方面的查询要求。上述的查询条件 filter和 get是使用等值的方法来匹配结果。若想使用大于、不等于或模糊查询的匹配方法，则可在查询条件filter和 get里使用下表的匹配符实现。&lt;/p&gt;
&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;匹配符&lt;/th&gt;
&lt;th&gt;使用&lt;/th&gt;
&lt;th&gt;说明&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;__exact&lt;/td&gt;
&lt;td&gt;filter（job__exact=&apos;开发&apos;)&lt;/td&gt;
&lt;td&gt;精确等于，如SQL的like&apos;开发&apos;。&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;__iexact&lt;/td&gt;
&lt;td&gt;filter（job__iexact=&apos;开发&apos;）&lt;/td&gt;
&lt;td&gt;精确等于并忽略大小写。&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;__contains&lt;/td&gt;
&lt;td&gt;filter（job__contains=&apos;开发&apos;）&lt;/td&gt;
&lt;td&gt;模糊匹配，如SQL的like&apos;%荣耀%&apos;。&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;__icontains&lt;/td&gt;
&lt;td&gt;filter（job__icontains=&apos;开发&apos;）&lt;/td&gt;
&lt;td&gt;模糊匹配，忽略大小写。&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;__gt&lt;/td&gt;
&lt;td&gt;filter（job__gt=5）&lt;/td&gt;
&lt;td&gt;大于。&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;__gte&lt;/td&gt;
&lt;td&gt;filter（job__gte=5）&lt;/td&gt;
&lt;td&gt;大于等于。&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;__lt&lt;/td&gt;
&lt;td&gt;filter（job__lt=5）&lt;/td&gt;
&lt;td&gt;小于。&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;__lte&lt;/td&gt;
&lt;td&gt;filter（job__lte=5）&lt;/td&gt;
&lt;td&gt;小于等于。&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;__in&lt;/td&gt;
&lt;td&gt;filter（job__in=[1,2,3]）&lt;/td&gt;
&lt;td&gt;判断是否在列表内。&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;__startswith&lt;/td&gt;
&lt;td&gt;filter（job__startswith=&apos;开发&apos;）&lt;/td&gt;
&lt;td&gt;以。。。开头。&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;__istartswith&lt;/td&gt;
&lt;td&gt;filter（job__istartswith=&apos;开发&apos;）&lt;/td&gt;
&lt;td&gt;以。。。开头并忽略大小写。&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;__endswith&lt;/td&gt;
&lt;td&gt;filter（job__endswith=&apos;开发&apos;）&lt;/td&gt;
&lt;td&gt;以。。。结尾。&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;__iendswith&lt;/td&gt;
&lt;td&gt;filter（job__iendswith=&apos;开发&apos;）&lt;/td&gt;
&lt;td&gt;以。。。结尾并忽略大小写。&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;__range&lt;/td&gt;
&lt;td&gt;filter（job__range=&apos;开发&apos;）&lt;/td&gt;
&lt;td&gt;在。。。范围内。&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;__year&lt;/td&gt;
&lt;td&gt;filter（job__year=&apos;2018&apos;）&lt;/td&gt;
&lt;td&gt;日期字段的年份。&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;__month&lt;/td&gt;
&lt;td&gt;filter（job__month=&apos;12&apos;）&lt;/td&gt;
&lt;td&gt;日期字段的月份。&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;__day&lt;/td&gt;
&lt;td&gt;filter（job__day=30）&lt;/td&gt;
&lt;td&gt;日期字段的天数。&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;__isnull&lt;/td&gt;
&lt;td&gt;filter（job__isnull=True/False）&lt;/td&gt;
&lt;td&gt;判断是否为空。&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;
&lt;p&gt;实例代码：&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;# 模糊查询图书名称含有&quot;编程&quot;的所有数据
# bookList = BookInfo.objects.filter(bookName__contains=&apos;编程&apos;)
# 查询图书价格大于等于50的所有数据
bookList = BookInfo.objects.filter(price__gte=50)
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;在查询数据时可以使用查询条件get或filter实现，但是两者的执行过程存在一定的差异，说明如下。&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;查询条件get:查询字段必须是主键或者唯一约束的字段，并且查询的数据必须存在，如果查询的字段有重复值或者查询的数据不存在，程序就会抛出异常信息。&lt;/li&gt;
&lt;li&gt;查询条件filter:查询字段没有限制，只要该字段是数据表的某一字段即可。查询结果以列表形式返回，如果查询结果为空（查询的数据在数据表中找不到)，就返回空列表。&lt;/li&gt;
&lt;/ul&gt;
&lt;h3&gt;Django5模型多表查询&lt;/h3&gt;
&lt;p&gt;我们在日常的开发中，常常需要对多张数据表同时进行数据查询。多表查询需要在数据表之间建立表关系才能够实现。一对多或一对一的表关系是通过外键实现关联的，而多表查询分为正向查询和反向查询。&lt;/p&gt;
&lt;p&gt;以模型BookInfo和BokkTypeInfo为例，如果查询主题是BookInfo，通过外键bookType_id去查询BooKTypeInfo的关联数据，那么该查询称为正向查询；如果查询对象的主题是模型BookTypeInfo，要查询它与模型BookInfo的关联数据，那么该查询称为反向查询；&lt;/p&gt;
&lt;p&gt;下面是一个实例：&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;def bookList2(request):
    &quot;&quot;&quot;
    多表查询  正常查询 和反向查询
    :param request:
    :return:
    &quot;&quot;&quot;
    # 正向查询
    book: BookInfo = BookInfo.objects.filter(id=2).first()
    print(book.bookType.bookTypeName)

    # 反向查询
    bookType = BookTypeInfo.objects.filter(id=1).first()
    print(bookType.bookinfo_set.first().bookName)
    print(bookType.bookinfo_set.all())

    content_value = {&quot;title&quot;: &quot;图书列表&quot;}
    return render(request, &apos;book/list.html&apos;)
&lt;/code&gt;&lt;/pre&gt;
&lt;h3&gt;Django5模型数据新增&lt;/h3&gt;
&lt;p&gt;Django对数据库的数据进行增、删、改操作是借助内置ORM框架所提供的API方法实现的,简单来说，它在模型基础类 Model里定义数据操作方法，通过类继承将这些操作方法传给开发者自定义的模型对象，再由模型对象调用即可实现数据操作。&lt;/p&gt;
&lt;p&gt;添加操作通过模型的save方法实现，添加下可以返回主键id值。&lt;/p&gt;
&lt;p&gt;我们在前面实例的基础上，来实现这个例子。&lt;/p&gt;
&lt;p&gt;因为添加页面是需要图书类别的数据，我们用下拉框实现。所以这里需要一个预处理操作。&lt;/p&gt;
&lt;p&gt;先在views.py里定义一个添加预处理方法preAdd&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;def preAdd(request):
    &quot;&quot;&quot;
    预处理，添加操作
    :param request:
    :return:
    &quot;&quot;&quot;
    bookTypeList = BookTypeInfo.objects.all()
    print(bookTypeList)
    content_value = {&quot;title&quot;: &quot;图书添加&quot;, &quot;bookTypeList&quot;: bookTypeList}
    return render(request, &apos;book/add.html&apos;, context=content_value)
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;urls.py里加下映射：&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;path(&apos;book/preAdd&apos;, helloWorld.views.preAdd),
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;原先的list.html，加上添加的链接：&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;&amp;lt;!DOCTYPE html&amp;gt;
&amp;lt;html lang=&quot;en&quot;&amp;gt;
&amp;lt;head&amp;gt;
    &amp;lt;meta charset=&quot;UTF-8&quot;&amp;gt;
    &amp;lt;title&amp;gt;{{ title }}&amp;lt;/title&amp;gt;
&amp;lt;/head&amp;gt;
&amp;lt;body&amp;gt;
&amp;lt;h3&amp;gt;{{ title }}&amp;lt;/h3&amp;gt;
&amp;lt;a href=&quot;/book/preAdd&quot;&amp;gt;添加&amp;lt;/a&amp;gt;&amp;lt;br/&amp;gt;&amp;lt;br/&amp;gt;
&amp;lt;table border=&quot;1&quot;&amp;gt;
    &amp;lt;tr&amp;gt;
        &amp;lt;th&amp;gt;编号&amp;lt;/th&amp;gt;
        &amp;lt;th&amp;gt;图书名称&amp;lt;/th&amp;gt;
        &amp;lt;th&amp;gt;价格&amp;lt;/th&amp;gt;
        &amp;lt;th&amp;gt;出版日期&amp;lt;/th&amp;gt;
        &amp;lt;th&amp;gt;图书类别&amp;lt;/th&amp;gt;
    &amp;lt;/tr&amp;gt;
    {百分号 for book in bookList %}
        &amp;lt;tr&amp;gt;
            &amp;lt;td&amp;gt;{{ book.id }}&amp;lt;/td&amp;gt;
            &amp;lt;td&amp;gt;{{ book.bookName }}&amp;lt;/td&amp;gt;
            &amp;lt;td&amp;gt;{{ book.price }}&amp;lt;/td&amp;gt;
            &amp;lt;td&amp;gt;{{ book.publishDate | date:&apos;Y-m-d&apos; }}&amp;lt;/td&amp;gt;
            &amp;lt;td&amp;gt;{{ book.bookType.bookTypeName }}&amp;lt;/td&amp;gt;
        &amp;lt;/tr&amp;gt;
    {百分号 endfor %}
&amp;lt;/table&amp;gt;
&amp;lt;/body&amp;gt;
&amp;lt;/html&amp;gt;
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;再创建下图书添加页面add.html：&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;&amp;lt;!DOCTYPE html&amp;gt;
&amp;lt;html lang=&quot;en&quot;&amp;gt;
&amp;lt;head&amp;gt;
    &amp;lt;meta charset=&quot;UTF-8&quot;&amp;gt;
    &amp;lt;title&amp;gt;{{ title }}&amp;lt;/title&amp;gt;
&amp;lt;/head&amp;gt;
&amp;lt;body&amp;gt;
&amp;lt;h3&amp;gt;{{ title }}&amp;lt;/h3&amp;gt;
&amp;lt;form action=&quot;/book/add&quot; method=&quot;post&quot;&amp;gt;
    {百分号 csrf_token %}
    &amp;lt;table&amp;gt;
        &amp;lt;tr&amp;gt;
            &amp;lt;td&amp;gt;图书名称：&amp;lt;/td&amp;gt;
            &amp;lt;td&amp;gt;
                &amp;lt;input type=&quot;text&quot; name=&quot;bookName&quot;&amp;gt;
            &amp;lt;/td&amp;gt;
        &amp;lt;/tr&amp;gt;
        &amp;lt;tr&amp;gt;
            &amp;lt;td&amp;gt;出版日期：&amp;lt;/td&amp;gt;
            &amp;lt;td&amp;gt;
                &amp;lt;input type=&quot;text&quot; name=&quot;publishDate&quot;&amp;gt;
            &amp;lt;/td&amp;gt;
        &amp;lt;/tr&amp;gt;
        &amp;lt;tr&amp;gt;
            &amp;lt;td&amp;gt;图书类别：&amp;lt;/td&amp;gt;
            &amp;lt;td&amp;gt;
                &amp;lt;select name=&quot;bookType_id&quot;&amp;gt;
                    {百分号 for bookType in bookTypeList %}
                        &amp;lt;option value=&quot;{{ bookType.id }}&quot;&amp;gt;{{ bookType.bookTypeName }}&amp;lt;/option&amp;gt;
                    {百分号 endfor %}
                &amp;lt;/select&amp;gt;
            &amp;lt;/td&amp;gt;
        &amp;lt;/tr&amp;gt;
        &amp;lt;tr&amp;gt;
            &amp;lt;td&amp;gt;图书价格：&amp;lt;/td&amp;gt;
            &amp;lt;td&amp;gt;
                &amp;lt;input type=&quot;text&quot; name=&quot;price&quot;&amp;gt;
            &amp;lt;/td&amp;gt;
        &amp;lt;/tr&amp;gt;
        &amp;lt;tr&amp;gt;
            &amp;lt;td colspan=&quot;2&quot;&amp;gt;
                &amp;lt;input type=&quot;submit&quot; value=&quot;提交&quot;&amp;gt;
            &amp;lt;/td&amp;gt;
        &amp;lt;/tr&amp;gt;
    &amp;lt;/table&amp;gt;
&amp;lt;/form&amp;gt;
&amp;lt;/body&amp;gt;
&amp;lt;/html&amp;gt;
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;最后在views.py里创建图书添加函数add：&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;def add(request):
    &quot;&quot;&quot;
    图书添加
    :param request:
    :return:
    &quot;&quot;&quot;
    # print(request.POST.get(&quot;bookName&quot;))
    # print(request.POST.get(&quot;publishDate&quot;))
    # print(request.POST.get(&quot;bookType_id&quot;))
    # print(request.POST.get(&quot;price&quot;))
    book = BookInfo()
    book.bookName = request.POST.get(&quot;bookName&quot;)
    book.publishDate = request.POST.get(&quot;publishDate&quot;)
    book.bookType_id = request.POST.get(&quot;bookType_id&quot;)
    book.price = request.POST.get(&quot;price&quot;)
    book.save()
    # 数据添加后，获取新增数据的主键id
    print(book.id)
    return bookList(request)
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;运行测试：浏览器输入：&lt;code&gt;http://127.0.0.1:8000/book/list&lt;/code&gt;&lt;/p&gt;
&lt;p&gt;![image-20240417214023951](D:\开发\Python\Django\2024版 一周掌握Django5视频教程 python web开发.assets\image-20240417214023951.png)&lt;/p&gt;
&lt;p&gt;点击添加链接，&lt;/p&gt;
&lt;p&gt;![image-20240417214111826](D:\开发\Python\Django\2024版 一周掌握Django5视频教程 python web开发.assets\image-20240417214111826.png)&lt;/p&gt;
&lt;p&gt;进入图书添加页面，输入图书信息，点提交：&lt;/p&gt;
&lt;p&gt;![image-20240417214143567](D:\开发\Python\Django\2024版 一周掌握Django5视频教程 python web开发.assets\image-20240417214143567.png)&lt;/p&gt;
&lt;p&gt;页面显示最新数据；&lt;/p&gt;
&lt;h3&gt;Django5模型数据修改&lt;/h3&gt;
&lt;p&gt;模型数据修改和添加都是用的save方法。&lt;/p&gt;
&lt;p&gt;我们结合案例先实现下；&lt;/p&gt;
&lt;p&gt;我们在views.py里先定义preUpdate方法，修改预处理，根据id获取图书信息，以及获取图书类别列表；&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;def preUpdate(request, id):
    &quot;&quot;&quot;
    预处理，修改操作
    :param request:
    :return:
    &quot;&quot;&quot;
    print(&quot;id:&quot;, id)
    book = BookInfo.objects.get(id=id)
    print(book)
    bookTypeList = BookTypeInfo.objects.all()
    print(bookTypeList)
    content_value = {&quot;title&quot;: &quot;图书修改&quot;, &quot;bookTypeList&quot;: bookTypeList, &quot;book&quot;: book}
    return render(request, &apos;book/edit.html&apos;, context=content_value)
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;urls.py里加下映射：&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;path(&apos;book/preUpdate/&amp;lt;int:id&amp;gt;&apos;, helloWorld.views.preUpdate),
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;book/list.html修改下，加下修改操作链接：&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;&amp;lt;!DOCTYPE html&amp;gt;
&amp;lt;html lang=&quot;en&quot;&amp;gt;
&amp;lt;head&amp;gt;
    &amp;lt;meta charset=&quot;UTF-8&quot;&amp;gt;
    &amp;lt;title&amp;gt;{{ title }}&amp;lt;/title&amp;gt;
&amp;lt;/head&amp;gt;
&amp;lt;body&amp;gt;
&amp;lt;h3&amp;gt;{{ title }}&amp;lt;/h3&amp;gt;
&amp;lt;a href=&quot;/book/preAdd&quot;&amp;gt;添加&amp;lt;/a&amp;gt;&amp;lt;br/&amp;gt;&amp;lt;br/&amp;gt;
&amp;lt;table border=&quot;1&quot;&amp;gt;
    &amp;lt;tr&amp;gt;
        &amp;lt;th&amp;gt;编号&amp;lt;/th&amp;gt;
        &amp;lt;th&amp;gt;图书名称&amp;lt;/th&amp;gt;
        &amp;lt;th&amp;gt;价格&amp;lt;/th&amp;gt;
        &amp;lt;th&amp;gt;出版日期&amp;lt;/th&amp;gt;
        &amp;lt;th&amp;gt;图书类别&amp;lt;/th&amp;gt;
        &amp;lt;th&amp;gt;操作&amp;lt;/th&amp;gt;
    &amp;lt;/tr&amp;gt;
    {百分号 for book in bookList %}
        &amp;lt;tr&amp;gt;
            &amp;lt;td&amp;gt;{{ book.id }}&amp;lt;/td&amp;gt;
            &amp;lt;td&amp;gt;{{ book.bookName }}&amp;lt;/td&amp;gt;
            &amp;lt;td&amp;gt;{{ book.price }}&amp;lt;/td&amp;gt;
            &amp;lt;td&amp;gt;{{ book.publishDate | date:&apos;Y-m-d&apos; }}&amp;lt;/td&amp;gt;
            &amp;lt;td&amp;gt;{{ book.bookType.bookTypeName }}&amp;lt;/td&amp;gt;
            &amp;lt;td&amp;gt;
                &amp;lt;a href=&quot;/book/preUpdate/{{ book.id }}&quot;&amp;gt;修改&amp;lt;/a&amp;gt;
            &amp;lt;/td&amp;gt;
        &amp;lt;/tr&amp;gt;
    {百分号 endfor %}
&amp;lt;/table&amp;gt;
&amp;lt;/body&amp;gt;
&amp;lt;/html&amp;gt;
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;新建编辑页面edit.html&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;&amp;lt;!DOCTYPE html&amp;gt;
&amp;lt;html lang=&quot;en&quot;&amp;gt;
&amp;lt;head&amp;gt;
    &amp;lt;meta charset=&quot;UTF-8&quot;&amp;gt;
    &amp;lt;title&amp;gt;{{ title }}&amp;lt;/title&amp;gt;
&amp;lt;/head&amp;gt;
&amp;lt;body&amp;gt;
&amp;lt;h3&amp;gt;{{ title }}&amp;lt;/h3&amp;gt;
&amp;lt;form action=&quot;/book/update&quot; method=&quot;post&quot;&amp;gt;
    {百分号 csrf_token %}
    &amp;lt;table&amp;gt;
        &amp;lt;tr&amp;gt;
            &amp;lt;td&amp;gt;图书名称：&amp;lt;/td&amp;gt;
            &amp;lt;td&amp;gt;
                &amp;lt;input type=&quot;text&quot; name=&quot;bookName&quot; value=&quot;{{ book.bookName }}&quot;&amp;gt;
            &amp;lt;/td&amp;gt;
        &amp;lt;/tr&amp;gt;
        &amp;lt;tr&amp;gt;
            &amp;lt;td&amp;gt;出版日期：&amp;lt;/td&amp;gt;
            &amp;lt;td&amp;gt;
                &amp;lt;input type=&quot;text&quot; name=&quot;publishDate&quot; value=&quot;{{ book.publishDate | date:&apos;Y-m-d&apos; }}&quot;&amp;gt;
            &amp;lt;/td&amp;gt;
        &amp;lt;/tr&amp;gt;
        &amp;lt;tr&amp;gt;
            &amp;lt;td&amp;gt;图书类别：&amp;lt;/td&amp;gt;
            &amp;lt;td&amp;gt;
                &amp;lt;select name=&quot;bookType_id&quot;&amp;gt;
                    {百分号 for bookType in bookTypeList %}
                        &amp;lt;option value=&quot;{{ bookType.id }}&quot;
                                {百分号 if book.bookType.id == bookType.id %}selected{百分号 endif %}&amp;gt;
                            {{ bookType.bookTypeName }}
                        &amp;lt;/option&amp;gt;
                    {百分号 endfor %}
                &amp;lt;/select&amp;gt;
            &amp;lt;/td&amp;gt;
        &amp;lt;/tr&amp;gt;
        &amp;lt;tr&amp;gt;
            &amp;lt;td&amp;gt;图书价格：&amp;lt;/td&amp;gt;
            &amp;lt;td&amp;gt;
                &amp;lt;input type=&quot;text&quot; name=&quot;price&quot; value=&quot;{{ book.price }}&quot;&amp;gt;
            &amp;lt;/td&amp;gt;
        &amp;lt;/tr&amp;gt;
        &amp;lt;tr&amp;gt;
            &amp;lt;td colspan=&quot;2&quot;&amp;gt;
                &amp;lt;input type=&quot;hidden&quot; name=&quot;id&quot; value=&quot;{{ book.id }}&quot;&amp;gt;
                &amp;lt;input type=&quot;submit&quot; value=&quot;提交&quot;&amp;gt;
            &amp;lt;/td&amp;gt;
        &amp;lt;/tr&amp;gt;
    &amp;lt;/table&amp;gt;
&amp;lt;/form&amp;gt;
&amp;lt;/body&amp;gt;
&amp;lt;/html&amp;gt;
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;再写一个update方法，保存图书信息&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;def update(request):
    &quot;&quot;&quot;
    图书修改
    :param request:
    :return:
    &quot;&quot;&quot;
    book = BookInfo()
    book.id = request.POST.get(&quot;id&quot;)
    book.bookName = request.POST.get(&quot;bookName&quot;)
    book.publishDate = request.POST.get(&quot;publishDate&quot;)
    book.bookType_id = request.POST.get(&quot;bookType_id&quot;)
    book.price = request.POST.get(&quot;price&quot;)
    book.save()
    return bookList(request)
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;urls.py里再加下映射：&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;path(&apos;book/update&apos;, helloWorld.views.update),
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;我们来测试下，浏览器输入：&lt;code&gt;http://127.0.0.1:8000/book/list&lt;/code&gt;&lt;/p&gt;
&lt;p&gt;![image-20240420161721347](D:\开发\Python\Django\2024版 一周掌握Django5视频教程 python web开发.assets\image-20240420161721347.png)&lt;/p&gt;
&lt;p&gt;我们点修改，修改id是5的图书，&lt;/p&gt;
&lt;p&gt;![image-20240420162225595](D:\开发\Python\Django\2024版 一周掌握Django5视频教程 python web开发.assets\image-20240420162225595.png)&lt;/p&gt;
&lt;p&gt;修改信息后，点提交；&lt;/p&gt;
&lt;p&gt;![image-20240420162256057](D:\开发\Python\Django\2024版 一周掌握Django5视频教程 python web开发.assets\image-20240420162256057.png)&lt;/p&gt;
&lt;p&gt;测试成功！&lt;/p&gt;
&lt;h3&gt;Django5模型数据删除&lt;/h3&gt;
&lt;p&gt;Django5 ORM框架提供了delete()方法来实现数据删除操作，下面是一些常用的方式，删除所有数据，删除指定id数据，根据filter条件删除删除。&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;# 删除所有数据
BookInfo.objects.all().delete()
# 删除指定id数据
BookInfo.objects.get(id=1).delete()
# 根据条件删除多条数据
BookInfo.objects.filter(price__gte=90).delete()
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;我们来完善下前面的实例：&lt;/p&gt;
&lt;p&gt;views.py里先定义delete方法。&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;def delete(request, id):
    &quot;&quot;&quot;
    图书删除
    :param request:
    :return:
    &quot;&quot;&quot;
    # 删除所有数据
    # BookInfo.objects.all().delete()
    # 删除指定id数据
    BookInfo.objects.get(id=id).delete()
    # 根据条件删除多条数据
    # BookInfo.objects.filter(price__gte=90).delete()
    return bookList(request)
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;urls.py里加下映射：&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;path(&apos;book/delete/&amp;lt;int:id&amp;gt;&apos;, helloWorld.views.delete),
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;book/list.html里加下删除操作：&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;&amp;lt;!DOCTYPE html&amp;gt;
&amp;lt;html lang=&quot;en&quot;&amp;gt;
&amp;lt;head&amp;gt;
    &amp;lt;meta charset=&quot;UTF-8&quot;&amp;gt;
    &amp;lt;title&amp;gt;{{ title }}&amp;lt;/title&amp;gt;
&amp;lt;/head&amp;gt;
&amp;lt;body&amp;gt;
&amp;lt;h3&amp;gt;{{ title }}&amp;lt;/h3&amp;gt;
&amp;lt;a href=&quot;/book/preAdd&quot;&amp;gt;添加&amp;lt;/a&amp;gt;&amp;lt;br/&amp;gt;&amp;lt;br/&amp;gt;
&amp;lt;table border=&quot;1&quot;&amp;gt;
    &amp;lt;tr&amp;gt;
        &amp;lt;th&amp;gt;编号&amp;lt;/th&amp;gt;
        &amp;lt;th&amp;gt;图书名称&amp;lt;/th&amp;gt;
        &amp;lt;th&amp;gt;价格&amp;lt;/th&amp;gt;
        &amp;lt;th&amp;gt;出版日期&amp;lt;/th&amp;gt;
        &amp;lt;th&amp;gt;图书类别&amp;lt;/th&amp;gt;
        &amp;lt;th&amp;gt;操作&amp;lt;/th&amp;gt;
    &amp;lt;/tr&amp;gt;
    {百分号 for book in bookList %}
        &amp;lt;tr&amp;gt;
            &amp;lt;td&amp;gt;{{ book.id }}&amp;lt;/td&amp;gt;
            &amp;lt;td&amp;gt;{{ book.bookName }}&amp;lt;/td&amp;gt;
            &amp;lt;td&amp;gt;{{ book.price }}&amp;lt;/td&amp;gt;
            &amp;lt;td&amp;gt;{{ book.publishDate | date:&apos;Y-m-d&apos; }}&amp;lt;/td&amp;gt;
            &amp;lt;td&amp;gt;{{ book.bookType.bookTypeName }}&amp;lt;/td&amp;gt;
            &amp;lt;td&amp;gt;
                &amp;lt;a href=&quot;/book/preUpdate/{{ book.id }}&quot;&amp;gt;修改&amp;lt;/a&amp;gt;
                &amp;lt;a href=&quot;/book/delete/{{ book.id }}&quot;&amp;gt;删除&amp;lt;/a&amp;gt;
            &amp;lt;/td&amp;gt;
        &amp;lt;/tr&amp;gt;
    {百分号 endfor %}
&amp;lt;/table&amp;gt;
&amp;lt;/body&amp;gt;
&amp;lt;/html&amp;gt;
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;我们测试下，浏览器输入 &lt;code&gt;http://127.0.0.1:8000/book/list&lt;/code&gt;&lt;/p&gt;
&lt;p&gt;![image-20240422120831581](D:\开发\Python\Django\2024版 一周掌握Django5视频教程 python web开发.assets\image-20240422120831581.png)&lt;/p&gt;
&lt;p&gt;点击删除链接，&lt;/p&gt;
&lt;p&gt;![image-20240422120843649](D:\开发\Python\Django\2024版 一周掌握Django5视频教程 python web开发.assets\image-20240422120843649.png)&lt;/p&gt;
&lt;p&gt;数据直接被删除，说明测试成功！&lt;/p&gt;
&lt;h3&gt;Django5 ORM执行SQL语句&lt;/h3&gt;
&lt;p&gt;Django在查询数据时，大多数查询都能使用ORM提供的API方法，但对于一些复杂的查询可能难以使用ORM的API方法实现，因此Django引入了SQL语句的执行方法，有以下3种实现方法。&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;extra:结果集修改器，一种提供额外查询参数的机制。&lt;/li&gt;
&lt;li&gt;raw:执行原始SQL并返回模型实例对象。&lt;/li&gt;
&lt;li&gt;execute:直接执行自定义SQL。&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;extra适合用于ORM难以实现的查询条件，将查询条件使用原生SQL语法实现，此方法需要依靠模型对象，在某程度上可防止SQL注入。它一共定义了6个参数，每个参数说明如下：&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;select:添加新的查询字段，即新增并定义模型之外的字段。where:设置查询条件。&lt;/li&gt;
&lt;li&gt;params:如果where设置了字符串格式化%s，那么该参数为where提供数值。tables:连接其他数据表，实现多表查询。&lt;/li&gt;
&lt;li&gt;order_by:设置数据的排序方式。&lt;/li&gt;
&lt;li&gt;select_params:如果select设置字符串格式化%s，那么该参数为select提供数值。&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;参考实例：&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;bookList = BookInfo.objects.extra(where=[&quot;price&amp;gt;%s&quot;], params=[90])
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;raw只能实现数据查询操作，并且也要依靠模型对象，它一共定义了4个参数，每个参数说明如下：&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;raw_query: sQL语句。&lt;/li&gt;
&lt;li&gt;params:如果raw_query设置字符串格式化%s，那么该参数为raw_query提供数值。&lt;/li&gt;
&lt;li&gt;translations:为查询的字段设置别名。&lt;/li&gt;
&lt;li&gt;using:数据库对象，即 Django 所连接的数据库。&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;参考实例：&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;bookList = BookInfo.objects.raw(&quot;select * from t_book where price&amp;gt;%s&quot;, params=[90])
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;execute的语法，它执行SQL语句无须经过Django的ORM框架。我们知道Django连接数据库需要借助第三方模块实现连接过程，如 MySQL的mysqlclient模块和SQLite 的sqlite3模块等，这些模块连接数据库之后，可通过游标的方式来执行SQL语句，而 execute就是使用这种方式执行SQL语句，实例如下:&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;cursor: CursorDebugWrapper = connection.cursor()
cursor.execute(&quot;select count(*) from t_book where price&amp;gt;90&quot;)
print(cursor.fetchone())
&lt;/code&gt;&lt;/pre&gt;
&lt;h3&gt;Django5 ORM数据库事务&lt;/h3&gt;
&lt;p&gt;事务是指作为单个逻辑执行的一系列操作，这些操作具有原子性，即这些操作要么完全执行，要么完全不执行。事务处理可以确保事务性单元内的所有操作都成功完成,否则不会执行数据操作。
事务应该具有4个属性:原子性（Atomicity)、一致性（Consistency)、隔离性(Isolation),持久性(Durability)，这4个属性通常称为ACID特性，说明如下。&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;原子性:一个事务是一个不可分割的工作单位，事务中包括的操作要么都做，要么都不做。&lt;/li&gt;
&lt;li&gt;一致性:事务必须使数据库从某个一致性状态变到另一个一致性状态，一致性与原子性是密切相关的。&lt;/li&gt;
&lt;li&gt;隔离性:一个事务的执行不能被其他事务干扰，即一个事务内部的操作及使用的数据对其他事务是隔离的，各个事务之间不能互相干扰。&lt;/li&gt;
&lt;li&gt;持久性:持久性也称永久性(Permanence)，指一个事务一旦提交，它对数据库中数据的改变应该是永久性的，其他操作或故障不应该对其有任何影响。&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;我们先来做一个用户转账的事例：&lt;/p&gt;
&lt;p&gt;models.py下新建AccountInfo账户信息模型&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;class AccountInfo(models.Model):
    user = models.CharField(max_length=20)
    account = models.FloatField()

    class Meta:
        db_table = &quot;t_account&quot;
        verbose_name = &quot;用户账户信息&quot;  # 给模型取个直观的名字
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;然后我们执行： &lt;code&gt;python manage.py makemigrations&lt;/code&gt; 生成数据库迁移文件，再执行：&lt;code&gt;python manage.py migrate&lt;/code&gt; 执行迁移文件，同步到数据库中；生成了t_account表&lt;/p&gt;
&lt;p&gt;![image-20240429214452680](D:\开发\Python\Django\2024版 一周掌握Django5视频教程 python web开发.assets\image-20240429214452680.png)&lt;/p&gt;
&lt;p&gt;表里，我们加两个测试数据：&lt;/p&gt;
&lt;p&gt;![image-20240429214512475](D:\开发\Python\Django\2024版 一周掌握Django5视频教程 python web开发.assets\image-20240429214512475.png)&lt;/p&gt;
&lt;p&gt;views.py里，我们写一个测试转账测试实例：&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;def transfer2(request):
    &quot;&quot;&quot;
    模拟转账
    :param request:
    :return:
    &quot;&quot;&quot;
    a1 = AccountInfo.objects.filter(user=&apos;张三&apos;)
    a1.update(account=F(&apos;account&apos;) + 100)
    a2 = AccountInfo.objects.filter(user=&apos;李四&apos;)
    a2.update(account=F(&apos;account&apos;) - 100)
    return HttpResponse(&quot;OK&quot;)
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;urls.py里配置下映射：&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;path(&apos;transfer2/&apos;, helloWorld.views.transfer2),
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;测试，浏览器输入：&lt;code&gt;http://127.0.0.1:8000/transfer2/&lt;/code&gt;，测试成功。&lt;/p&gt;
&lt;p&gt;![image-20240429215125253](D:\开发\Python\Django\2024版 一周掌握Django5视频教程 python web开发.assets\image-20240429215125253.png)&lt;/p&gt;
&lt;p&gt;这里涉及到2个数据库操作，假如第二个数据库操作失败，那张三就多了100，但是李四钱没少，这时候金融账务就对不上了，除了问题。&lt;/p&gt;
&lt;p&gt;我们来模拟下吧，转出代码，除以0：&lt;/p&gt;
&lt;p&gt;![image-20240429220203045](D:\开发\Python\Django\2024版 一周掌握Django5视频教程 python web开发.assets\image-20240429220203045.png)&lt;/p&gt;
&lt;p&gt;再运行测试下，页面报错，&lt;/p&gt;
&lt;p&gt;![image-20240429220300096](D:\开发\Python\Django\2024版 一周掌握Django5视频教程 python web开发.assets\image-20240429220300096.png)&lt;/p&gt;
&lt;p&gt;李四的钱也没被扣，&lt;/p&gt;
&lt;p&gt;![image-20240429220320889](D:\开发\Python\Django\2024版 一周掌握Django5视频教程 python web开发.assets\image-20240429220320889.png)&lt;/p&gt;
&lt;p&gt;这时候，Django5提供的事务功能就派上用场了。&lt;/p&gt;
&lt;p&gt;Django5主要有4个事务方法：&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;atomic():在视图函数或视图类里使用事务。&lt;/li&gt;
&lt;li&gt;savepoint():开启事务。&lt;/li&gt;
&lt;li&gt;savepoint_rollback():回滚事务。&lt;/li&gt;
&lt;li&gt;savepoint_commit():提交事务。&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;我们用上事务，改下代码：&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;@transaction.atomic
def transfer(request):
    &quot;&quot;&quot;
    模拟转账
    :param request:
    :return:
    &quot;&quot;&quot;
    # 开启事务
    sid = transaction.savepoint()
    try:
        a1 = AccountInfo.objects.filter(user=&apos;张三&apos;)
        a1.update(account=F(&apos;account&apos;) + 100)
        a2 = AccountInfo.objects.filter(user=&apos;李四&apos;)
        a2.update(account=F(&apos;account&apos;) - 100 / 0)
        # 提交事务 （如不设置，当程序执行完成后，会自动提交事务）
        transaction.savepoint_commit(sid)
    except Exception as e:
        print(&quot;异常信息：&quot;, e)
        # 事务回滚
        transaction.savepoint_rollback(sid)
    return HttpResponse(&quot;OK&quot;)
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;我们再测试下，发现回滚了，钱都没有变化；&lt;/p&gt;
&lt;h2&gt;Django5表单定义与使用&lt;/h2&gt;
&lt;p&gt;表单是搜集用户数据信息的各种表单元素的集合，其作用是实现网页上的数据交互，比如用户在网站输入数据信息，然后提交到网站服务器端进行处理（如数据录入和用户登录注册等）。
网页表单是Web开发的一项基本功能，Django5的表单功能由Form类实现，主要分为两种:django.forms.Form和 django.forms.ModelForm。前者是一个基础的表单功能，后者是在前者的基础上结合模型所生成的数据表单。&lt;/p&gt;
&lt;h3&gt;Django5 Form表单定义与使用&lt;/h3&gt;
&lt;p&gt;首先forms.py里定义下BookInfoForm类&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;class BookInfoForm(Form):
    &quot;&quot;&quot;
    图书表单
    &quot;&quot;&quot;
    bookName = forms.CharField(max_length=20, label=&quot;图书名称&quot;)
    price = forms.FloatField(label=&quot;图书价格&quot;)
    publishDate = forms.DateField(label=&quot;出版日期&quot;)
    # 获取图书类别列表
    bookTypeList = BookTypeInfo.objects.values()
    # 图书类别以下拉框形式显示，下拉框选项id是图书类别Id，下拉框选项文本是图书类别名称
    choices = [(v[&apos;id&apos;], v[&apos;bookTypeName&apos;]) for v, v in enumerate(bookTypeList)]
    bookType_id = forms.ChoiceField(required=False, choices=choices, label=&quot;图书类别&quot;)
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;views.py下新建preAdd2方法&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;def preAdd2(request):
    &quot;&quot;&quot;
    预处理，添加操作 使用form表单
    :param request:
    :return:
    &quot;&quot;&quot;
    form = BookInfoForm()
    context_value = {&quot;title&quot;: &quot;图书添加&quot;, &quot;form&quot;: form}
    return render(request, &apos;book/add2.html&apos;, context_value)
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;urls.py加下映射：&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;path(&apos;book/preAdd2&apos;, helloWorld.views.preAdd2),
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;book/list.html加一个新的添加链接：&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;&amp;lt;a href=&quot;/book/preAdd2&quot;&amp;gt;添加(使用form)&amp;lt;/a&amp;gt;&amp;lt;br/&amp;gt;&amp;lt;br/&amp;gt;
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;我们测试下哈，&lt;code&gt;http://127.0.0.1:8000/book/list&lt;/code&gt;，点下 添加(使用form)&lt;/p&gt;
&lt;p&gt;![image-20240502162122785](D:\开发\Python\Django\2024版 一周掌握Django5视频教程 python web开发.assets\image-20240502162122785.png)&lt;/p&gt;
&lt;p&gt;![image-20240502162426534](D:\开发\Python\Django\2024版 一周掌握Django5视频教程 python web开发.assets\image-20240502162426534.png)&lt;/p&gt;
&lt;p&gt;输入表单信息，点击提交，测试成功。&lt;/p&gt;
&lt;p&gt;表单Form的常用属性和方法如下：&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;data:默认值为None，以字典形式表示，字典的键为表单字段，代表将数据绑定到对应的表单字段。&lt;/li&gt;
&lt;li&gt;auto_id:默认值为id_%s，以字符串格式化表示，若设置HTML元素控件的id属性，比如表单字段job，则元素控件id属性为id_job，%s 代表表单字段名称。&lt;/li&gt;
&lt;li&gt;prefix:默认值为None，以字符串表示，设置表单的控件属性name和id的属性值，如果一个网页里使用多个相同的表单，那么设置该属性可以区分每个表单。&lt;/li&gt;
&lt;li&gt;initial:默认值为None，以字典形式表示，在表单的实例化过程中设置初始化值。&lt;/li&gt;
&lt;li&gt;label_suffix:若参数值为None，则默认为冒号,以表单字段job为例,其HTML控件含有label标签(&amp;lt;label for=&quot;id_job&quot;&amp;gt;职位:&amp;lt;/label&amp;gt; )，其中 label标签里的冒号由参数label_suffix设置。field_order:默认值为None，则以表单字段定义的先后顺序进行排列，若要自定义排序，则将每个表单字段按先后顺序放置在列表里，并把列表作为该参数的值。&lt;/li&gt;
&lt;li&gt;use_required_attribute:默认值为None(或为True )，为表单字段所对应的 HTML控件设置required属性，该控件为必填项，数据不能为空，若设为False，则HTML控件为可填项。errors():验证表单的数据是否存在异常，若存在异常，则获取异常信息，异常信息可设为字典或JSON格式。&lt;/li&gt;
&lt;li&gt;is_valid():验证表单数据是否存在异常，若存在，则返回False，否则返回True。&lt;/li&gt;
&lt;li&gt;as_table():将表单字段以HTML的&amp;lt;table&amp;gt;标签生成网页表单。&lt;/li&gt;
&lt;li&gt;as_ul():将表单字段以HTML的&amp;lt;ul&amp;gt;标签生成网页表单。&lt;/li&gt;
&lt;li&gt;as _p():将表单字段以HTML的&amp;lt;p&amp;gt;标签生成网页表单。&lt;/li&gt;
&lt;li&gt;has_changed():对比用于提交的表单数据与表单初始化数据是否发生变化。&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;表单定义字段如如下类型：&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;CharField:文本框，参数max_length 和min_length分别设置文本长度。&lt;/li&gt;
&lt;li&gt;IntegerField:数值框，参数max_value设置最大值，min_value设置最小值。&lt;/li&gt;
&lt;li&gt;FloatField:数值框，继承IntegerField，验证数据是否为浮点数。&lt;/li&gt;
&lt;li&gt;DecimalField:数值框，继承IntegerField，验证数值是否设有小数点，参数max_digits设置最大位数，参数decimal_places设置小数点最大位数。&lt;/li&gt;
&lt;li&gt;DateField:文本框，继承BaseTemporalField，具有验证日期格式的功能，参数input_formats设置日期格式。&lt;/li&gt;
&lt;li&gt;TimeField:文本框，继承BaseTemporalField，验证数据是否为datetime.time或特定时间格式的字符串。&lt;/li&gt;
&lt;li&gt;DateTimeField:文本框，继承 BaseTemporalField，验证数据是否为datetime.datetime ,datetime.date或特定日期时间格式的字符串。&lt;/li&gt;
&lt;li&gt;DurationField:文本框，验证数据是否为一个有效的时间段。&lt;/li&gt;
&lt;li&gt;RegexField:文本框，继承CharField，验证数据是否与某个正则表达式匹配，参数regex设置正则表达式。&lt;/li&gt;
&lt;li&gt;EmailField:文本框，继承CharField，验证数据是否为合法的邮箱地址。&lt;/li&gt;
&lt;li&gt;FileField:文件上传框，参数max_length设置上传文件名的最大长度，参数allow_empty_file设置是否允许文件内容为空。&lt;/li&gt;
&lt;li&gt;ImageField:文件上传控件，继承FileField，验证文件是否为Pillow库可识别的图像格式。FilePathField:文件选择控件，在特定的目录选择文件，参数 path是必需参数，参数值为目录的绝对路径;参数recursive、match、 allow_files和allow_folders为可选参数。
URLField:文本框，继承CharField，验证数据是否为有效的路由地址。&lt;/li&gt;
&lt;li&gt;BooleanField:复选框，设有选项True和 False，如果字段带有required=True，复选框就默认为True。&lt;/li&gt;
&lt;li&gt;NullBooleanField:复选框，继承BooleanField，设有3个选项，分别为None、True和 False。ChoiceField:下拉框，参数choices 以元组形式表示，用于设置下拉框的选项列表。&lt;/li&gt;
&lt;li&gt;TypedChoiceField:下拉框，继承 ChoiceField，参数coerce 代表强制转换数据类型，参数empty_value表示空值，默认为空字符串。&lt;/li&gt;
&lt;li&gt;MultipleChoiceField:下拉框，继承ChoiceField，验证数据是否在下拉框的选项列表。&lt;/li&gt;
&lt;li&gt;TypedMultipleChoiceField:下拉框，继承MultipleChoiceField，验证数据是否在下拉框的选项列表，并且可强制转换数据类型，参数coerce代表强制转换数据类型，参数 empty_value表示空值，默认为空字符串。&lt;/li&gt;
&lt;li&gt;ComboField:文本框，为表单字段设置验证功能，比如字段类型为CharField，为该字段添加EmailField，使字段具有邮箱验证功能。&lt;/li&gt;
&lt;li&gt;MultiValueField:文本框，将多个表单字段合并成一个新的字段。&lt;/li&gt;
&lt;li&gt;SplitDateTimeField:文本框，继承MultiValueField，验证数据是否为datetime.datetime或特定日期时间格式的字符串。&lt;/li&gt;
&lt;li&gt;GenericIPAddressField:文本框，继承CharField，验证数据是否为有效的IP地址。&lt;/li&gt;
&lt;li&gt;SlugField:文本框，继承CharField，验证数据是否只包括字母、数字、下画线及连字符。&lt;/li&gt;
&lt;li&gt;UUIDField:文本框，继承CharField，验证数据是否为UUID格式。&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;每个表单有一些常用属性如下：&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;required:输入的数据是否为空，默认值为True。&lt;/li&gt;
&lt;li&gt;widget:设置HTML控件的样式。&lt;/li&gt;
&lt;li&gt;label:用于生成label标签的网页内容。initial:设置表单字段的初始值。&lt;/li&gt;
&lt;li&gt;help_text:设置帮助提示信息。&lt;/li&gt;
&lt;li&gt;error_messages:设置错误信息，以字典形式表示，比如{&apos;required&quot;: &apos;不能为空&apos;, &apos;invalid&apos;: &apos;格式错误}。&lt;/li&gt;
&lt;li&gt;show_hidden_initial:参数值为True/False，是否在当前控件后面再加一个隐藏的且具有默认值的控件（ 可用于检验两次输入的值是否一致)。&lt;/li&gt;
&lt;li&gt;validators:自定义数据验证规则。以列表格式表示，列表元素为函数名。localize:参数值为True/False，设置本地化，不同时区自动显示当地时间。disabled:参数值为True/False，HTML控件是否可以编辑。&lt;/li&gt;
&lt;li&gt;label_suffix:设置label 的后缀内容。&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;参数widget是一个forms.widgets对象，有4大类小部件：文本框类型，下拉框(复选框)类型，文件上传类型和复合框类型；&lt;/p&gt;
&lt;p&gt;文本框类型：&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;TextInput，对应CharField字段，文本框内容设置为文本格式&lt;/li&gt;
&lt;li&gt;NumberInput，对应IntegerField字段，文本框内容只允许输入数值&lt;/li&gt;
&lt;li&gt;Emaillnput，对应 EmailField字段，验证输入值是否为邮箱地址格式&lt;/li&gt;
&lt;li&gt;URLInput，对应URLField字段，验证输入值是否为路由地址格式&lt;/li&gt;
&lt;li&gt;PasswordInput，对应CharField字段，输入值以“*”显示&lt;/li&gt;
&lt;li&gt;HiddenInput，对应CharField字段,隐藏文本框,不显示在网页上&lt;/li&gt;
&lt;li&gt;DateInput，对应DateField字段，验证输入值是否为日期格式&lt;/li&gt;
&lt;li&gt;DateTimeInput，对应DateTimeField字段，验证输入值是否为日期时间格式&lt;/li&gt;
&lt;li&gt;TimeInput，对应TimeField字段，验证输入值是否为时间格式&lt;/li&gt;
&lt;li&gt;Textarea，对应CharField字段，将文本框设为Textarea格式&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;下拉框(复选框)类型：&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;CheckboxInput，对应 BooleanField字段,设置复选框,选项为True和 False&lt;/li&gt;
&lt;li&gt;Select，对应 ChoiceField字段，设置下拉框&lt;/li&gt;
&lt;li&gt;NullBooleanSelect，对应NullBooleanField，设置复选框，选项为None、True和 False&lt;/li&gt;
&lt;li&gt;SelectMultiple，对应ChoiceField字段，与Select类似，允许选择多个值&lt;/li&gt;
&lt;li&gt;RadioSelect，对应ChoiceField字段，将数据列表设置为单选按钮&lt;/li&gt;
&lt;li&gt;CheckboxSelectMultiple，对应ChoiceField字段，与SelectMultiple类似，设置为复选框列表&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;文件上传类型：&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;FileInput，对应 FileField 或 ImageField字段&lt;/li&gt;
&lt;li&gt;ClearableFileInput，对应 FileField 或ImageField字段，但多了复选框，允许清除上传的文件和图像&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;复合框类型：&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;MultipleHiddenInput，隐藏一个或多个HTML的控件&lt;/li&gt;
&lt;li&gt;SplitDateTimeWidget，组合使用Datelnput和 Timelnput&lt;/li&gt;
&lt;li&gt;SplitHiddenDateTimeWidget，与SplitDateTimeWidget类似，但将控件隐藏，不显示在网页上&lt;/li&gt;
&lt;li&gt;SelectDateWidget，组合使用3个Select，分别生成年、月、日的下拉框&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;我们优化下前面表单实例，加一个字段验证，以及样式；&lt;/p&gt;
&lt;p&gt;forms.py修改下BookInfoForm类：&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;class BookInfoForm(Form):
    &quot;&quot;&quot;
    图书表单
    &quot;&quot;&quot;
    bookName = forms.CharField(
        max_length=20,
        label=&quot;图书名称&quot;,
        required=True,
        widget=widgets.TextInput(attrs={&quot;placeholder&quot;: &quot;请输入用户名&quot;, &quot;class&quot;: &quot;inputCls&quot;})
    )
    price = forms.FloatField(label=&quot;图书价格&quot;)
    publishDate = forms.DateField(label=&quot;出版日期&quot;)
    # 获取图书类别列表
    bookTypeList = BookTypeInfo.objects.values()
    # 图书类别以下拉框形式显示，下拉框选项id是图书类别Id，下拉框选项文本是图书类别名称
    choices = [(v[&apos;id&apos;], v[&apos;bookTypeName&apos;]) for v, v in enumerate(bookTypeList)]
    bookType_id = forms.ChoiceField(required=False, choices=choices, label=&quot;图书类别&quot;)
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;add2.html加个样式：&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;    &amp;lt;style&amp;gt;
        .inputCls {
            width: 200px;
        }
    &amp;lt;/style&amp;gt;
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;运行测试：&lt;/p&gt;
&lt;p&gt;![image-20240502184012437](D:\开发\Python\Django\2024版 一周掌握Django5视频教程 python web开发.assets\image-20240502184012437.png)&lt;/p&gt;
&lt;p&gt;![image-20240502184019222](D:\开发\Python\Django\2024版 一周掌握Django5视频教程 python web开发.assets\image-20240502184019222.png)&lt;/p&gt;
&lt;h3&gt;Django5 ModelForm表单定义与使用&lt;/h3&gt;
&lt;p&gt;我们知道Django的表单分为两种: django.forms.Form和 django.forms.ModelForm。前者是一个基础的表单功能，后者是在前者的基础上结合模型所生成的模型表单。模型表单是将模型字段转换成表单字段，由表单字段生成HTML控件，从而生成网页表单。&lt;/p&gt;
&lt;p&gt;ModelForm有9个属性，说明如下：&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;model:必需属性，用于绑定Model对象。‘&lt;/li&gt;
&lt;li&gt;fields:可选属性，设置模型内哪些字段转换成表单字段，默认值为None，代表所有的模型字段，也可以将属性值设为&apos;&lt;em&gt;all&lt;/em&gt;&apos;，同样表示所有的模型字段。若只需部分模型字段，则将模型字段写入一个列表或元组里，再把该列表或元组作为属性值。&lt;/li&gt;
&lt;li&gt;exclude:可选属性，与fields 相反，禁止模型字段转换成表单字段。属性值以列表或元组表示，若设置了该属性，则属性fields 无须设置。&lt;/li&gt;
&lt;li&gt;labels:可选属性，设置表单字段的参数label，属性值以字典表示，字典的键为模型字段。&lt;/li&gt;
&lt;li&gt;widgets:可选属性，设置表单字段的参数 widget，属性值以字典表示，字典的键为模型字段。&lt;/li&gt;
&lt;li&gt;localized_fields:可选参数,将模型字段设为本地化的表单字段,常用于日期类型的模型字段。&lt;/li&gt;
&lt;li&gt;field_classes:可选属性,将模型字段重新定义,默认情况下,模型字段与表单字段遵从Django内置的转换规则。&lt;/li&gt;
&lt;li&gt;help_texts:可选属性,设置表单字段的参数help_text。&lt;/li&gt;
&lt;li&gt;error messages:可选属性，设置表单字段的参数error_messages.&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;模型字段转换表单字段遵从Django内置的规则进行转换，两者的转换规则如下：&lt;/p&gt;
&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;模型字段类型&lt;/th&gt;
&lt;th&gt;表单字段类型&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;AutoField&lt;/td&gt;
&lt;td&gt;不能转换表单字段&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;BigAutoField&lt;/td&gt;
&lt;td&gt;不能转换表单字段&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;BigIntegerField&lt;/td&gt;
&lt;td&gt;IntegerField&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;BinaryField&lt;/td&gt;
&lt;td&gt;CharField&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;BooleanField&lt;/td&gt;
&lt;td&gt;BooleanField或者NullBooleanField&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;CharField&lt;/td&gt;
&lt;td&gt;CharField&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;DateField&lt;/td&gt;
&lt;td&gt;DateField&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;DateTimeField&lt;/td&gt;
&lt;td&gt;DateTimeField&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;DecimalField&lt;/td&gt;
&lt;td&gt;DecimalField&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;EmailField&lt;/td&gt;
&lt;td&gt;EmailField&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;FileField&lt;/td&gt;
&lt;td&gt;FileField&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;FilePathField&lt;/td&gt;
&lt;td&gt;FilePathField&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;ForeignKey&lt;/td&gt;
&lt;td&gt;ModelChoiceField&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;ImageField&lt;/td&gt;
&lt;td&gt;ImageField&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;IntegerField&lt;/td&gt;
&lt;td&gt;IntegerField&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;IPAddressField&lt;/td&gt;
&lt;td&gt;IPAddressField&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;GenericlPAddressField&lt;/td&gt;
&lt;td&gt;GenericIPAddressField&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;ManyToManyField&lt;/td&gt;
&lt;td&gt;ModelMultipleChoiceField&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;NullBooleanField&lt;/td&gt;
&lt;td&gt;NullBooleanField&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;PositiveIntegerField&lt;/td&gt;
&lt;td&gt;IntegerField&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;PositiveSmallIntegerField&lt;/td&gt;
&lt;td&gt;IntegerField&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;SlugField&lt;/td&gt;
&lt;td&gt;SlugField&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;SmalllntegerField&lt;/td&gt;
&lt;td&gt;IntegerField&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;TextField&lt;/td&gt;
&lt;td&gt;CharField&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;TimeField&lt;/td&gt;
&lt;td&gt;TimeField&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;URLField&lt;/td&gt;
&lt;td&gt;URLField&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;
&lt;p&gt;我们用ModelForm改写下前面的图书添加实例：&lt;/p&gt;
&lt;p&gt;forms.py里创建BookInfoModelForm类，继承ModelForm&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;class BookInfoModelForm(ModelForm):
    # 配置中心
    class Meta:
        model = BookInfo  # 导入model
        fields = &apos;__all__&apos;  # 代表所有字段
        # fields = [&apos;bookName&apos;, &apos;price&apos;]  # 指定字段
        widgets = {  # 定义控件
            &apos;bookName&apos;: forms.TextInput(attrs={&quot;placeholder&quot;: &quot;请输入用户名&quot;, &apos;id&apos;: &apos;bookName&apos;, &apos;class&apos;: &apos;inputCls&apos;})
        }
        labels = {  # 指定标签
            &apos;bookName&apos;: &apos;图书名称&apos;,
            &apos;price&apos;: &apos;图书价格&apos;,
            &apos;publishDate&apos;: &apos;出版日期&apos;,
            &apos;bookType&apos;: &apos;图书类别&apos;
        }
        help_texts = {
            &apos;bookName&apos;: &apos;请输入图书名称&apos;
        }
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;views.py里定义preAdd3函数&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;def preAdd3(request):
    &quot;&quot;&quot;
    预处理，添加操作 使用modelForm表单
    :param request:
    :return:
    &quot;&quot;&quot;
    form = BookInfoModelForm()
    context_value = {&quot;title&quot;: &quot;图书添加&quot;, &quot;form&quot;: form}
    return render(request, &apos;book/add2.html&apos;, context_value)
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;urls.py里定义下映射：&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;path(&apos;book/preAdd3&apos;, helloWorld.views.preAdd3),
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;book/list.html里加下新的添加链接&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;&amp;lt;a href=&quot;/book/preAdd3&quot;&amp;gt;添加(使用ModelForm)&amp;lt;/a&amp;gt;&amp;lt;br/&amp;gt;&amp;lt;br/&amp;gt;
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;运行测试，浏览器输入：&lt;code&gt;http://127.0.0.1:8000/book/list&lt;/code&gt;&lt;/p&gt;
&lt;p&gt;![image-20240504112032669](D:\开发\Python\Django\2024版 一周掌握Django5视频教程 python web开发.assets\image-20240504112032669.png)&lt;/p&gt;
&lt;p&gt;其他表单没问题，就这下拉框出现问题了；&lt;/p&gt;
&lt;p&gt;![image-20240504112339476](D:\开发\Python\Django\2024版 一周掌握Django5视频教程 python web开发.assets\image-20240504112339476.png)&lt;/p&gt;
&lt;p&gt;谷歌浏览器F12开发者工具，审查元素看下：&lt;/p&gt;
&lt;p&gt;![image-20240504112427491](D:\开发\Python\Django\2024版 一周掌握Django5视频教程 python web开发.assets\image-20240504112427491.png)&lt;/p&gt;
&lt;p&gt;我们看到value默认取的图书类型的主键id，但是选项文本取值就不对了。这时候我们可以通过魔法方法&lt;code&gt;__str__&lt;/code&gt;来实现，默认打印对象输出图书类别名称；&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;class BookTypeInfo(models.Model):
    id = models.AutoField(primary_key=True)
    bookTypeName = models.CharField(max_length=20)

    class Meta:
        db_table = &quot;t_bookType&quot;
        verbose_name = &quot;图书类别信息&quot;  # 给模型取个直观的名字

    def __str__(self):
        return self.bookTypeName
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;再测试下，下拉框显示图书类别名称了&lt;/p&gt;
&lt;p&gt;![image-20240504122941561](D:\开发\Python\Django\2024版 一周掌握Django5视频教程 python web开发.assets\image-20240504122941561.png)&lt;/p&gt;
&lt;p&gt;这里要注意一个细节，默认转换的时候，下拉框的name是bookType，所以我们要改下views.py里的add方法，&lt;/p&gt;
&lt;p&gt;![image-20240504135712325](D:\开发\Python\Django\2024版 一周掌握Django5视频教程 python web开发.assets\image-20240504135712325.png)&lt;/p&gt;
&lt;p&gt;对应的改成bookType&lt;/p&gt;
&lt;p&gt;我们最后测试下：&lt;/p&gt;
&lt;p&gt;![image-20240504135746801](D:\开发\Python\Django\2024版 一周掌握Django5视频教程 python web开发.assets\image-20240504135746801.png)&lt;/p&gt;
&lt;p&gt;测试成功：&lt;/p&gt;
&lt;p&gt;![image-20240504135800392](D:\开发\Python\Django\2024版 一周掌握Django5视频教程 python web开发.assets\image-20240504135800392.png)&lt;/p&gt;
&lt;p&gt;平时开发，建议大家还是用ModelForm，简单方便。&lt;/p&gt;
&lt;h2&gt;Django5内置Admin系统&lt;/h2&gt;
&lt;p&gt;Admin后台系统也称为网站后台管理系统，主要对网站的信息进行管理，如文字、图片、影音和其他日常使用的文件的发布、更新、删除等操作，也包括功能信息的统计和管理，如用户信息、订单信息和访客信息等。简单来说，它是对网站数据库和文件进行快速操作和管理的系统，以使网页内容能够及时得到更新和调整。&lt;/p&gt;
&lt;h3&gt;Django5内置Admin系统初体验&lt;/h3&gt;
&lt;p&gt;当一个网站上线之后，网站管理员通过网站后台系统对网站进行管理和维护。&lt;/p&gt;
&lt;p&gt;Django 已内置Admin后台系统，在创建Django项目的时候，可以从配置文件settings.py中看到项目已默认启用Admin后台系统。&lt;/p&gt;
&lt;p&gt;![image-20240505174347908](D:\开发\Python\Django\2024版 一周掌握Django5视频教程 python web开发.assets\image-20240505174347908.png)&lt;/p&gt;
&lt;p&gt;urls.py里定义了Admin系统的首页地址：&lt;/p&gt;
&lt;p&gt;![image-20240505175239671](D:\开发\Python\Django\2024版 一周掌握Django5视频教程 python web开发.assets\image-20240505175239671.png)&lt;/p&gt;
&lt;p&gt;&lt;code&gt;我们浏览器输入http://127.0.0.1:8000/admin/&lt;/code&gt;即可进入Admin系统首页，默认跳转到Admin系统登录页面。&lt;/p&gt;
&lt;p&gt;![image-20240505175502087](D:\开发\Python\Django\2024版 一周掌握Django5视频教程 python web开发.assets\image-20240505175502087.png)&lt;/p&gt;
&lt;p&gt;我们发现是英文，我们一般开发交付给客户，必须是本地化中文。我们可以加一个中文本地化的中间件即可实现；&lt;/p&gt;
&lt;p&gt;settings.py里加下：&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;# 使用中文
&apos;django.middleware.locale.LocaleMiddleware&apos;,
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;![image-20240505175758064](D:\开发\Python\Django\2024版 一周掌握Django5视频教程 python web开发.assets\image-20240505175758064.png)&lt;/p&gt;
&lt;p&gt;注意下有顺序要求。&lt;/p&gt;
&lt;p&gt;Admin系统用户，权限，认证相关的表有如下6个，其中auth_user是用来存后台管理员信息，默认里面是没有数据的。&lt;/p&gt;
&lt;p&gt;![image-20240506085511539](D:\开发\Python\Django\2024版 一周掌握Django5视频教程 python web开发.assets\image-20240506085511539.png)&lt;/p&gt;
&lt;p&gt;我们可以通过python内置的manage.py的&lt;code&gt;createsuperuser&lt;/code&gt;命令来创建超级管理员的账号和密码：&lt;/p&gt;
&lt;p&gt;![image-20240506090038357](D:\开发\Python\Django\2024版 一周掌握Django5视频教程 python web开发.assets\image-20240506090038357.png)&lt;/p&gt;
&lt;p&gt;输入 &lt;code&gt;createsuperuser&lt;/code&gt;命令，提示让我们输入用户名，再输入邮箱，以及密码和确认密码，最终我们可以强制输入y，确认。&lt;/p&gt;
&lt;p&gt;这样auth_user数据库表有就有管理员数据了。&lt;/p&gt;
&lt;p&gt;![image-20240506090436557](D:\开发\Python\Django\2024版 一周掌握Django5视频教程 python web开发.assets\image-20240506090436557.png)&lt;/p&gt;
&lt;p&gt;我们回到Admin登录页面，输入刚才创建的用户名和密码：&lt;/p&gt;
&lt;p&gt;![image-20240506090529904](D:\开发\Python\Django\2024版 一周掌握Django5视频教程 python web开发.assets\image-20240506090529904.png)&lt;/p&gt;
&lt;p&gt;点击登录按钮，则进入系统管理主页；&lt;/p&gt;
&lt;p&gt;![image-20240506090607951](D:\开发\Python\Django\2024版 一周掌握Django5视频教程 python web开发.assets\image-20240506090607951.png)&lt;/p&gt;
&lt;p&gt;在Admin后台系统中可以看到，网页布局分为站点管理、认证和授权、用户和组，分别说明如下:
(1）站点管理是整个Admin后台的主体页面，整个项目的App所定义的模型都会在此页面显示。
(2）认证和授权是Django内置的用户认证系统，包括用户信息、权限管理和用户组设置等功能。
(3）用户和组是认证和授权所定义的模型，分别对应数据表auth_user和 auth_user_groups。&lt;/p&gt;
&lt;h3&gt;Django5注册模型到Admin系统&lt;/h3&gt;
&lt;p&gt;我们开发业务系统的时候，会定义很多的业务模型，我们可以把模型注册到Admin系统，让Admin系统帮我们维护这些模型。也就是在Admin后台自动给模型实现增删改查功能。&lt;/p&gt;
&lt;p&gt;注册模型到Admin系统有两个方式，我们都来演示下：&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;方式一，直接将模型注册到admin后台，以BookTypeInfo模型为例：&lt;/strong&gt;&lt;/p&gt;
&lt;p&gt;打开admin.py，&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;from helloWorld.models import BookTypeInfo

# Register your models here.
# 方法一，将模型直接注册到admin后台
admin.site.register(BookTypeInfo)
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;![image-20240510152304669](D:\开发\Python\Django\2024版 一周掌握Django5视频教程 python web开发.assets\image-20240510152304669.png)&lt;/p&gt;
&lt;p&gt;这样admin后台就出现了图书类别信息的管理，我们可以点进去，&lt;/p&gt;
&lt;p&gt;![image-20240510152407737](D:\开发\Python\Django\2024版 一周掌握Django5视频教程 python web开发.assets\image-20240510152407737.png)&lt;/p&gt;
&lt;p&gt;![image-20240510152426186](D:\开发\Python\Django\2024版 一周掌握Django5视频教程 python web开发.assets\image-20240510152426186.png)&lt;/p&gt;
&lt;p&gt;我们可以图书类别信息进行增删改查操作；&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;方式二：自定义类，继承ModelAdmin，以BookInfo为例&lt;/strong&gt;&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;# 方法二，自定义类，继承ModelAdmin
@admin.register(BookInfo)
class BookInfoAdmin(admin.ModelAdmin):
    # 设置显示的字段
    list_display = [&apos;id&apos;, &apos;bookName&apos;, &apos;price&apos;, &apos;publishDate&apos;, &apos;bookType&apos;]
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;我们可以点进ModelAdmin类里看下，我们可以对模型的增删改查操作做精细化的配置，包括显示字段，分页，可编辑字段，查询字段，排序等。&lt;/p&gt;
&lt;p&gt;![image-20240510160956077](D:\开发\Python\Django\2024版 一周掌握Django5视频教程 python web开发.assets\image-20240510160956077.png)&lt;/p&gt;
&lt;p&gt;Admin后台就多了图书信息&lt;/p&gt;
&lt;p&gt;![image-20240510160809366](D:\开发\Python\Django\2024版 一周掌握Django5视频教程 python web开发.assets\image-20240510160809366.png)&lt;/p&gt;
&lt;p&gt;点进去：&lt;/p&gt;
&lt;p&gt;![image-20240510160826010](D:\开发\Python\Django\2024版 一周掌握Django5视频教程 python web开发.assets\image-20240510160826010.png)&lt;/p&gt;
&lt;p&gt;![image-20240510160842616](D:\开发\Python\Django\2024版 一周掌握Django5视频教程 python web开发.assets\image-20240510160842616.png)&lt;/p&gt;
&lt;p&gt;我们同样可以对图书信息做增删改查操作；&lt;/p&gt;
&lt;h3&gt;Django5内置Admin系统自定义设置&lt;/h3&gt;
&lt;p&gt;我们在使用Django5的内置Admin系统时会发现一些默认的设置，并不符合我们的业务需求，我们需要自定义设置下；&lt;/p&gt;
&lt;p&gt;比如模块项目管理这块标题，默认用了模块项目名称，很不好：&lt;/p&gt;
&lt;p&gt;![image-20240519102655899](D:\开发\Python\Django\2024版 一周掌握Django5视频教程 python web开发.assets\image-20240519102655899.png)&lt;/p&gt;
&lt;p&gt;我们打开helloWorld项目的apps.py，配置类里加下 &lt;code&gt;verbose_name = &apos;网站图书管理&apos;&lt;/code&gt;&lt;/p&gt;
&lt;p&gt;![image-20240519103019595](D:\开发\Python\Django\2024版 一周掌握Django5视频教程 python web开发.assets\image-20240519103019595.png)&lt;/p&gt;
&lt;p&gt;这样我们会发现，用户体验好多了；&lt;/p&gt;
&lt;p&gt;![image-20240519103037816](D:\开发\Python\Django\2024版 一周掌握Django5视频教程 python web开发.assets\image-20240519103037816.png)&lt;/p&gt;
&lt;p&gt;还有一个地方，网站标题和子标题，也不友好。&lt;/p&gt;
&lt;p&gt;![image-20240519103418806](D:\开发\Python\Django\2024版 一周掌握Django5视频教程 python web开发.assets\image-20240519103418806.png)&lt;/p&gt;
&lt;p&gt;我们可以打开admin.py，设置如下：&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;# 设置网站标题和应用标题
admin.site.site_title = &apos;锋哥后台管理&apos;
admin.site.index_title = &apos;图书管理模块&apos;
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;![image-20240519103731524](D:\开发\Python\Django\2024版 一周掌握Django5视频教程 python web开发.assets\image-20240519103731524.png)&lt;/p&gt;
&lt;p&gt;这样就更友好了。&lt;/p&gt;
&lt;p&gt;最后一个地方，最不能接受；&lt;/p&gt;
&lt;p&gt;![image-20240519104350947](D:\开发\Python\Django\2024版 一周掌握Django5视频教程 python web开发.assets\image-20240519104350947.png)&lt;/p&gt;
&lt;p&gt;![image-20240519104406033](D:\开发\Python\Django\2024版 一周掌握Django5视频教程 python web开发.assets\image-20240519104406033.png)&lt;/p&gt;
&lt;p&gt;我们可以通过在admin.py设置 &lt;code&gt;admin.site.site_header&lt;/code&gt; 实现；&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;admin.site.site_header = &quot;python222网站管理系统&quot;
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;![image-20240519104617099](D:\开发\Python\Django\2024版 一周掌握Django5视频教程 python web开发.assets\image-20240519104617099.png)&lt;/p&gt;
&lt;p&gt;![image-20240519104635649](D:\开发\Python\Django\2024版 一周掌握Django5视频教程 python web开发.assets\image-20240519104635649.png)&lt;/p&gt;
&lt;p&gt;这样一设置，客户会认为是我们专业定制开发一样的。&lt;/p&gt;
&lt;h3&gt;Django5内置Admin系统二次开发&lt;/h3&gt;
&lt;p&gt;前面我们体验了Admin系统，以及模型注册，自定义设置。但是依然满足不了我们实际的业务开发需求。接下来，我们来讲下更细致的Admin系统二次开发。&lt;/p&gt;
&lt;h4&gt;创建一个普通管理员账户&lt;/h4&gt;
&lt;p&gt;首先我们在Admin后台系统里新建一个普通管理员账号。&lt;/p&gt;
&lt;p&gt;认证和授权的用户右侧点击“新增”&lt;/p&gt;
&lt;p&gt;![image-20240520111329663](D:\开发\Python\Django\2024版 一周掌握Django5视频教程 python web开发.assets\image-20240520111329663.png)&lt;/p&gt;
&lt;p&gt;输入用户名和密码后，点击“保存”&lt;/p&gt;
&lt;p&gt;![image-20240520111535202](D:\开发\Python\Django\2024版 一周掌握Django5视频教程 python web开发.assets\image-20240520111535202.png)&lt;/p&gt;
&lt;p&gt;勾选“职员状态”&lt;/p&gt;
&lt;p&gt;![image-20240520111856991](D:\开发\Python\Django\2024版 一周掌握Django5视频教程 python web开发.assets\image-20240520111856991.png)&lt;/p&gt;
&lt;p&gt;把所有系统权限都授权给fengge用户。&lt;/p&gt;
&lt;p&gt;![image-20240520114145871](D:\开发\Python\Django\2024版 一周掌握Django5视频教程 python web开发.assets\image-20240520114145871.png)&lt;/p&gt;
&lt;p&gt;最后点“保存”&lt;/p&gt;
&lt;p&gt;这样我们就可以用fengge这个用户登录系统了。&lt;/p&gt;
&lt;p&gt;![image-20240520114330280](D:\开发\Python\Django\2024版 一周掌握Django5视频教程 python web开发.assets\image-20240520114330280.png)&lt;/p&gt;
&lt;h4&gt;设置不可编辑字段 get_readonly_fields()&lt;/h4&gt;
&lt;p&gt;业务开发时，有时候一些敏感字段，我们不允许普通管理员修改。我们可以通过重写ModelAdmin的get_readonly_fields()方法实现；&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;    # 重写分类方法，设置只读字段
    def get_readonly_fields(self, request, obj=None):
        if request.user.is_superuser:
            self.readonly_fields = []
        else:
            self.readonly_fields = [&apos;bookName&apos;]
        return self.readonly_fields
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;我们用普通管理员fengge登录，然后点击编辑，&lt;/p&gt;
&lt;p&gt;![image-20240527105444970](D:\开发\Python\Django\2024版 一周掌握Django5视频教程 python web开发.assets\image-20240527105444970.png)&lt;/p&gt;
&lt;p&gt;图书名称已经变成只读了。&lt;/p&gt;
&lt;p&gt;这里我们同时也发现，字段label名称是英文BookName，原因是我们没有设置属性字段的verbose_name&lt;/p&gt;
&lt;p&gt;我们可以在models.py里，加下verbose_name配置即可；&lt;/p&gt;
&lt;p&gt;![image-20240527105707956](D:\开发\Python\Django\2024版 一周掌握Django5视频教程 python web开发.assets\image-20240527105707956.png)&lt;/p&gt;
&lt;p&gt;再进入下系统，&lt;/p&gt;
&lt;p&gt;![image-20240527105739852](D:\开发\Python\Django\2024版 一周掌握Django5视频教程 python web开发.assets\image-20240527105739852.png)&lt;/p&gt;
&lt;p&gt;搞定，其他字段大家自行设置下。&lt;/p&gt;
&lt;p&gt;用python222管理员登录的话，是所有字段都可以编辑的。&lt;/p&gt;
&lt;p&gt;![image-20240527105833399](D:\开发\Python\Django\2024版 一周掌握Django5视频教程 python web开发.assets\image-20240527105833399.png)&lt;/p&gt;
&lt;p&gt;当然还有很多细粒度设置的方法，如下&lt;/p&gt;
&lt;p&gt;formfield_for_foreignkey() 设置外键下拉框过滤筛选&lt;/p&gt;
&lt;p&gt;formfield_for_foreignkey() 重写外键下拉框数据，比如增加下拉选项。&lt;/p&gt;
&lt;p&gt;save_model() 添加或者修改处理逻辑重写 ，可以增加一些日志等处理。&lt;/p&gt;
&lt;p&gt;等等...&lt;/p&gt;
&lt;h4&gt;自定义Admin模版&lt;/h4&gt;
&lt;p&gt;Admin后台管理系统的模版文件和Django框架内置提供的，我们可以在源码里找到。&lt;/p&gt;
&lt;p&gt;具体位置在django -&amp;gt; contrib -&amp;gt; admin -&amp;gt; templates 下&lt;/p&gt;
&lt;p&gt;![image-20240531124117384](D:\开发\Python\Django\2024版 一周掌握Django5视频教程 python web开发.assets\image-20240531124117384.png)&lt;/p&gt;
&lt;p&gt;很多时候我们需要修改默认的模版，包括程序功能，样式等，来达到业务需求。&lt;/p&gt;
&lt;p&gt;我们可以直接修改源码里的模版，但是这种方式不好，如果一台机器有多个项目，会影响其他项目使用。&lt;/p&gt;
&lt;p&gt;我们提倡在项目的模块项目的templates下，通过优先级来实现修改模版。&lt;/p&gt;
&lt;p&gt;具体方式如下：&lt;/p&gt;
&lt;p&gt;模块项目(比如我们这是helloWorld项目)的templates下，新建admin目录，然后admin目录下创建你需要覆盖的模版名称。&lt;/p&gt;
&lt;p&gt;比如我们覆盖下修改的模版change_form.html。&lt;/p&gt;
&lt;p&gt;![image-20240531124816320](D:\开发\Python\Django\2024版 一周掌握Django5视频教程 python web开发.assets\image-20240531124816320.png)&lt;/p&gt;
&lt;p&gt;从django源码里复制一份源码，贴进去，然后根据需求我们改下。&lt;/p&gt;
&lt;p&gt;我们先测试下&lt;/p&gt;
&lt;p&gt;change_form.html官方模版源码是如下：&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;{百分号 extends &quot;admin/base_site.html&quot; %}
{百分号 load i18n admin_urls static admin_modify %}

{百分号 block extrahead %}{{ block.super }}
&amp;lt;script src=&quot;{百分号 url &apos;admin:jsi18n&apos; %}&quot;&amp;gt;&amp;lt;/script&amp;gt;
{{ media }}
{百分号 endblock %}

{百分号 block extrastyle %}{{ block.super }}&amp;lt;link rel=&quot;stylesheet&quot; href=&quot;{百分号 static &quot;admin/css/forms.css&quot; %}&quot;&amp;gt;{百分号 endblock %}

{百分号 block coltype %}colM{百分号 endblock %}

{百分号 block bodyclass %}{{ block.super }} app-{{ opts.app_label }} model-{{ opts.model_name }} change-form{百分号 endblock %}

{百分号 if not is_popup %}
{百分号 block breadcrumbs %}
&amp;lt;div class=&quot;breadcrumbs&quot;&amp;gt;
&amp;lt;a href=&quot;{百分号 url &apos;admin:index&apos; %}&quot;&amp;gt;{百分号 translate &apos;Home&apos; %}&amp;lt;/a&amp;gt;
&amp;amp;rsaquo; &amp;lt;a href=&quot;{百分号 url &apos;admin:app_list&apos; app_label=opts.app_label %}&quot;&amp;gt;{{ opts.app_config.verbose_name }}&amp;lt;/a&amp;gt;
&amp;amp;rsaquo; {百分号 if has_view_permission %}&amp;lt;a href=&quot;{百分号 url opts|admin_urlname:&apos;changelist&apos; %}&quot;&amp;gt;{{ opts.verbose_name_plural|capfirst }}&amp;lt;/a&amp;gt;{百分号 else %}{{ opts.verbose_name_plural|capfirst }}{百分号 endif %}
&amp;amp;rsaquo; {百分号 if add %}{百分号 blocktranslate with name=opts.verbose_name %}Add {{ name }}{百分号 endblocktranslate %}{百分号 else %}{{ original|truncatewords:&quot;18&quot; }}{百分号 endif %}
&amp;lt;/div&amp;gt;
{百分号 endblock %}
{百分号 endif %}

{百分号 block content %}&amp;lt;div id=&quot;content-main&quot;&amp;gt;
{百分号 block object-tools %}
{百分号 if change and not is_popup %}
  &amp;lt;ul class=&quot;object-tools&quot;&amp;gt;
    {百分号 block object-tools-items %}
      {百分号 change_form_object_tools %}
    {百分号 endblock %}
  &amp;lt;/ul&amp;gt;
{百分号 endif %}
{百分号 endblock %}
&amp;lt;form {百分号 if has_file_field %}enctype=&quot;multipart/form-data&quot; {百分号 endif %}{百分号 if form_url %}action=&quot;{{ form_url }}&quot; {百分号 endif %}method=&quot;post&quot; id=&quot;{{ opts.model_name }}_form&quot; novalidate&amp;gt;{百分号 csrf_token %}{百分号 block form_top %}{百分号 endblock %}
&amp;lt;div&amp;gt;
{百分号 if is_popup %}&amp;lt;input type=&quot;hidden&quot; name=&quot;{{ is_popup_var }}&quot; value=&quot;1&quot;&amp;gt;{百分号 endif %}
{百分号 if to_field %}&amp;lt;input type=&quot;hidden&quot; name=&quot;{{ to_field_var }}&quot; value=&quot;{{ to_field }}&quot;&amp;gt;{百分号 endif %}
{百分号 if save_on_top %}{百分号 block submit_buttons_top %}{百分号 submit_row %}{百分号 endblock %}{百分号 endif %}
{百分号 if errors %}
    &amp;lt;p class=&quot;errornote&quot;&amp;gt;
    {百分号 blocktranslate count counter=errors|length %}Please correct the error below.{百分号 plural %}Please correct the errors below.{百分号 endblocktranslate %}
    &amp;lt;/p&amp;gt;
    {{ adminform.form.non_field_errors }}
{百分号 endif %}

{百分号 block field_sets %}
{百分号 for fieldset in adminform %}
  {百分号 include &quot;admin/includes/fieldset.html&quot; %}
{百分号 endfor %}
{百分号 endblock %}

{百分号 block after_field_sets %}{百分号 endblock %}

{百分号 block inline_field_sets %}
{百分号 for inline_admin_formset in inline_admin_formsets %}
    {百分号 include inline_admin_formset.opts.template %}
{百分号 endfor %}
{百分号 endblock %}

{百分号 block after_related_objects %}{百分号 endblock %}

{百分号 block submit_buttons_bottom %}{百分号 submit_row %}{百分号 endblock %}

{百分号 block admin_change_form_document_ready %}
    &amp;lt;script id=&quot;django-admin-form-add-constants&quot;
            src=&quot;{百分号 static &apos;admin/js/change_form.js&apos; %}&quot;
            {百分号 if adminform and add %}
                data-model-name=&quot;{{ opts.model_name }}&quot;
            {百分号 endif %}
            async&amp;gt;
    &amp;lt;/script&amp;gt;
{百分号 endblock %}

{# JavaScript for prepopulated fields #}
{百分号 prepopulated_fields_js %}

&amp;lt;/div&amp;gt;
&amp;lt;/form&amp;gt;&amp;lt;/div&amp;gt;
{百分号 endblock %}

&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;运行效果如下：&lt;/p&gt;
&lt;p&gt;![image-20240531125051355](D:\开发\Python\Django\2024版 一周掌握Django5视频教程 python web开发.assets\image-20240531125051355.png)&lt;/p&gt;
&lt;p&gt;我们从测试有效性的出发点来删除源码，改造如下：&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;{百分号 extends &quot;admin/base_site.html&quot; %}
{百分号 load i18n admin_urls static admin_modify %}

{百分号 block extrahead %}{{ block.super }}
    &amp;lt;script src=&quot;{百分号 url &apos;admin:jsi18n&apos; %}&quot;&amp;gt;&amp;lt;/script&amp;gt;
    {{ media }}
{百分号 endblock %}

{百分号 block extrastyle %}{{ block.super }}
    &amp;lt;link rel=&quot;stylesheet&quot; href=&quot;{百分号 static &quot;admin/css/forms.css&quot; %}&quot;&amp;gt;{百分号 endblock %}

{百分号 block coltype %}colM{百分号 endblock %}


&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;再刷新页面看下：&lt;/p&gt;
&lt;p&gt;![image-20240531125211903](D:\开发\Python\Django\2024版 一周掌握Django5视频教程 python web开发.assets\image-20240531125211903.png)&lt;/p&gt;
&lt;p&gt;说明我们这种方式是有效的。&lt;/p&gt;
&lt;h2&gt;Django5内置Auth认证系统&lt;/h2&gt;
&lt;p&gt;我们在开发一个网站的时候，无可避免的需要设计实现网站的用户系统。此时我们需要实现包括用户注册、用户登录、用户认证、注销、修改密码等功能，这还真是个麻烦的事情呢。&lt;/p&gt;
&lt;p&gt;为了解决上述痛点，Django 内置了强大的用户认证系统--auth，可以高效地实现登录和权限等需求。它默认使用 auth_user 表来存储用户数据。&lt;/p&gt;
&lt;p&gt;前面我们已经通过数据迁移生成了用户权限认证系统的物流表；里面包含系统用户表，权限表，用户组，以及用户组权限关联表，用户和组关联表，用户权限关联表。&lt;/p&gt;
&lt;p&gt;![image-20240604163124747](D:\开发\Python\Django\2024版 一周掌握Django5视频教程 python web开发.assets\image-20240604163124747.png)&lt;/p&gt;
&lt;h3&gt;用户注册实现&lt;/h3&gt;
&lt;p&gt;我们实现Auth认证系统里的用户注册的话，用的是auth模版models.py里定义的User模型。&lt;/p&gt;
&lt;p&gt;![image-20240604205218102](D:\开发\Python\Django\2024版 一周掌握Django5视频教程 python web开发.assets\image-20240604205218102.png)&lt;/p&gt;
&lt;p&gt;通过auth内置的User，我们可以直接操作用户相关功能；&lt;/p&gt;
&lt;p&gt;首先urls.py里定义下映射：&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;    # 跳转注册页面
    path(&apos;auth/toRegister&apos;, helloWorld.views.to_register),
    # 提交注册请求
    path(&apos;auth/register&apos;, helloWorld.views.register),
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;![image-20240604205949628](D:\开发\Python\Django\2024版 一周掌握Django5视频教程 python web开发.assets\image-20240604205949628.png)&lt;/p&gt;
&lt;p&gt;templates下新建auth目录，再新建login.html和register.html两个页面；（用户注册后，跳转到登录页面）&lt;/p&gt;
&lt;p&gt;register.html页面源码：&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;&amp;lt;!DOCTYPE html&amp;gt;
&amp;lt;html lang=&quot;en&quot;&amp;gt;
&amp;lt;head&amp;gt;
    &amp;lt;meta charset=&quot;UTF-8&quot;&amp;gt;
    &amp;lt;title&amp;gt;注册页面&amp;lt;/title&amp;gt;
&amp;lt;/head&amp;gt;
&amp;lt;body&amp;gt;
&amp;lt;form action=&quot;/auth/register&quot; method=&quot;post&quot;&amp;gt;
    {百分号 csrf_token %}
    &amp;lt;table&amp;gt;
        &amp;lt;tr&amp;gt;
            &amp;lt;th&amp;gt;用户注册&amp;lt;/th&amp;gt;
        &amp;lt;/tr&amp;gt;
        &amp;lt;tr&amp;gt;
            &amp;lt;td&amp;gt;用户名：&amp;lt;/td&amp;gt;
            &amp;lt;td&amp;gt;&amp;lt;input type=&quot;text&quot; name=&quot;username&quot; value=&quot;{{ username }}&quot;&amp;gt;&amp;lt;/td&amp;gt;
        &amp;lt;/tr&amp;gt;
        &amp;lt;tr&amp;gt;
            &amp;lt;td&amp;gt;密码：&amp;lt;/td&amp;gt;
            &amp;lt;td&amp;gt;&amp;lt;input type=&quot;password&quot; name=&quot;password&quot; value=&quot;{{ password }}&quot;&amp;gt;&amp;lt;/td&amp;gt;
        &amp;lt;/tr&amp;gt;
        &amp;lt;tr&amp;gt;
            &amp;lt;td&amp;gt;
                &amp;lt;input type=&quot;submit&quot; value=&quot;提交&quot;&amp;gt;
            &amp;lt;/td&amp;gt;
            &amp;lt;td&amp;gt;
                &amp;lt;font color=&quot;red&quot;&amp;gt;{{ errorInfo }}&amp;lt;/font&amp;gt;
            &amp;lt;/td&amp;gt;
        &amp;lt;/tr&amp;gt;
    &amp;lt;/table&amp;gt;
&amp;lt;/form&amp;gt;
&amp;lt;/body&amp;gt;
&amp;lt;/html&amp;gt;
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;login.html页面源码（临时的）：&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;&amp;lt;!DOCTYPE html&amp;gt;
&amp;lt;html lang=&quot;en&quot;&amp;gt;
&amp;lt;head&amp;gt;
    &amp;lt;meta charset=&quot;UTF-8&quot;&amp;gt;
    &amp;lt;title&amp;gt;Title&amp;lt;/title&amp;gt;
&amp;lt;/head&amp;gt;
&amp;lt;body&amp;gt;
登录页面
&amp;lt;/body&amp;gt;
&amp;lt;/html&amp;gt;
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;views.py实现to_register和register两个方法。新增用户用的是create_user，判断用户是否存在通过filter&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;def to_register(request):
    &quot;&quot;&quot;
    跳转注册页面
    :param request:
    :return:
    &quot;&quot;&quot;
    return render(request, &apos;auth/register.html&apos;)


def register(request):
    &quot;&quot;&quot;
    用户注册
    :param request:
    :return:
    &quot;&quot;&quot;
    username = request.POST.get(&apos;username&apos;)
    password = request.POST.get(&apos;password&apos;)
    # 检验用户名是否存在
    result = User.objects.filter(username=username)
    if result:
        return render(request, &apos;auth/register.html&apos;,
                      context={&quot;errorInfo&quot;: &quot;该用户名已存在&quot;, &quot;username&quot;: username, &quot;password&quot;: password})
    User.objects.create_user(username=username, password=password)
    return render(request, &quot;auth/login.html&quot;)
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;测试，浏览器输入  &lt;code&gt;http://127.0.0.1:8000/auth/toRegister&lt;/code&gt;&lt;/p&gt;
&lt;p&gt;![image-20240604210638779](D:\开发\Python\Django\2024版 一周掌握Django5视频教程 python web开发.assets\image-20240604210638779.png)&lt;/p&gt;
&lt;p&gt;输入用户名和密码，点提交；&lt;/p&gt;
&lt;p&gt;auth_user表，就会有用户数据：&lt;/p&gt;
&lt;p&gt;![image-20240604210719850](D:\开发\Python\Django\2024版 一周掌握Django5视频教程 python web开发.assets\image-20240604210719850.png)&lt;/p&gt;
&lt;p&gt;如果用户名重复，则报错提示：&lt;/p&gt;
&lt;p&gt;![image-20240604210749271](D:\开发\Python\Django\2024版 一周掌握Django5视频教程 python web开发.assets\image-20240604210749271.png)&lt;/p&gt;
&lt;h3&gt;用户登录实现&lt;/h3&gt;
&lt;p&gt;用户登录功能，后端验证主要通过auth模块提供的authenticate校验方法，以及login登录方法实现。&lt;/p&gt;
&lt;p&gt;首先urls.py里加下映射：&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;    # 跳转登录页面
    path(&apos;auth/toLogin&apos;, helloWorld.views.to_login),
    # 提交登录请求
    path(&apos;auth/login&apos;, helloWorld.views.login),
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;登录页面login.html：&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;&amp;lt;!DOCTYPE html&amp;gt;
&amp;lt;html lang=&quot;en&quot;&amp;gt;
&amp;lt;head&amp;gt;
    &amp;lt;meta charset=&quot;UTF-8&quot;&amp;gt;
    &amp;lt;title&amp;gt;登录页面&amp;lt;/title&amp;gt;
&amp;lt;/head&amp;gt;
&amp;lt;body&amp;gt;
&amp;lt;form action=&quot;/auth/login&quot; method=&quot;post&quot;&amp;gt;
    {百分号 csrf_token %}
    &amp;lt;table&amp;gt;
        &amp;lt;tr&amp;gt;
            &amp;lt;th&amp;gt;用户登录&amp;lt;/th&amp;gt;
        &amp;lt;/tr&amp;gt;
        &amp;lt;tr&amp;gt;
            &amp;lt;td&amp;gt;用户名：&amp;lt;/td&amp;gt;
            &amp;lt;td&amp;gt;&amp;lt;input type=&quot;text&quot; name=&quot;username&quot; value=&quot;{{ username }}&quot;&amp;gt;&amp;lt;/td&amp;gt;
        &amp;lt;/tr&amp;gt;
        &amp;lt;tr&amp;gt;
            &amp;lt;td&amp;gt;密码：&amp;lt;/td&amp;gt;
            &amp;lt;td&amp;gt;&amp;lt;input type=&quot;password&quot; name=&quot;password&quot; value=&quot;{{ password }}&quot;&amp;gt;&amp;lt;/td&amp;gt;
        &amp;lt;/tr&amp;gt;
        &amp;lt;tr&amp;gt;
            &amp;lt;td&amp;gt;
                &amp;lt;input type=&quot;submit&quot; value=&quot;提交&quot;&amp;gt;
            &amp;lt;/td&amp;gt;
            &amp;lt;td&amp;gt;
                &amp;lt;font color=&quot;red&quot;&amp;gt;{{ errorInfo }}&amp;lt;/font&amp;gt;
            &amp;lt;/td&amp;gt;
        &amp;lt;/tr&amp;gt;
    &amp;lt;/table&amp;gt;
&amp;lt;/form&amp;gt;
&amp;lt;/body&amp;gt;
&amp;lt;/html&amp;gt;
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;网站首页index.html，登录成功后跳转：&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;&amp;lt;!DOCTYPE html&amp;gt;
&amp;lt;html lang=&quot;en&quot;&amp;gt;
&amp;lt;head&amp;gt;
    &amp;lt;meta charset=&quot;UTF-8&quot;&amp;gt;
    &amp;lt;title&amp;gt;网站首页&amp;lt;/title&amp;gt;
&amp;lt;/head&amp;gt;
&amp;lt;body&amp;gt;
网站首页,欢迎: {{ request.user }}
&amp;lt;/body&amp;gt;
&amp;lt;/html&amp;gt;
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;views.py里实现to_login和login方法。&lt;/p&gt;
&lt;p&gt;通过auth.authenticate校验用户是否已经存在。校验用户成功后，返回的是一个封装好的用户对象；校验错误则返回None&lt;/p&gt;
&lt;p&gt;用户对象is_active方法判断用户是否激活。&lt;/p&gt;
&lt;p&gt;通过调用auth.login，用户登录成功之后，返回给客户端登录的凭证或者说是令牌、随机字符串，则不需要我们去操作diango_session表，会自动创建session&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;当执行完&lt;code&gt;auth.authenticate&lt;/code&gt;和&lt;code&gt;auth.login&lt;/code&gt;后，也就是登录成功后，我们就可以通过&lt;code&gt;request.user&lt;/code&gt;直接获取到当前登录的用户对象数据&lt;/strong&gt;&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;strong&gt;登录成功的情况下，该方法获得的是登录用户对象&lt;/strong&gt;&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;登录不成功的情况下，该方法获得的是匿名对象&lt;code&gt;AnonymousUser&lt;/code&gt;&lt;/strong&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;pre&gt;&lt;code&gt;def to_login(request):
    &quot;&quot;&quot;
    跳转登录页面
    :param request:
    :return:
    &quot;&quot;&quot;

    return render(request, &apos;auth/login.html&apos;)


def login(request):
    &quot;&quot;&quot;
    登录处理
    :param request:
    :return:
    &quot;&quot;&quot;
    username = request.POST.get(&apos;username&apos;)
    password = request.POST.get(&apos;password&apos;)
    # 通过auth模块来检验加密后的密码 ，校验成功返回用户对象，否则返回None
    resUser: User = auth.authenticate(request, username=username, password=password)
    if resUser and resUser.is_active:
        print(resUser, type(resUser))
        # 用户登录成功之后（返回给客户端登录的凭证或者说是令牌、随机字符串）
        auth.login(request, resUser)
        return render(request, &apos;auth/index.html&apos;)
    else:
        return render(request, &apos;auth/login.html&apos;,
                      context={&quot;errorInfo&quot;: &quot;用户名或者密码错误&quot;, &quot;username&quot;: username, &quot;password&quot;: password})
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;![image-20240605173910313](D:\开发\Python\Django\2024版 一周掌握Django5视频教程 python web开发.assets\image-20240605173910313.png)&lt;/p&gt;
&lt;p&gt;我们来测试下：浏览器输入： &lt;code&gt;http://127.0.0.1:8000/auth/toLogin&lt;/code&gt; 进入登录页面：&lt;/p&gt;
&lt;p&gt;![image-20240605175154664](D:\开发\Python\Django\2024版 一周掌握Django5视频教程 python web开发.assets\image-20240605175154664.png)&lt;/p&gt;
&lt;p&gt;用户名密码输入错误，提示报错信息：&lt;/p&gt;
&lt;p&gt;![image-20240605175437174](D:\开发\Python\Django\2024版 一周掌握Django5视频教程 python web开发.assets\image-20240605175437174.png)&lt;/p&gt;
&lt;p&gt;用户名密码输入正确，则跳转到主页：&lt;/p&gt;
&lt;p&gt;![image-20240605175508638](D:\开发\Python\Django\2024版 一周掌握Django5视频教程 python web开发.assets\image-20240605175508638.png)&lt;/p&gt;
&lt;h3&gt;用户修改密码实现&lt;/h3&gt;
&lt;p&gt;用户修改密码主要通过request.user对象的set_password实现，当然校验原密码用check_password，设置完后，需要保存，调用save()方法。&lt;/p&gt;
&lt;p&gt;我们urls.py里加下映射；&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;    # 修改密码 get请求直接跳转页面，post请求执行处理
    path(&apos;auth/setPwd&apos;, helloWorld.views.setPwd),
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;新建setPwd.html&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;&amp;lt;!DOCTYPE html&amp;gt;
&amp;lt;html lang=&quot;en&quot;&amp;gt;
&amp;lt;head&amp;gt;
    &amp;lt;meta charset=&quot;UTF-8&quot;&amp;gt;
    &amp;lt;title&amp;gt;修改密码&amp;lt;/title&amp;gt;
&amp;lt;/head&amp;gt;
&amp;lt;body&amp;gt;
&amp;lt;form action=&quot;/auth/setPwd&quot; method=&quot;post&quot;&amp;gt;
    {百分号 csrf_token %}
    &amp;lt;table&amp;gt;
        &amp;lt;tr&amp;gt;
            &amp;lt;th&amp;gt;修改密码&amp;lt;/th&amp;gt;
        &amp;lt;/tr&amp;gt;
        &amp;lt;tr&amp;gt;
            &amp;lt;td&amp;gt;用户名：&amp;lt;/td&amp;gt;
            &amp;lt;td&amp;gt;&amp;lt;input type=&quot;text&quot; name=&quot;username&quot; value=&quot;{{ request.user }}&quot; readonly&amp;gt;&amp;lt;/td&amp;gt;
        &amp;lt;/tr&amp;gt;
        &amp;lt;tr&amp;gt;
            &amp;lt;td&amp;gt;原密码：&amp;lt;/td&amp;gt;
            &amp;lt;td&amp;gt;&amp;lt;input type=&quot;password&quot; name=&quot;oldPwd&quot; value=&quot;{{ oldPwd }}&quot;&amp;gt;&amp;lt;/td&amp;gt;
        &amp;lt;/tr&amp;gt;
        &amp;lt;tr&amp;gt;
            &amp;lt;td&amp;gt;新密码：&amp;lt;/td&amp;gt;
            &amp;lt;td&amp;gt;&amp;lt;input type=&quot;password&quot; name=&quot;newPwd&quot; value=&quot;{{ newPwd }}&quot;&amp;gt;&amp;lt;/td&amp;gt;
        &amp;lt;/tr&amp;gt;
        &amp;lt;tr&amp;gt;
            &amp;lt;td&amp;gt;
                &amp;lt;input type=&quot;submit&quot; value=&quot;提交&quot;&amp;gt;
            &amp;lt;/td&amp;gt;
            &amp;lt;td&amp;gt;
                &amp;lt;font color=&quot;red&quot;&amp;gt;{{ errorInfo }}&amp;lt;/font&amp;gt;
            &amp;lt;/td&amp;gt;
        &amp;lt;/tr&amp;gt;
    &amp;lt;/table&amp;gt;
&amp;lt;/form&amp;gt;
&amp;lt;/body&amp;gt;
&amp;lt;/html&amp;gt;
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;views.py里实现setPwd函数：&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;def setPwd(request):
    &quot;&quot;&quot;
    修改密码
    :param request:
    :return:
    &quot;&quot;&quot;
    if request.method == &quot;POST&quot;:
        oldPwd = request.POST.get(&quot;oldPwd&quot;)
        newPwd = request.POST.get(&quot;newPwd&quot;)
        # 1,校验用户密码 check_password
        isRight = request.user.check_password(oldPwd)
        if not isRight:
            return render(request, &apos;auth/setPwd.html&apos;,
                          context={&quot;errorInfo&quot;: &quot;原密码错误&quot;, &quot;oldPwd&quot;: oldPwd, &quot;newPwd&quot;: newPwd})
        # 2,设置新密码 set_password 实现加密
        request.user.set_password(newPwd)
        # 3,保存用户信息
        request.user.save()
        return render(request, &apos;auth/index.html&apos;)
    return render(request, &quot;auth/setPwd.html&quot;)
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;我们测试下，先用户登录，然后浏览器输入：  &lt;code&gt;http://127.0.0.1:8000/auth/setPwd&lt;/code&gt; 进入修改密码页面；&lt;/p&gt;
&lt;p&gt;![image-20240606080817594](D:\开发\Python\Django\2024版 一周掌握Django5视频教程 python web开发.assets\image-20240606080817594.png)&lt;/p&gt;
&lt;p&gt;如果原密码输入错误，提示报错信息&lt;/p&gt;
&lt;p&gt;![image-20240606080838677](D:\开发\Python\Django\2024版 一周掌握Django5视频教程 python web开发.assets\image-20240606080838677.png)&lt;/p&gt;
&lt;p&gt;校验成功，跳转主页；&lt;/p&gt;
&lt;p&gt;![image-20240606080910733](D:\开发\Python\Django\2024版 一周掌握Django5视频教程 python web开发.assets\image-20240606080910733.png)&lt;/p&gt;
&lt;p&gt;系统用户表的密码也会被修改，同时是加密的后的密码；&lt;/p&gt;
&lt;p&gt;![image-20240606080937599](D:\开发\Python\Django\2024版 一周掌握Django5视频教程 python web开发.assets\image-20240606080937599.png)&lt;/p&gt;
&lt;h3&gt;用户注销实现&lt;/h3&gt;
&lt;p&gt;用户注销通过auth.logout方法实现。我们来完善上前面的例子。用户登录后进入主页，显示注销功能；&lt;/p&gt;
&lt;p&gt;如果用户没登录，则显示登录功能；&lt;/p&gt;
&lt;p&gt;views.py里实现Logout方法：&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;def logout(request):
    &quot;&quot;&quot;
    注销
    :param request:
    :return:
    &quot;&quot;&quot;
    auth.logout(request)
    return render(request, &apos;auth/index.html&apos;)
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;再实现一个跳转主页的方法to_index：&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;def to_index(request):
    &quot;&quot;&quot;
    跳转主页
    :param request:
    :return:
    &quot;&quot;&quot;
    return render(request, &apos;auth/index.html&apos;)
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;urls.py里加映射：&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;# 跳转主页
    path(&apos;auth/index&apos;, helloWorld.views.to_index),

    # 用户注销
    path(&apos;auth/logout&apos;, helloWorld.views.logout),
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;主页index.html修改如下：&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;&amp;lt;!DOCTYPE html&amp;gt;
&amp;lt;html lang=&quot;en&quot;&amp;gt;
&amp;lt;head&amp;gt;
    &amp;lt;meta charset=&quot;UTF-8&quot;&amp;gt;
    &amp;lt;title&amp;gt;网站首页&amp;lt;/title&amp;gt;
&amp;lt;/head&amp;gt;
&amp;lt;body&amp;gt;
网站首页
{百分号 if request.user.is_authenticated %}
    ,欢迎: {{ request.user }}&amp;lt;br/&amp;gt;
    &amp;lt;a href=&quot;/auth/logout&quot;&amp;gt;注销&amp;lt;/a&amp;gt;
{百分号 else %}
    &amp;lt;a href=&quot;/auth/toLogin&quot;&amp;gt;登录&amp;lt;/a&amp;gt;
{百分号 endif %}

&amp;lt;/body&amp;gt;
&amp;lt;/html&amp;gt;
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;通过is_authenticated()方法可以判断用户是否登录认证；&lt;/p&gt;
&lt;p&gt;我们测试下：浏览器地址栏输入：&lt;code&gt;http://127.0.0.1:8000/auth/index&lt;/code&gt;&lt;/p&gt;
&lt;p&gt;![image-20240608160442602](D:\开发\Python\Django\2024版 一周掌握Django5视频教程 python web开发.assets\image-20240608160442602.png)&lt;/p&gt;
&lt;p&gt;点击登录，用户登录后再次跳转主页：&lt;/p&gt;
&lt;p&gt;![image-20240608160656377](D:\开发\Python\Django\2024版 一周掌握Django5视频教程 python web开发.assets\image-20240608160656377.png)&lt;/p&gt;
&lt;p&gt;我们点击注销：&lt;/p&gt;
&lt;p&gt;![image-20240608160709371](D:\开发\Python\Django\2024版 一周掌握Django5视频教程 python web开发.assets\image-20240608160709371.png)&lt;/p&gt;
&lt;h2&gt;Django5内置其他高级功能&lt;/h2&gt;
&lt;p&gt;Django5为开发者提供了常见的Web应用支持，如会话控制、缓存机制、CSRF 防护、消息框架、分页功能、国际化和本地化、和自定义中间件等，有效的提高了开发者的开发效率。&lt;/p&gt;
&lt;p&gt;这些支持点前面课程基本都有涉及，部分没涉及的，比如缓存机制，我们一般用redis，这些我们后面实战可以安排。感谢大家支持。&lt;/p&gt;
&lt;p&gt;后面计划安排两个Django5实战，一个是Vue3+Django5的通用权限系统，还有一个是Django5的博客系统，用来巩固和提升大家的python web开发技术。&lt;/p&gt;
</content:encoded></item><item><title>向往波特尔 1 级的夜空</title><link>https://meteor-comet.github.io/posts/starry-night-2021/</link><guid isPermaLink="true">https://meteor-comet.github.io/posts/starry-night-2021/</guid><description>记录一下21年夏天拍摄的星空</description><pubDate>Mon, 13 Sep 2021 00:00:00 GMT</pubDate><content:encoded>&lt;blockquote&gt;
&lt;p&gt;波特尔暗空分类法（Bortle scale）是业余天文学中用以测量特定观测点天空亮度的分类法，它量化了天体的可观察性以及光污染对天文观测的干扰程度。&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;&lt;img src=&quot;/images/in-post/post_starry_night/post_starry_night2.jpg&quot; alt=&quot;img1&quot; /&gt;
这是我第一次尝试拍摄星空时的曝光图。当时只是偶然发现新换的手机具备拍摄银河的条件，心血来潮随意调整了参数，拍下了这张噪点颇多的星空图。毫无疑问，光污染在其中起到了很大的干扰作用。&lt;/p&gt;
&lt;h2&gt;Fantasy&lt;/h2&gt;
&lt;p&gt;一直以来，我都渴望去几乎没有光污染的地方拍摄星空。那里纯净的夜空与苍穹交织，所拍摄出的效果必然比我所在的小城市要好得多。遗憾的是，时间总是捉襟见肘，使得这一梦想至今未能实现。&lt;/p&gt;
&lt;p&gt;当我意识到自己所在的拍摄地点光污染严重，星空的呈现受到很大影响时，我决定尝试拍摄一些不受光污染干扰的其他夜空题材。偶然间，我刷到了几张星轨的图片，被那种壮丽的旋转轨迹深深吸引，于是萌生了学习拍摄和合成星轨的念头。
虽然我始终用手机拍摄，显得有些不专业，但当图片呈现出来后，设备的差异似乎并不那么重要了。
拍摄过程其实很简单，我使用了延时拍摄技术，让设备在一段时间内连续拍摄，将大量图片合成一小段视频。至于星轨图的处理，我借助了PS中的堆栈法，参考
&lt;s&gt;&amp;lt;a href=&quot;http://wjd.name/starstail/&quot; target=&quot;_blank&quot;&amp;gt;小白星轨后期教程：叠加堆栈法&amp;lt;/a&amp;gt;&lt;/s&gt;
&amp;lt;a href=&quot;https://zhuanlan.zhihu.com/p/23355184&quot; target=&quot;_blank&quot;&amp;gt;【后期技法】如何使用PS中堆栈功能完成星轨的后期&amp;lt;/a&amp;gt;&lt;/p&gt;
&lt;p&gt;幸运的是，尽管拍摄时并未特别注意北极星的位置，仅凭感觉选了一个开阔的地方开始拍摄，最终的效果却意外的差强人意。
&lt;img src=&quot;/images/in-post/post_starry_night/post_starry_night3.jpg&quot; alt=&quot;img2&quot; /&gt;
&amp;lt;video src=&quot;/images/in-post/post_starry_night/post_starry_night.mp4&quot; width=&quot;700&quot; controls&amp;gt;
&amp;lt;/video&amp;gt;&lt;/p&gt;
&lt;h2&gt;Future&lt;/h2&gt;
&lt;p&gt;&lt;s&gt;未来或许没有太多时间去继续拍摄夜景，毕竟这只是一个兴趣爱好，而光污染5级的环境下也确实难以捕捉到太多理想的画面。然而，等到毕业后，如果时间允许，我一定会踏上追逐星空的旅途，专门去记录那片无垠的苍穹，捕捉属于我的夜空之梦。&lt;/s&gt;
太中二了,自己没绷住&lt;/p&gt;
</content:encoded></item><item><title>中国高等教育的系统性失败</title><link>https://meteor-comet.github.io/posts/the-systematic-failure-of-higher-education-in-china/</link><guid isPermaLink="true">https://meteor-comet.github.io/posts/the-systematic-failure-of-higher-education-in-china/</guid><description>The Systematic Failure of Higher Education in China</description><pubDate>Tue, 19 Jan 2021 00:00:00 GMT</pubDate><content:encoded>&lt;p&gt;高票 &lt;a href=&quot;https://www.zhihu.com/question/439622084/answer/1681505518&quot;&gt;@Youngster38324 的回答&lt;/a&gt;透露出来的本质上是「&lt;strong&gt;中国高等教育的系统性失败&lt;/strong&gt;」，逐层来看:&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;
&lt;p&gt;「&lt;strong&gt;进大学前唯分数和同质化教育&lt;/strong&gt;」导致了太多人去大学后根本不知道自己要干嘛，很多人专业根本就不是自己选的更不要说知道自己有没有兴趣了，即便是很多高分考生也路径依赖地以为继续努力填鸭就能成功，没有意识到高考后的人生已经换赛道了。这里对比美国高中生的 AP（Advanced Placement）预科制度以及整体社会鼓励向自我发展看而不是向钱看的风气。&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;「&lt;strong&gt;进大学后专业制度没有容错性&lt;/strong&gt;」，即便已经发现自己不喜欢被录取专业了也没有办法，因为转专业制度毫无人性（通常要求你先要在你已经不喜欢了的本专业内卷到班级前多少名）。对比美国本科，专业可以 undecided（先上课再决定专业，比如经典的哈佛 CS50，你上下来感兴趣了再去选 CS）；学位本身只是某个学科下课程学分累计的自然结果，因此可以灵活的转专业与多学位；班级这种促进内卷的概念被弱化，强调跟自己比关注个人的成长，学生自己控制上几年课，念几个学位，中间休学一下都没关系。&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;前两步的结果就是导致大学为了毕业率把「&lt;strong&gt;评判标准搞成了大锅饭平均主义&lt;/strong&gt;」，为了能够每年顺利向社会输送一批（80% 将来都不会从事本专业）的人才，打分根据每年学生情况动态规划自适应，把大学搞成了「严进宽出」。相反美国的大学教育强调「宽进严出」，无论你底子如何，无论重修几次，你只要通过了某个（相对稳定的）客观标准就是合格，为了保证该制度的机械性运作，辅以严格的日常作业计分，对「作弊」零容忍（自动化判重，发现一次重修两次退学）。&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;平均主义进一步导致「&lt;strong&gt;课程设置没有灵活性无法自定义&lt;/strong&gt;」，老师不但要照顾及格率还有一颗圣母心希望那些对专业没什么兴趣的人能好歹学到点东西，同时又真心欣赏且想要给予好学的尖子生资源，最后即便绞尽脑汁了也还是只能弄出个在差生里下限高在尖子生里上限低的课程安排两面不当人 —— 尖子生觉得课程要求太低不能激发自己的潜力喂不饱（常见于私下要求加难度或者去无学分旁听），摸鱼的觉得老师影响了我的快乐学习（常见于课堂上一布置作业下面就叫苦连天）该挂还是挂或者 60 分万岁。对比美国，学生的课程表可以自行安排，通常一个课会开多个班次照顾灵活性，学霸可以比别人多上任意节课，也可以跨专业选课或者减少课程增加实习或研究，而且难度自选只要你点过前置技能就行。&lt;/p&gt;
&lt;/li&gt;
&lt;/ol&gt;
&lt;p&gt;有类似迟先生这样诉求的人很多，可是一个系统很难由系统内的个体改变，所以很多个体选择了做局部优化趋利避害陪玩成为既得利益者，或者全局优化更换自己所处的系统。&lt;/p&gt;
&lt;p&gt;只要所处系统里的大部分个体都已经默许了这个游戏规则，无论迟先生是「凡尔赛」还是「理想主义」，改变赛道规则就会被其他个体认为侵害到利益。小孩才分对错，成年人的屁股都是歪得，都是各取所需。&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;都是这个时代的缩影。&lt;/strong&gt;&lt;/p&gt;
</content:encoded></item></channel></rss>