-
Notifications
You must be signed in to change notification settings - Fork 0
Expand file tree
/
Copy pathatom.xml
More file actions
476 lines (240 loc) · 401 KB
/
Copy pathatom.xml
File metadata and controls
476 lines (240 loc) · 401 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
453
454
455
456
457
458
459
460
461
462
463
464
465
466
467
468
469
470
471
472
473
474
475
476
<?xml version="1.0" encoding="utf-8"?>
<feed xmlns="http://www.w3.org/2005/Atom">
<title>probie's blog</title>
<subtitle>记录学习和生活的平凡</subtitle>
<link href="https://probieluo.github.io/atom.xml" rel="self"/>
<link href="https://probieluo.github.io/"/>
<updated>2026-05-08T09:17:42.067Z</updated>
<id>https://probieluo.github.io/</id>
<author>
<name>probie</name>
</author>
<generator uri="https://hexo.io/">Hexo</generator>
<entry>
<title>wpf border空白区域无法点击</title>
<link href="https://probieluo.github.io/posts/wpf-border-hittest/"/>
<id>https://probieluo.github.io/posts/wpf-border-hittest/</id>
<published>2026-05-08T09:00:55.000Z</published>
<updated>2026-05-08T09:17:42.067Z</updated>
<content type="html"><![CDATA[<p>以下代码在无内容空白区域无法点击选中</p><p>因为border的Background默认null,为null时border区域不参与点击命中测试!</p><p>改成Background=Transparent或者让它不为null即可</p><figure class="highlight xml"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br><span class="line">25</span><br><span class="line">26</span><br><span class="line">27</span><br><span class="line">28</span><br><span class="line">29</span><br><span class="line">30</span><br><span class="line">31</span><br><span class="line">32</span><br><span class="line">33</span><br><span class="line">34</span><br><span class="line">35</span><br><span class="line">36</span><br><span class="line">37</span><br><span class="line">38</span><br><span class="line">39</span><br><span class="line">40</span><br><span class="line">41</span><br><span class="line">42</span><br><span class="line">43</span><br><span class="line">44</span><br><span class="line">45</span><br><span class="line">46</span><br><span class="line">47</span><br><span class="line">48</span><br><span class="line">49</span><br><span class="line">50</span><br><span class="line">51</span><br><span class="line">52</span><br><span class="line">53</span><br><span class="line">54</span><br><span class="line">55</span><br><span class="line">56</span><br><span class="line">57</span><br><span class="line">58</span><br><span class="line">59</span><br><span class="line">60</span><br><span class="line">61</span><br><span class="line">62</span><br><span class="line">63</span><br><span class="line">64</span><br><span class="line">65</span><br><span class="line">66</span><br><span class="line">67</span><br><span class="line">68</span><br><span class="line">69</span><br><span class="line">70</span><br><span class="line">71</span><br><span class="line">72</span><br><span class="line">73</span><br><span class="line">74</span><br><span class="line">75</span><br><span class="line">76</span><br><span class="line">77</span><br><span class="line">78</span><br><span class="line">79</span><br><span class="line">80</span><br><span class="line">81</span><br><span class="line">82</span><br><span class="line">83</span><br><span class="line">84</span><br><span class="line">85</span><br><span class="line">86</span><br><span class="line">87</span><br><span class="line">88</span><br><span class="line">89</span><br><span class="line">90</span><br><span class="line">91</span><br><span class="line">92</span><br><span class="line">93</span><br><span class="line">94</span><br><span class="line">95</span><br><span class="line">96</span><br><span class="line">97</span><br><span class="line">98</span><br><span class="line">99</span><br><span class="line">100</span><br><span class="line">101</span><br><span class="line">102</span><br></pre></td><td class="code"><pre><span class="line"><span class="tag"><<span class="name">ItemsControl</span></span></span><br><span class="line"><span class="tag"> <span class="attr">Grid.Row</span>=<span class="string">"1"</span></span></span><br><span class="line"><span class="tag"> <span class="attr">Margin</span>=<span class="string">"20"</span></span></span><br><span class="line"><span class="tag"> <span class="attr">ItemsSource</span>=<span class="string">"{Binding Items}"</span>></span></span><br><span class="line"> <span class="tag"><<span class="name">ItemsControl.ItemsPanel</span>></span></span><br><span class="line"> <span class="tag"><<span class="name">ItemsPanelTemplate</span>></span></span><br><span class="line"> <span class="tag"><<span class="name">WrapPanel</span></span></span><br><span class="line"><span class="tag"> <span class="attr">HorizontalAlignment</span>=<span class="string">"Left"</span></span></span><br><span class="line"><span class="tag"> <span class="attr">VerticalAlignment</span>=<span class="string">"Top"</span></span></span><br><span class="line"><span class="tag"> <span class="attr">Orientation</span>=<span class="string">"Horizontal"</span>/></span></span><br><span class="line"> <span class="tag"></<span class="name">ItemsPanelTemplate</span>></span></span><br><span class="line"> <span class="tag"></<span class="name">ItemsControl.ItemsPanel</span>></span></span><br><span class="line"></span><br><span class="line"> <span class="tag"><<span class="name">ItemsControl.ItemTemplate</span>></span></span><br><span class="line"> <span class="tag"><<span class="name">DataTemplate</span>></span></span><br><span class="line"> <span class="tag"><<span class="name">Grid</span> <span class="attr">Margin</span>=<span class="string">"5"</span>></span></span><br><span class="line"> <span class="tag"><<span class="name">Grid.ColumnDefinitions</span>></span></span><br><span class="line"> <span class="tag"><<span class="name">ColumnDefinition</span> <span class="attr">Width</span>=<span class="string">"*"</span>/></span></span><br><span class="line"> <span class="tag"><<span class="name">ColumnDefinition</span> <span class="attr">Width</span>=<span class="string">"30"</span>/></span></span><br><span class="line"> <span class="tag"></<span class="name">Grid.ColumnDefinitions</span>></span></span><br><span class="line"></span><br><span class="line"> <span class="tag"><<span class="name">Border</span></span></span><br><span class="line"><span class="tag"> <span class="attr">x:Name</span>=<span class="string">"StepBorder"</span></span></span><br><span class="line"><span class="tag"> <span class="attr">BorderBrush</span>=<span class="string">"#DDD"</span></span></span><br><span class="line"><span class="tag"> <span class="attr">BorderThickness</span>=<span class="string">"1"</span></span></span><br><span class="line"><span class="tag"> <span class="attr">CornerRadius</span>=<span class="string">"8"</span></span></span><br><span class="line"><span class="tag"> <span class="attr">Cursor</span>=<span class="string">"Hand"</span>></span></span><br><span class="line"> <span class="tag"><<span class="name">Grid</span> <span class="attr">Margin</span>=<span class="string">"10"</span>></span></span><br><span class="line"> <span class="tag"><<span class="name">Grid.ColumnDefinitions</span>></span></span><br><span class="line"> <span class="tag"><<span class="name">ColumnDefinition</span> <span class="attr">Width</span>=<span class="string">"40"</span>/></span></span><br><span class="line"> <span class="tag"><<span class="name">ColumnDefinition</span> <span class="attr">Width</span>=<span class="string">"120"</span>/></span></span><br><span class="line"> <span class="tag"></<span class="name">Grid.ColumnDefinitions</span>></span></span><br><span class="line"> <span class="tag"><<span class="name">Ellipse</span></span></span><br><span class="line"><span class="tag"> <span class="attr">Width</span>=<span class="string">"40"</span></span></span><br><span class="line"><span class="tag"> <span class="attr">Height</span>=<span class="string">"40"</span></span></span><br><span class="line"><span class="tag"> <span class="attr">Fill</span>=<span class="string">"#bee6fd"</span>/></span></span><br><span class="line"> <span class="tag"><<span class="name">TextBlock</span></span></span><br><span class="line"><span class="tag"> <span class="attr">HorizontalAlignment</span>=<span class="string">"Center"</span></span></span><br><span class="line"><span class="tag"> <span class="attr">VerticalAlignment</span>=<span class="string">"Center"</span></span></span><br><span class="line"><span class="tag"> <span class="attr">Text</span>=<span class="string">"{Binding StepNumber}"</span>/></span></span><br><span class="line"></span><br><span class="line"> <span class="tag"><<span class="name">TextBlock</span></span></span><br><span class="line"><span class="tag"> <span class="attr">Grid.Column</span>=<span class="string">"1"</span></span></span><br><span class="line"><span class="tag"> <span class="attr">Width</span>=<span class="string">"100"</span></span></span><br><span class="line"><span class="tag"> <span class="attr">Margin</span>=<span class="string">"5,0,0,0"</span></span></span><br><span class="line"><span class="tag"> <span class="attr">VerticalAlignment</span>=<span class="string">"Center"</span></span></span><br><span class="line"><span class="tag"> <span class="attr">Text</span>=<span class="string">"{Binding AlgorithmName}"</span></span></span><br><span class="line"><span class="tag"> <span class="attr">TextWrapping</span>=<span class="string">"Wrap"</span>/></span></span><br><span class="line"> <span class="tag"></<span class="name">Grid</span>></span></span><br><span class="line"> <span class="tag"><<span class="name">Border.InputBindings</span>></span></span><br><span class="line"> <span class="tag"><<span class="name">MouseBinding</span></span></span><br><span class="line"><span class="tag"> <span class="attr">Command</span>=<span class="string">"{Binding DataContext.SelectItemCommand, RelativeSource={RelativeSource AncestorType=ItemsControl}}"</span></span></span><br><span class="line"><span class="tag"> <span class="attr">CommandParameter</span>=<span class="string">"{Binding .}"</span></span></span><br><span class="line"><span class="tag"> <span class="attr">MouseAction</span>=<span class="string">"LeftClick"</span>/></span></span><br><span class="line"></span><br><span class="line"> <span class="tag"><<span class="name">MouseBinding</span> <span class="attr">Command</span>=<span class="string">"{Binding DataContext.EditCommand, RelativeSource={RelativeSource AncestorType=ItemsControl}}"</span></span></span><br><span class="line"><span class="tag"> <span class="attr">MouseAction</span>=<span class="string">"LeftDoubleClick"</span>/></span></span><br><span class="line"> <span class="tag"></<span class="name">Border.InputBindings</span>></span></span><br><span class="line"> <span class="tag"></<span class="name">Border</span>></span></span><br><span class="line"></span><br><span class="line"> <span class="tag"><<span class="name">iconPacks:PackIconMaterial</span></span></span><br><span class="line"><span class="tag"> <span class="attr">Grid.Column</span>=<span class="string">"1"</span></span></span><br><span class="line"><span class="tag"> <span class="attr">Margin</span>=<span class="string">"10,0,0,0"</span></span></span><br><span class="line"><span class="tag"> <span class="attr">VerticalAlignment</span>=<span class="string">"Center"</span></span></span><br><span class="line"><span class="tag"> <span class="attr">Kind</span>=<span class="string">"ArrowRight"</span>></span></span><br><span class="line"></span><br><span class="line"> <span class="tag"><<span class="name">iconPacks:PackIconMaterial.Visibility</span>></span></span><br><span class="line"> <span class="tag"><<span class="name">MultiBinding</span> <span class="attr">Converter</span>=<span class="string">"{StaticResource StepNumberToArrowVisiblityConverter}"</span>></span></span><br><span class="line"> <span class="tag"><<span class="name">Binding</span> <span class="attr">Path</span>=<span class="string">"StepNumber"</span>/></span></span><br><span class="line"> <span class="tag"><<span class="name">Binding</span> <span class="attr">Path</span>=<span class="string">"DataContext.Items.Count"</span></span></span><br><span class="line"><span class="tag"> <span class="attr">RelativeSource</span>=<span class="string">"{RelativeSource AncestorType=ItemsControl}"</span>/></span></span><br><span class="line"> <span class="tag"></<span class="name">MultiBinding</span>></span></span><br><span class="line"> <span class="tag"></<span class="name">iconPacks:PackIconMaterial.Visibility</span>></span></span><br><span class="line"> <span class="tag"></<span class="name">iconPacks:PackIconMaterial</span>></span></span><br><span class="line"> <span class="tag"></<span class="name">Grid</span>></span></span><br><span class="line"></span><br><span class="line"> <span class="tag"><<span class="name">DataTemplate.Triggers</span>></span></span><br><span class="line"> <span class="tag"><<span class="name">MultiDataTrigger</span>></span></span><br><span class="line"> <span class="tag"><<span class="name">MultiDataTrigger.Conditions</span>></span></span><br><span class="line"> <span class="tag"><<span class="name">Condition</span>></span></span><br><span class="line"> <span class="tag"><<span class="name">Condition.Binding</span>></span></span><br><span class="line"> <span class="tag"><<span class="name">MultiBinding</span> <span class="attr">Converter</span>=<span class="string">"{StaticResource IsSelectedItemConverter}"</span>></span></span><br><span class="line"> <span class="tag"><<span class="name">Binding</span> <span class="attr">Path</span>=<span class="string">"."</span>/></span></span><br><span class="line"> <span class="tag"><<span class="name">Binding</span> <span class="attr">Path</span>=<span class="string">"DataContext.SelectedItem"</span></span></span><br><span class="line"><span class="tag"> <span class="attr">RelativeSource</span>=<span class="string">"{RelativeSource AncestorType=ItemsControl}"</span>/></span></span><br><span class="line"> <span class="tag"></<span class="name">MultiBinding</span>></span></span><br><span class="line"> <span class="tag"></<span class="name">Condition.Binding</span>></span></span><br><span class="line"> <span class="tag"><<span class="name">Condition.Value</span>></span>True<span class="tag"></<span class="name">Condition.Value</span>></span></span><br><span class="line"> <span class="tag"></<span class="name">Condition</span>></span></span><br><span class="line"> <span class="tag"></<span class="name">MultiDataTrigger.Conditions</span>></span></span><br><span class="line"></span><br><span class="line"> <span class="tag"><<span class="name">Setter</span> <span class="attr">TargetName</span>=<span class="string">"StepBorder"</span></span></span><br><span class="line"><span class="tag"> <span class="attr">Property</span>=<span class="string">"BorderBrush"</span></span></span><br><span class="line"><span class="tag"> <span class="attr">Value</span>=<span class="string">"#3498db"</span>/></span></span><br><span class="line"> <span class="tag"><<span class="name">Setter</span> <span class="attr">TargetName</span>=<span class="string">"StepBorder"</span></span></span><br><span class="line"><span class="tag"> <span class="attr">Property</span>=<span class="string">"Background"</span></span></span><br><span class="line"><span class="tag"> <span class="attr">Value</span>=<span class="string">"#0078d7"</span>/></span></span><br><span class="line"> <span class="tag"></<span class="name">MultiDataTrigger</span>></span></span><br><span class="line"> <span class="tag"></<span class="name">DataTemplate.Triggers</span>></span></span><br><span class="line"> <span class="tag"></<span class="name">DataTemplate</span>></span></span><br><span class="line"> <span class="tag"></<span class="name">ItemsControl.ItemTemplate</span>></span></span><br><span class="line"><span class="tag"></<span class="name">ItemsControl</span>></span></span><br></pre></td></tr></table></figure>]]></content>
<summary type="html"><p>以下代码在无内容空白区域无法点击选中</p>
<p>因为border的Background默认null,为null时border区域不参与点击命中测试!</p>
<p>改成Background&#x3D;Transparent或者让它不为null即可</p>
<figure</summary>
<category term="编程技术" scheme="https://probieluo.github.io/categories/%E7%BC%96%E7%A8%8B%E6%8A%80%E6%9C%AF/"/>
<category term="wpf" scheme="https://probieluo.github.io/tags/wpf/"/>
</entry>
<entry>
<title>拼豆日记</title>
<link href="https://probieluo.github.io/posts/pindou-diary/"/>
<id>https://probieluo.github.io/posts/pindou-diary/</id>
<published>2026-05-01T15:51:22.000Z</published>
<updated>2026-05-08T08:52:58.307Z</updated>
<content type="html"><![CDATA[<p>下了一个星期的雨,终于赶到五一给了个好天气。</p><p><img src="/../assets/%E5%BE%AE%E4%BF%A1%E5%9B%BE%E7%89%87_20260501235807_114_385.jpg" alt="出来玩最重要的就是出来"></p><p>本来出来也没想好要干啥来着,看到拼豆店就进去玩了一下,之前也是没接触过的,但这玩意太上头,一玩就是一天,早上十点拼了一个,之后吃了个午饭又去了,最后成功玩到店打样,买的全天票也是值回票价了😄。</p><p>战果展示</p><p><img src="/../assets/%E5%BE%AE%E4%BF%A1%E5%9B%BE%E7%89%87_20260501235806_112_385.jpg" alt="终于拼完了"></p><p>耿鬼超帅有没有</p><p><img src="/../assets/%E5%BE%AE%E4%BF%A1%E5%9B%BE%E7%89%87_20260501235800_106_385.jpg" alt="小火龙轮廓"></p><p><img src="/../assets/%E5%BE%AE%E4%BF%A1%E5%9B%BE%E7%89%87_20260501235802_108_385.jpg" alt="小火龙完成!"></p><p><img src="/../assets/%E5%BE%AE%E4%BF%A1%E5%9B%BE%E7%89%87_20260501235801_107_385.jpg" alt="女朋友拼了个库洛米"></p><p><img src="/../assets/%E5%BE%AE%E4%BF%A1%E5%9B%BE%E7%89%87_20260501235803_109_385.jpg" alt="胖丁"></p><p><img src="/../assets/%E5%BE%AE%E4%BF%A1%E5%9B%BE%E7%89%87_20260501235804_110_385.jpg" alt="小火龙"></p><p><img src="/../assets/%E5%BE%AE%E4%BF%A1%E5%9B%BE%E7%89%87_20260501235805_111_385.jpg" alt="杰尼龟"></p><p><img src="/../assets/%E5%BE%AE%E4%BF%A1%E5%9B%BE%E7%89%87_20260501235807_113_385.jpg" alt="拼完出来以及九点了,吃饭吧"></p><p><img src="/../assets/%E5%BE%AE%E4%BF%A1%E5%9B%BE%E7%89%87_20260501235758_104_385.jpg" alt="终于回家了,忍不住又拍了一张,今日战果!"></p><p><img src="/../assets/%E5%BE%AE%E4%BF%A1%E5%9B%BE%E7%89%87_20260501235759_105_385.jpg" alt="女朋友拼了两个大图"></p><p>拼豆图纸</p><p><img src="/../assets/%E5%9B%BE%E7%BA%B81.jpg"></p><p><img src="/../assets/%E5%9B%BE%E7%BA%B82.jpg"></p><p><img src="/../assets/%E5%9B%BE%E7%BA%B83.jpg"></p><p><img src="/../assets/%E5%9B%BE%E7%BA%B84.jpg"></p><p><img src="/../assets/%E5%9B%BE%E7%BA%B85.jpg"></p><p><img src="/../assets/%E5%9B%BE%E7%BA%B86.jpg"></p><p><img src="/../assets/%E5%9B%BE%E7%BA%B87.jpg"></p><p><img src="/../assets/%E5%9B%BE%E7%BA%B88.jpg"></p><p><img src="/../assets/%E5%9B%BE%E7%BA%B89.jpg"></p>]]></content>
<summary type="html"><p>下了一个星期的雨,终于赶到五一给了个好天气。</p>
<p><img src="/../assets/%E5%BE%AE%E4%BF%A1%E5%9B%BE%E7%89%87_20260501235807_114_385.jpg" alt="出来玩最重要的就是出来"></p</summary>
<category term="随笔" scheme="https://probieluo.github.io/categories/%E9%9A%8F%E7%AC%94/"/>
</entry>
<entry>
<title>wpf多值转换器错用导致ui阻塞</title>
<link href="https://probieluo.github.io/posts/wpf-converter-block-ui/"/>
<id>https://probieluo.github.io/posts/wpf-converter-block-ui/</id>
<published>2026-04-30T09:43:00.000Z</published>
<updated>2026-05-08T09:06:15.084Z</updated>
<content type="html"><![CDATA[<p>也是个很奇葩的问题了,有一个多值转换器IMultiValueConverter在ItemsControl里当成IValueConverter使用,编译不报错,可正常运行,但就是会阻塞ui。代码如下</p><p>xaml</p><figure class="highlight xml"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br></pre></td><td class="code"><pre><span class="line"><span class="tag"><<span class="name">ItemsControl</span> <span class="attr">ItemsSource</span>=<span class="string">"{Binding Items}"</span>></span></span><br><span class="line"> <span class="tag"><<span class="name">ItemsControl.ItemTemplate</span>></span></span><br><span class="line"> <span class="tag"><<span class="name">DataTemplate</span>></span></span><br><span class="line"> <span class="tag"><<span class="name">Border</span>></span></span><br><span class="line"> <span class="tag"><<span class="name">Grid</span>></span></span><br><span class="line"> <span class="tag"><<span class="name">TextBox</span></span></span><br><span class="line"><span class="tag"> <span class="attr">Text</span>=<span class="string">"{Binding Rate, Converter={StaticResource DataTransferRateUnitValuesConverter}}"</span> /></span></span><br><span class="line"> <span class="tag"></<span class="name">Grid</span>></span></span><br><span class="line"> <span class="tag"></<span class="name">Border</span>></span></span><br><span class="line"> <span class="tag"></<span class="name">DataTemplate</span>></span></span><br><span class="line"> <span class="tag"></<span class="name">ItemsControl.ItemTemplate</span>></span></span><br><span class="line"><span class="tag"></<span class="name">ItemsControl</span>></span></span><br></pre></td></tr></table></figure><p>csharp</p><figure class="highlight csharp"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br><span class="line">25</span><br><span class="line">26</span><br><span class="line">27</span><br><span class="line">28</span><br><span class="line">29</span><br><span class="line">30</span><br><span class="line">31</span><br><span class="line">32</span><br><span class="line">33</span><br><span class="line">34</span><br><span class="line">35</span><br><span class="line">36</span><br><span class="line">37</span><br><span class="line">38</span><br><span class="line">39</span><br><span class="line">40</span><br><span class="line">41</span><br><span class="line">42</span><br><span class="line">43</span><br><span class="line">44</span><br><span class="line">45</span><br><span class="line">46</span><br><span class="line">47</span><br><span class="line">48</span><br><span class="line">49</span><br><span class="line">50</span><br><span class="line">51</span><br><span class="line">52</span><br><span class="line">53</span><br><span class="line">54</span><br><span class="line">55</span><br><span class="line">56</span><br><span class="line">57</span><br><span class="line">58</span><br><span class="line">59</span><br><span class="line">60</span><br><span class="line">61</span><br><span class="line">62</span><br><span class="line">63</span><br><span class="line">64</span><br><span class="line">65</span><br><span class="line">66</span><br><span class="line">67</span><br><span class="line">68</span><br><span class="line">69</span><br><span class="line">70</span><br><span class="line">71</span><br><span class="line">72</span><br><span class="line">73</span><br><span class="line">74</span><br><span class="line">75</span><br><span class="line">76</span><br><span class="line">77</span><br><span class="line">78</span><br><span class="line">79</span><br><span class="line">80</span><br><span class="line">81</span><br><span class="line">82</span><br><span class="line">83</span><br><span class="line">84</span><br><span class="line">85</span><br><span class="line">86</span><br><span class="line">87</span><br><span class="line">88</span><br><span class="line">89</span><br><span class="line">90</span><br><span class="line">91</span><br></pre></td><td class="code"><pre><span class="line"><span class="keyword">public</span> <span class="keyword">class</span> <span class="title">DataTransferRateUnitValuesConverter</span> : <span class="title">IMultiValueConverter</span></span><br><span class="line">{</span><br><span class="line"> <span class="function"><span class="keyword">public</span> <span class="built_in">object</span> <span class="title">Convert</span>(<span class="params"><span class="built_in">object</span>[] values, Type targetType, <span class="built_in">object</span> parameter, CultureInfo culture</span>)</span></span><br><span class="line"> {</span><br><span class="line"> <span class="keyword">if</span> (parameter != <span class="literal">null</span> && parameter.ToString() == <span class="string">"bps"</span> && values.Length == <span class="number">1</span> && values[<span class="number">0</span>] <span class="keyword">is</span> <span class="built_in">double</span> rate1)</span><br><span class="line"> {</span><br><span class="line"> <span class="keyword">return</span> <span class="string">$"<span class="subst">{DataUnitHelper.GetTransferRate(rate1)}</span>"</span>;</span><br><span class="line"> }</span><br><span class="line"> <span class="keyword">if</span> (values.Length == <span class="number">2</span> && values[<span class="number">0</span>] <span class="keyword">is</span> <span class="built_in">string</span> channelname && values[<span class="number">1</span>] <span class="keyword">is</span> <span class="built_in">double</span> rate)</span><br><span class="line"> {</span><br><span class="line"> <span class="keyword">if</span> (channelname.StartsWith(<span class="string">"pps"</span>, StringComparison.CurrentCultureIgnoreCase))</span><br><span class="line"> {</span><br><span class="line"> <span class="keyword">return</span> <span class="string">$"<span class="subst">{rate:<span class="number">0.</span>###}</span> pps"</span>;</span><br><span class="line"> }</span><br><span class="line"> <span class="keyword">else</span></span><br><span class="line"> {</span><br><span class="line"> <span class="keyword">return</span> <span class="string">$"<span class="subst">{DataUnitHelper.GetTransferRate(rate)}</span>"</span>;</span><br><span class="line"> }</span><br><span class="line"> }</span><br><span class="line"></span><br><span class="line"> <span class="keyword">return</span> <span class="string">"0 bps"</span>;</span><br><span class="line"> }</span><br><span class="line"></span><br><span class="line"> <span class="function"><span class="keyword">public</span> <span class="built_in">object</span>[] <span class="title">ConvertBack</span>(<span class="params"><span class="built_in">object</span> <span class="keyword">value</span>, Type[] targetTypes, <span class="built_in">object</span> parameter, CultureInfo culture</span>)</span></span><br><span class="line"> {</span><br><span class="line"> <span class="keyword">throw</span> <span class="keyword">new</span> NotImplementedException();</span><br><span class="line"> }</span><br><span class="line">}</span><br><span class="line"></span><br><span class="line"><span class="keyword">public</span> <span class="keyword">class</span> <span class="title">DataUnitHelper</span></span><br><span class="line">{</span><br><span class="line"> <span class="keyword">private</span> <span class="keyword">static</span> <span class="keyword">readonly</span> <span class="built_in">ulong</span> TbLength = Convert.ToUInt64(Math.Pow(<span class="number">1024</span>, <span class="number">4</span>));</span><br><span class="line"> <span class="keyword">private</span> <span class="keyword">static</span> <span class="keyword">readonly</span> <span class="built_in">ulong</span> GbLength = Convert.ToUInt64(Math.Pow(<span class="number">1024</span>, <span class="number">3</span>));</span><br><span class="line"> <span class="keyword">private</span> <span class="keyword">static</span> <span class="keyword">readonly</span> <span class="built_in">ulong</span> MbLength = Convert.ToUInt64(Math.Pow(<span class="number">1024</span>, <span class="number">2</span>));</span><br><span class="line"> <span class="keyword">private</span> <span class="keyword">static</span> <span class="keyword">readonly</span> <span class="built_in">ulong</span> KbLength = <span class="number">1024</span>;</span><br><span class="line"></span><br><span class="line"> <span class="keyword">private</span> <span class="keyword">static</span> <span class="keyword">readonly</span> <span class="built_in">ulong</span> TbpsLength = Convert.ToUInt64(Math.Pow(<span class="number">10</span>, <span class="number">12</span>));</span><br><span class="line"> <span class="keyword">private</span> <span class="keyword">static</span> <span class="keyword">readonly</span> <span class="built_in">ulong</span> GbpsLength = Convert.ToUInt64(Math.Pow(<span class="number">10</span>, <span class="number">9</span>));</span><br><span class="line"> <span class="keyword">private</span> <span class="keyword">static</span> <span class="keyword">readonly</span> <span class="built_in">ulong</span> MbpsLength = Convert.ToUInt64(Math.Pow(<span class="number">10</span>, <span class="number">6</span>));</span><br><span class="line"> <span class="keyword">private</span> <span class="keyword">static</span> <span class="keyword">readonly</span> <span class="built_in">ulong</span> KbpsLength = Convert.ToUInt64(Math.Pow(<span class="number">10</span>, <span class="number">3</span>));</span><br><span class="line"></span><br><span class="line"> <span class="function"><span class="keyword">public</span> <span class="keyword">static</span> <span class="built_in">string</span> <span class="title">GetLength</span>(<span class="params"><span class="built_in">ulong</span> length</span>)</span></span><br><span class="line"> {</span><br><span class="line"> <span class="keyword">if</span> (length >= TbLength)</span><br><span class="line"> {</span><br><span class="line"> <span class="keyword">return</span> (length * <span class="number">1.0</span> / TbLength).ToString(<span class="string">"0.###"</span>) + <span class="string">" TB"</span>;</span><br><span class="line"> }</span><br><span class="line"></span><br><span class="line"> <span class="keyword">if</span> (length >= GbLength)</span><br><span class="line"> {</span><br><span class="line"> <span class="keyword">return</span> (length * <span class="number">1.0</span> / (GbLength)).ToString(<span class="string">"0.###"</span>) + <span class="string">" GB"</span>;</span><br><span class="line"> }</span><br><span class="line"></span><br><span class="line"> <span class="keyword">if</span> (length >= MbLength)</span><br><span class="line"> {</span><br><span class="line"> <span class="keyword">return</span> (length * <span class="number">1.0</span> / (MbLength)).ToString(<span class="string">"0.###"</span>) + <span class="string">" MB"</span>;</span><br><span class="line"> }</span><br><span class="line"></span><br><span class="line"> <span class="keyword">if</span> (length >= KbLength)</span><br><span class="line"> {</span><br><span class="line"> <span class="keyword">return</span> (length * <span class="number">1.0</span> / KbLength).ToString(<span class="string">"0.###"</span>) + <span class="string">" KB"</span>;</span><br><span class="line"> }</span><br><span class="line"></span><br><span class="line"> <span class="keyword">return</span> length + <span class="string">" B"</span>;</span><br><span class="line"> }</span><br><span class="line"></span><br><span class="line"> <span class="function"><span class="keyword">public</span> <span class="keyword">static</span> <span class="built_in">string</span> <span class="title">GetTransferRate</span>(<span class="params"><span class="built_in">double</span> rate</span>)</span></span><br><span class="line"> {</span><br><span class="line"> <span class="keyword">if</span> (rate >= TbpsLength)</span><br><span class="line"> {</span><br><span class="line"> <span class="keyword">return</span> (rate * <span class="number">1.0</span> / TbpsLength).ToString(<span class="string">"0.###"</span>) + <span class="string">" Tbps"</span>;</span><br><span class="line"> }</span><br><span class="line"></span><br><span class="line"> <span class="keyword">if</span> (rate >= GbpsLength)</span><br><span class="line"> {</span><br><span class="line"> <span class="keyword">return</span> (rate * <span class="number">1.0</span> / (GbpsLength)).ToString(<span class="string">"0.###"</span>) + <span class="string">" Gbps"</span>;</span><br><span class="line"> }</span><br><span class="line"></span><br><span class="line"> <span class="keyword">if</span> (rate >= MbpsLength)</span><br><span class="line"> {</span><br><span class="line"> <span class="keyword">return</span> (rate * <span class="number">1.0</span> / (MbpsLength)).ToString(<span class="string">"0.###"</span>) + <span class="string">" Mbps"</span>;</span><br><span class="line"> }</span><br><span class="line"></span><br><span class="line"> <span class="keyword">if</span> (rate >= KbpsLength)</span><br><span class="line"> {</span><br><span class="line"> <span class="keyword">return</span> (rate * <span class="number">1.0</span> / KbpsLength).ToString(<span class="string">"0.###"</span>) + <span class="string">" Kbps"</span>;</span><br><span class="line"> }</span><br><span class="line"></span><br><span class="line"> <span class="keyword">return</span> rate.ToString(<span class="string">"0.###"</span>) + <span class="string">" bps"</span>;</span><br><span class="line"> }</span><br><span class="line">}</span><br></pre></td></tr></table></figure><p>经查阅资料,原来wpf做了自动兼容容错:把IMultiValueConverter当成 IValueConverter用,wpf不会崩溃,只会默默返回 DependencyProperty.UnsetValue</p><p>故而ui阻塞过程:</p><p>用错转换器 → WPF 引擎进入无限兼容 / 重试逻辑 → ui线程死循环阻塞</p><p>当然,改过来就行了。</p><p>我是因为这个转换器原本是IValueConverter,由于需求变化,改成了IMultiValueConverter,但妙就秒在这个转换器很多地方在用,因为不会显式的提醒编译报错,故而遗漏这个,这里也是很奇怪,其他的地方没有ItemsControl包裹确实是会提醒报错地方的,悲啊,最后定位到这个转换器全局查找才找到的。</p>]]></content>
<summary type="html"><p>也是个很奇葩的问题了,有一个多值转换器IMultiValueConverter在ItemsControl里当成IValueConverter使用,编译不报错,可正常运行,但就是会阻塞ui。代码如下</p>
<p>xaml</p>
<figure class="highlig</summary>
<category term="编程技术" scheme="https://probieluo.github.io/categories/%E7%BC%96%E7%A8%8B%E6%8A%80%E6%9C%AF/"/>
<category term="wpf" scheme="https://probieluo.github.io/tags/wpf/"/>
</entry>
<entry>
<title>wpf命令里使用了await为什么还会阻塞ui</title>
<link href="https://probieluo.github.io/posts/wpf-command-await-block-ui/"/>
<id>https://probieluo.github.io/posts/wpf-command-await-block-ui/</id>
<published>2026-04-30T05:48:34.000Z</published>
<updated>2026-05-08T09:06:34.428Z</updated>
<content type="html"><![CDATA[<p>在最近在开发中遇到个很有意思的问题,我在一个命令里面使用了一些sql执行操作,虽然这些sql执行都使用了<code>await</code>等待sql耗时操作,但ui还是阻塞了,百思不得其解,思考了半天问了我们组长,组长说在await执行完成后还是会回到ui线程,最好把这些与ui无关代码的用task.run包起来,放在后台线程。</p><p>虽然问题成功得到了解决,但对await阻塞了ui线程这个问题还是不解,我们知道await让出了线程,不阻塞ui,等任务完成后会回来继续跑,那它为什么阻塞了ui呢?至少看起来是阻塞了ui。</p><p>经查阅资料,实际上await并不会一开始就把所有操作都放在后台,只有遇到了真正的io等待时才会切到后台线程,下面是我在命令里执行的异步方法</p><div class="collapse-box-control"> <div class="collapse-box-header"><div class="collapse-box-icon"><i class="fa fa-plus"></i></div><span>代码示例</span></div> <div class="collapse-box-content"><div class="inner"> <figure class="highlight csharp"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br><span class="line">25</span><br><span class="line">26</span><br><span class="line">27</span><br><span class="line">28</span><br><span class="line">29</span><br><span class="line">30</span><br><span class="line">31</span><br><span class="line">32</span><br><span class="line">33</span><br><span class="line">34</span><br><span class="line">35</span><br><span class="line">36</span><br><span class="line">37</span><br><span class="line">38</span><br><span class="line">39</span><br><span class="line">40</span><br><span class="line">41</span><br><span class="line">42</span><br><span class="line">43</span><br><span class="line">44</span><br><span class="line">45</span><br><span class="line">46</span><br><span class="line">47</span><br><span class="line">48</span><br><span class="line">49</span><br><span class="line">50</span><br><span class="line">51</span><br><span class="line">52</span><br><span class="line">53</span><br><span class="line">54</span><br><span class="line">55</span><br><span class="line">56</span><br><span class="line">57</span><br><span class="line">58</span><br><span class="line">59</span><br><span class="line">60</span><br><span class="line">61</span><br><span class="line">62</span><br><span class="line">63</span><br><span class="line">64</span><br><span class="line">65</span><br><span class="line">66</span><br><span class="line">67</span><br><span class="line">68</span><br><span class="line">69</span><br><span class="line">70</span><br><span class="line">71</span><br><span class="line">72</span><br><span class="line">73</span><br><span class="line">74</span><br><span class="line">75</span><br><span class="line">76</span><br><span class="line">77</span><br><span class="line">78</span><br><span class="line">79</span><br><span class="line">80</span><br><span class="line">81</span><br><span class="line">82</span><br><span class="line">83</span><br><span class="line">84</span><br><span class="line">85</span><br><span class="line">86</span><br><span class="line">87</span><br><span class="line">88</span><br><span class="line">89</span><br><span class="line">90</span><br><span class="line">91</span><br><span class="line">92</span><br><span class="line">93</span><br><span class="line">94</span><br><span class="line">95</span><br><span class="line">96</span><br><span class="line">97</span><br><span class="line">98</span><br><span class="line">99</span><br><span class="line">100</span><br><span class="line">101</span><br><span class="line">102</span><br><span class="line">103</span><br><span class="line">104</span><br><span class="line">105</span><br><span class="line">106</span><br><span class="line">107</span><br><span class="line">108</span><br><span class="line">109</span><br><span class="line">110</span><br><span class="line">111</span><br><span class="line">112</span><br><span class="line">113</span><br><span class="line">114</span><br><span class="line">115</span><br><span class="line">116</span><br><span class="line">117</span><br><span class="line">118</span><br><span class="line">119</span><br><span class="line">120</span><br><span class="line">121</span><br><span class="line">122</span><br><span class="line">123</span><br><span class="line">124</span><br><span class="line">125</span><br><span class="line">126</span><br><span class="line">127</span><br><span class="line">128</span><br><span class="line">129</span><br><span class="line">130</span><br><span class="line">131</span><br><span class="line">132</span><br><span class="line">133</span><br><span class="line">134</span><br><span class="line">135</span><br><span class="line">136</span><br><span class="line">137</span><br><span class="line">138</span><br><span class="line">139</span><br><span class="line">140</span><br><span class="line">141</span><br><span class="line">142</span><br><span class="line">143</span><br><span class="line">144</span><br><span class="line">145</span><br><span class="line">146</span><br><span class="line">147</span><br><span class="line">148</span><br><span class="line">149</span><br><span class="line">150</span><br><span class="line">151</span><br><span class="line">152</span><br><span class="line">153</span><br><span class="line">154</span><br><span class="line">155</span><br><span class="line">156</span><br><span class="line">157</span><br><span class="line">158</span><br><span class="line">159</span><br><span class="line">160</span><br><span class="line">161</span><br><span class="line">162</span><br><span class="line">163</span><br><span class="line">164</span><br><span class="line">165</span><br><span class="line">166</span><br><span class="line">167</span><br><span class="line">168</span><br><span class="line">169</span><br><span class="line">170</span><br><span class="line">171</span><br><span class="line">172</span><br><span class="line">173</span><br><span class="line">174</span><br><span class="line">175</span><br><span class="line">176</span><br><span class="line">177</span><br><span class="line">178</span><br><span class="line">179</span><br><span class="line">180</span><br><span class="line">181</span><br><span class="line">182</span><br><span class="line">183</span><br><span class="line">184</span><br><span class="line">185</span><br><span class="line">186</span><br><span class="line">187</span><br><span class="line">188</span><br><span class="line">189</span><br><span class="line">190</span><br><span class="line">191</span><br><span class="line">192</span><br><span class="line">193</span><br><span class="line">194</span><br><span class="line">195</span><br><span class="line">196</span><br><span class="line">197</span><br><span class="line">198</span><br><span class="line">199</span><br><span class="line">200</span><br><span class="line">201</span><br><span class="line">202</span><br><span class="line">203</span><br><span class="line">204</span><br><span class="line">205</span><br><span class="line">206</span><br><span class="line">207</span><br><span class="line">208</span><br><span class="line">209</span><br><span class="line">210</span><br><span class="line">211</span><br><span class="line">212</span><br><span class="line">213</span><br><span class="line">214</span><br><span class="line">215</span><br><span class="line">216</span><br><span class="line">217</span><br><span class="line">218</span><br><span class="line">219</span><br><span class="line">220</span><br><span class="line">221</span><br><span class="line">222</span><br><span class="line">223</span><br><span class="line">224</span><br><span class="line">225</span><br><span class="line">226</span><br><span class="line">227</span><br><span class="line">228</span><br><span class="line">229</span><br><span class="line">230</span><br><span class="line">231</span><br><span class="line">232</span><br><span class="line">233</span><br><span class="line">234</span><br><span class="line">235</span><br><span class="line">236</span><br><span class="line">237</span><br><span class="line">238</span><br><span class="line">239</span><br><span class="line">240</span><br><span class="line">241</span><br><span class="line">242</span><br><span class="line">243</span><br><span class="line">244</span><br><span class="line">245</span><br><span class="line">246</span><br><span class="line">247</span><br><span class="line">248</span><br><span class="line">249</span><br><span class="line">250</span><br><span class="line">251</span><br><span class="line">252</span><br><span class="line">253</span><br><span class="line">254</span><br><span class="line">255</span><br><span class="line">256</span><br><span class="line">257</span><br><span class="line">258</span><br><span class="line">259</span><br><span class="line">260</span><br><span class="line">261</span><br><span class="line">262</span><br><span class="line">263</span><br><span class="line">264</span><br><span class="line">265</span><br><span class="line">266</span><br><span class="line">267</span><br><span class="line">268</span><br><span class="line">269</span><br><span class="line">270</span><br><span class="line">271</span><br><span class="line">272</span><br><span class="line">273</span><br><span class="line">274</span><br><span class="line">275</span><br><span class="line">276</span><br><span class="line">277</span><br><span class="line">278</span><br><span class="line">279</span><br><span class="line">280</span><br><span class="line">281</span><br><span class="line">282</span><br><span class="line">283</span><br><span class="line">284</span><br><span class="line">285</span><br><span class="line">286</span><br><span class="line">287</span><br><span class="line">288</span><br><span class="line">289</span><br><span class="line">290</span><br><span class="line">291</span><br><span class="line">292</span><br><span class="line">293</span><br><span class="line">294</span><br><span class="line">295</span><br><span class="line">296</span><br><span class="line">297</span><br><span class="line">298</span><br><span class="line">299</span><br><span class="line">300</span><br><span class="line">301</span><br><span class="line">302</span><br><span class="line">303</span><br><span class="line">304</span><br><span class="line">305</span><br><span class="line">306</span><br><span class="line">307</span><br><span class="line">308</span><br><span class="line">309</span><br><span class="line">310</span><br><span class="line">311</span><br><span class="line">312</span><br><span class="line">313</span><br><span class="line">314</span><br><span class="line">315</span><br><span class="line">316</span><br><span class="line">317</span><br><span class="line">318</span><br><span class="line">319</span><br><span class="line">320</span><br><span class="line">321</span><br><span class="line">322</span><br><span class="line">323</span><br><span class="line">324</span><br><span class="line">325</span><br><span class="line">326</span><br><span class="line">327</span><br><span class="line">328</span><br><span class="line">329</span><br><span class="line">330</span><br><span class="line">331</span><br><span class="line">332</span><br><span class="line">333</span><br><span class="line">334</span><br><span class="line">335</span><br><span class="line">336</span><br><span class="line">337</span><br><span class="line">338</span><br><span class="line">339</span><br><span class="line">340</span><br><span class="line">341</span><br><span class="line">342</span><br><span class="line">343</span><br><span class="line">344</span><br><span class="line">345</span><br><span class="line">346</span><br><span class="line">347</span><br><span class="line">348</span><br><span class="line">349</span><br><span class="line">350</span><br><span class="line">351</span><br><span class="line">352</span><br><span class="line">353</span><br><span class="line">354</span><br><span class="line">355</span><br><span class="line">356</span><br><span class="line">357</span><br><span class="line">358</span><br><span class="line">359</span><br><span class="line">360</span><br><span class="line">361</span><br><span class="line">362</span><br><span class="line">363</span><br><span class="line">364</span><br><span class="line">365</span><br><span class="line">366</span><br><span class="line">367</span><br><span class="line">368</span><br><span class="line">369</span><br><span class="line">370</span><br><span class="line">371</span><br><span class="line">372</span><br><span class="line">373</span><br><span class="line">374</span><br><span class="line">375</span><br><span class="line">376</span><br><span class="line">377</span><br><span class="line">378</span><br><span class="line">379</span><br><span class="line">380</span><br><span class="line">381</span><br><span class="line">382</span><br><span class="line">383</span><br><span class="line">384</span><br><span class="line">385</span><br><span class="line">386</span><br><span class="line">387</span><br><span class="line">388</span><br><span class="line">389</span><br><span class="line">390</span><br><span class="line">391</span><br><span class="line">392</span><br><span class="line">393</span><br><span class="line">394</span><br><span class="line">395</span><br><span class="line">396</span><br><span class="line">397</span><br><span class="line">398</span><br><span class="line">399</span><br><span class="line">400</span><br><span class="line">401</span><br><span class="line">402</span><br><span class="line">403</span><br><span class="line">404</span><br><span class="line">405</span><br><span class="line">406</span><br><span class="line">407</span><br><span class="line">408</span><br><span class="line">409</span><br><span class="line">410</span><br><span class="line">411</span><br><span class="line">412</span><br><span class="line">413</span><br><span class="line">414</span><br><span class="line">415</span><br><span class="line">416</span><br><span class="line">417</span><br><span class="line">418</span><br><span class="line">419</span><br><span class="line">420</span><br><span class="line">421</span><br><span class="line">422</span><br><span class="line">423</span><br><span class="line">424</span><br><span class="line">425</span><br><span class="line">426</span><br><span class="line">427</span><br><span class="line">428</span><br><span class="line">429</span><br><span class="line">430</span><br><span class="line">431</span><br><span class="line">432</span><br><span class="line">433</span><br><span class="line">434</span><br><span class="line">435</span><br><span class="line">436</span><br><span class="line">437</span><br><span class="line">438</span><br><span class="line">439</span><br></pre></td><td class="code"><pre><span class="line"><span class="function"><span class="keyword">public</span> <span class="keyword">async</span> Task<PerformanceTestResult> <span class="title">DBFilePerformanceTest</span>(<span class="params"><span class="built_in">string</span> fileName</span>)</span></span><br><span class="line">{</span><br><span class="line"> <span class="keyword">if</span> (!File.Exists(fileName)) <span class="keyword">throw</span> <span class="keyword">new</span> InvalidOperationException(<span class="string">"db文件不存在"</span>);</span><br><span class="line"></span><br><span class="line"> <span class="keyword">if</span> (_settings.PerformanceTestConfig == <span class="literal">null</span>)</span><br><span class="line"> {</span><br><span class="line"> _settings.PerformanceTestConfig = <span class="keyword">new</span> PerformanceTestConfig();</span><br><span class="line"> _settings.SaveConfig();</span><br><span class="line"> }</span><br><span class="line"></span><br><span class="line"> Dictionary<<span class="built_in">string</span>, <span class="built_in">object</span>> sheets = [];</span><br><span class="line"> List<PerformanceTestReportParsingVolumeDto> sheet1data = [];</span><br><span class="line"> List<PerformanceTestReportExportDto> sheet2data = [];</span><br><span class="line"></span><br><span class="line"> <span class="keyword">using</span> <span class="keyword">var</span> fsql = <span class="keyword">new</span> FreeSqlBuilder().UseConnectionString(DataType.Sqlite, AppBase.GetSqliteConnectionString(fileName)).Build();</span><br><span class="line"></span><br><span class="line"> <span class="meta">#<span class="keyword">region</span> 低频采样率参数个数 推荐90000</span></span><br><span class="line"></span><br><span class="line"> <span class="keyword">var</span> sql_sumcount_ch10_low = <span class="string">"select count(1) from ch10_parameterid where samplerate<512 "</span>;</span><br><span class="line"> <span class="keyword">var</span> sql_sumcount_npd_low = <span class="string">"select count(1) from npd_parameterid where samplerate<512 "</span>;</span><br><span class="line"> <span class="keyword">var</span> sql_sumcount_npdgd_low = <span class="string">"select count(1) from npdgd_parameterid where samplerate<512 "</span>;</span><br><span class="line"> <span class="keyword">var</span> sql_sumcount_npdx_low = <span class="string">"select count(1) from npdx_parameterid where samplerate<512 "</span>;</span><br><span class="line"> <span class="keyword">var</span> sql_sumcount_para_low = <span class="string">"select count(1) from parameterid where samplerate<512 "</span>;</span><br><span class="line"> <span class="built_in">long</span>? sum_count_ch10_low = <span class="number">0</span>, sum_count_npd_low = <span class="number">0</span>, sum_count_npdgd_low = <span class="number">0</span>, sum_count_npdx_low = <span class="number">0</span>, sum_count_para_low = <span class="number">0</span>;</span><br><span class="line"> <span class="keyword">try</span></span><br><span class="line"> {</span><br><span class="line"> sum_count_para_low = <span class="keyword">await</span> fsql.Ado.ExecuteScalarAsync(sql_sumcount_para_low) <span class="keyword">as</span> <span class="built_in">long</span>?;</span><br><span class="line"> sum_count_para_low = sum_count_para_low <span class="keyword">is</span> <span class="literal">null</span> ? <span class="number">0</span> : sum_count_para_low;</span><br><span class="line"> }</span><br><span class="line"> <span class="keyword">catch</span> (Exception ex)</span><br><span class="line"> {</span><br><span class="line"> _logger.Error(ex);</span><br><span class="line"> }</span><br><span class="line"> <span class="keyword">try</span></span><br><span class="line"> {</span><br><span class="line"> sum_count_ch10_low = <span class="keyword">await</span> fsql.Ado.ExecuteScalarAsync(sql_sumcount_ch10_low) <span class="keyword">as</span> <span class="built_in">long</span>?;</span><br><span class="line"> sum_count_ch10_low = sum_count_ch10_low <span class="keyword">is</span> <span class="literal">null</span> ? <span class="number">0</span> : sum_count_ch10_low;</span><br><span class="line"> }</span><br><span class="line"> <span class="keyword">catch</span> (Exception ex)</span><br><span class="line"> {</span><br><span class="line"> _logger.Error(ex);</span><br><span class="line"> }</span><br><span class="line"> <span class="keyword">try</span></span><br><span class="line"> {</span><br><span class="line"> sum_count_npd_low = <span class="keyword">await</span> fsql.Ado.ExecuteScalarAsync(sql_sumcount_npd_low) <span class="keyword">as</span> <span class="built_in">long</span>?;</span><br><span class="line"> sum_count_npd_low = sum_count_npd_low <span class="keyword">is</span> <span class="literal">null</span> ? <span class="number">0</span> : sum_count_npd_low;</span><br><span class="line"> }</span><br><span class="line"> <span class="keyword">catch</span> (Exception ex)</span><br><span class="line"> {</span><br><span class="line"> _logger.Error(ex);</span><br><span class="line"> }</span><br><span class="line"> <span class="keyword">try</span></span><br><span class="line"> {</span><br><span class="line"> sum_count_npdgd_low = <span class="keyword">await</span> fsql.Ado.ExecuteScalarAsync(sql_sumcount_npdgd_low) <span class="keyword">as</span> <span class="built_in">long</span>?;</span><br><span class="line"> sum_count_npdgd_low = sum_count_npdgd_low <span class="keyword">is</span> <span class="literal">null</span> ? <span class="number">0</span> : sum_count_npdgd_low;</span><br><span class="line"> }</span><br><span class="line"> <span class="keyword">catch</span> (Exception ex)</span><br><span class="line"> {</span><br><span class="line"> _logger.Error(ex);</span><br><span class="line"> }</span><br><span class="line"> <span class="keyword">try</span></span><br><span class="line"> {</span><br><span class="line"> sum_count_npdx_low = <span class="keyword">await</span> fsql.Ado.ExecuteScalarAsync(sql_sumcount_npdx_low) <span class="keyword">as</span> <span class="built_in">long</span>?;</span><br><span class="line"> sum_count_npdx_low = sum_count_npdx_low <span class="keyword">is</span> <span class="literal">null</span> ? <span class="number">0</span> : sum_count_npdx_low;</span><br><span class="line"> }</span><br><span class="line"> <span class="keyword">catch</span> (Exception ex)</span><br><span class="line"> {</span><br><span class="line"> _logger.Error(ex);</span><br><span class="line"> }</span><br><span class="line"> <span class="built_in">long</span> sum_count_low = sum_count_ch10_low.Value + sum_count_npd_low.Value + sum_count_npdgd_low.Value + sum_count_npdx_low.Value + sum_count_para_low.Value;</span><br><span class="line"> <span class="keyword">if</span> (sum_count_low > _settings.PerformanceTestConfig.LowFreqParaCountMaximum)</span><br><span class="line"> {</span><br><span class="line"> <span class="keyword">var</span> data1 = <span class="keyword">new</span> PerformanceTestReportParsingVolumeDto()</span><br><span class="line"> {</span><br><span class="line"> Description = <span class="string">$"系统支持解析的低频参数个数最多 <span class="subst">{_settings.PerformanceTestConfig.LowFreqParaCountMaximum}</span> 个"</span>,</span><br><span class="line"> Maximum = _settings.PerformanceTestConfig.LowFreqParaCountMaximum,</span><br><span class="line"> CurrentValue = sum_count_low,</span><br><span class="line"> OverrunPercentage = <span class="string">$"<span class="subst">{(sum_count_low - _settings.PerformanceTestConfig.LowFreqParaCountMaximum) * <span class="number">1.0</span> / _settings.PerformanceTestConfig.LowFreqParaCountMaximum:P2}</span>"</span></span><br><span class="line"> };</span><br><span class="line"> sheet1data.Add(data1);</span><br><span class="line"> }</span><br><span class="line"></span><br><span class="line"> <span class="meta">#<span class="keyword">endregion</span> 低频采样率参数个数 推荐90000</span></span><br><span class="line"></span><br><span class="line"> <span class="meta">#<span class="keyword">region</span> 高频(>=512)采样率参数个数 推荐1500</span></span><br><span class="line"></span><br><span class="line"> <span class="keyword">var</span> sql_sumcount_ch10 = <span class="string">"select count(1) from ch10_parameterid where samplerate>=512 and samplerate<8192 "</span>;</span><br><span class="line"> <span class="keyword">var</span> sql_sumcount_npd = <span class="string">"select count(1) from npd_parameterid where samplerate>=512 and samplerate<8192 "</span>;</span><br><span class="line"> <span class="keyword">var</span> sql_sumcount_npdgd = <span class="string">"select count(1) from npdgd_parameterid where samplerate>=512 and samplerate<8192 "</span>;</span><br><span class="line"> <span class="keyword">var</span> sql_sumcount_npdx = <span class="string">"select count(1) from npdx_parameterid where samplerate>=512 and samplerate<8192 "</span>;</span><br><span class="line"> <span class="keyword">var</span> sql_sumcount_para = <span class="string">"select count(1) from parameterid where samplerate>=512 and samplerate<8192 "</span>;</span><br><span class="line"> <span class="built_in">long</span>? sum_count_ch10 = <span class="number">0</span>, sum_count_npd = <span class="number">0</span>, sum_count_npdgd = <span class="number">0</span>, sum_count_npdx = <span class="number">0</span>, sum_count_para = <span class="number">0</span>;</span><br><span class="line"> <span class="keyword">try</span></span><br><span class="line"> {</span><br><span class="line"> sum_count_para = <span class="keyword">await</span> fsql.Ado.ExecuteScalarAsync(sql_sumcount_para) <span class="keyword">as</span> <span class="built_in">long</span>?;</span><br><span class="line"> sum_count_para = sum_count_para <span class="keyword">is</span> <span class="literal">null</span> ? <span class="number">0</span> : sum_count_para;</span><br><span class="line"> }</span><br><span class="line"> <span class="keyword">catch</span> (Exception ex)</span><br><span class="line"> {</span><br><span class="line"> _logger.Error(ex);</span><br><span class="line"> }</span><br><span class="line"> <span class="keyword">try</span></span><br><span class="line"> {</span><br><span class="line"> sum_count_ch10 = <span class="keyword">await</span> fsql.Ado.ExecuteScalarAsync(sql_sumcount_ch10) <span class="keyword">as</span> <span class="built_in">long</span>?;</span><br><span class="line"> sum_count_ch10 = sum_count_ch10 <span class="keyword">is</span> <span class="literal">null</span> ? <span class="number">0</span> : sum_count_ch10;</span><br><span class="line"> }</span><br><span class="line"> <span class="keyword">catch</span> (Exception ex)</span><br><span class="line"> {</span><br><span class="line"> _logger.Error(ex);</span><br><span class="line"> }</span><br><span class="line"> <span class="keyword">try</span></span><br><span class="line"> {</span><br><span class="line"> sum_count_npd = <span class="keyword">await</span> fsql.Ado.ExecuteScalarAsync(sql_sumcount_npd) <span class="keyword">as</span> <span class="built_in">long</span>?;</span><br><span class="line"> sum_count_npd = sum_count_npd <span class="keyword">is</span> <span class="literal">null</span> ? <span class="number">0</span> : sum_count_npd;</span><br><span class="line"> }</span><br><span class="line"> <span class="keyword">catch</span> (Exception ex)</span><br><span class="line"> {</span><br><span class="line"> _logger.Error(ex);</span><br><span class="line"> }</span><br><span class="line"> <span class="keyword">try</span></span><br><span class="line"> {</span><br><span class="line"> sum_count_npdgd = <span class="keyword">await</span> fsql.Ado.ExecuteScalarAsync(sql_sumcount_npdgd) <span class="keyword">as</span> <span class="built_in">long</span>?;</span><br><span class="line"> sum_count_npdgd = sum_count_npdgd <span class="keyword">is</span> <span class="literal">null</span> ? <span class="number">0</span> : sum_count_npdgd;</span><br><span class="line"> }</span><br><span class="line"> <span class="keyword">catch</span> (Exception ex)</span><br><span class="line"> {</span><br><span class="line"> _logger.Error(ex);</span><br><span class="line"> }</span><br><span class="line"> <span class="keyword">try</span></span><br><span class="line"> {</span><br><span class="line"> sum_count_npdx = <span class="keyword">await</span> fsql.Ado.ExecuteScalarAsync(sql_sumcount_npdx) <span class="keyword">as</span> <span class="built_in">long</span>?;</span><br><span class="line"> sum_count_npdx = sum_count_npdx <span class="keyword">is</span> <span class="literal">null</span> ? <span class="number">0</span> : sum_count_npdx;</span><br><span class="line"> }</span><br><span class="line"> <span class="keyword">catch</span> (Exception ex)</span><br><span class="line"> {</span><br><span class="line"> _logger.Error(ex);</span><br><span class="line"> }</span><br><span class="line"> <span class="built_in">long</span> sum_count = sum_count_ch10.Value + sum_count_npd.Value + sum_count_npdgd.Value + sum_count_npdx.Value + sum_count_para.Value;</span><br><span class="line"> <span class="keyword">if</span> (sum_count > _settings.PerformanceTestConfig.High512HzParaCountMaximum)</span><br><span class="line"> {</span><br><span class="line"> <span class="keyword">var</span> data1 = <span class="keyword">new</span> PerformanceTestReportParsingVolumeDto()</span><br><span class="line"> {</span><br><span class="line"> Description = <span class="string">$"系统支持解析的高频参数(>=512Hz)个数最多 <span class="subst">{_settings.PerformanceTestConfig.High512HzParaCountMaximum}</span> 个"</span>,</span><br><span class="line"> Maximum = _settings.PerformanceTestConfig.High512HzParaCountMaximum,</span><br><span class="line"> CurrentValue = sum_count,</span><br><span class="line"> OverrunPercentage = <span class="string">$"<span class="subst">{(sum_count - _settings.PerformanceTestConfig.High512HzParaCountMaximum) * <span class="number">1.0</span> / _settings.PerformanceTestConfig.High512HzParaCountMaximum:P2}</span>"</span></span><br><span class="line"> };</span><br><span class="line"> sheet1data.Add(data1);</span><br><span class="line"> }</span><br><span class="line"></span><br><span class="line"> <span class="meta">#<span class="keyword">endregion</span> 高频(>=512)采样率参数个数 推荐1500</span></span><br><span class="line"></span><br><span class="line"> <span class="meta">#<span class="keyword">region</span> 高频8K采样率参数个数 推荐200</span></span><br><span class="line"></span><br><span class="line"> <span class="keyword">var</span> sql_sumcount_ch10_8k = <span class="string">"select count(1) from ch10_parameterid where samplerate=8192 "</span>;</span><br><span class="line"> <span class="keyword">var</span> sql_sumcount_npd_8k = <span class="string">"select count(1) from npd_parameterid where samplerate=8192 "</span>;</span><br><span class="line"> <span class="keyword">var</span> sql_sumcount_npdgd_8k = <span class="string">"select count(1) from npdgd_parameterid where samplerate=8192 "</span>;</span><br><span class="line"> <span class="keyword">var</span> sql_sumcount_npdx_8k = <span class="string">"select count(1) from npdx_parameterid where samplerate=8192 "</span>;</span><br><span class="line"> <span class="keyword">var</span> sql_sumcount_para_8k = <span class="string">"select count(1) from parameterid where samplerate=8192 "</span>;</span><br><span class="line"> <span class="built_in">long</span>? sum_count_ch10_8k = <span class="number">0</span>, sum_count_npd_8k = <span class="number">0</span>, sum_count_npdgd_8k = <span class="number">0</span>, sum_count_npdx_8k = <span class="number">0</span>, sum_count_para_8k = <span class="number">0</span>;</span><br><span class="line"> <span class="keyword">try</span></span><br><span class="line"> {</span><br><span class="line"> sum_count_para_8k = <span class="keyword">await</span> fsql.Ado.ExecuteScalarAsync(sql_sumcount_para_8k) <span class="keyword">as</span> <span class="built_in">long</span>?;</span><br><span class="line"> sum_count_para_8k = sum_count_para_8k <span class="keyword">is</span> <span class="literal">null</span> ? <span class="number">0</span> : sum_count_para_8k;</span><br><span class="line"> }</span><br><span class="line"> <span class="keyword">catch</span> (Exception ex)</span><br><span class="line"> {</span><br><span class="line"> _logger.Error(ex);</span><br><span class="line"> }</span><br><span class="line"> <span class="keyword">try</span></span><br><span class="line"> {</span><br><span class="line"> sum_count_ch10_8k = <span class="keyword">await</span> fsql.Ado.ExecuteScalarAsync(sql_sumcount_ch10_8k) <span class="keyword">as</span> <span class="built_in">long</span>?;</span><br><span class="line"> sum_count_ch10_8k = sum_count_ch10_8k <span class="keyword">is</span> <span class="literal">null</span> ? <span class="number">0</span> : sum_count_ch10_8k;</span><br><span class="line"> }</span><br><span class="line"> <span class="keyword">catch</span> (Exception ex)</span><br><span class="line"> {</span><br><span class="line"> _logger.Error(ex);</span><br><span class="line"> }</span><br><span class="line"> <span class="keyword">try</span></span><br><span class="line"> {</span><br><span class="line"> sum_count_npd_8k = <span class="keyword">await</span> fsql.Ado.ExecuteScalarAsync(sql_sumcount_npd_8k) <span class="keyword">as</span> <span class="built_in">long</span>?;</span><br><span class="line"> sum_count_npd_8k = sum_count_npd_8k <span class="keyword">is</span> <span class="literal">null</span> ? <span class="number">0</span> : sum_count_npd_8k;</span><br><span class="line"> }</span><br><span class="line"> <span class="keyword">catch</span> (Exception ex)</span><br><span class="line"> {</span><br><span class="line"> _logger.Error(ex);</span><br><span class="line"> }</span><br><span class="line"> <span class="keyword">try</span></span><br><span class="line"> {</span><br><span class="line"> sum_count_npdgd_8k = <span class="keyword">await</span> fsql.Ado.ExecuteScalarAsync(sql_sumcount_npdgd_8k) <span class="keyword">as</span> <span class="built_in">long</span>?;</span><br><span class="line"> sum_count_npdgd_8k = sum_count_npdgd_8k <span class="keyword">is</span> <span class="literal">null</span> ? <span class="number">0</span> : sum_count_npdgd_8k;</span><br><span class="line"> }</span><br><span class="line"> <span class="keyword">catch</span> (Exception ex)</span><br><span class="line"> {</span><br><span class="line"> _logger.Error(ex);</span><br><span class="line"> }</span><br><span class="line"> <span class="keyword">try</span></span><br><span class="line"> {</span><br><span class="line"> sum_count_npdx_8k = <span class="keyword">await</span> fsql.Ado.ExecuteScalarAsync(sql_sumcount_npdx_8k) <span class="keyword">as</span> <span class="built_in">long</span>?;</span><br><span class="line"> sum_count_npdx_8k = sum_count_npdx_8k <span class="keyword">is</span> <span class="literal">null</span> ? <span class="number">0</span> : sum_count_npdx_8k;</span><br><span class="line"> }</span><br><span class="line"> <span class="keyword">catch</span> (Exception ex)</span><br><span class="line"> {</span><br><span class="line"> _logger.Error(ex);</span><br><span class="line"> }</span><br><span class="line"> <span class="built_in">long</span> sum_count_8k = sum_count_ch10_8k.Value + sum_count_npd_8k.Value + sum_count_npdgd_8k.Value + sum_count_npdx_8k.Value + sum_count_para_8k.Value;</span><br><span class="line"> <span class="keyword">if</span> (sum_count_8k > _settings.PerformanceTestConfig.High8192ParaCountMaximum)</span><br><span class="line"> {</span><br><span class="line"> <span class="keyword">var</span> data1 = <span class="keyword">new</span> PerformanceTestReportParsingVolumeDto()</span><br><span class="line"> {</span><br><span class="line"> Description = <span class="string">$"系统支持解析的高频参数(8192Hz)个数最多 <span class="subst">{_settings.PerformanceTestConfig.High8192ParaCountMaximum}</span> 个"</span>,</span><br><span class="line"> Maximum = _settings.PerformanceTestConfig.High8192ParaCountMaximum,</span><br><span class="line"> CurrentValue = sum_count_8k,</span><br><span class="line"> OverrunPercentage = <span class="string">$"<span class="subst">{(sum_count_8k - _settings.PerformanceTestConfig.High8192ParaCountMaximum) * <span class="number">1.0</span> / _settings.PerformanceTestConfig.High8192ParaCountMaximum:P2}</span>"</span></span><br><span class="line"> };</span><br><span class="line"> sheet1data.Add(data1);</span><br><span class="line"> }</span><br><span class="line"></span><br><span class="line"> <span class="meta">#<span class="keyword">endregion</span> 高频8K采样率参数个数 推荐200</span></span><br><span class="line"></span><br><span class="line"> <span class="meta">#<span class="keyword">region</span> 单条数据流8K采样率参数个数,单条流只查表npd_parameterid 推荐100</span></span><br><span class="line"></span><br><span class="line"> <span class="keyword">var</span> sql_sumsamplerate_groupbyStreamID_8k = <span class="string">"select streamid,sum(1) Count from npd_parameterid where samplerate=8192 group by streamid"</span>;</span><br><span class="line"> <span class="keyword">var</span> npdParameterIDGroupByStreamIDDtos_8k = <span class="keyword">await</span> fsql.Ado.QueryAsync<NPDParameterIDGroupByStreamIDDto>(sql_sumsamplerate_groupbyStreamID_8k);</span><br><span class="line"></span><br><span class="line"> <span class="keyword">foreach</span> (<span class="keyword">var</span> item <span class="keyword">in</span> npdParameterIDGroupByStreamIDDtos_8k)</span><br><span class="line"> {</span><br><span class="line"> <span class="keyword">if</span> (item.Count != <span class="literal">null</span> && item.Count > _settings.PerformanceTestConfig.Sample8KSingleStreamParaCountMaximum)</span><br><span class="line"> {</span><br><span class="line"> <span class="keyword">var</span> data1 = <span class="keyword">new</span> PerformanceTestReportParsingVolumeDto()</span><br><span class="line"> {</span><br><span class="line"> Description = <span class="string">$"单条数据流最多支持 <span class="subst">{_settings.PerformanceTestConfig.Sample8KSingleStreamParaCountMaximum}</span> 个 8192 Hz 参数"</span>,</span><br><span class="line"> Maximum = _settings.PerformanceTestConfig.Sample8KSingleStreamParaCountMaximum,</span><br><span class="line"> CurrentValue = item.Count.Value,</span><br><span class="line"> OverrunPercentage = <span class="string">$"<span class="subst">{(item.Count - _settings.PerformanceTestConfig.Sample8KSingleStreamParaCountMaximum) * <span class="number">1.0</span> / _settings.PerformanceTestConfig.Sample8KSingleStreamParaCountMaximum:P2}</span>"</span>,</span><br><span class="line"> CaseNumber = item.StreamID <span class="comment">// 机箱号</span></span><br><span class="line"> };</span><br><span class="line"> sheet1data.Add(data1);</span><br><span class="line"> }</span><br><span class="line"> }</span><br><span class="line"></span><br><span class="line"> <span class="meta">#<span class="keyword">endregion</span> 单条数据流8K采样率参数个数,单条流只查表npd_parameterid 推荐100</span></span><br><span class="line"></span><br><span class="line"> <span class="meta">#<span class="keyword">region</span> 总导出性能 推荐360 * 32 * 1200 13824000</span></span><br><span class="line"></span><br><span class="line"> <span class="keyword">var</span> sql_paramgroups = <span class="string">"select rate,parameters from paramgroups"</span>;</span><br><span class="line"> <span class="keyword">var</span> paramgroupsDtos = <span class="keyword">await</span> fsql.Ado.QueryAsync<ParamGroupsDto>(sql_paramgroups);</span><br><span class="line"> <span class="built_in">double</span> sum_rate = <span class="number">0</span>;</span><br><span class="line"> <span class="keyword">foreach</span> (<span class="keyword">var</span> item <span class="keyword">in</span> paramgroupsDtos)</span><br><span class="line"> {</span><br><span class="line"> <span class="keyword">var</span> parameters = Encoding.UTF8.GetString(item.Parameters);</span><br><span class="line"> sum_rate += item.Rate * parameters.Split(<span class="string">';'</span>).Length;</span><br><span class="line"> }</span><br><span class="line"></span><br><span class="line"> <span class="keyword">if</span> (sum_rate > _settings.PerformanceTestConfig.ExportVolumeMaximum)</span><br><span class="line"> {</span><br><span class="line"> <span class="keyword">var</span> data = <span class="keyword">new</span> PerformanceTestReportExportDto()</span><br><span class="line"> {</span><br><span class="line"> Description = <span class="string">$"导出参数资源总和最大支持 <span class="subst">{_settings.PerformanceTestConfig.ExportVolumeMaximum}</span> 点/秒"</span>,</span><br><span class="line"> Maximum = <span class="string">$"<span class="subst">{_settings.PerformanceTestConfig.ExportVolumeMaximum}</span>"</span>,</span><br><span class="line"> CurrentValue = <span class="string">$"<span class="subst">{sum_rate}</span>"</span>,</span><br><span class="line"> OverrunPercentage = <span class="string">$"<span class="subst">{(sum_rate - _settings.PerformanceTestConfig.ExportVolumeMaximum) / _settings.PerformanceTestConfig.ExportVolumeMaximum:P2}</span>"</span></span><br><span class="line"> };</span><br><span class="line"> sheet2data.Add(data);</span><br><span class="line"> }</span><br><span class="line"></span><br><span class="line"> <span class="meta">#<span class="keyword">endregion</span> 总导出性能 推荐360 * 32 * 1200 13824000</span></span><br><span class="line"></span><br><span class="line"> <span class="meta">#<span class="keyword">region</span> 单个低频导出性能 推荐 32hz 1500、128hz 370、256hz 180</span></span><br><span class="line"></span><br><span class="line"> <span class="keyword">var</span> sql_paramgroups_32hz = <span class="string">"select name,rate,parameters from paramgroups where rate = 32"</span>;</span><br><span class="line"> <span class="keyword">var</span> sql_paramgroups_128hz = <span class="string">"select name,rate,parameters from paramgroups where rate = 128"</span>;</span><br><span class="line"> <span class="keyword">var</span> sql_paramgroups_256hz = <span class="string">"select name,rate,parameters from paramgroups where rate = 256"</span>;</span><br><span class="line"> <span class="keyword">var</span> paramgroupsDtos_32hz = <span class="keyword">await</span> fsql.Ado.QueryAsync<ParamGroupsDto>(sql_paramgroups_32hz);</span><br><span class="line"> <span class="keyword">var</span> paramgroupsDtos_128hz = <span class="keyword">await</span> fsql.Ado.QueryAsync<ParamGroupsDto>(sql_paramgroups_128hz);</span><br><span class="line"> <span class="keyword">var</span> paramgroupsDtos_256hz = <span class="keyword">await</span> fsql.Ado.QueryAsync<ParamGroupsDto>(sql_paramgroups_256hz);</span><br><span class="line"></span><br><span class="line"> <span class="keyword">foreach</span> (<span class="keyword">var</span> item <span class="keyword">in</span> paramgroupsDtos_32hz)</span><br><span class="line"> {</span><br><span class="line"> <span class="built_in">string</span> paras = Encoding.UTF8.GetString(item.Parameters);</span><br><span class="line"> <span class="built_in">int</span> paraCount = paras.Split(<span class="string">';'</span>).Length;</span><br><span class="line"> <span class="keyword">if</span> (paraCount > _settings.PerformanceTestConfig.SingleExportMaximum_32hz)</span><br><span class="line"> {</span><br><span class="line"> <span class="keyword">var</span> data = <span class="keyword">new</span> PerformanceTestReportExportDto()</span><br><span class="line"> {</span><br><span class="line"> Description = <span class="string">$"单个低频参数组 32Hz 参数上限 <span class="subst">{_settings.PerformanceTestConfig.SingleExportMaximum_32hz}</span>"</span>,</span><br><span class="line"> Maximum = <span class="string">$"<span class="subst">{_settings.PerformanceTestConfig.SingleExportMaximum_32hz}</span>"</span>,</span><br><span class="line"> CurrentValue = <span class="string">$"<span class="subst">{paraCount}</span>"</span>,</span><br><span class="line"> OverrunPercentage = <span class="string">$"<span class="subst">{(paraCount - _settings.PerformanceTestConfig.SingleExportMaximum_32hz) * <span class="number">1.0</span> / _settings.PerformanceTestConfig.SingleExportMaximum_32hz:P2}</span>"</span>,</span><br><span class="line"> GroupName = item.Name,</span><br><span class="line"> };</span><br><span class="line"> sheet2data.Add(data);</span><br><span class="line"> }</span><br><span class="line"> }</span><br><span class="line"></span><br><span class="line"> <span class="keyword">foreach</span> (<span class="keyword">var</span> item <span class="keyword">in</span> paramgroupsDtos_128hz)</span><br><span class="line"> {</span><br><span class="line"> <span class="built_in">string</span> paras = Encoding.UTF8.GetString(item.Parameters);</span><br><span class="line"> <span class="built_in">int</span> paraCount = paras.Split(<span class="string">';'</span>).Length;</span><br><span class="line"> <span class="keyword">if</span> (paraCount > _settings.PerformanceTestConfig.SingleExportMaximum_128hz)</span><br><span class="line"> {</span><br><span class="line"> <span class="keyword">var</span> data = <span class="keyword">new</span> PerformanceTestReportExportDto()</span><br><span class="line"> {</span><br><span class="line"> Description = <span class="string">$"单个低频参数组 128Hz 参数上限 <span class="subst">{_settings.PerformanceTestConfig.SingleExportMaximum_128hz}</span>"</span>,</span><br><span class="line"> Maximum = <span class="string">$"<span class="subst">{_settings.PerformanceTestConfig.SingleExportMaximum_128hz}</span>"</span>,</span><br><span class="line"> CurrentValue = <span class="string">$"<span class="subst">{paraCount}</span>"</span>,</span><br><span class="line"> OverrunPercentage = <span class="string">$"<span class="subst">{(paraCount - _settings.PerformanceTestConfig.SingleExportMaximum_128hz) * <span class="number">1.0</span> / _settings.PerformanceTestConfig.SingleExportMaximum_128hz:P2}</span>"</span>,</span><br><span class="line"> GroupName = item.Name,</span><br><span class="line"> };</span><br><span class="line"> sheet2data.Add(data);</span><br><span class="line"> }</span><br><span class="line"> }</span><br><span class="line"></span><br><span class="line"> <span class="keyword">foreach</span> (<span class="keyword">var</span> item <span class="keyword">in</span> paramgroupsDtos_256hz)</span><br><span class="line"> {</span><br><span class="line"> <span class="built_in">string</span> paras = Encoding.UTF8.GetString(item.Parameters);</span><br><span class="line"> <span class="built_in">int</span> paraCount = paras.Split(<span class="string">';'</span>).Length;</span><br><span class="line"> <span class="keyword">if</span> (paraCount > _settings.PerformanceTestConfig.SingleExportMaximum_256hz)</span><br><span class="line"> {</span><br><span class="line"> <span class="keyword">var</span> data = <span class="keyword">new</span> PerformanceTestReportExportDto()</span><br><span class="line"> {</span><br><span class="line"> Description = <span class="string">$"单个低频参数组 256Hz 参数上限 <span class="subst">{_settings.PerformanceTestConfig.SingleExportMaximum_256hz}</span>"</span>,</span><br><span class="line"> Maximum = <span class="string">$"<span class="subst">{_settings.PerformanceTestConfig.SingleExportMaximum_256hz}</span>"</span>,</span><br><span class="line"> CurrentValue = <span class="string">$"<span class="subst">{paraCount}</span>"</span>,</span><br><span class="line"> OverrunPercentage = <span class="string">$"<span class="subst">{(paraCount - _settings.PerformanceTestConfig.SingleExportMaximum_256hz) * <span class="number">1.0</span> / _settings.PerformanceTestConfig.SingleExportMaximum_256hz:P2}</span>"</span>,</span><br><span class="line"> GroupName = item.Name,</span><br><span class="line"> };</span><br><span class="line"> sheet2data.Add(data);</span><br><span class="line"> }</span><br><span class="line"> }</span><br><span class="line"></span><br><span class="line"> <span class="meta">#<span class="keyword">endregion</span> 单个低频导出性能 推荐 32hz 1500、128hz 370、256hz 180</span></span><br><span class="line"></span><br><span class="line"> <span class="meta">#<span class="keyword">region</span> 单个高频导出性能 推荐 512hz 150、1024hz 100、8192hz 20、16384hz 8</span></span><br><span class="line"></span><br><span class="line"> <span class="keyword">var</span> sql_paramgroups_512hz = <span class="string">"select name,rate,parameters from paramgroups where rate = 512"</span>;</span><br><span class="line"> <span class="keyword">var</span> sql_paramgroups_1024hz = <span class="string">"select name,rate,parameters from paramgroups where rate = 1024"</span>;</span><br><span class="line"> <span class="keyword">var</span> sql_paramgroups_8192hz = <span class="string">"select name,rate,parameters from paramgroups where rate = 8192"</span>;</span><br><span class="line"> <span class="keyword">var</span> sql_paramgroups_16384hz = <span class="string">"select name,rate,parameters from paramgroups where rate = 16384"</span>;</span><br><span class="line"> <span class="keyword">var</span> paramgroupsDtos_512hz = <span class="keyword">await</span> fsql.Ado.QueryAsync<ParamGroupsDto>(sql_paramgroups_512hz);</span><br><span class="line"> <span class="keyword">var</span> paramgroupsDtos_1024hz = <span class="keyword">await</span> fsql.Ado.QueryAsync<ParamGroupsDto>(sql_paramgroups_1024hz);</span><br><span class="line"> <span class="keyword">var</span> paramgroupsDtos_8192hz = <span class="keyword">await</span> fsql.Ado.QueryAsync<ParamGroupsDto>(sql_paramgroups_8192hz);</span><br><span class="line"> <span class="keyword">var</span> paramgroupsDtos_16384hz = <span class="keyword">await</span> fsql.Ado.QueryAsync<ParamGroupsDto>(sql_paramgroups_16384hz);</span><br><span class="line"></span><br><span class="line"> <span class="keyword">foreach</span> (<span class="keyword">var</span> item <span class="keyword">in</span> paramgroupsDtos_512hz)</span><br><span class="line"> {</span><br><span class="line"> <span class="built_in">string</span> paras = Encoding.UTF8.GetString(item.Parameters);</span><br><span class="line"> <span class="built_in">int</span> paraCount = paras.Split(<span class="string">';'</span>).Length;</span><br><span class="line"> <span class="keyword">if</span> (paraCount > _settings.PerformanceTestConfig.SingleExportMaximum_512hz)</span><br><span class="line"> {</span><br><span class="line"> <span class="keyword">var</span> data = <span class="keyword">new</span> PerformanceTestReportExportDto()</span><br><span class="line"> {</span><br><span class="line"> Description = <span class="string">$"单个高频参数组 512Hz 参数上限 <span class="subst">{_settings.PerformanceTestConfig.SingleExportMaximum_512hz}</span>"</span>,</span><br><span class="line"> Maximum = <span class="string">$"<span class="subst">{_settings.PerformanceTestConfig.SingleExportMaximum_512hz}</span>"</span>,</span><br><span class="line"> CurrentValue = <span class="string">$"<span class="subst">{paraCount}</span>"</span>,</span><br><span class="line"> OverrunPercentage = <span class="string">$"<span class="subst">{(paraCount - _settings.PerformanceTestConfig.SingleExportMaximum_512hz) * <span class="number">1.0</span> / _settings.PerformanceTestConfig.SingleExportMaximum_512hz:P2}</span>"</span>,</span><br><span class="line"> GroupName = item.Name,</span><br><span class="line"> };</span><br><span class="line"> sheet2data.Add(data);</span><br><span class="line"> }</span><br><span class="line"> }</span><br><span class="line"> <span class="keyword">foreach</span> (<span class="keyword">var</span> item <span class="keyword">in</span> paramgroupsDtos_1024hz)</span><br><span class="line"> {</span><br><span class="line"> <span class="built_in">string</span> paras = Encoding.UTF8.GetString(item.Parameters);</span><br><span class="line"> <span class="built_in">int</span> paraCount = paras.Split(<span class="string">';'</span>).Length;</span><br><span class="line"> <span class="keyword">if</span> (paraCount > _settings.PerformanceTestConfig.SingleExportMaximum_1024hz)</span><br><span class="line"> {</span><br><span class="line"> <span class="keyword">var</span> data = <span class="keyword">new</span> PerformanceTestReportExportDto()</span><br><span class="line"> {</span><br><span class="line"> Description = <span class="string">$"单个高频参数组 1024Hz 参数上限 <span class="subst">{_settings.PerformanceTestConfig.SingleExportMaximum_1024hz}</span>"</span>,</span><br><span class="line"> Maximum = <span class="string">$"<span class="subst">{_settings.PerformanceTestConfig.SingleExportMaximum_1024hz}</span>"</span>,</span><br><span class="line"> CurrentValue = <span class="string">$"<span class="subst">{paraCount}</span>"</span>,</span><br><span class="line"> OverrunPercentage = <span class="string">$"<span class="subst">{(paraCount - _settings.PerformanceTestConfig.SingleExportMaximum_1024hz) * <span class="number">1.0</span> / _settings.PerformanceTestConfig.SingleExportMaximum_1024hz:P2}</span>"</span>,</span><br><span class="line"> GroupName = item.Name,</span><br><span class="line"> };</span><br><span class="line"> sheet2data.Add(data);</span><br><span class="line"> }</span><br><span class="line"> }</span><br><span class="line"> <span class="keyword">foreach</span> (<span class="keyword">var</span> item <span class="keyword">in</span> paramgroupsDtos_8192hz)</span><br><span class="line"> {</span><br><span class="line"> <span class="built_in">string</span> paras = Encoding.UTF8.GetString(item.Parameters);</span><br><span class="line"> <span class="built_in">int</span> paraCount = paras.Split(<span class="string">';'</span>).Length;</span><br><span class="line"> <span class="keyword">if</span> (paraCount > _settings.PerformanceTestConfig.SingleExportMaximum_8192hz)</span><br><span class="line"> {</span><br><span class="line"> <span class="keyword">var</span> data = <span class="keyword">new</span> PerformanceTestReportExportDto()</span><br><span class="line"> {</span><br><span class="line"> Description = <span class="string">$"单个高频参数组 8192Hz 参数上限 <span class="subst">{_settings.PerformanceTestConfig.SingleExportMaximum_8192hz}</span>"</span>,</span><br><span class="line"> Maximum = <span class="string">$"<span class="subst">{_settings.PerformanceTestConfig.SingleExportMaximum_8192hz}</span>"</span>,</span><br><span class="line"> CurrentValue = <span class="string">$"<span class="subst">{paraCount}</span>"</span>,</span><br><span class="line"> OverrunPercentage = <span class="string">$"<span class="subst">{(paraCount - _settings.PerformanceTestConfig.SingleExportMaximum_8192hz) * <span class="number">1.0</span> / _settings.PerformanceTestConfig.SingleExportMaximum_8192hz:P2}</span>"</span>,</span><br><span class="line"> GroupName = item.Name,</span><br><span class="line"> };</span><br><span class="line"> sheet2data.Add(data);</span><br><span class="line"> }</span><br><span class="line"> }</span><br><span class="line"> <span class="keyword">foreach</span> (<span class="keyword">var</span> item <span class="keyword">in</span> paramgroupsDtos_16384hz)</span><br><span class="line"> {</span><br><span class="line"> <span class="built_in">string</span> paras = Encoding.UTF8.GetString(item.Parameters);</span><br><span class="line"> <span class="built_in">int</span> paraCount = paras.Split(<span class="string">';'</span>).Length;</span><br><span class="line"> <span class="keyword">if</span> (paraCount > _settings.PerformanceTestConfig.SingleExportMaximum_16384hz)</span><br><span class="line"> {</span><br><span class="line"> <span class="keyword">var</span> data = <span class="keyword">new</span> PerformanceTestReportExportDto()</span><br><span class="line"> {</span><br><span class="line"> Description = <span class="string">$"单个高频参数组 16384Hz 参数上限 <span class="subst">{_settings.PerformanceTestConfig.SingleExportMaximum_16384hz}</span>"</span>,</span><br><span class="line"> Maximum = <span class="string">$"<span class="subst">{_settings.PerformanceTestConfig.SingleExportMaximum_16384hz}</span>"</span>,</span><br><span class="line"> CurrentValue = <span class="string">$"<span class="subst">{paraCount}</span>"</span>,</span><br><span class="line"> OverrunPercentage = <span class="string">$"<span class="subst">{(paraCount - _settings.PerformanceTestConfig.SingleExportMaximum_16384hz) * <span class="number">1.0</span> / _settings.PerformanceTestConfig.SingleExportMaximum_16384hz:P2}</span>"</span>,</span><br><span class="line"> GroupName = item.Name,</span><br><span class="line"> };</span><br><span class="line"> sheet2data.Add(data);</span><br><span class="line"> }</span><br><span class="line"> }</span><br><span class="line"></span><br><span class="line"> <span class="meta">#<span class="keyword">endregion</span> 单个高频导出性能 推荐 512hz 150、1024hz 100、8192hz 20、16384hz 8</span></span><br><span class="line"></span><br><span class="line"> <span class="keyword">if</span> (sheet1data.Count > <span class="number">0</span>)</span><br><span class="line"> {</span><br><span class="line"> sheets.Add(<span class="string">"解析性能"</span>, sheet1data);</span><br><span class="line"> }</span><br><span class="line"> <span class="keyword">if</span> (sheet2data.Count > <span class="number">0</span>)</span><br><span class="line"> {</span><br><span class="line"> sheets.Add(<span class="string">"参数组存储性能"</span>, sheet2data);</span><br><span class="line"> }</span><br><span class="line"></span><br><span class="line"> <span class="keyword">if</span> (sheets.Count > <span class="number">0</span>)</span><br><span class="line"> {</span><br><span class="line"> <span class="keyword">return</span> <span class="keyword">new</span> PerformanceTestResult { IsPass = <span class="literal">false</span>, ReportPath = saveFileDialog.FileName, ReportExportItems = sheet2data, ReportParserItems = sheet1data };</span><br><span class="line"> }</span><br><span class="line"></span><br><span class="line"> <span class="keyword">return</span> <span class="keyword">new</span> PerformanceTestResult { IsPass = <span class="literal">true</span> };</span><br><span class="line">}</span><br><span class="line"></span><br><span class="line"><span class="keyword">public</span> <span class="keyword">class</span> <span class="title">PerformanceTestResult</span></span><br><span class="line">{</span><br><span class="line"> <span class="keyword">public</span> <span class="built_in">bool</span> IsPass { <span class="keyword">get</span>; <span class="keyword">set</span>; }</span><br><span class="line"> <span class="keyword">public</span> <span class="built_in">string</span> ReportPath { <span class="keyword">get</span>; <span class="keyword">set</span>; }</span><br><span class="line"> <span class="keyword">public</span> List<<span class="built_in">object</span>>? ReportParserItems { <span class="keyword">get</span>; <span class="keyword">set</span>; }</span><br><span class="line"> <span class="keyword">public</span> List<<span class="built_in">object</span>>? ReportExportItems { <span class="keyword">get</span>; <span class="keyword">set</span>; }</span><br><span class="line">}</span><br></pre></td></tr></table></figure> </div></div> </div><p>里面很多代码其实是在ui线程跑的,如代码里面很多for循环、逻辑计算、对象创建、ORM 解析… 都是cpu代码,不会自动切换线程的,只有到底层io调用时,如 fsql.Ado.ExecuteScalarAsync 才会释放ui线程,而fsql.Ado.ExecuteScalarAsync里面也并不会全程在后台线程执行,如sqlite的官方异步方法它里面根本就没有真正的异步io,因为它是嵌入式数据库,读写本地文件,而Windows底层限制,读写磁盘文件不支持异步操作,里面实际上是同步方法。</p><p>并非只要带async、只要加 await,就不在ui线程,async只是提供「io等待时可以让出线程」的能力,但方法开头的一堆前置cpu逻辑,依旧原地阻塞当前线程。</p><p>所以,把这些循环、计算、对象创建放到task.run里面吧!当然也可以用ConfigureAwait(false)让await完成后要不要切回原线程,但管不了await之前的前置cpu逻辑。</p>]]></content>
<summary type="html"><p>在最近在开发中遇到个很有意思的问题,我在一个命令里面使用了一些sql执行操作,虽然这些sql执行都使用了<code>await</code>等待sql耗时操作,但ui还是阻塞了,百思不得其解,思考了半天问了我们组长,组长说在await执行完成后还是会回到ui线程,最好把这些</summary>
<category term="编程技术" scheme="https://probieluo.github.io/categories/%E7%BC%96%E7%A8%8B%E6%8A%80%E6%9C%AF/"/>
<category term="wpf" scheme="https://probieluo.github.io/tags/wpf/"/>
<category term="asynchronous-programming" scheme="https://probieluo.github.io/tags/asynchronous-programming/"/>
</entry>
<entry>
<title>项目管理十大知识领域-10项目采购管理</title>
<link href="https://probieluo.github.io/posts/project-management-10-project-procurement-management/"/>
<id>https://probieluo.github.io/posts/project-management-10-project-procurement-management/</id>
<published>2026-04-29T13:30:09.000Z</published>
<updated>2026-04-30T02:39:35.685Z</updated>
<content type="html"><![CDATA[<p>项目采购管理包括从项目团队从外部采购或获取所需产品、服务或成果的各个过程。</p><h2 id="管理基础"><a href="#管理基础" class="headerlink" title="管理基础"></a>管理基础</h2><h3 id="协议-采购合同"><a href="#协议-采购合同" class="headerlink" title="协议/采购合同"></a>协议/采购合同</h3><p>项目采购管理过程涉及协议来描述买卖双方的关系。因应用领域不同,协议可以是合同、服务水平协议、谅解备忘录(MOUS)、协议备忘录(MOA)或订购单。</p><p>协议就是邀约-应答。</p><h2 id="项目采购管理过程"><a href="#项目采购管理过程" class="headerlink" title="项目采购管理过程"></a>项目采购管理过程</h2><ul><li>规划采购管理:纪录项目采购决策、明确采购方法及识别潜在卖方。</li><li>实施采购:获取卖方应答、选择卖方并授予合同。</li><li>控制采购:管理采购关系、监督合同绩效、实施必要变更和纠偏,以及关闭合同。</li></ul><table><thead><tr><th align="left">过程</th><th align="left">输入</th><th align="left">工具与技术</th><th align="left">输出</th></tr></thead><tbody><tr><td align="left">规划采购管理</td><td align="left"><ul><li>项目立项文件</li><li>项目章程</li><li>项目管理计划</li><li>项目文件</li><li>事业环境因素</li><li>组织过程资产</li></ul></td><td align="left"><ul><li>专家判断</li><li>数据收集</li><li>数据分析</li><li>供方选择分析</li><li>会议</li></ul></td><td align="left"><ul><li>采购管理计划</li><li>采购策略</li><li>采购工作说明书</li><li>招标文件</li><li>自制或外购决策</li><li>独立成本估算</li><li>供方选择标准</li><li>变更请求</li><li>项目文件(更新)</li><li>组织过程资产(更新)</li></ul></td></tr><tr><td align="left">实施采购</td><td align="left"><ul><li>项目管理计划</li><li>项目文件</li><li>采购文档</li><li>卖方建议书</li><li>事业环境因素</li><li>组织过程资产</li></ul></td><td align="left"><ul><li>专家判断</li><li>广告</li><li>投标人会议</li><li>数据分析</li><li>人际关系与团队技能</li></ul></td><td align="left"><ul><li>选定的卖方</li><li>协议</li><li>变更请求</li><li>项目管理计划(更新)</li><li>项目文件(更新)</li><li>组织过程资产(更新)</li></ul></td></tr><tr><td align="left">控制采购</td><td align="left"><ul><li>项目管理计划</li><li>项目文件</li><li>采购文档</li><li>协议</li><li>工作绩效数据</li><li>批准的变更请求</li><li>事业环境因素</li><li>组织过程资产</li></ul></td><td align="left"><ul><li>专家判断</li><li>索赔管理</li><li>数据分析</li><li>检查</li><li>审计</li></ul></td><td align="left"><ul><li>采购关闭</li><li>采购文档(更新)</li><li>工作绩效信息</li><li>变更请求</li><li>项目管理计划(更新)</li><li>项目文件(更新)</li><li>组织过程资产(更新)</li></ul></td></tr></tbody></table><h3 id="裁剪考虑因素"><a href="#裁剪考虑因素" class="headerlink" title="裁剪考虑因素"></a>裁剪考虑因素</h3><blockquote><p>ps: 每个项目都是独一无二的,要根据项目实际情况对项目管理方法、流程、工具、文档进行裁剪,找到最适合项目的管理方式。比如对于小型项目来说 PMBOK 里的5大过程组、10大知识领域流程太繁杂了,可以合并或者裁剪部分过程、简化文档、减少会议,从而提升效率。</p></blockquote><p>因为每个项目都是独特的,项目经理可能需要根据需要裁剪项目采购管理过程,考虑因素包括:</p><ul><li>采购复杂性</li><li>物理地点</li><li>治理与法规环境</li><li>承包商的可用性</li></ul>]]></content>
<summary type="html"><p>项目采购管理包括从项目团队从外部采购或获取所需产品、服务或成果的各个过程。</p>
<h2 id="管理基础"><a href="#管理基础" class="headerlink" title="管理基础"></a>管理基础</h2><h3 id="协议-采购合同"><a </summary>
<category term="项目管理" scheme="https://probieluo.github.io/categories/%E9%A1%B9%E7%9B%AE%E7%AE%A1%E7%90%86/"/>
</entry>
<entry>
<title>项目管理十大知识领域-09项目风险管理</title>
<link href="https://probieluo.github.io/posts/project-management-09-project-risk-management/"/>
<id>https://probieluo.github.io/posts/project-management-09-project-risk-management/</id>
<published>2026-04-28T12:56:46.000Z</published>
<updated>2026-04-30T02:39:35.683Z</updated>
<content type="html"><![CDATA[<p>项目风险管理包括规划管理、风险识别、风险分析、风险应对、风险监督等各个过程。</p><h2 id="管理基础"><a href="#管理基础" class="headerlink" title="管理基础"></a>管理基础</h2><h3 id="项目风险概述"><a href="#项目风险概述" class="headerlink" title="项目风险概述"></a>项目风险概述</h3><p>项目风险会对项目目标产生负面和正面的影响,也就是机会和风险。风险管理旨在强化机会、规避或减轻威胁。</p><h3 id="风险的属性"><a href="#风险的属性" class="headerlink" title="风险的属性"></a>风险的属性</h3><ul><li>风险事件的<code>随机性</code>:风险事件的发生及后果都具有偶然性。</li><li>风险的相对性:风险总是相对于项目活动主题而言的。同样的风险对于不同的主题有不同的影响。人们对风险的承受能力因活动、人和时间而异。影响人们风险承受能力的因素有:<ul><li>收益的大小:收益越大,我愿意承担的风险越大</li><li>投入的大小:投入越多越不想承担风险</li><li>项目活动主体的地位和资源:资源多、地位高风险承受能力大</li></ul></li><li>风险的可变性:任何事情和矛盾在一定条件下都是可向自己的反面转化。风险的可变性含义:<ul><li>风险性质的变化</li><li>风险后果的变化</li><li>出现了新风险</li></ul></li></ul><h3 id="风险的分类"><a href="#风险的分类" class="headerlink" title="风险的分类"></a>风险的分类</h3><ul><li><p>按风险后果分类</p><ul><li><p>纯粹风险:没有机会全是威胁。只会造成两钟后果:造成损失和不造成损失</p></li><li><p>投机风险:即带来了机会,又隐含威胁。有三种后果:造成损失、不造成损失、获得收益</p><p>纯粹风险和投机风险在一定条件下可相互转化。项目管理人员需避免投机风险转化纯粹风险。</p><p>风险不是零和游戏。很多情况下,涉及风险都会蒙受损失。</p></li></ul></li><li><p>按风险来源划分</p><ul><li>自然风险</li><li>人为风险</li></ul></li><li><p>按风险是否可管理划分:可管理的风险指的是可以预测并可采取相应措施加以控制的风险。风险能否管理取决于风险的不确定性是否可消除及活动主体的管理水平。</p></li><li><p>按风险影响范围划分</p><ul><li>局部风险</li><li>总体风险:处于关键路线上的活动一旦延误,就要推迟整个项目的完成日期,会形成总体风险。</li></ul></li><li><p>按风险后果的承担者划分:如项目业主风险、政府风险、承包商风险、投资方风险、设计单位风险、监理单位风险、供应商风险…</p></li><li><p>按风险的可预测性划分</p><ul><li>已知风险:指在认真、严格地分析项目及计划之后就能明确地那些经常发生地,而且其后果亦可预见的风险</li><li>可预测风险:指根据经验,可以预见其发生但不可预见其风险的后果的风险。</li><li>不可预测风险:有可能发生,发生的可能性不能预见的风险。未知风险或未识别的风险</li></ul></li></ul><h3 id="风险成本及其负担"><a href="#风险成本及其负担" class="headerlink" title="风险成本及其负担"></a>风险成本及其负担</h3><p>风险事件造成的损失或减少的收益以及为防止其发生所采取措施而支付的费用,都构成风险成本。</p><ul><li>风险损失的有形成本<ul><li>直接损失:财产损失人员伤亡的价值</li><li>间接损失:直接损失以外的其他损失、责任损失以及因此造成的收益的减少。</li></ul></li><li>风险损失的无形成本:指由于风险所具有的不确定性而使项目主体在风险事件发生之前或之后付出的代价。主要表现在3个方面:<ul><li>风险损失减少了机会</li><li>风险阻碍了生产率的提高</li><li>风险造成资源分配不当</li></ul></li><li>风险预防与控制的成本</li><li>风险成本的负担:风险成本不单要由项目主体负担,在许多情况下,与项目活动有关的其他方面,客观上也要承担一部分的风险成本。项目主体负担的那部分为个体负担成本,其他方面负担的部分为社会负担成本。</li></ul><h3 id="非事件类风险"><a href="#非事件类风险" class="headerlink" title="非事件类风险"></a>非事件类风险</h3><ul><li>变异类风险:已规划的目标、活动、决策在关键方面存在不确定性。</li><li>模糊性风险:对未来可能发生什么存在不确定性。</li></ul><h3 id="项目韧性"><a href="#项目韧性" class="headerlink" title="项目韧性"></a>项目韧性</h3><p>加强项目韧性应对突发性风险。</p><h2 id="项目风险管理过程"><a href="#项目风险管理过程" class="headerlink" title="项目风险管理过程"></a>项目风险管理过程</h2><table><thead><tr><th align="left">过程</th><th align="left">输入</th><th align="left">工具与技术</th><th align="left">输出</th></tr></thead><tbody><tr><td align="left">规划风险管理</td><td align="left"><ul><li>项目章程</li><li>项目管理计划</li><li>项目文件</li><li>事业环境因素</li><li>组织过程资产</li></ul></td><td align="left"><ul><li>专家判断</li><li>数据分析</li><li>会议</li></ul></td><td align="left"><ul><li>风险管理计划</li></ul></td></tr><tr><td align="left">识别风险</td><td align="left"><ul><li>项目管理计划</li><li>项目文件</li><li>采购文档</li><li>协议</li><li>事业环境因素</li><li>组织过程资产</li></ul></td><td align="left"><ul><li>专家判断</li><li>数据收集</li><li>数据分析</li><li>人际关系与团队技能</li><li>提示清单</li><li>会议</li></ul></td><td align="left"><ul><li>风险登记册</li><li>风险报告</li><li>项目文件(更新)</li></ul></td></tr><tr><td align="left">实施定性风险分析</td><td align="left"><ul><li>项目管理计划</li><li>项目文件</li><li>事业环境因素</li><li>组织过程资产</li></ul></td><td align="left"><ul><li>专家判断</li><li>数据收集</li><li>数据分析</li><li>人际关系与团队技能</li><li>风险分类</li><li>数据表现</li><li>会议</li></ul></td><td align="left"><ul><li>项目文件(更新)</li></ul></td></tr><tr><td align="left">实施定量风险分析</td><td align="left"><ul><li>项目管理计划</li><li>项目文件</li><li>事业环境因素</li><li>组织过程资产</li></ul></td><td align="left"><ul><li>专家判断</li><li>数据收集</li><li>人际关系与团队技能</li><li>不确定性表现方式</li><li>数据分析</li></ul></td><td align="left"><ul><li>项目文件(更新)</li></ul></td></tr><tr><td align="left">规划风险应对</td><td align="left"><ul><li>项目管理计划</li><li>项目文件</li><li>事业环境因素</li><li>组织过程资产</li></ul></td><td align="left"><ul><li>专家判断</li><li>数据收集</li><li>人际关系与团队技能</li><li>威胁应对策略</li><li>机会应对策略</li><li>应急应对策略</li><li>整体项目风险应对策略</li><li>数据分析</li><li>决策</li></ul></td><td align="left"><ul><li>变更请求</li><li>项目管理计划(更新)</li><li>项目文件(更新)</li></ul></td></tr><tr><td align="left">实施风险应对</td><td align="left"><ul><li>项目管理计划</li><li>项目文件</li><li>组织过程资产</li></ul></td><td align="left"><ul><li>专家判断</li><li>人际关系与团队技能</li><li>项目管理信息系统</li></ul></td><td align="left"><ul><li>变更请求</li><li>项目文件(更新)</li></ul></td></tr><tr><td align="left">监督风险</td><td align="left"><ul><li>项目管理计划</li><li>项目文件</li><li>工作绩效数据</li><li>工作绩效报告</li></ul></td><td align="left"><ul><li>数据分析</li><li>审计</li><li>会议</li></ul></td><td align="left"><ul><li>工作绩效信息</li><li>变更请求</li><li>项目管理计划(更新)</li><li>项目文件(更新)</li><li>组织过程资产(更新)</li></ul></td></tr></tbody></table><h3 id="裁剪考虑因素"><a href="#裁剪考虑因素" class="headerlink" title="裁剪考虑因素"></a>裁剪考虑因素</h3><blockquote><p>ps: 每个项目都是独一无二的,要根据项目实际情况对项目管理方法、流程、工具、文档进行裁剪,找到最适合项目的管理方式。比如对于小型项目来说 PMBOK 里的5大过程组、10大知识领域流程太繁杂了,可以合并或者裁剪部分过程、简化文档、减少会议,从而提升效率。</p></blockquote><p>因为每个项目都是独特的,项目经理可能需要根据需要裁剪项目风险管理过程,考虑因素包括:</p><ul><li>项目规模:由预算、进度、范围、人数所体现的项目规模,要求采取更详细的风险管理方法吗?或者项目小到只需用简化的风险管理过程吗?</li><li>项目复杂性:由高水平创新、新技术应用、界面或外部依赖关系导致的项目复杂性提高,是否要求采用更稳健的风险管理方法?</li><li>项目重要性:项目的战略重要性有多大?</li><li>开发方法:瀑布或预测型开发方式,项目的风险管理过程可以按阶段开展,敏捷或适应型开发方法,项目的风险管理过程可以在每个重复过程中开展?</li></ul><h3 id="规划风险管理"><a href="#规划风险管理" class="headerlink" title="规划风险管理"></a>规划风险管理</h3><p>定义:规划风险管理是定义如何实施项目风险管理活动的过程。</p><p>主要作用:确保风险管理的水平、方法、可见度与项目风险程度匹配;与对组织和其他干系人的重要程度匹配。</p><p>此过程仅开展一次或仅在项目的预定义点开展。</p><h4 id="输入"><a href="#输入" class="headerlink" title="输入"></a>输入</h4><ul><li>项目章程</li><li>项目管理计划:应考虑所有已批准的项目管理子计划,使风险管理计划与各计划相协调;同时,各子计划中所列的方法论也可能影响规划风险管理过程。</li><li>项目文件:干系人登记册。其中概述了干系人在项目中的角色和其对项目风险的态度,可用于确定项目风险管理的角色和职责。</li><li>事业环境因素</li><li>组织过程资产</li></ul><h4 id="工具"><a href="#工具" class="headerlink" title="工具"></a>工具</h4><ol><li>专家判断</li><li>数据分析:干系人分析法</li><li>会议</li></ol><h4 id="输出"><a href="#输出" class="headerlink" title="输出"></a>输出</h4><p>风险管理计划:风险管理计划内容包括:</p><ul><li>风险管理策略</li><li>方法论</li><li>角色与职责</li><li>资金</li><li>时间安排</li><li>风险类别</li><li>干系人风险偏好</li><li>风险概率与影响</li><li>概率与影响矩阵</li><li>报告格式</li><li>跟踪</li></ul><h3 id="识别风险"><a href="#识别风险" class="headerlink" title="识别风险"></a>识别风险</h3><p>定义:识别风险是识别单个风险对项目及整体项目风险的来源并纪录风险特征的过程。</p><p>主要作用:1.纪录现有的单个项目风险,以及整体项目风险的来源;2.汇总相关信息,以便项目团队能够恰当地应对已识别地风险。</p><p>此过程在整个项目期间开展。</p><h4 id="输入-1"><a href="#输入-1" class="headerlink" title="输入"></a>输入</h4><h4 id="工具-1"><a href="#工具-1" class="headerlink" title="工具"></a>工具</h4><h4 id="输出-1"><a href="#输出-1" class="headerlink" title="输出"></a>输出</h4><ul><li>风险登记册</li><li>风险报告</li></ul><h3 id="实施定性风险分析"><a href="#实施定性风险分析" class="headerlink" title="实施定性风险分析"></a>实施定性风险分析</h3><p>定义:实施定性风险分析是通过评估单个项目风险发生地概率和影响及其特征对风险进行<code>优先级排序</code>,从而为后续分析或行动提供基础的过程。</p><p>主要作用:1.重点关注高优先级的风险。</p><p>此过程在整个项目期间开展。</p><h4 id="输入-2"><a href="#输入-2" class="headerlink" title="输入"></a>输入</h4><h4 id="工具-2"><a href="#工具-2" class="headerlink" title="工具"></a>工具</h4><ul><li>数据分析<ul><li>风险数据质量评估</li><li>风险概率和影响评估</li><li>其他风险参数评估:为方便未来分析和行动,在对单个项目风险进行优先级排序时,项目团队可能考虑以下风险特征<ul><li>紧迫性</li><li>邻近性</li><li>可管理性</li><li>可控性</li><li>可监测性</li><li>连通性</li><li>战略影响力</li><li>密切度</li></ul></li></ul></li><li>数据表现<ul><li>概率和影响矩阵</li><li>层级图</li></ul></li></ul><h4 id="输出-2"><a href="#输出-2" class="headerlink" title="输出"></a>输出</h4><h3 id="实施定量风险分析"><a href="#实施定量风险分析" class="headerlink" title="实施定量风险分析"></a>实施定量风险分析</h3><p>定义:实施定量风险分析是就已识别的单个项目风险和不确定性的其他来源对整体项目目标的影响进行定量分析的过程。</p><p>主要作用:1.量化整体项目风险;2.提供额外的定量风险信息,以支持风险应对规划。</p><p>此过程本非每个项目需要,如需要需在整个项目持续开展。</p><h4 id="输入-3"><a href="#输入-3" class="headerlink" title="输入"></a>输入</h4><h4 id="工具-3"><a href="#工具-3" class="headerlink" title="工具"></a>工具</h4><ul><li>数据分析<ul><li>模拟</li><li>敏感性分析:敏感性分析的结果通常用<code>龙卷风图</code>表示</li><li>决策树分析:用决策树在若干备选行动方案中选择一个最佳方案。</li><li>影响图:不确定条件下进行决策的图形辅助工具。</li></ul></li></ul><h4 id="输出-3"><a href="#输出-3" class="headerlink" title="输出"></a>输出</h4><h3 id="规划风险应对"><a href="#规划风险应对" class="headerlink" title="规划风险应对"></a>规划风险应对</h3><p>定义:规划风险的应对措施是为了应对项目风险而制定可选方案、选择应对策略并商定应对行动的过程。</p><p>主要作用:1.制定应对整体项目风险和单个项目风险的适当方法;2.分配资源,并根据需要将相关活动添加进项目文件和项目管理计划中。</p><p>此过程需在整个项目开展。</p><h4 id="输入-4"><a href="#输入-4" class="headerlink" title="输入"></a>输入</h4><h4 id="工具-4"><a href="#工具-4" class="headerlink" title="工具"></a>工具</h4><ul><li>威胁应对策略<ul><li>上报</li><li>规避</li><li>转移</li><li>减轻</li><li>接受</li></ul></li><li>机会应对策略<ul><li>上报</li><li>开拓</li><li>分享</li><li>提高</li><li>接受</li></ul></li><li>应急应对策略</li><li>整体项目风险应对策略<ul><li>规避</li><li>开拓</li><li>转移或分享</li><li>减轻或提高</li><li>接受</li></ul></li></ul><h4 id="输出-4"><a href="#输出-4" class="headerlink" title="输出"></a>输出</h4><h3 id="实施风险应对"><a href="#实施风险应对" class="headerlink" title="实施风险应对"></a>实施风险应对</h3><p>定义:实施风险应对是执行商定的风险应对计划的过程。</p><p>主要作用:1.确保按计划执行商定的风险应对措施;2.管理整体项目风险入口、最小化单个项目威胁,以及最大化单个项目机会。</p><p>此过程需在整个项目开展。</p><h4 id="输入-5"><a href="#输入-5" class="headerlink" title="输入"></a>输入</h4><h4 id="工具-5"><a href="#工具-5" class="headerlink" title="工具"></a>工具</h4><h4 id="输出-5"><a href="#输出-5" class="headerlink" title="输出"></a>输出</h4><h3 id="监督风险"><a href="#监督风险" class="headerlink" title="监督风险"></a>监督风险</h3><p>定义:监督风险是在整个项目期间,监督风险应对计划的实施,并跟踪已识别风险、识别和分析新风险,以及评估风险管理有效性的过程。</p><p>主要作用:保证项目决策是在整体项目风险和单个项目风险当前信息的基础上进行。</p><p>此过程需在整个项目开展。</p><h4 id="输入-6"><a href="#输入-6" class="headerlink" title="输入"></a>输入</h4><h4 id="工具-6"><a href="#工具-6" class="headerlink" title="工具"></a>工具</h4><h4 id="输出-6"><a href="#输出-6" class="headerlink" title="输出"></a>输出</h4><h2 id="风险管理示例"><a href="#风险管理示例" class="headerlink" title="风险管理示例"></a>风险管理示例</h2><h3 id="风险登记册"><a href="#风险登记册" class="headerlink" title="风险登记册"></a>风险登记册</h3><table><thead><tr><th>风险ID</th><th>风险描述</th><th>风险类别</th><th>概率</th><th>影响</th><th>风险值</th><th>应对措施</th><th>所有者</th><th>状态</th></tr></thead><tbody><tr><td>R001</td><td>核心技术人员离职</td><td>人力资源</td><td>高</td><td>高</td><td>高</td><td>提高薪酬、完善制度、知识转移</td><td>技术总监</td><td>监督中</td></tr><tr><td>R002</td><td>第三方供应商延误交付</td><td>供应链</td><td>中</td><td>高</td><td>高</td><td>提前采购、签署SLA、准备备选方案</td><td>采购经理</td><td>活跃</td></tr><tr><td>R003</td><td>需求变更频繁</td><td>需求变更</td><td>高</td><td>中</td><td>中</td><td>严格变更流程、加强沟通、建立变更委员会</td><td>项目经理</td><td>活跃</td></tr><tr><td>R004</td><td>技术方案可行性风险</td><td>技术</td><td>中</td><td>高</td><td>高</td><td>进行技术评估、POC验证、预留备选方案</td><td>技术负责人</td><td>活跃</td></tr><tr><td>R005</td><td>项目经费超支</td><td>成本</td><td>中</td><td>中</td><td>中</td><td>严格预算控制、定期审查、应急储备</td><td>财务经理</td><td>监督中</td></tr><tr><td>R006</td><td>关键路径延误</td><td>进度</td><td>中</td><td>高</td><td>高</td><td>加强进度管理、优化资源配置、预留缓冲</td><td>项目经理</td><td>活跃</td></tr><tr><td>R007</td><td>沟通不畅导致理解偏差</td><td>沟通</td><td>中</td><td>中</td><td>中</td><td>建立清晰的沟通计划、定期会议、文档规范</td><td>项目经理</td><td>活跃</td></tr><tr><td>R008</td><td>第三方系统集成问题</td><td>技术集成</td><td>中</td><td>高</td><td>高</td><td>提前进行接口设计、集成测试计划、备选集成方案</td><td>集成负责人</td><td>活跃</td></tr></tbody></table>]]></content>
<summary type="html"><p>项目风险管理包括规划管理、风险识别、风险分析、风险应对、风险监督等各个过程。</p>
<h2 id="管理基础"><a href="#管理基础" class="headerlink" title="管理基础"></a>管理基础</h2><h3 id="项目风险概述"><a </summary>
<category term="项目管理" scheme="https://probieluo.github.io/categories/%E9%A1%B9%E7%9B%AE%E7%AE%A1%E7%90%86/"/>
</entry>
<entry>
<title>项目管理十大知识领域-08项目干系人管理</title>
<link href="https://probieluo.github.io/posts/project-management-08-project-stakeholder-management/"/>
<id>https://probieluo.github.io/posts/project-management-08-project-stakeholder-management/</id>
<published>2026-04-23T15:00:00.000Z</published>
<updated>2026-04-30T02:39:35.683Z</updated>
<content type="html"><![CDATA[<p>项目干系人管理包括识别能够影响项目或会受项目影响的人员、团队或组织,分析干系人对项目的期望和影响,制定管理策略有效调动干系人参与项目与项目决策和执行。</p><span id="more"></span><h2 id="项目干系人管理过程、"><a href="#项目干系人管理过程、" class="headerlink" title="项目干系人管理过程、"></a>项目干系人管理过程、</h2><ul><li>识别干系人:定期识别干系人,分析和纪录他们的利益、参与度、相互依赖性、影响力和对项目潜在的影响。</li><li>规划干系人参与:根据干系人的需求、期望、利益和对项目的潜在影响,制定项目干系人参与项目的方法。</li><li>管理干系人参与:与干系人进行沟通和协作,以满足其需求与期望,并处理问题,以促进干系人合理参与。</li><li>监督干系人参与:监督项目干系人关系,并通过修订参与策略和计划来引导干系人合理参与项目。</li></ul><table><thead><tr><th align="left">过程</th><th align="left">输入</th><th align="left">工具与技术</th><th align="left">输出</th></tr></thead><tbody><tr><td align="left">识别干系人</td><td align="left"><ul><li>项目立项文件</li><li>项目章程</li><li>项目管理计划</li><li>项目文件</li><li>协议</li><li>事业环境因素</li><li>组织过程资产</li></ul></td><td align="left"><ul><li>专家判断</li><li>数据收集</li><li>数据分析</li><li>数据表现</li><li>会议</li></ul></td><td align="left"><ul><li>干系人登记册</li><li>变更请求</li><li>项目管理计划(更新)</li><li>项目文件(更新)</li></ul></td></tr><tr><td align="left">规划干系人参与</td><td align="left"><ul><li>项目章程</li><li>项目管理计划</li><li>项目文件</li><li>协议</li><li>事业环境因素</li><li>组织过程资产</li></ul></td><td align="left"><ul><li>专家判断</li><li>数据收集</li><li>数据分析</li><li>决策</li><li>数据表现</li><li>会议</li></ul></td><td align="left"><ul><li>干系人参与计划</li></ul></td></tr><tr><td align="left">管理干系人参与</td><td align="left"><ul><li>项目管理计划</li><li>项目文件</li><li>事业环境因素</li><li>组织过程资产</li></ul></td><td align="left"><ul><li>专家判断</li><li>沟通技能</li><li>人际关系与团队技能</li><li>基本规则</li><li>会议</li></ul></td><td align="left"><ul><li>变更请求</li><li>项目管理计划(更新)</li><li>项目文件(更新)</li></ul></td></tr><tr><td align="left">监督干系人参与</td><td align="left"><ul><li>项目管理计划</li><li>项目文件</li><li>工作绩效数据</li><li>事业环境因素</li><li>组织过程资产</li></ul></td><td align="left"><ul><li>数据分析</li><li>决策</li><li>数据表现</li><li>沟通技能</li><li>人际关系与团队技能</li><li>会议</li></ul></td><td align="left"><ul><li>工作绩效信息</li><li>变更请求</li><li>项目管理计划(更新)</li><li>项目文件(更新)</li></ul></td></tr></tbody></table><h3 id="裁剪考虑因素"><a href="#裁剪考虑因素" class="headerlink" title="裁剪考虑因素"></a>裁剪考虑因素</h3><blockquote><p>ps: 每个项目都是独一无二的,要根据项目实际情况对项目管理方法、流程、工具、文档进行裁剪,找到最适合项目的管理方式。比如对于小型项目来说 PMBOK 里的5大过程组、10大知识领域流程太繁杂了,可以合并或者裁剪部分过程、简化文档、减少会议,从而提升效率。</p></blockquote>]]></content>
<summary type="html"><p>项目干系人管理包括识别能够影响项目或会受项目影响的人员、团队或组织,分析干系人对项目的期望和影响,制定管理策略有效调动干系人参与项目与项目决策和执行。</p></summary>
<category term="项目管理" scheme="https://probieluo.github.io/categories/%E9%A1%B9%E7%9B%AE%E7%AE%A1%E7%90%86/"/>
</entry>
<entry>
<title>项目管理十大知识领域-07项目沟通管理</title>
<link href="https://probieluo.github.io/posts/project-management-07-project-communication-management/"/>
<id>https://probieluo.github.io/posts/project-management-07-project-communication-management/</id>
<published>2026-04-22T15:37:14.000Z</published>
<updated>2026-04-23T00:32:22.723Z</updated>
<content type="html"><![CDATA[<p>项目沟通管理就是要确保及时、正确地产生、收集、分发、存储和最终处理项目信息所需的过程。项目沟通管理过程揭示了实现成功沟通所需的人员、观点、信息这三个元素的联络关系。</p><h2 id="管理基础"><a href="#管理基础" class="headerlink" title="管理基础"></a>管理基础</h2><h3 id="沟通"><a href="#沟通" class="headerlink" title="沟通"></a>沟通</h3><p>沟通的具体形式:</p><ul><li>书面形式</li><li>口头形式</li><li>证实或非正式形式</li><li>手势动作</li><li>媒体形式</li><li>遣词造句</li></ul><h3 id="沟通模型"><a href="#沟通模型" class="headerlink" title="沟通模型"></a>沟通模型</h3><p>沟通的基本模型用于显示信息如何在双方之间被发送和被接收。沟通的关键要素:</p><ul><li>编码:把思想或想法转化为他人理解的语言</li><li>信息与反馈信息:编码过程所得到的结果</li><li>媒介:用来传递信息的方法</li><li>噪声:干扰信息传输和理解的一切因素</li><li>解码:把信息还原成有有意义的思想或想法</li></ul><p>沟通模型五种状态:已发送、已收到、已理解、已认可、已转化为积极的行动</p><h3 id="沟通分类"><a href="#沟通分类" class="headerlink" title="沟通分类"></a>沟通分类</h3><p>沟通活动可按多种维度分类:</p><ul><li>内部沟通和外部沟通</li><li>正式沟通和非正式沟通</li><li>官方和非官方沟通</li><li>书面与口头沟通</li><li>层级沟通:干系人箭根据内部职权不同,采取向上(针对高层),向下(针对团队成员)、横向(针对同级项目经理或其他成员)等不同沟通方式。</li></ul><h3 id="沟通技巧"><a href="#沟通技巧" class="headerlink" title="沟通技巧"></a>沟通技巧</h3><p>一般来说,有效的沟通活动和成果创建有三个基本属性:</p><ol><li>沟通目的明确</li><li>尽量了解沟通接收方,满足其需求及偏好</li><li>监督并衡量沟通的效果</li></ol><p>书面沟通5C原则:</p><ol><li>正确的语法和拼写(Correctness)</li><li>简洁的表述(Concise)</li><li>清晰的目的和表述(Clarity)</li><li>连贯的思维逻辑(Coherent)</li><li>善用语句控制和承接(Controlling)</li></ol><p>其他沟通技巧:</p><ol><li>积极倾听,与说话人保持互动,并总结对话内容</li><li>理解文化和个人差异,减少误解</li><li>识别、设定并管理干系人期望,减少干系人群体间自相矛盾的期望</li><li>强化技能:说服个人、团队或组织采取行动;激励和鼓励人们;指导人们改进绩效;解决冲突</li></ol><h2 id="项目沟通管理过程"><a href="#项目沟通管理过程" class="headerlink" title="项目沟通管理过程"></a>项目沟通管理过程</h2><table><thead><tr><th align="left">过程</th><th align="left">输入</th><th align="left">工具与技术</th><th align="left">输出</th></tr></thead><tbody><tr><td align="left">规划沟通管理</td><td align="left"><ul><li>项目章程</li><li>项目管理计划</li><li>项目文件</li><li>事业环境因素</li><li>组织过程资产</li></ul></td><td align="left"><ul><li>专家判断</li><li>沟通需求分析</li><li>沟通技术</li><li>沟通模型</li><li>沟通方法</li><li>人际关系与团队技能</li><li>数据表现</li><li>会议</li></ul></td><td align="left"><ul><li>沟通管理计划</li><li>项目管理计划(更新)</li><li>项目文件(更新)</li></ul></td></tr><tr><td align="left">管理沟通</td><td align="left"><ul><li>项目管理计划</li><li>项目文件</li><li>工作绩效报告</li><li>事业环境因素</li><li>组织过程资产</li></ul></td><td align="left"><ul><li>沟通技术</li><li>沟通方法</li><li>沟通技能</li><li>项目管理信息系统</li><li>项目报告</li><li>人际关系与团队技能</li><li>会议</li></ul></td><td align="left"><ul><li>项目沟通纪录</li><li>项目管理计划(更新)</li><li>项目文件(更新)</li><li>组织过程资产(更新)</li></ul></td></tr><tr><td align="left">监督沟通</td><td align="left"><ul><li>项目管理计划</li><li>项目文件</li><li>工作绩效数据</li><li>事业环境因素</li><li>组织过程资产</li></ul></td><td align="left"><ul><li>专家判断</li><li>项目管理信息系统</li><li>数据表现</li><li>人际关系与团队技能</li><li>会议</li></ul></td><td align="left"><ul><li>工作绩效信息</li><li>变更请求</li><li>项目管理计划(更新)</li><li>项目文件(更新)</li></ul></td></tr></tbody></table><h3 id="裁剪考虑因素"><a href="#裁剪考虑因素" class="headerlink" title="裁剪考虑因素"></a>裁剪考虑因素</h3><blockquote><p>ps: 每个项目都是独一无二的,要根据项目实际情况对项目管理方法、流程、工具、文档进行裁剪,找到最适合项目的管理方式。比如对于小型项目来说 PMBOK 里的5大过程组、10大知识领域流程太繁杂了,可以合并或者裁剪部分过程、简化文档、减少会议,从而提升效率。</p></blockquote><p>因为每个项目都是独特的,项目经理可能需要根据需要裁剪项目沟通管理过程,考虑因素包括:</p><ul><li>干系人</li><li>物理地点:团队成员身处何地?团队是否集中办公?团队是否位于同一地理区域?团队是否分散多个时区?</li><li>沟通技术</li><li>语言</li><li>知识管理:组织是否有正式的知识管理库?是否采用管理库?</li></ul>]]></content>
<summary type="html"><p>项目沟通管理就是要确保及时、正确地产生、收集、分发、存储和最终处理项目信息所需的过程。项目沟通管理过程揭示了实现成功沟通所需的人员、观点、信息这三个元素的联络关系。</p>
<h2 id="管理基础"><a href="#管理基础" class="headerlink" t</summary>
<category term="项目管理" scheme="https://probieluo.github.io/categories/%E9%A1%B9%E7%9B%AE%E7%AE%A1%E7%90%86/"/>
</entry>
<entry>
<title>项目管理十大知识领域-06项目资源管理</title>
<link href="https://probieluo.github.io/posts/project-management-06-project-resource-management/"/>
<id>https://probieluo.github.io/posts/project-management-06-project-resource-management/</id>
<published>2026-04-21T11:48:55.000Z</published>
<updated>2026-04-23T00:32:22.722Z</updated>
<content type="html"><![CDATA[<h2 id="管理基础"><a href="#管理基础" class="headerlink" title="管理基础"></a>管理基础</h2><h3 id="相关术语"><a href="#相关术语" class="headerlink" title="相关术语"></a>相关术语</h3><h4 id="项目团队"><a href="#项目团队" class="headerlink" title="项目团队"></a>项目团队</h4><h4 id="项目管理团队"><a href="#项目管理团队" class="headerlink" title="项目管理团队"></a>项目管理团队</h4><h4 id="项目经理"><a href="#项目经理" class="headerlink" title="项目经理"></a>项目经理</h4><h4 id="领导和管理"><a href="#领导和管理" class="headerlink" title="领导和管理"></a>领导和管理</h4><p>领导设定目标,管理者率众实现目标。</p><h4 id="权力"><a href="#权力" class="headerlink" title="权力"></a>权力</h4><p>项目经理权力有五种来源</p><ol><li>职位权力</li><li>惩罚权力</li><li>奖励权力</li><li>专家权力:来源于个人的专业技能。来自一线的中层管理者经常具有很大的专家权力。</li><li>参照权力:由于成为别人学习和参照榜样所拥有的力量。具有优秀品质的领导者的参照权力很大。</li></ol><p>职位权力、惩罚权力、奖励权力来自组织授权,专家权力、参照权力来自管理者自身。</p><p>项目经理注重运用奖励权力、专家权力、参照权力,避免使用惩罚权力。</p><h4 id="冲突和竞争"><a href="#冲突和竞争" class="headerlink" title="冲突和竞争"></a>冲突和竞争</h4><p>冲突不一定有害,一团和气的集体不一定是一个高效率的集体。</p><h4 id="团队发展阶段"><a href="#团队发展阶段" class="headerlink" title="团队发展阶段"></a>团队发展阶段</h4><p>优秀的团队一般需经历以下五个阶段</p><ol><li>形成阶段:相互认识了解项目情况,开始形成共同目标</li><li>震荡阶段:希望被现实打破。个体开始争执,互相指责,怀疑项目经理能力</li><li>规范阶段:经过一段时间模糊,成员开始协同工作,开始相互信任,项目经理能力得到认可</li><li>发挥阶段:团队成员配合默契,对项目经理信任加强,成员间相互依靠,平稳高效地解决问题,集体荣誉感强</li><li>解散阶段:所有工作完成,项目结束,团队解散</li></ol><p>上面阶段不一定按顺序进行。如果团队成员曾共事过,项目团队也可跳过某个阶段。</p><h4 id="激励理论"><a href="#激励理论" class="headerlink" title="激励理论"></a>激励理论</h4><ol><li><p>马斯洛需求层次理论</p><table><thead><tr><th>层级</th><th>需求类型</th><th>说明</th></tr></thead><tbody><tr><td>5</td><td>自我实现需求</td><td>实现个人潜能、自我完善、追求理想</td></tr><tr><td>4</td><td>尊重需求</td><td>获得他人认可、自尊、地位</td></tr><tr><td>3</td><td>社交需求</td><td>归属感、友谊、爱</td></tr><tr><td>2</td><td>安全需求</td><td>人身安全、工作保障、健康</td></tr><tr><td>1</td><td>生理需求</td><td>衣食住行等基本生存需求</td></tr></tbody></table> <figure class="highlight plaintext"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br></pre></td><td class="code"><pre><span class="line"> 自我实现需求</span><br><span class="line"> (Self-Actualization)</span><br><span class="line"> _____________</span><br><span class="line"> / \</span><br><span class="line"> / \</span><br><span class="line"> / 尊重需求(Esteem) \</span><br><span class="line"> /___________________\</span><br><span class="line"> / \</span><br><span class="line"> / 社交需求(Social) \</span><br><span class="line"> /_________________________\</span><br><span class="line"> / \</span><br><span class="line"> / 安全需求(Safety Needs) \</span><br><span class="line"> /_______________________________\</span><br><span class="line"> / \</span><br><span class="line"> / 生理需求(Physiological Needs) \</span><br><span class="line">/_____________________________________\</span><br></pre></td></tr></table></figure></li><li><p>赫茨伯格的双因素理论</p><ul><li>保健因素(Hygiene Factor):与工作条件或环境有关,能防止人们产生不满意感的一类因素</li><li>激励因素(Moticator):与员工的工作本身或工作内容有关,能促使人们产生工作满意感的一类因素</li></ul></li><li><p>X理论和Y理论</p><p> X理论对人性有如下假设:人天性好逸恶劳、人生来就以自我为中心、漠视组织要求、人缺乏进取心、逃避责任、安于现状、人通常容易受骗、人天生反对改革、人的工作动机就是为了获取经济报酬。</p><p> X理论注重满足员工生理需求和安全需求,激励仅在生理和安全层次起作用,注重惩罚。</p><p> Y理论对人性的假设与X理论完全相反:人天生不是好逸恶劳、人热爱工作、从工作中获取满足感和成就感、外来的控制和惩罚对人们实现组织目标不是一个有效的方法、下属能自我确定目标、自我指挥、自我控制、大多数人具有一定的想象力和创造力…</p><p> Y理论认为激励在需求的各个层次都起作用,常见激励办法是:将员工个人目标与组织目标融合、扩大员工工作范围、尽可能把员工工作安排得富有意义与挑战性,使其工作之后获得自豪感,满足其自尊与自我实现。</p><p> 应用时应该因人而异、因团队发展得阶段而异。</p></li><li><p>期望理论</p><p> 这是一种通过考察人们得努力程度与其获得的报酬直接得因果关系,来说明激励过程,并以选择合适的行为达到最终得奖励目标的理论。</p><p> 期望理论认为一个目标对人的激励成都受<code>目标效价</code>(指实现该目标对个人有多大价值的主观判断。如果实现该目标对个人价值很高,个人的积极性就高;反之积极性则低)和<code>期望值</code>(指个人对实现目标可能性大小的主观估计。个人认为实现该目标可能性大,就会努力争取,从而在较高程度上发挥目标的激励作用)</p><p> 期望理论认为,激励水平等于目标效价与期望值的乘积;</p><pre><code> 激发力量 = 目标效价 * 期望值</code></pre><p> 期望理论在实践中的基本原则</p><ol><li>管理者不要泛泛地抓一般的激励措施,而应当抓多数被组织成员认为效价最大的激励措施</li><li>设置某一激励目标应尽可能加大其效价的综合值。如每月奖金多少与年终奖挂钩。</li><li>适当加大不同人实际所得效价的差值,加大组织行为行为与非希望行为之间的差值,如只奖不罚与奖罚分明,其激励效果是大不一样的。</li><li>适当控制期望概率与实际概率。期望概率要适当,当一个期望概率远高于实际概率时可能产生挫折,而期望概率远小于实际概率时又会减少某一目标的激发理论。实际概率最好大于平均的个人期望概率,使大多数人受益。</li></ol></li></ol><h2 id="项目资源管理过程"><a href="#项目资源管理过程" class="headerlink" title="项目资源管理过程"></a>项目资源管理过程</h2><table><thead><tr><th align="left">过程</th><th align="left">输入</th><th align="left">工具与技术</th><th align="left">输出</th></tr></thead><tbody><tr><td align="left">规划资源管理</td><td align="left"><ul><li>项目章程</li><li>项目管理计划</li><li>项目文件</li><li>事业环境因素</li><li>组织过程资产</li></ul></td><td align="left"><ul><li>专家判断</li><li>数据表现</li><li>组织理论</li><li>会议</li></ul></td><td align="left"><ul><li>资源管理计划</li><li>团队章程</li><li>项目文件(更新)</li></ul></td></tr><tr><td align="left">估算活动资源</td><td align="left"><ul><li>项目管理计划</li><li>项目文件</li><li>事业环境因素</li><li>组织过程资产</li></ul></td><td align="left"><ul><li>专家判断</li><li>自下而上估算</li><li>类别估算</li><li>参数估算</li><li>数据分析</li><li>项目管理信息系统</li><li>会议</li></ul></td><td align="left"><ul><li>资源需求</li><li>估算依据</li><li>资源分解结构</li><li>项目文件(更新)</li></ul></td></tr><tr><td align="left">获取资源</td><td align="left"><ul><li>项目管理计划</li><li>项目文件</li><li>事业环境因素</li><li>组织过程资产</li></ul></td><td align="left"><ul><li>决策</li><li>人际关系与团队技能</li><li>预分派</li><li>虚拟团队</li></ul></td><td align="left"><ul><li>物质资源分配单</li><li>项目团队派工单</li><li>资源日历</li><li>变更请求</li><li>项目管理计划(更新)</li><li>项目文件(更新)</li><li>事业环境因素(更新)</li><li>组织过程资产(更新)</li></ul></td></tr><tr><td align="left">建设团队</td><td align="left"><ul><li>项目管理计划</li><li>项目文件</li><li>事业环境因素</li><li>组织过程资产</li></ul></td><td align="left"><ul><li>集中办公</li><li>虚拟办公</li><li>沟通技术</li><li>人际关系与团队技能</li><li>认可与奖励</li><li>培训</li><li>个人和团队评估</li><li>会议</li></ul></td><td align="left"><ul><li>团队绩效评价</li><li>变更请求</li><li>项目管理计划(更新)</li><li>项目文件(更新)</li><li>事业环境因素(更新)</li><li>组织过程资产(更新)</li></ul></td></tr><tr><td align="left">管理团队</td><td align="left"><ul><li>项目管理计划</li><li>项目文件</li><li>工作绩效报告</li><li>团队绩效评价</li><li>事业环境因素</li><li>组织过程资产</li></ul></td><td align="left"><ul><li>人际关系与团队技能</li><li>项目管理信息系统</li></ul></td><td align="left"><ul><li>变更请求</li><li>项目管理计划(更新)</li><li>项目文件(更新)</li><li>事业环境因素(更新)</li></ul></td></tr><tr><td align="left">控制资源</td><td align="left"><ul><li>项目管理计划</li><li>项目文件</li><li>工作绩效数据</li><li>协议</li><li>组织过程资产</li></ul></td><td align="left"><ul><li>数据分析</li><li>问题解决</li><li>人际关系与团队技能</li><li>项目管理信息系统</li></ul></td><td align="left"><ul><li>工作绩效信息</li><li>变更请求</li><li>项目管理计划(更新)</li><li>项目文件(更新)</li></ul></td></tr></tbody></table><h3 id="裁剪考虑因素"><a href="#裁剪考虑因素" class="headerlink" title="裁剪考虑因素"></a>裁剪考虑因素</h3><blockquote><p>ps: 每个项目都是独一无二的,要根据项目实际情况对项目管理方法、流程、工具、文档进行裁剪,找到最适合项目的管理方式。比如对于小型项目来说 PMBOK 里的5大过程组、10大知识领域流程太繁杂了,可以合并或者裁剪部分过程、简化文档、减少会议,从而提升效率。</p></blockquote><p>因为每个项目都是独特的,项目经理可能需要根据需要裁剪项目资源管理过程,考虑因素包括:</p><ul><li>多元化:团队的多元化背景是什么?</li><li>物理位置:团队成员和实物资源的物理位置在哪里?</li><li>行业特定资源获取:所在行业需要哪些特殊资源?</li><li>团队成员获取:如何获取项目团队成员?项目团队成员是全职还是兼职?</li><li>团队管理</li><li>生命周期方法</li></ul>]]></content>
<summary type="html"><h2 id="管理基础"><a href="#管理基础" class="headerlink" title="管理基础"></a>管理基础</h2><h3 id="相关术语"><a href="#相关术语" class="headerlink" title="相关术语"></a</summary>
<category term="项目管理" scheme="https://probieluo.github.io/categories/%E9%A1%B9%E7%9B%AE%E7%AE%A1%E7%90%86/"/>
</entry>
<entry>
<title>项目管理十大知识领域-05项目质量管理</title>
<link href="https://probieluo.github.io/posts/project-management-05-project-quality-management/"/>
<id>https://probieluo.github.io/posts/project-management-05-project-quality-management/</id>
<published>2026-04-21T11:48:05.000Z</published>
<updated>2026-04-22T01:59:38.156Z</updated>
<content type="html"><![CDATA[<h2 id="管理基础"><a href="#管理基础" class="headerlink" title="管理基础"></a>管理基础</h2><h3 id="质量与项目质量"><a href="#质量与项目质量" class="headerlink" title="质量与项目质量"></a>质量与项目质量</h3><h4 id="1-质量"><a href="#1-质量" class="headerlink" title="1. 质量"></a>1. 质量</h4><p>质量通常指产品质量,广义上还包括工作质量。</p><p>质量与等级的区别。质量作为实现的性能或成果,是一系列内在特性满足要求的程度。等级是对用途相同但技术特性不同的可交付成果的级别分类。</p><p>预防胜于检查</p><h4 id="2-项目质量"><a href="#2-项目质量" class="headerlink" title="2. 项目质量"></a>2. 项目质量</h4><p>项目合同通常是进行项目质量管理的主要依据</p><h3 id="质量管理"><a href="#质量管理" class="headerlink" title="质量管理"></a>质量管理</h3><h4 id="质量管理-1"><a href="#质量管理-1" class="headerlink" title="质量管理"></a>质量管理</h4><h4 id="质量方针和质量目标"><a href="#质量方针和质量目标" class="headerlink" title="质量方针和质量目标"></a>质量方针和质量目标</h4><p>质量方针是指由组织最高管理者发布的该组织总的质量宗旨和方向</p><p>质量目标是指在质量方面所追求的目的</p><h4 id="五种质量管理水平"><a href="#五种质量管理水平" class="headerlink" title="五种质量管理水平"></a>五种质量管理水平</h4><ol><li>让客户发现缺陷</li><li>控制质量过程中先检测和纠正缺陷</li><li>纠正过程本身</li><li>质量融入项目和产品的规划和设计中</li><li>在组织内创建产品质量文化</li></ol><h3 id="质量管理标准体系"><a href="#质量管理标准体系" class="headerlink" title="质量管理标准体系"></a>质量管理标准体系</h3><p>全员质量管理(<code>TQM</code>)是一种全员、全过程、全组织的品质管理。由结构、技术、人员、变革推动者四个要素组成。</p><p>TQM四个核心特征:</p><ul><li>全员参与的质量管理</li><li>全过程的质量管理</li><li>全面方法的质量管理</li><li>全面结果的质量管理</li></ul><h3 id="新趋势"><a href="#新趋势" class="headerlink" title="新趋势"></a>新趋势</h3><ul><li>客户满意</li><li>持续改进:“计划-实施-检查-行动”(PDCA)是质量改进的基础</li></ul><h2 id="项目质量管理过程"><a href="#项目质量管理过程" class="headerlink" title="项目质量管理过程"></a>项目质量管理过程</h2><table><thead><tr><th align="left">过程</th><th align="left">输入</th><th align="left">工具与技术</th><th align="left">输出</th></tr></thead><tbody><tr><td align="left">规划质量管理</td><td align="left"><ul><li>项目章程</li><li>项目管理计划</li><li>项目文件</li><li>事业环境因素</li><li>组织过程资产</li></ul></td><td align="left"><ul><li>专家判断</li><li>数据收集</li><li>数据分析</li><li>决策技术</li><li>数据表现</li><li>测试与检查的规划</li><li>会议</li></ul></td><td align="left"><ul><li>质量管理计划</li><li>质量测量指标</li><li>项目管理计划(更新)</li><li>项目文件(更新)</li></ul></td></tr><tr><td align="left">管理质量</td><td align="left"><ul><li>项目管理计划</li><li>项目文件</li><li>项目文件</li><li>组织过程资产</li></ul></td><td align="left"><ul><li>数据收集</li><li>数据分析</li><li>决策技术</li><li>数据表现</li><li>审计</li><li>面向X的设计</li><li>问题解决</li><li>质量改进方法</li></ul></td><td align="left"><ul><li>质量报告</li><li>测试与评估文件</li><li>变更请求</li><li>项目管理计划(更新)</li><li>项目文件(更新)</li></ul></td></tr><tr><td align="left">控制质量</td><td align="left"><ul><li>项目管理计划</li><li>项目文件</li><li>可交付成果</li><li>工作绩效数据</li><li>批准的变更请求</li><li>事业环境因素</li><li>组织过程资产</li></ul></td><td align="left"><ul><li>数据收集</li><li>数据分析</li><li>检查</li><li>测试/产品评估</li><li>数据表现</li><li>会议</li></ul></td><td align="left"><ul><li>工作绩效信息</li><li>质量控制测量结果</li><li>核实的可交付成果</li><li>变更请求</li><li>项目管理计划(更新)</li><li>项目文件(更新)</li></ul></td></tr></tbody></table><blockquote><p>规划质量管理属于规划过程组,管理质量属于执行过程组,控制质量属于监控过程组</p></blockquote><h3 id="裁剪考虑因素"><a href="#裁剪考虑因素" class="headerlink" title="裁剪考虑因素"></a>裁剪考虑因素</h3><blockquote><p>ps: 每个项目都是独一无二的,要根据项目实际情况对项目管理方法、流程、工具、文档进行裁剪,找到最适合项目的管理方式。比如对于小型项目来说 PMBOK 里的5大过程组、10大知识领域流程太繁杂了,可以合并或者裁剪部分过程、简化文档、减少会议,从而提升效率。</p></blockquote><p>因为每个项目都是独特的,项目经理可能需要根据需要裁剪项目质量管理过程,考虑因素包括:</p><ul><li>政策合规与审计</li><li>标准与法规合规性</li><li>持续改进</li><li>干系人参与</li></ul>]]></content>
<summary type="html"><h2 id="管理基础"><a href="#管理基础" class="headerlink" title="管理基础"></a>管理基础</h2><h3 id="质量与项目质量"><a href="#质量与项目质量" class="headerlink" title="质量与</summary>
<category term="项目管理" scheme="https://probieluo.github.io/categories/%E9%A1%B9%E7%9B%AE%E7%AE%A1%E7%90%86/"/>
</entry>
<entry>
<title>摸鱼派聊天室VisualStudio扩展</title>
<link href="https://probieluo.github.io/posts/visualstudio-extension-fishpi/"/>
<id>https://probieluo.github.io/posts/visualstudio-extension-fishpi/</id>
<published>2026-04-17T01:16:38.000Z</published>
<updated>2026-04-17T01:28:42.189Z</updated>
<content type="html"><![CDATA[<p>这是一个将摸鱼派聊天室集成到 Visual Studio 的扩展,在打代码的同时无缝摸鱼,目前只试过VS2026,不过2022应该也是可以的,其他的就不知道了。</p><p><del>后续可能会更新,完善了会打包到拓展市场。</del> 市场已发布 <a href="https://marketplace.visualstudio.com/items?itemName=fishpi-csharp.fishpivs3a86116a59f44c3ea71adc161012381e2e">鱼排聊天室VisualStudio拓展</a></p><p><strong>项目主页与下载</strong></p><ul><li>Releases: <a href="https://github.com/probieLuo/FishpiVS2026Plugin/releases">https://github.com/probieLuo/FishpiVS2026Plugin/releases</a></li></ul><p><strong>安装</strong></p><ol><li>访问 Releases 页面下载最新的 <code>.vsix</code> 文件。</li><li>安装前请先关闭 Visual Studio。</li><li>执行 <code>FishpiVS2026Plugin.vsix</code> 进行安装。</li></ol><p><strong>配置</strong></p><ol><li>登录摸鱼派网页版(<a href="https://fishpi.cn/">https://fishpi.cn</a>)。</li><li>打开地址: <a href="https://fishpi.cn/chat-room/node/get">https://fishpi.cn/chat-room/node/get</a> ,获取 <code>node</code> 与 <code>apikey</code>。</li><li>在 Visual Studio 中打开扩展窗口:视图 => 其他窗口 => Fishpi。</li><li>在扩展窗口中点击 <code>set</code>,填写 <code>node</code> 和 <code>apikey</code> 并保存。</li></ol><p><img src="https://github.com/probieLuo/FishpiVS2026Plugin/blob/master/resources/image.png?raw=true" alt="获取key"></p><p><img src="https://github.com/probieLuo/FishpiVS2026Plugin/blob/master/resources/image-1.png?raw=true" alt="设置key"></p><p><img src="https://github.com/probieLuo/FishpiVS2026Plugin/blob/master/resources/image-2.png?raw=true" alt="窗口位置"></p><p>谢谢使用!</p><hr><p><strong>Todo</strong></p><ul><li><input checked="" disabled="" type="checkbox"> 消息引用</li><li><input checked="" disabled="" type="checkbox"> 撤回消息</li><li><input checked="" disabled="" type="checkbox"> 领取活跃奖励</li><li><input checked="" disabled="" type="checkbox"> 查看活跃度</li><li><input checked="" disabled="" type="checkbox"> 清风明月</li><li><input checked="" disabled="" type="checkbox"> 聊天室小尾巴</li><li><input checked="" disabled="" type="checkbox"> 屏蔽机器人消息</li><li><input checked="" disabled="" type="checkbox"> 屏蔽空消息</li><li><input disabled="" type="checkbox"> 收发红包</li><li><input disabled="" type="checkbox"> 其他摸鱼小功能…</li></ul><p>效果展示~</p><p><img src="https://github.com/probieLuo/FishpiVS2026Plugin/blob/master/resources/%E5%B1%8F%E5%B9%95%E5%BD%95%E5%88%B62026-03-09161012.gif?raw=true" alt="效果gif"></p>]]></content>
<summary type="html"><p>这是一个将摸鱼派聊天室集成到 Visual Studio 的扩展,在打代码的同时无缝摸鱼,目前只试过VS2026,不过2022应该也是可以的,其他的就不知道了。</p>
<p><del>后续可能会更新,完善了会打包到拓展市场。</del> 市场已发布 <a href="ht</summary>
<category term="编程技术" scheme="https://probieluo.github.io/categories/%E7%BC%96%E7%A8%8B%E6%8A%80%E6%9C%AF/"/>
<category term="VisualStudio扩展" scheme="https://probieluo.github.io/tags/VisualStudio%E6%89%A9%E5%B1%95/"/>
</entry>
<entry>
<title>项目管理十大知识领域-04项目成本管理</title>
<link href="https://probieluo.github.io/posts/project-management-04-project-cost-management/"/>
<id>https://probieluo.github.io/posts/project-management-04-project-cost-management/</id>
<published>2026-04-16T16:05:52.000Z</published>
<updated>2026-04-22T09:21:12.915Z</updated>
<content type="html"><![CDATA[<p>项目成本管理是为了项目在批准的预算内完成,对成本进行规划、估算、预算、融资、筹资、管理和控制的过程。应考虑干系人对成本的要求。</p><span id="more"></span><h2 id="管理基础"><a href="#管理基础" class="headerlink" title="管理基础"></a>管理基础</h2><h3 id="重要性和意义"><a href="#重要性和意义" class="headerlink" title="重要性和意义"></a>重要性和意义</h3><p>项目管理主要受范围、时间、成本、质量的<strong>约束</strong>,项目成本管理在项目中占有重要地位。项目成本管理就是要确保项目在批准的预算内完成。如果项目建设实际成本超出批准的投资预算,就很容易造成成本失控。</p><p>成本失控的原因:</p><ol><li><p>对工程认识不足</p><ul><li>对信息系统工程成本控制的特点认识不足,对难度估计不足</li><li>工程项目规模不合理</li><li>工程项目设计和实施人员缺乏成本意识,导致项目设计不满足成本控制要求</li><li>对项目成本的使用缺乏责任感</li></ul></li><li><p>组织制度不健全</p><ul><li>制度不完善</li><li>责任不落实,缺乏成本控制责任感,没有落实具体的成本控制人员</li><li>承建单位项目经理中没有明确的投资分工,导致对投资控制领导、督察不力等</li></ul></li><li><p>方法问题</p><ul><li>缺乏项目投资控制有关报表及数据处理方法</li><li>缺乏系统成本控制程序和明确的具体要求</li><li>缺乏科学的成本控制方法和工作制度</li><li>缺乏对计算机辅助投资控制程序的利用</li><li>缺乏对计划值与实际值动态比较分析</li></ul></li><li><p>技术的制约</p><ul><li>项目成本估算在项目建设早期,对项目信息了解不深,项目规划设计不够完善,不能满足成本估算的需求</li><li>估算方法不恰当</li><li>成本计算数据不准确或有漏项,导致计算成本偏低</li><li>物资或设备价格上涨</li><li>变更引起相关成本增加</li><li>对可能遇见的风险估计不足,导致实施成本大量增加</li></ul></li><li><p>需求管理不当。项目需求分析出现失误,项目范围频繁变更。</p></li></ol><h3 id="相关术语定义"><a href="#相关术语定义" class="headerlink" title="相关术语定义"></a>相关术语定义</h3><ol><li><p>项目成本</p><p>项目成本指项目活动或其组成部分的货币价值。具体的成本一般包括直接工时、其他直接费用、间接工时、其他间接费用、采购价格。项目全过程所耗用的各种成本总和为项目成本。</p></li><li><p>产品的全生命周期成本</p></li><li><p>成本的类型</p><ul><li>可变成本</li><li>固定成本</li><li>直接成本:直接归属于项目工作的成本</li><li>间接成本:一般管理费用科目或几个项目共同负担的项目成本所分摊给本项目的费用</li><li>机会成本:做出某一选择后同时失去其他选择,其他选择中最大的损失</li><li>沉没成本:历史成本,投资时尽量排除沉没成本的干扰</li></ul></li><li><p>应急储备和管理储备</p><p> 应急储备是包含在成本基准内的一部分预算,应对已识别风险。应对影响项目的“已知-未知”风险(知道项目有那些风险,但不知道具体是否发生)。</p><p> 管理储备是为了管理控制的目的而特别留出的项目预算,用来应对项目范围中不可预见的工作。应对影响项目的“未知-未知”风险(不知道项目有那些风险,不知道具体是否发生)。不包含在成本基准中,但属于项目总预算的一部分。</p></li><li><p>成本基准</p><p> 指经批准的按时间安排的成本支出计划。</p></li></ol><h2 id="项目成本管理过程"><a href="#项目成本管理过程" class="headerlink" title="项目成本管理过程"></a>项目成本管理过程</h2><table><thead><tr><th align="left">过程</th><th align="left">输入</th><th align="left">工具与技术</th><th align="left">输出</th></tr></thead><tbody><tr><td align="left">规划成本管理</td><td align="left"><ul><li>项目章程</li><li>项目管理计划</li><li>事业环境因素</li><li>组织过程资产</li></ul></td><td align="left"><ul><li>专家判断</li><li>数据分析</li><li>会议</li></ul></td><td align="left">成本管理计划</td></tr><tr><td align="left">估算成本</td><td align="left"><ul><li>项目管理计划</li><li>项目文件</li><li>事业环境因素</li><li>组织过程资产</li></ul></td><td align="left"><ul><li>专家判断</li><li>类别估算</li><li>参数估算</li><li>自下而上估算</li><li>三点估算</li><li>数据分析</li><li>项目管理信息系统</li><li>决策</li></ul></td><td align="left"><ul><li>成本估算</li><li>估算依据</li><li>项目文件(更新)</li></ul></td></tr><tr><td align="left">制定预算</td><td align="left"><ul><li>项目管理计划</li><li>可行性研究文件</li><li>项目文件</li><li>协议</li><li>事业环境因素</li><li>组织过程资产</li></ul></td><td align="left"><ul><li>专家判断</li><li>成本汇总</li><li>数据分析</li><li>历史信息审核</li><li>资金限制平衡</li><li>融资</li></ul></td><td align="left"><ul><li>成本基准</li><li>项目资金需求</li><li>项目文件(更新)</li></ul></td></tr><tr><td align="left">控制成本</td><td align="left"><ul><li>项目管理计划</li><li>项目资金需求</li><li>项目文件</li><li>工作绩效数据</li><li>组织过程资产</li></ul></td><td align="left"><ul><li>专家判断</li><li>数据分析</li><li>完工尚需绩效指数</li><li>项目管理信息系统</li></ul></td><td align="left"><ul><li>工作绩效信息</li><li>成本预测</li><li>变更请求</li><li>项目管理计划(更新)</li><li>项目文件(更新)</li></ul></td></tr></tbody></table><blockquote><p>规划成本管理、估算成本、制定预算都属于规划过程组,控制成本属于监控过程组</p></blockquote><h3 id="裁剪考虑因素"><a href="#裁剪考虑因素" class="headerlink" title="裁剪考虑因素"></a>裁剪考虑因素</h3><blockquote><p>ps: 每个项目都是独一无二的,要根据项目实际情况对项目管理方法、流程、工具、文档进行裁剪,找到最适合项目的管理方式。比如对于小型项目来说 PMBOK 里的5大过程组、10大知识领域流程太繁杂了,可以合并或者裁剪部分过程、简化文档、减少会议,从而提升效率。</p></blockquote><p>因为每个项目都是独特的,项目经理可能需要根据需要裁剪项目成本管理过程,考虑因素包括:</p><ul><li>知识复用</li><li>估算和预算</li><li>挣值管理</li><li>敏捷方法的使用</li><li>治理</li></ul><h2 id="规划成本管理"><a href="#规划成本管理" class="headerlink" title="规划成本管理"></a>规划成本管理</h2><p>定义:确定如何估算、预算、管理、监督、控制项目成本。</p><p>作用:在整个项目期间为如何管理项目成本提供指南和方向。</p><p>仅开展一次</p><h3 id="输入"><a href="#输入" class="headerlink" title="输入"></a>输入</h3><p>项目章程</p><p>进度管理计划、风险管理计划</p><p>事业环境因素、组织过程资产</p><h3 id="输出"><a href="#输出" class="headerlink" title="输出"></a>输出</h3><p><strong>成本管理计划</strong>:描述将如何规划、安排和控制项目成本。</p><ul><li>计量单位</li><li>精确度</li><li>准确度</li><li>绩效测量规则</li><li>组织程序链接</li><li>控制临界值</li><li>报告格式</li></ul><h2 id="估算成本"><a href="#估算成本" class="headerlink" title="估算成本"></a>估算成本</h2><p>定义:对完成项目工作所需资源成本进行近似估算的过程。</p><p>作用:确定项目所需资金。</p><p>整个项目期间定期开展</p><h3 id="输入-1"><a href="#输入-1" class="headerlink" title="输入"></a>输入</h3><p>项目管理计划:成本管理计划、质量管理计划、范围基准(项目范围说明书、WBS、WBS字典)</p><p>项目文件:风险登记册、经验教训登记册、资源需求、项目进度计划</p><p>事业环境因素、组织过程资产</p><h3 id="工具"><a href="#工具" class="headerlink" title="工具"></a>工具</h3><p>三点估算:</p><ul><li><p>最可能成本 $C_M$</p></li><li><p>最乐观成本 $C_O$</p></li><li><p>最悲观成本 $C_P$</p><p> 三角分布 $C_E=(C_M+C_O+C_P)/3$</p><p> 贝塔分布 $C_E=(C_M+4C_O+C_P)/6$</p></li></ul><h3 id="输出-1"><a href="#输出-1" class="headerlink" title="输出"></a>输出</h3><p><strong>成本估算</strong></p><p><strong>估算依据</strong></p><p>项目文件(更新):假设日志、经验教训登记册、风险登记册</p><h2 id="制定预算"><a href="#制定预算" class="headerlink" title="制定预算"></a>制定预算</h2><p>定义:汇总所有单个活动或工作包的成本,建立一个<code>经批准的成本基准</code>的过程</p><p>作用:确定可以依据其来进行监督和控制项目绩效的成本基准</p><p>仅开展一次</p><h3 id="输入-2"><a href="#输入-2" class="headerlink" title="输入"></a>输入</h3><p>项目管理计划:成本管理计划、资源管理计划、范围基准</p><p>可行性研究文件:可行性研究报告、项目评估报告</p><p>项目文件:估算依据、成本估算、项目进度计划、风险登记册</p><p>协议、事业环境因素、组织过程资产</p><h3 id="输出-2"><a href="#输出-2" class="headerlink" title="输出"></a>输出</h3><p><strong>成本基准</strong>:经批准的按时间分配的项目预算。成本基准是不同进度的活动经批准的预算的总和。</p><ul><li>项目预算和成本基准的各个组成部分:<ul><li>汇总各项目活动估算、活动应急储备,得到相关工作包的成本</li><li>汇总各工作包成本估算、应急储备,得到控制账户的成本</li><li>汇总各控制账户的成本,得到成本基准</li><li>最后在成本基准上加上管理储备,得到项目预算</li></ul></li></ul><p>项目资金需求</p><p>项目文件(更新):成本估算、项目进度计划、风险登记册</p><h2 id="控制成本"><a href="#控制成本" class="headerlink" title="控制成本"></a>控制成本</h2><p>定义:进度项目状态,更新项目成本和管理<code>成本基准</code>变更的过程</p><p>作用:在整个项目期间保持对成本基准的维护</p><p>整个项目期间开展</p><p>项目控制成本的目标</p><ol><li>对造成成本基准变更的因素施加影响</li><li>确保变更请求得到及时处理</li><li>变更发生时管理变更</li><li>确保成本不超过批准的资金限额</li><li>监督成本绩效</li><li>对照资金支出,监督工作绩效</li><li>防止成本或资源使用报告中出现未经批准的变更</li><li>向干系人报告所有经批准的变更及其相关成本</li><li>设法把预期的成本超支控制在可接受范围内</li></ol><h3 id="输入-3"><a href="#输入-3" class="headerlink" title="输入"></a>输入</h3><p><strong>成本基准</strong>:经批准的按时间分配的项目预算。成本基准是不同进度的活动经批准的预算的总和。</p><ul><li>项目预算和成本基准的各个组成部分:<ul><li>汇总各项目活动估算、活动应急储备,得到相关工作包的成本</li><li>汇总各工作包成本估算、应急储备,得到控制账户的成本</li><li>汇总各控制账户的成本,得到成本基准</li><li>最后在成本基准上加上管理储备,得到项目预算</li></ul></li></ul><p>项目资金需求</p><p>项目文件:经验教训登记册</p><p>工作绩效数据</p><p>组织过程资产</p><h3 id="工具技术"><a href="#工具技术" class="headerlink" title="工具技术"></a>工具技术</h3><p>数据分析</p><ul><li>挣值分析</li><li>偏差分析</li><li>趋势分析</li></ul><p>完工尚需绩效指数</p><h3 id="输出-3"><a href="#输出-3" class="headerlink" title="输出"></a>输出</h3><p>工作绩效信息</p><p>成本预测</p><p>变更请求</p><p>项目管理计划(更新):成本管理计划、成本基准、绩效测量基准</p><p>项目文件(更新):假设日志、估算依据、成本估算、经验教训登记册、风险登记册</p>]]></content>
<summary type="html"><p>项目成本管理是为了项目在批准的预算内完成,对成本进行规划、估算、预算、融资、筹资、管理和控制的过程。应考虑干系人对成本的要求。</p></summary>
<category term="项目管理" scheme="https://probieluo.github.io/categories/%E9%A1%B9%E7%9B%AE%E7%AE%A1%E7%90%86/"/>
</entry>
<entry>
<title>艾尔登法环全成就</title>
<link href="https://probieluo.github.io/posts/eldenring-passage-sharing/"/>
<id>https://probieluo.github.io/posts/eldenring-passage-sharing/</id>
<published>2026-04-16T00:52:19.000Z</published>
<updated>2026-04-22T09:10:27.183Z</updated>
<content type="html"><![CDATA[<p>之前趁着steam冬促入了艾尔登法环,一直没时间玩,放在库里吃灰<del>都花钱买了还要我花时间玩?</del>,最近也是肝了一个月全成就了,这游戏太上头了,之前刚开始玩不知道存档,唉,傻乎乎的玩了三个周目才全成就,我真是太强了🥲。</p><p><img src="/../assets/EldenRing01.png" alt="EldenRing01"></p><p><img src="/../assets/EldenRing02.jpg" alt="EldenRing02"></p><p><img src="/../assets/EldenRing03.jpg" alt="EldenRing03"></p><p><img src="/../assets/EldenRing04.jpg" alt="EldenRing04"></p><p><img src="/../assets/EldenRing05.jpg" alt="EldenRing05"></p><p><img src="/../assets/EldenRing06.jpg" alt="EldenRing06"></p><p><img src="/../assets/EldenRing07.jpg" alt="EldenRing07"></p><p><img src="/../assets/EldenRing08.jpg" alt="EldenRing08"></p><p><img src="/../assets/EldenRing09.jpg" alt="EldenRing09"></p><p><img src="/../assets/EldenRing10.jpg" alt="EldenRing10"></p><p><img src="/../assets/EldenRing11.jpg" alt="EldenRing11"></p><p><img src="/../assets/EldenRing12.jpg" alt="EldenRing12"></p><p><img src="/../assets/EldenRing13.jpg" alt="EldenRing13"></p><p><img src="/../assets/EldenRing14.jpg" alt="EldenRing14"></p><p><img src="/../assets/EldenRing15.jpg" alt="EldenRing15"></p><p><img src="/../assets/EldenRing16.jpg" alt="EldenRing16"></p><p><img src="/../assets/EldenRing17.jpg" alt="EldenRing17"></p><p>之前由于专心于全成就,其他结局忘记截屏了,可惜可惜。</p>]]></content>
<summary type="html"><p>之前趁着steam冬促入了艾尔登法环,一直没时间玩,放在库里吃灰<del>都花钱买了还要我花时间玩?</del>,最近也是肝了一个月全成就了,这游戏太上头了,之前刚开始玩不知道存档,唉,傻乎乎的玩了三个周目才全成就,我真是太强了🥲。</p>
<p><img src="/.</summary>
<category term="随笔" scheme="https://probieluo.github.io/categories/%E9%9A%8F%E7%AC%94/"/>
<category term="游戏" scheme="https://probieluo.github.io/tags/%E6%B8%B8%E6%88%8F/"/>
</entry>
<entry>
<title>项目管理十大知识领域-03项目进度管理</title>
<link href="https://probieluo.github.io/posts/project-management-03-project-schedule-management/"/>
<id>https://probieluo.github.io/posts/project-management-03-project-schedule-management/</id>
<published>2026-04-11T08:46:49.000Z</published>
<updated>2026-04-22T09:21:12.915Z</updated>
<content type="html"><![CDATA[<p>项目进度管理是为了保证项目按时完成,对项目所需的各个过程进行管理,包括规划进度、定义活动、排列活动顺序、估算活动持续时间、制定项目进度计划、控制进度。小型项目中,定义活动、排列活动顺序、估算活动持续时间、制定项目进度计划联系紧密,可视为一个过程,可由一个人较短时间完成(裁剪)。</p><span id="more"></span><h2 id="项目进度管理过程"><a href="#项目进度管理过程" class="headerlink" title="项目进度管理过程"></a>项目进度管理过程</h2><ul><li>规划进度管理:为了规划、编制、管理、执行、控制项目进度,制定政策、程序、文档。</li><li>定义活动:识别和记录为完成项目可交付成果而需采取的具体活动。</li><li>排列活动顺序:识别和纪录项目活动之间的关系。</li><li>估算活动持续时间:根据资源估算的结果,估算各项活动所需工作时段数。</li><li>制定进度计划:分析活动顺序、持续时间、资源需求、进度制约因素,创建项目进度模型S,落实执行和监控情况。</li><li>控制进度(监控过程组):监督项目状态,以更新项目进度和管理进度基准的变更。</li></ul><p>除了控制进度过程是监控过程组其他过程都是规划过程组</p><table><thead><tr><th align="left">过程</th><th align="left">输入</th><th align="left">工具与技术</th><th align="left">输出</th></tr></thead><tbody><tr><td align="left">规划进度管理</td><td align="left"><ul><li>项目章程</li><li>项目管理计划</li><li>事业环境因素</li><li>组织过程资产</li></ul></td><td align="left"><ul><li>专家判断</li><li>数据分析</li><li>会议</li></ul></td><td align="left">进度管理计划</td></tr><tr><td align="left">定义活动</td><td align="left"><ul><li>项目管理计划</li><li>事业环境因素</li><li>组织过程资产</li></ul></td><td align="left"><ul><li>专家判断</li><li>分解</li><li>滚动式规划</li><li>会议</li></ul></td><td align="left"><ul><li>活动清单</li><li>活动属性</li><li>里程碑清单</li><li>变更请求</li><li>项目管理计划(更新)</li></ul></td></tr><tr><td align="left">排列活动顺序</td><td align="left"><ul><li>项目管理计划</li><li>项目文件</li><li>事业环境因素</li><li>组织过程资产</li></ul></td><td align="left"><ul><li>紧前关系绘图法</li><li>箭线图法</li><li>确定和整合依赖关系</li><li>提前量和滞后量</li><li>项目管理信息系统</li></ul></td><td align="left"><ul><li>项目进度网络图</li><li>项目文件(更新)</li></ul></td></tr><tr><td align="left">估算活动持续时间</td><td align="left"><ul><li>项目管理计划</li><li>项目文件</li><li>事业环境因素</li><li>组织过程资产</li></ul></td><td align="left"><ul><li>专家判断</li><li>类比估算</li><li>参数估算</li><li>三点估算</li><li>自下而上估算</li><li>数据分析</li><li>决策</li><li>会议</li></ul></td><td align="left"><ul><li>持续时间估算</li><li>估算依据</li><li>项目文件(更新)</li></ul></td></tr><tr><td align="left">制定进度计划</td><td align="left"><ul><li>项目管理计划</li><li>项目文件</li><li>协议</li><li>事业环境因素</li><li>组织过程资产</li></ul></td><td align="left"><ul><li>进度网络分析</li><li>关键路径法</li><li>资源优化</li><li>数据分析</li><li>提前量和滞后量</li><li>进度压缩</li><li>计划评审技术</li><li>项目管理信息系统</li><li>敏捷或适应型发布规划</li></ul></td><td align="left"><ul><li>进度基准</li><li>项目进度计划</li><li>进度数据</li><li>项目日历</li><li>变更请求</li><li>项目管理计划(更新)</li><li>项目文件(更新)</li></ul></td></tr><tr><td align="left">控制进度</td><td align="left"><ul><li>项目管理计划</li><li>项目文件</li><li>工作绩效数据</li><li>组织过程资产</li></ul></td><td align="left"><ul><li>数据分析</li><li>关键路径法</li><li>项目管理信息系统</li><li>资源优化</li><li>提前量和滞后量</li><li>进度压缩</li></ul></td><td align="left"><ul><li>工作绩效信息</li><li>进度预测</li><li>变更请求</li><li>项目管理计划(更新)</li><li>项目文件(更新)</li></ul></td></tr></tbody></table><h3 id="裁剪考虑因素"><a href="#裁剪考虑因素" class="headerlink" title="裁剪考虑因素"></a>裁剪考虑因素</h3><blockquote><p>ps: 每个项目都是独一无二的,要根据项目实际情况对项目管理方法、流程、工具、文档进行裁剪,找到最适合项目的管理方式。比如对于小型项目来说 PMBOK 里的5大过程组、10大知识领域流程太繁杂了,可以合并或者裁剪部分过程、简化文档、减少会议,从而提升效率。</p></blockquote><p>因为每个项目都是独特的,项目经理可能需要根据需要裁剪项目进度管理过程,考虑因素包括:</p><ul><li>生命周期方法</li><li>资源可用性</li><li>项目维度</li><li>技术支持</li></ul><h3 id="敏捷适应方法"><a href="#敏捷适应方法" class="headerlink" title="敏捷适应方法"></a>敏捷适应方法</h3>]]></content>
<summary type="html"><p>项目进度管理是为了保证项目按时完成,对项目所需的各个过程进行管理,包括规划进度、定义活动、排列活动顺序、估算活动持续时间、制定项目进度计划、控制进度。小型项目中,定义活动、排列活动顺序、估算活动持续时间、制定项目进度计划联系紧密,可视为一个过程,可由一个人较短时间完成(裁剪)。</p></summary>
<category term="项目管理" scheme="https://probieluo.github.io/categories/%E9%A1%B9%E7%9B%AE%E7%AE%A1%E7%90%86/"/>
</entry>
<entry>
<title>项目管理十大知识领域-02项目范围管理</title>
<link href="https://probieluo.github.io/posts/project-management-02-project-scope-management/"/>
<id>https://probieluo.github.io/posts/project-management-02-project-scope-management/</id>
<published>2026-04-11T08:44:58.000Z</published>
<updated>2026-04-22T09:21:12.915Z</updated>
<content type="html"><![CDATA[<p>项目管理范围包括确保项目做且只做所需的全部工作,以成功完成项目。</p><span id="more"></span><h2 id="管理基础"><a href="#管理基础" class="headerlink" title="管理基础"></a>管理基础</h2><p>项目环境中,范围有两种含义</p><ul><li>产品范围 指产品、服务或成果所具有的特征和功能。产品范围完成情况是根据产品需求来衡量的。</li><li>项目范围 包括产品范围,是为交付具有规定特性和功能的产品、服务或成果而必须完成的工作。项目范围完成情况是根据项目管理计划来衡量的。</li></ul><h2 id="项目范围管理过程"><a href="#项目范围管理过程" class="headerlink" title="项目范围管理过程"></a>项目范围管理过程</h2><table><thead><tr><th align="left">过程</th><th align="left">输入</th><th align="left">工具与技术</th><th align="left">输出</th></tr></thead><tbody><tr><td align="left">规划范围管理</td><td align="left"><ul><li>项目章程</li><li>项目管理计划</li><li>事业环境因素</li><li>组织过程资产</li></ul></td><td align="left"><ul><li>专家判断</li><li>数据分析</li><li>会议</li></ul></td><td align="left"><ul><li>范围管理计划</li><li>需求管理计划</li></ul></td></tr><tr><td align="left">收集需求</td><td align="left"><ul><li>立项管理文件</li><li>项目章程</li><li>项目管理计划</li><li>项目文件</li><li>协议</li><li>事业环境因素</li><li>组织过程资产</li></ul></td><td align="left"><ul><li>专家判断</li><li>数据收集</li><li>数据分析</li><li>决策</li><li>数据表现</li><li>人际关系与团队技能</li><li>系统交互图</li><li>原型法</li></ul></td><td align="left"><ul><li>需求文件</li><li>需求跟踪矩阵</li></ul></td></tr><tr><td align="left">定义范围</td><td align="left"><ul><li>项目章程</li><li>项目管理计划</li><li>项目文件</li><li>事业环境因素</li><li>组织过程资产</li></ul></td><td align="left"><ul><li>专家判断</li><li>数据分析</li><li>决策</li><li>人际关系与团队技能</li><li>产品分析</li></ul></td><td align="left"><ul><li>项目范围说明书</li><li>项目文件(更新)</li></ul></td></tr><tr><td align="left">创建WBS</td><td align="left"><ul><li>项目管理计划</li><li>项目文件</li><li>事业环境因素</li><li>组织过程资产</li></ul></td><td align="left"><ul><li>专家判断</li><li>分解</li></ul></td><td align="left"><ul><li>范围基准</li><li>项目文件(更新)</li></ul></td></tr><tr><td align="left">确认范围</td><td align="left"><ul><li>项目管理计划</li><li>项目文件</li><li>工作绩效数据</li><li>核实的可交付成果</li></ul></td><td align="left"><ul><li>检查</li><li>决策</li></ul></td><td align="left"><ul><li>验收的可交付成果</li><li>变更请求</li><li>工作绩效信息</li><li>项目文件(更新)</li></ul></td></tr><tr><td align="left">控制范围</td><td align="left"><ul><li>项目管理计划</li><li>项目文件</li><li>工作绩效数据</li><li>组织过程资产</li></ul></td><td align="left">数据分析</td><td align="left"><ul><li>工作绩效信息</li><li>变更请求</li><li>项目管理计划(更新)</li><li>项目文件(更新)</li></ul></td></tr></tbody></table><h3 id="裁剪考虑因素"><a href="#裁剪考虑因素" class="headerlink" title="裁剪考虑因素"></a>裁剪考虑因素</h3><blockquote><p>ps: 每个项目都是独一无二的,要根据项目实际情况对项目管理方法、流程、工具、文档进行裁剪,找到最适合项目的管理方式。比如对于小型项目来说 PMBOK 里的5大过程组、10大知识领域流程太繁杂了,可以合并或者裁剪部分过程、简化文档、减少会议,从而提升效率。</p></blockquote><p>因为每个项目都是独特的,项目经理可能需要根据需要裁剪项目范围管理过程,考虑因素包括:</p><ul><li>知识和需求管理</li><li>确认和控制</li><li>开发方法</li><li>需求的稳定性</li><li>治理</li></ul><h3 id="敏捷适应方法"><a href="#敏捷适应方法" class="headerlink" title="敏捷适应方法"></a>敏捷适应方法</h3>]]></content>
<summary type="html"><p>项目管理范围包括确保项目做且只做所需的全部工作,以成功完成项目。</p></summary>
<category term="项目管理" scheme="https://probieluo.github.io/categories/%E9%A1%B9%E7%9B%AE%E7%AE%A1%E7%90%86/"/>
</entry>
<entry>
<title>项目管理十大知识领域-01项目整合管理</title>
<link href="https://probieluo.github.io/posts/project-management-01-project-integration-management/"/>
<id>https://probieluo.github.io/posts/project-management-01-project-integration-management/</id>
<published>2026-04-11T08:43:47.000Z</published>
<updated>2026-04-22T09:21:12.929Z</updated>
<content type="html"><![CDATA[<p>项目整合管理包括识别、定义、组合、统一、协调项目管理过程组的各个过程和项目管理活动。项目整合管理贯穿项目始终。</p><span id="more"></span><p>整合管理目标:</p><ol><li>资源分配</li><li>平衡竞争性需求</li><li>研究各种备选方法</li><li>裁剪过程以实现项目目标</li><li>管理各个项目管理知识领域之间的依赖关系</li></ol><h2 id="管理基础"><a href="#管理基础" class="headerlink" title="管理基础"></a>管理基础</h2><h3 id="执行整合"><a href="#执行整合" class="headerlink" title="执行整合"></a>执行整合</h3><p>项目整合管理由项目经理负责,项目经理负责整合所有其他知识领域的成果,并掌握项目总体情况。</p><ol><li><p>过程层面整合</p><p> 项目管理过程有的过程只发生一次,有的过程在整个项目期间会相互重叠并重复发生多次。项目经理在过程层面整合整合,项目经理如果无法整合相互作用的项目过程,那么实现项目目标的可能性将会很小。</p></li><li><p>认知层面整合</p><p> 管理项目有很多方法,方法的选择通常取决于项目具体特点。项目经理应熟练掌握所有项目管理知识领域,帮助项目经理把经验、见解、领导力等运用到项目管理中。</p></li><li><p>背景层面整合</p><p> 一些新技术的出现让组织和项目所处环境发生很大变化。在执行并管理整合,项目经理需要意识到这些新因素,决定如何利用新环境因素,让项目获得成功。</p></li></ol><h3 id="整合复杂性"><a href="#整合复杂性" class="headerlink" title="整合复杂性"></a>整合复杂性</h3><p>项目复杂性源于组织的系统行为、人类行为以及组织或环境中的不确定性。</p><p>在项目整合之前,项目经理需要考虑项目面临的内外部因素,检查项目特征或属性。作为项目的一种特征,复杂性含义是:</p><ol><li>包含多个部分</li><li>不同部分存在关联</li><li>不同部分之间的动态交互作用</li><li>这些交互作用产生的行为远大于各部分简单相加</li></ol><h3 id="项目管理计划和项目文件"><a href="#项目管理计划和项目文件" class="headerlink" title="项目管理计划和项目文件"></a>项目管理计划和项目文件</h3><p><strong>项目管理过程中会产生两大类文件</strong>:<code>项目管理文件</code>;<code>项目文件</code></p><blockquote><p>ps 计划和文件不是一个东西</p><p>管理计划指的是规则程序、单位、流程、方法工具… 不包含具体的东西</p><p>具体的东西在项目文件中</p></blockquote><table><thead><tr><th>项目管理计划</th><th>项目文件</th><th>项目文件</th></tr></thead><tbody><tr><td>- 范围管理计划<br>- 需求管理计划<br>- 进度管理计划<br>- 成本管理计划<br>- 质量管理计划<br>- 资源管理计划<br>- 沟通管理计划<br>- 风险管理计划<br>- 采购管理计划<br>- 干系人参与计划<br>- 变更管理计划<br>- 配置管理计划<br>- 范围基准<br>- 进度基准<br>- 成本基准<br>- 绩效测量基准<br>- 项目生命周期描述<br>- 开发方法</td><td>- 活动属性<br>- 活动清单<br>- 假设日志<br>- 估算依据<br>- 变更日志<br>- 成本估算<br>- 持续时间估算<br>- 问题日志<br>- 经验教训登记册<br>- 里程碑清单<br>- 资源分配配单<br>- 项目日历<br>- 项目沟通记录<br>- 项目进度计划<br>- 项目进度网络图<br>- 项目范围说明书</td><td>- 项目团队派工单<br>- 质量控制测量结果<br>- 质量测量指标<br>- 质量报告<br>- 需求文件<br>- 需求跟踪矩阵<br>- 资源分解结构<br>- 资源日历<br>- 资源需求<br>- 风险登记册<br>- 风险报告<br>- 进度数据<br>- 进度预测<br>- 干系人登记册<br>- 团队章程<br>- 测试与评估文件</td></tr></tbody></table><blockquote><p>其实就是12个子计划(十大知识领域的计划+配置变更计划)、3个基准、3个其他(开发 绩效 周期)</p></blockquote><h2 id="项目整合管理过程"><a href="#项目整合管理过程" class="headerlink" title="项目整合管理过程"></a>项目整合管理过程</h2><table><thead><tr><th align="left">过程</th><th align="left">输入</th><th align="left">工具与技术</th><th align="left">输出</th></tr></thead><tbody><tr><td align="left">制定项目章程</td><td align="left"><ul><li>立项管理文件</li><li>协议</li><li>事业环境因素</li><li>组织过程资产</li></ul></td><td align="left"><ul><li>专家判断</li><li>数据收集</li><li>人际关系与团队技能</li><li>会议</li></ul></td><td align="left"><ul><li>项目章程</li><li>假设日志</li></ul></td></tr><tr><td align="left">制订项目管理计划</td><td align="left"><ul><li>项目章程</li><li>其他知识领域规划过程的输出</li><li>事业环境因素</li><li>组织过程资产</li></ul></td><td align="left"><ul><li>专家判断</li><li>数据收集</li><li>人际关系与团队技能</li><li>会议</li></ul></td><td align="left"><ul><li>项目管理计划</li></ul></td></tr><tr><td align="left">指导与管理项目工作</td><td align="left"><ul><li>项目管理计划</li><li>项目文件</li><li>批准的变更请求</li><li>事业环境因素</li><li>组织过程资产</li></ul></td><td align="left"><ul><li>专家判断</li><li>项目管理信息系统</li><li>会议</li></ul></td><td align="left"><ul><li>可交付成果</li><li>工作绩效数据</li><li>问题日志</li><li>变更请求</li><li>项目管理计划(更新)</li><li>项目文件(更新)</li><li>组织过程资产(更新)</li></ul></td></tr><tr><td align="left">管理项目知识</td><td align="left"><ul><li>项目管理计划</li><li>项目文件</li><li>可交付成果</li><li>事业环境因素</li><li>组织过程资产</li></ul></td><td align="left"><ul><li>专家判断</li><li>知识管理</li><li>信息管理</li><li>人际关系与团队技能</li></ul></td><td align="left"><ul><li>经验教训登记册</li><li>项目管理计划(更新)</li><li>组织过程资产(更新)</li></ul></td></tr><tr><td align="left">监控项目工作</td><td align="left"><ul><li>项目管理计划</li><li>项目文件</li><li>工作绩效信息</li><li>协议</li><li>事业环境因素</li><li>组织过程资产</li></ul></td><td align="left"><ul><li>专家判断</li><li>数据分析</li><li>决策</li><li>会议</li></ul></td><td align="left"><ul><li>工作绩效报告</li><li>变更请求</li><li>项目管理计划(更新)</li><li>项目文件(更新)</li></ul></td></tr><tr><td align="left">实施整体变更控制</td><td align="left"><ul><li>项目管理计划</li><li>项目文件</li><li>工作绩效报告</li><li>变更请求</li><li>事业环境因素</li><li>组织过程资产</li></ul></td><td align="left"><ul><li>专家判断</li><li>变更控制工具</li><li>数据分析</li><li>决策</li><li>会议</li></ul></td><td align="left"><ul><li>批准的变更请求</li><li>项目管理计划(更新)</li><li>变更日志</li></ul></td></tr><tr><td align="left">结束项目或阶段</td><td align="left"><ul><li>项目章程</li><li>项目管理计划</li><li>项目文件</li><li>验收的可交付成果</li><li>立项管理文件</li><li>协议</li><li>采购文档</li><li>组织过程资产</li></ul></td><td align="left"><ul><li>专家判断</li><li>数据分析</li><li>会议</li></ul></td><td align="left"><ul><li>项目文件(更新)</li><li>最终产品、服务或成果</li><li>项目最终报告</li><li>组织过程资产</li></ul></td></tr></tbody></table><h3 id="裁剪考虑因素"><a href="#裁剪考虑因素" class="headerlink" title="裁剪考虑因素"></a>裁剪考虑因素</h3><blockquote><p>ps: 每个项目都是独一无二的,要根据项目实际情况对项目管理方法、流程、工具、文档进行裁剪,找到最适合项目的管理方式。比如对于小型项目来说 PMBOK 里的5大过程组、10大知识领域流程太繁杂了,可以合并或者裁剪部分过程、简化文档、减少会议,从而提升效率。</p></blockquote><p>因为每个项目都是独特的,项目经理可能需要根据需要裁剪项目整合管理过程,考虑因素包括:</p><ul><li>项目生命周期</li><li>开发生命周期</li><li>管理方法</li><li>知识管理</li><li>变更</li><li>治理</li><li>经验教训</li><li>效益</li></ul><h3 id="敏捷适应方法"><a href="#敏捷适应方法" class="headerlink" title="敏捷适应方法"></a>敏捷适应方法</h3><blockquote><p>这里一般论文会考到 需要背一些术语随便糊弄下:) </p><p>比如论文考 你是如何把敏捷的思想运用到你的项目中的? </p><p>我在做项目的过程中,考虑了敏捷的思想,我知道在敏捷环境中,我们的项目范围是动态的,我们的<code>产品待办事项列表</code>可以不断的去更新、维护、分析、排序,根据团队吞吐能力去确定要办的事项,我还把我的过程精简了,给我的团队松绑了,在质量检查和评估去内建在产品的开发过程中</p></blockquote><ol><li><strong>范围动态</strong> <del>为了适应敏捷环境,项目范围不会一成不变的,产品待办事项列表会不断的被更新、维护、分析和排序。根据团队吞吐能力确定在一个冲刺(Sprint)中要完成的待办事项。</del>用产品待办列表灵活调整</li><li><strong>过程精简</strong> <del>敏捷开发不局限于49个管理过程、5大过程组,不需要冗长苛刻的整体变更控制程序;进度、成本、范围也不是按照三大基准来控制,对文档的要求要务实简约。团队必须给自己减负和松绑,才能更好的创造价值。</del>简化流程、不僵化套用标准</li><li><strong>状态可视</strong> <del>开发前的原型设计,需求分析中的卡诺模型,每日站会中的看板、燃尽图,评审中的实际效果展示,回顾中的价值流图,都体现了可视化的思想。</del>看板、燃尽图、原型等可视化管理</li><li><strong>质量内建</strong> <del>结对编程、测试驱动开发等方法都强调了产品质量不能依赖事后检查,要在产品开发的过程中进行质量检查和评估。</del>开发过程中就做质量保障</li><li><strong>团队自组织</strong> <del>整合的工作不在以项目经理为主。在敏捷团队中,Scrum Master只是为团队创造一个充分参与、高效互动、集体决策的开发环境和氛围,而关于干什么、怎么干、由谁来干,都是团队自己协商决定的。</del>下放决策权,Scrum Master 只做支持</li></ol><p>采用敏捷适应方法可以帮助项目经理将决策权下放,团队成员自行决定并控制具体产品的规划和交付,项目经理重点营造出合作型的决策氛围,确保团队有能力应对变更,促进团队成员以相关领域专家身份参与整合管理。</p>]]></content>
<summary type="html"><p>项目整合管理包括识别、定义、组合、统一、协调项目管理过程组的各个过程和项目管理活动。项目整合管理贯穿项目始终。</p></summary>
<category term="项目管理" scheme="https://probieluo.github.io/categories/%E9%A1%B9%E7%9B%AE%E7%AE%A1%E7%90%86/"/>
</entry>
<entry>
<title>项目管理十大知识领域-笔记01</title>
<link href="https://probieluo.github.io/posts/project-management-note-01/"/>
<id>https://probieluo.github.io/posts/project-management-note-01/</id>
<published>2026-04-11T06:32:52.000Z</published>
<updated>2026-04-14T05:01:57.972Z</updated>
<content type="html"><![CDATA[<ul><li>通常情况下,上一个过程的输出一般是下一个过程的输入</li><li>通常情况下,xx管理计划是本知识领域接下来每一个过程的输入</li></ul><h2 id="工具与技术"><a href="#工具与技术" class="headerlink" title="工具与技术"></a>工具与技术</h2><h3 id="数据收集"><a href="#数据收集" class="headerlink" title="数据收集"></a>数据收集</h3><ul><li>头脑风暴</li><li>焦点小组:召集预定的干系人和主题专家,了解他们对产品、服务、成果的期望和态度。由主持人引导</li><li>访谈</li><li>核对单</li><li>标杆对照</li><li>问卷调查</li></ul><h3 id="人际关系与团队技能"><a href="#人际关系与团队技能" class="headerlink" title="人际关系与团队技能"></a>人际关系与团队技能</h3><ul><li>冲突管理</li><li>引导</li><li>会议管理</li><li>积极倾听</li><li>领导力</li><li>人际交往</li><li>大局观</li><li>名义小组技术:用于促进头脑风暴的技术,通过投票排列选出最有用的创意</li><li>观察与交谈</li></ul><h3 id="数据分析"><a href="#数据分析" class="headerlink" title="数据分析"></a>数据分析</h3><ul><li>备选方案分析</li><li>成本效益分析</li><li>挣值分析</li><li>根本原因分析</li><li>趋势分析</li><li>偏差分析</li><li>文件分析</li><li>回归分析</li></ul><h3 id="决策"><a href="#决策" class="headerlink" title="决策"></a>决策</h3><ul><li>投票</li><li>独裁型决策制定</li><li>多标准决策分析</li></ul><h3 id="数据表现"><a href="#数据表现" class="headerlink" title="数据表现"></a>数据表现</h3><ul><li>亲和图:用于对大量创意进行分组的技术,以便进一步审查、分析</li><li>思维导图</li></ul><h2 id="可交付成果"><a href="#可交付成果" class="headerlink" title="可交付成果"></a>可交付成果</h2><ul><li>指导与管理项目工作=>可交付成果</li><li></li><li>确认范围=>确认=>验收的可交成果</li><li>验收的可交付成果=>结束项目或阶段=>产品/服务/成果</li></ul><h2 id="控制过程组"><a href="#控制过程组" class="headerlink" title="控制过程组"></a>控制过程组</h2><ul><li><p>所有控制过程组的输入都有工作绩效数据(整合是工作绩效信息),项目管理计划。</p></li><li><p>所有控制过程组的输出都有工作绩效信息(整合是工作绩效报告),变更请求,更新(项目管理计划、项目文件、组织过程资产,这三个不一定都有)。</p></li></ul><h2 id="规划过程组"><a href="#规划过程组" class="headerlink" title="规划过程组"></a>规划过程组</h2><ul><li>规划xx管理的输入一般都有项目章程、项目管理计划.</li></ul>]]></content>
<summary type="html"><ul>
<li>通常情况下,上一个过程的输出一般是下一个过程的输入</li>
<li>通常情况下,xx管理计划是本知识领域接下来每一个过程的输入</li>
</ul>
<h2 id="工具与技术"><a href="#工具与技术" class="headerlink" titl</summary>
<category term="项目管理" scheme="https://probieluo.github.io/categories/%E9%A1%B9%E7%9B%AE%E7%AE%A1%E7%90%86/"/>
</entry>
<entry>
<title>RedisUsage</title>
<link href="https://probieluo.github.io/posts/redies-usage/"/>
<id>https://probieluo.github.io/posts/redies-usage/</id>
<published>2025-11-25T16:49:03.000Z</published>
<updated>2026-04-22T09:12:55.306Z</updated>
<content type="html"><![CDATA[<h2 id="前言"><a href="#前言" class="headerlink" title="前言"></a>前言</h2><p>作为 .NET 开发人员,Redis 是一个必须掌握的技术。Redis 是一个开源的内存数据结构存储系统,可以用作数据库、缓存和消息代理。</p><span id="more"></span><h2 id="什么是-Redis?"><a href="#什么是-Redis?" class="headerlink" title="什么是 Redis?"></a>什么是 Redis?</h2><p>Redis(Remote Dictionary Server)是一个基于内存的键值存储系统,具有以下特点:</p><ul><li><strong>高性能</strong>:数据存储在内存中,读写速度极快</li><li><strong>丰富的数据类型</strong>:支持字符串、哈希、列表、集合、有序集合等</li><li><strong>持久化</strong>:支持数据持久化到磁盘</li><li><strong>原子性操作</strong>:所有操作都是原子性的</li><li><strong>支持事务</strong>:支持事务操作</li><li><strong>发布订阅</strong>:支持消息发布/订阅模式</li></ul><h2 id="安装-Redis"><a href="#安装-Redis" class="headerlink" title="安装 Redis"></a>安装 Redis</h2><h3 id="Windows"><a href="#Windows" class="headerlink" title="Windows"></a>Windows</h3><ol><li>下载 Redis for Windows:访问 <a href="https://github.com/microsoftarchive/redis/releases">https://github.com/microsoftarchive/redis/releases</a></li><li>解压到指定目录</li><li>运行 <code>redis-server.exe redis.windows.conf</code> 启动服务</li><li>运行 <code>redis-cli.exe</code> 打开客户端</li><li>可选:redis客户端工具:Tiny RDM</li></ol><h4 id="tips"><a href="#tips" class="headerlink" title="tips:"></a>tips:</h4><blockquote><p>powershell执行请加<code>./</code> 如 <code>.\redis-server.exe redis.windows.conf</code></p></blockquote><blockquote><p><code>redis.windows.conf</code> 为Windows 专用的 Redis 配置文件,这里启动服务时不加也是默认的<code>redis.windows.conf</code>,此配置文件可修改密码、绑定IP端口、限制最大内存…</p></blockquote><h2 id="Redis-基本数据类型"><a href="#Redis-基本数据类型" class="headerlink" title="Redis 基本数据类型"></a>Redis 基本数据类型</h2><h3 id="1-String(字符串)"><a href="#1-String(字符串)" class="headerlink" title="1. String(字符串)"></a>1. String(字符串)</h3><p>字符串是 Redis 最基本的数据类型,一个键最大能存储 512MB。</p><figure class="highlight bash"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br></pre></td><td class="code"><pre><span class="line"><span class="comment"># 设置值</span></span><br><span class="line">SET mykey <span class="string">"Hello Redis"</span></span><br><span class="line"></span><br><span class="line"><span class="comment"># 获取值</span></span><br><span class="line">GET mykey</span><br><span class="line"></span><br><span class="line"><span class="comment"># 设置带过期时间的值(秒)</span></span><br><span class="line">SETEX session:user1 3600 <span class="string">"userdata"</span></span><br><span class="line"></span><br><span class="line"><span class="comment"># 递增/递减</span></span><br><span class="line">INCR counter</span><br><span class="line">DECR counter</span><br></pre></td></tr></table></figure><h3 id="2-Hash(哈希)"><a href="#2-Hash(哈希)" class="headerlink" title="2. Hash(哈希)"></a>2. Hash(哈希)</h3><p>哈希是一个键值对集合,适合存储对象。</p><figure class="highlight bash"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br></pre></td><td class="code"><pre><span class="line"><span class="comment"># 设置单个字段</span></span><br><span class="line">HSET user:1000 name <span class="string">"张三"</span></span><br><span class="line">HSET user:1000 age 30</span><br><span class="line">HSET user:1000 email <span class="string">"zhangsan@example.com"</span></span><br><span class="line"></span><br><span class="line"><span class="comment"># 设置多个字段</span></span><br><span class="line">HMSET user:1001 name <span class="string">"李四"</span> age 25 email <span class="string">"lisi@example.com"</span></span><br><span class="line"></span><br><span class="line"><span class="comment"># 获取单个字段</span></span><br><span class="line">HGET user:1000 name</span><br><span class="line"></span><br><span class="line"><span class="comment"># 获取所有字段</span></span><br><span class="line">HGETALL user:1000</span><br><span class="line"></span><br><span class="line"><span class="comment"># 获取多个字段</span></span><br><span class="line">HMGET user:1000 name age</span><br></pre></td></tr></table></figure><h3 id="3-List(列表)"><a href="#3-List(列表)" class="headerlink" title="3. List(列表)"></a>3. List(列表)</h3><p>列表是简单的字符串列表,按插入顺序排序。</p><figure class="highlight bash"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br></pre></td><td class="code"><pre><span class="line"><span class="comment"># 从左侧插入 列表的头部添加元素</span></span><br><span class="line">LPUSH mylist <span class="string">"world"</span></span><br><span class="line">LPUSH mylist <span class="string">"hello"</span></span><br><span class="line"></span><br><span class="line"><span class="comment"># 从右侧插入 列表的尾部添加元素</span></span><br><span class="line">RPUSH mylist <span class="string">"!"</span></span><br><span class="line"></span><br><span class="line"><span class="comment"># 获取列表元素</span></span><br><span class="line">LRANGE mylist 0 -1</span><br><span class="line"></span><br><span class="line"><span class="comment"># 获取列表长度</span></span><br><span class="line">LLEN mylist</span><br><span class="line"></span><br><span class="line"><span class="comment"># 弹出元素</span></span><br><span class="line">LPOP mylist</span><br><span class="line">RPOP mylist</span><br></pre></td></tr></table></figure><h3 id="4-Set(集合)"><a href="#4-Set(集合)" class="headerlink" title="4. Set(集合)"></a>4. Set(集合)</h3><p>集合是无序的字符串集合,不允许重复元素。</p><figure class="highlight bash"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br></pre></td><td class="code"><pre><span class="line"><span class="comment"># 添加元素</span></span><br><span class="line">SADD myset <span class="string">"apple"</span></span><br><span class="line">SADD myset <span class="string">"banana"</span></span><br><span class="line">SADD myset <span class="string">"orange"</span></span><br><span class="line"></span><br><span class="line"><span class="comment"># 获取所有元素</span></span><br><span class="line">SMEMBERS myset</span><br><span class="line"></span><br><span class="line"><span class="comment"># 判断元素是否存在</span></span><br><span class="line">SISMEMBER myset <span class="string">"apple"</span></span><br><span class="line"></span><br><span class="line"><span class="comment"># 集合运算</span></span><br><span class="line">SINTER set1 set2 <span class="comment"># 交集</span></span><br><span class="line">SUNION set1 set2 <span class="comment"># 并集</span></span><br><span class="line">SDIFF set1 set2 <span class="comment"># 差集</span></span><br></pre></td></tr></table></figure><h3 id="5-Sorted-Set(有序集合)"><a href="#5-Sorted-Set(有序集合)" class="headerlink" title="5. Sorted Set(有序集合)"></a>5. Sorted Set(有序集合)</h3><p>有序集合在集合的基础上增加了一个分数(score)参数,元素按分数排序。</p><figure class="highlight bash"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br></pre></td><td class="code"><pre><span class="line"><span class="comment"># 添加元素</span></span><br><span class="line">ZADD leaderboard 100 <span class="string">"player1"</span></span><br><span class="line">ZADD leaderboard 200 <span class="string">"player2"</span></span><br><span class="line">ZADD leaderboard 150 <span class="string">"player3"</span></span><br><span class="line"></span><br><span class="line"><span class="comment"># 获取排名(从低到高)</span></span><br><span class="line">ZRANGE leaderboard 0 -1 WITHSCORES</span><br><span class="line"></span><br><span class="line"><span class="comment"># 获取排名(从高到低)</span></span><br><span class="line">ZREVRANGE leaderboard 0 -1 WITHSCORES</span><br><span class="line"></span><br><span class="line"><span class="comment"># 获取分数</span></span><br><span class="line">ZSCORE leaderboard <span class="string">"player1"</span></span><br><span class="line"></span><br><span class="line"><span class="comment"># 增加分数</span></span><br><span class="line">ZINCRBY leaderboard 50 <span class="string">"player1"</span></span><br></pre></td></tr></table></figure><h2 id="Csharp使用Redis"><a href="#Csharp使用Redis" class="headerlink" title="Csharp使用Redis"></a>Csharp使用Redis</h2><h3 id="安装-NuGet-包"><a href="#安装-NuGet-包" class="headerlink" title="安装 NuGet 包"></a>安装 NuGet 包</h3><p>推荐使用 <code>StackExchange.Redis</code>,这是 .NET 最流行的 Redis 客户端库。</p><figure class="highlight bash"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line">dotnet add package StackExchange.Redis</span><br></pre></td></tr></table></figure><h3 id="基本连接"><a href="#基本连接" class="headerlink" title="基本连接"></a>基本连接</h3><figure class="highlight csharp"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br></pre></td><td class="code"><pre><span class="line"><span class="keyword">using</span> StackExchange.Redis;</span><br><span class="line"></span><br><span class="line"><span class="comment">// 创建连接</span></span><br><span class="line"><span class="keyword">var</span> redis = ConnectionMultiplexer.Connect(<span class="string">"localhost:6379"</span>);</span><br><span class="line"><span class="keyword">var</span> db = redis.GetDatabase();</span><br><span class="line"></span><br><span class="line"><span class="comment">// 简单的 Set/Get 操作</span></span><br><span class="line">db.StringSet(<span class="string">"mykey"</span>, <span class="string">"Hello Redis from .NET"</span>);</span><br><span class="line"><span class="keyword">var</span> <span class="keyword">value</span> = db.StringGet(<span class="string">"mykey"</span>);</span><br></pre></td></tr></table></figure><h3 id="配置连接选项"><a href="#配置连接选项" class="headerlink" title="配置连接选项"></a>配置连接选项</h3><figure class="highlight csharp"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br></pre></td><td class="code"><pre><span class="line"><span class="keyword">var</span> configOptions = <span class="keyword">new</span> ConfigurationOptions</span><br><span class="line">{</span><br><span class="line"> EndPoints = { <span class="string">"localhost:6379"</span> },</span><br><span class="line"> Password = <span class="string">"your_password"</span>,</span><br><span class="line"> ConnectTimeout = <span class="number">5000</span>,</span><br><span class="line"> SyncTimeout = <span class="number">5000</span>,</span><br><span class="line"> AbortOnConnectFail = <span class="literal">false</span>,</span><br><span class="line"> DefaultDatabase = <span class="number">0</span></span><br><span class="line">};</span><br><span class="line"></span><br><span class="line"><span class="keyword">var</span> redis = ConnectionMultiplexer.Connect(configOptions);</span><br></pre></td></tr></table></figure><h3 id="实际应用示例"><a href="#实际应用示例" class="headerlink" title="实际应用示例"></a>实际应用示例</h3><h4 id="1-缓存用户信息"><a href="#1-缓存用户信息" class="headerlink" title="1. 缓存用户信息"></a>1. 缓存用户信息</h4><figure class="highlight csharp"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br><span class="line">25</span><br><span class="line">26</span><br><span class="line">27</span><br><span class="line">28</span><br><span class="line">29</span><br><span class="line">30</span><br><span class="line">31</span><br><span class="line">32</span><br><span class="line">33</span><br></pre></td><td class="code"><pre><span class="line"><span class="keyword">public</span> <span class="keyword">class</span> <span class="title">UserService</span></span><br><span class="line">{</span><br><span class="line"> <span class="keyword">private</span> <span class="keyword">readonly</span> IDatabase _redis;</span><br><span class="line"> </span><br><span class="line"> <span class="function"><span class="keyword">public</span> <span class="title">UserService</span>(<span class="params">IConnectionMultiplexer redis</span>)</span></span><br><span class="line"> {</span><br><span class="line"> _redis = redis.GetDatabase();</span><br><span class="line"> }</span><br><span class="line"> </span><br><span class="line"> <span class="function"><span class="keyword">public</span> <span class="keyword">async</span> Task<User> <span class="title">GetUserAsync</span>(<span class="params"><span class="built_in">int</span> userId</span>)</span></span><br><span class="line"> {</span><br><span class="line"> <span class="keyword">var</span> cacheKey = <span class="string">$"user:<span class="subst">{userId}</span>"</span>;</span><br><span class="line"> </span><br><span class="line"> <span class="comment">// 尝试从缓存获取</span></span><br><span class="line"> <span class="keyword">var</span> cachedUser = <span class="keyword">await</span> _redis.StringGetAsync(cacheKey);</span><br><span class="line"> <span class="keyword">if</span> (cachedUser.HasValue)</span><br><span class="line"> {</span><br><span class="line"> <span class="keyword">return</span> JsonSerializer.Deserialize<User>(cachedUser);</span><br><span class="line"> }</span><br><span class="line"> </span><br><span class="line"> <span class="comment">// 从数据库获取</span></span><br><span class="line"> <span class="keyword">var</span> user = <span class="keyword">await</span> _dbContext.Users.FindAsync(userId);</span><br><span class="line"> </span><br><span class="line"> <span class="comment">// 存入缓存(1小时过期)</span></span><br><span class="line"> <span class="keyword">if</span> (user != <span class="literal">null</span>)</span><br><span class="line"> {</span><br><span class="line"> <span class="keyword">var</span> serialized = JsonSerializer.Serialize(user);</span><br><span class="line"> <span class="keyword">await</span> _redis.StringSetAsync(cacheKey, serialized, TimeSpan.FromHours(<span class="number">1</span>));</span><br><span class="line"> }</span><br><span class="line"> </span><br><span class="line"> <span class="keyword">return</span> user;</span><br><span class="line"> }</span><br><span class="line">}</span><br></pre></td></tr></table></figure><h4 id="2-分布式锁"><a href="#2-分布式锁" class="headerlink" title="2. 分布式锁"></a>2. 分布式锁</h4><figure class="highlight csharp"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br><span class="line">25</span><br><span class="line">26</span><br><span class="line">27</span><br><span class="line">28</span><br><span class="line">29</span><br><span class="line">30</span><br><span class="line">31</span><br><span class="line">32</span><br><span class="line">33</span><br><span class="line">34</span><br><span class="line">35</span><br><span class="line">36</span><br><span class="line">37</span><br><span class="line">38</span><br><span class="line">39</span><br><span class="line">40</span><br><span class="line">41</span><br><span class="line">42</span><br><span class="line">43</span><br></pre></td><td class="code"><pre><span class="line"><span class="keyword">public</span> <span class="keyword">class</span> <span class="title">DistributedLockService</span></span><br><span class="line">{</span><br><span class="line"> <span class="keyword">private</span> <span class="keyword">readonly</span> IDatabase _redis;</span><br><span class="line"> </span><br><span class="line"> <span class="function"><span class="keyword">public</span> <span class="keyword">async</span> Task<<span class="built_in">bool</span>> <span class="title">AcquireLockAsync</span>(<span class="params"><span class="built_in">string</span> resource, <span class="built_in">string</span> token, TimeSpan expiry</span>)</span></span><br><span class="line"> {</span><br><span class="line"> <span class="keyword">var</span> key = <span class="string">$"lock:<span class="subst">{resource}</span>"</span>;</span><br><span class="line"> <span class="keyword">return</span> <span class="keyword">await</span> _redis.StringSetAsync(key, token, expiry, When.NotExists);</span><br><span class="line"> }</span><br><span class="line"> </span><br><span class="line"> <span class="function"><span class="keyword">public</span> <span class="keyword">async</span> Task<<span class="built_in">bool</span>> <span class="title">ReleaseLockAsync</span>(<span class="params"><span class="built_in">string</span> resource, <span class="built_in">string</span> token</span>)</span></span><br><span class="line"> {</span><br><span class="line"> <span class="keyword">var</span> key = <span class="string">$"lock:<span class="subst">{resource}</span>"</span>;</span><br><span class="line"> <span class="keyword">var</span> currentValue = <span class="keyword">await</span> _redis.StringGetAsync(key);</span><br><span class="line"> </span><br><span class="line"> <span class="keyword">if</span> (currentValue == token)</span><br><span class="line"> {</span><br><span class="line"> <span class="keyword">return</span> <span class="keyword">await</span> _redis.KeyDeleteAsync(key);</span><br><span class="line"> }</span><br><span class="line"> </span><br><span class="line"> <span class="keyword">return</span> <span class="literal">false</span>;</span><br><span class="line"> }</span><br><span class="line"> </span><br><span class="line"> <span class="comment">// 使用示例</span></span><br><span class="line"> <span class="function"><span class="keyword">public</span> <span class="keyword">async</span> Task <span class="title">ProcessWithLockAsync</span>()</span></span><br><span class="line"> {</span><br><span class="line"> <span class="keyword">var</span> token = Guid.NewGuid().ToString();</span><br><span class="line"> <span class="keyword">var</span> lockAcquired = <span class="keyword">await</span> AcquireLockAsync(<span class="string">"myresource"</span>, token, TimeSpan.FromSeconds(<span class="number">30</span>));</span><br><span class="line"> </span><br><span class="line"> <span class="keyword">if</span> (lockAcquired)</span><br><span class="line"> {</span><br><span class="line"> <span class="keyword">try</span></span><br><span class="line"> {</span><br><span class="line"> <span class="comment">// 执行需要加锁的操作</span></span><br><span class="line"> <span class="keyword">await</span> DoSomethingAsync();</span><br><span class="line"> }</span><br><span class="line"> <span class="keyword">finally</span></span><br><span class="line"> {</span><br><span class="line"> <span class="keyword">await</span> ReleaseLockAsync(<span class="string">"myresource"</span>, token);</span><br><span class="line"> }</span><br><span class="line"> }</span><br><span class="line"> }</span><br><span class="line">}</span><br></pre></td></tr></table></figure><h4 id="3-计数器和限流"><a href="#3-计数器和限流" class="headerlink" title="3. 计数器和限流"></a>3. 计数器和限流</h4><figure class="highlight csharp"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br><span class="line">25</span><br><span class="line">26</span><br><span class="line">27</span><br><span class="line">28</span><br></pre></td><td class="code"><pre><span class="line"><span class="keyword">public</span> <span class="keyword">class</span> <span class="title">RateLimiter</span></span><br><span class="line">{</span><br><span class="line"> <span class="keyword">private</span> <span class="keyword">readonly</span> IDatabase _redis;</span><br><span class="line"> </span><br><span class="line"> <span class="function"><span class="keyword">public</span> <span class="keyword">async</span> Task<<span class="built_in">bool</span>> <span class="title">IsAllowedAsync</span>(<span class="params"><span class="built_in">string</span> userId, <span class="built_in">int</span> maxRequests, TimeSpan window</span>)</span></span><br><span class="line"> {</span><br><span class="line"> <span class="keyword">var</span> key = <span class="string">$"ratelimit:<span class="subst">{userId}</span>"</span>;</span><br><span class="line"> <span class="keyword">var</span> count = <span class="keyword">await</span> _redis.StringIncrementAsync(key);</span><br><span class="line"> </span><br><span class="line"> <span class="keyword">if</span> (count == <span class="number">1</span>)</span><br><span class="line"> {</span><br><span class="line"> <span class="keyword">await</span> _redis.KeyExpireAsync(key, window);</span><br><span class="line"> }</span><br><span class="line"> </span><br><span class="line"> <span class="keyword">return</span> count <= maxRequests;</span><br><span class="line"> }</span><br><span class="line">}</span><br><span class="line"></span><br><span class="line"><span class="comment">// 使用示例</span></span><br><span class="line"><span class="keyword">var</span> rateLimiter = <span class="keyword">new</span> RateLimiter(redis);</span><br><span class="line"><span class="keyword">if</span> (<span class="keyword">await</span> rateLimiter.IsAllowedAsync(<span class="string">"user123"</span>, <span class="number">100</span>, TimeSpan.FromMinutes(<span class="number">1</span>)))</span><br><span class="line">{</span><br><span class="line"> <span class="comment">// 处理请求</span></span><br><span class="line">}</span><br><span class="line"><span class="keyword">else</span></span><br><span class="line">{</span><br><span class="line"> <span class="comment">// 请求过于频繁,返回 429</span></span><br><span class="line">}</span><br></pre></td></tr></table></figure><h4 id="4-发布-订阅"><a href="#4-发布-订阅" class="headerlink" title="4. 发布/订阅"></a>4. 发布/订阅</h4><figure class="highlight csharp"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br><span class="line">25</span><br></pre></td><td class="code"><pre><span class="line"><span class="comment">// 订阅者</span></span><br><span class="line"><span class="keyword">public</span> <span class="keyword">class</span> <span class="title">MessageSubscriber</span></span><br><span class="line">{</span><br><span class="line"> <span class="function"><span class="keyword">public</span> <span class="keyword">void</span> <span class="title">Subscribe</span>(<span class="params">IConnectionMultiplexer redis</span>)</span></span><br><span class="line"> {</span><br><span class="line"> <span class="keyword">var</span> subscriber = redis.GetSubscriber();</span><br><span class="line"> </span><br><span class="line"> subscriber.Subscribe(<span class="string">"notifications"</span>, (channel, message) =></span><br><span class="line"> {</span><br><span class="line"> Console.WriteLine(<span class="string">$"收到消息: <span class="subst">{message}</span>"</span>);</span><br><span class="line"> });</span><br><span class="line"> }</span><br><span class="line">}</span><br><span class="line"></span><br><span class="line"><span class="comment">// 发布者</span></span><br><span class="line"><span class="keyword">public</span> <span class="keyword">class</span> <span class="title">MessagePublisher</span></span><br><span class="line">{</span><br><span class="line"> <span class="keyword">private</span> <span class="keyword">readonly</span> IConnectionMultiplexer _redis;</span><br><span class="line"> </span><br><span class="line"> <span class="function"><span class="keyword">public</span> <span class="keyword">async</span> Task <span class="title">PublishAsync</span>(<span class="params"><span class="built_in">string</span> message</span>)</span></span><br><span class="line"> {</span><br><span class="line"> <span class="keyword">var</span> subscriber = _redis.GetSubscriber();</span><br><span class="line"> <span class="keyword">await</span> subscriber.PublishAsync(<span class="string">"notifications"</span>, message);</span><br><span class="line"> }</span><br><span class="line">}</span><br></pre></td></tr></table></figure><h4 id="5-排行榜系统"><a href="#5-排行榜系统" class="headerlink" title="5. 排行榜系统"></a>5. 排行榜系统</h4><figure class="highlight csharp"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br><span class="line">25</span><br><span class="line">26</span><br><span class="line">27</span><br><span class="line">28</span><br></pre></td><td class="code"><pre><span class="line"><span class="keyword">public</span> <span class="keyword">class</span> <span class="title">LeaderboardService</span></span><br><span class="line">{</span><br><span class="line"> <span class="keyword">private</span> <span class="keyword">readonly</span> IDatabase _redis;</span><br><span class="line"> <span class="keyword">private</span> <span class="keyword">const</span> <span class="built_in">string</span> LeaderboardKey = <span class="string">"game:leaderboard"</span>;</span><br><span class="line"> </span><br><span class="line"> <span class="function"><span class="keyword">public</span> <span class="keyword">async</span> Task <span class="title">UpdateScoreAsync</span>(<span class="params"><span class="built_in">string</span> playerId, <span class="built_in">double</span> score</span>)</span></span><br><span class="line"> {</span><br><span class="line"> <span class="keyword">await</span> _redis.SortedSetAddAsync(LeaderboardKey, playerId, score);</span><br><span class="line"> }</span><br><span class="line"> </span><br><span class="line"> <span class="keyword">public</span> <span class="keyword">async</span> Task<List<(<span class="built_in">string</span> PlayerId, <span class="built_in">double</span> Score)>> GetTopPlayersAsync(<span class="built_in">int</span> count)</span><br><span class="line"> {</span><br><span class="line"> <span class="keyword">var</span> entries = <span class="keyword">await</span> _redis.SortedSetRangeByRankWithScoresAsync(</span><br><span class="line"> LeaderboardKey, </span><br><span class="line"> <span class="number">0</span>, </span><br><span class="line"> count - <span class="number">1</span>, </span><br><span class="line"> Order.Descending</span><br><span class="line"> );</span><br><span class="line"> </span><br><span class="line"> <span class="keyword">return</span> entries.Select(e => (e.Element.ToString(), e.Score)).ToList();</span><br><span class="line"> }</span><br><span class="line"> </span><br><span class="line"> <span class="keyword">public</span> <span class="keyword">async</span> Task<<span class="built_in">long</span>?> GetPlayerRankAsync(<span class="built_in">string</span> playerId)</span><br><span class="line"> {</span><br><span class="line"> <span class="keyword">var</span> rank = <span class="keyword">await</span> _redis.SortedSetRankAsync(LeaderboardKey, playerId, Order.Descending);</span><br><span class="line"> <span class="keyword">return</span> rank.HasValue ? rank.Value + <span class="number">1</span> : <span class="literal">null</span>;</span><br><span class="line"> }</span><br><span class="line">}</span><br></pre></td></tr></table></figure><h2 id="Redis-持久化"><a href="#Redis-持久化" class="headerlink" title="Redis 持久化"></a>Redis 持久化</h2><h3 id="RDB(快照)"><a href="#RDB(快照)" class="headerlink" title="RDB(快照)"></a>RDB(快照)</h3><ul><li>在指定时间间隔内生成数据集的时间点快照</li><li>适合备份和灾难恢复</li><li>配置示例:</li></ul><figure class="highlight bash"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br></pre></td><td class="code"><pre><span class="line"><span class="comment"># redis.conf</span></span><br><span class="line">save 900 1 <span class="comment"># 900秒内至少1个键被修改</span></span><br><span class="line">save 300 10 <span class="comment"># 300秒内至少10个键被修改</span></span><br><span class="line">save 60 10000 <span class="comment"># 60秒内至少10000个键被修改</span></span><br></pre></td></tr></table></figure><h3 id="AOF(追加文件)"><a href="#AOF(追加文件)" class="headerlink" title="AOF(追加文件)"></a>AOF(追加文件)</h3><ul><li>记录每个写操作</li><li>更好的数据持久性</li><li>配置示例:</li></ul><figure class="highlight bash"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br></pre></td><td class="code"><pre><span class="line"><span class="comment"># redis.conf</span></span><br><span class="line">appendonly <span class="built_in">yes</span></span><br><span class="line">appendfsync everysec <span class="comment"># 每秒同步一次</span></span><br></pre></td></tr></table></figure><h2 id="最佳实践"><a href="#最佳实践" class="headerlink" title="最佳实践"></a>最佳实践</h2><h3 id="1-键命名规范"><a href="#1-键命名规范" class="headerlink" title="1. 键命名规范"></a>1. 键命名规范</h3><figure class="highlight csharp"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br></pre></td><td class="code"><pre><span class="line"><span class="comment">// 使用冒号分隔的命名空间</span></span><br><span class="line"><span class="string">"user:1000:profile"</span></span><br><span class="line"><span class="string">"product:5678:stock"</span></span><br><span class="line"><span class="string">"session:abc123def"</span></span><br><span class="line"></span><br><span class="line"><span class="comment">// 避免过长的键名,建议不超过1024字节</span></span><br></pre></td></tr></table></figure><h3 id="2-设置过期时间"><a href="#2-设置过期时间" class="headerlink" title="2. 设置过期时间"></a>2. 设置过期时间</h3><figure class="highlight csharp"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br></pre></td><td class="code"><pre><span class="line"><span class="comment">// 始终为缓存数据设置过期时间,避免内存泄漏</span></span><br><span class="line"><span class="keyword">await</span> db.StringSetAsync(<span class="string">"key"</span>, <span class="string">"value"</span>, TimeSpan.FromHours(<span class="number">1</span>));</span><br></pre></td></tr></table></figure><h3 id="3-使用管道(Pipeline)"><a href="#3-使用管道(Pipeline)" class="headerlink" title="3. 使用管道(Pipeline)"></a>3. 使用管道(Pipeline)</h3><figure class="highlight csharp"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br></pre></td><td class="code"><pre><span class="line"><span class="comment">// 批量操作使用管道,减少网络往返</span></span><br><span class="line"><span class="keyword">var</span> batch = db.CreateBatch();</span><br><span class="line"><span class="keyword">var</span> tasks = <span class="keyword">new</span> List<Task>();</span><br><span class="line"></span><br><span class="line"><span class="keyword">for</span> (<span class="built_in">int</span> i = <span class="number">0</span>; i < <span class="number">1000</span>; i++)</span><br><span class="line">{</span><br><span class="line"> tasks.Add(batch.StringSetAsync(<span class="string">$"key:<span class="subst">{i}</span>"</span>, <span class="string">$"value:<span class="subst">{i}</span>"</span>));</span><br><span class="line">}</span><br><span class="line"></span><br><span class="line">batch.Execute();</span><br><span class="line"><span class="keyword">await</span> Task.WhenAll(tasks);</span><br></pre></td></tr></table></figure><h3 id="4-避免大键"><a href="#4-避免大键" class="headerlink" title="4. 避免大键"></a>4. 避免大键</h3><p>不要在单个键中存储过大的数据(建议不超过10KB)</p><p>考虑拆分大对象或使用哈希结构</p><h3 id="5-使用连接池"><a href="#5-使用连接池" class="headerlink" title="5. 使用连接池"></a>5. 使用连接池</h3><figure class="highlight csharp"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br></pre></td><td class="code"><pre><span class="line"><span class="comment">// StackExchange.Redis 内置连接池,使用单例模式</span></span><br><span class="line">services.AddSingleton<IConnectionMultiplexer>(...);</span><br></pre></td></tr></table></figure><h2 id="性能优化"><a href="#性能优化" class="headerlink" title="性能优化"></a>性能优化</h2><h3 id="1-使用异步方法"><a href="#1-使用异步方法" class="headerlink" title="1. 使用异步方法"></a>1. 使用异步方法</h3><figure class="highlight csharp"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br></pre></td><td class="code"><pre><span class="line"><span class="comment">// 优先使用异步方法</span></span><br><span class="line"><span class="keyword">await</span> db.StringGetAsync(<span class="string">"key"</span>);</span><br><span class="line"><span class="keyword">await</span> db.HashSetAsync(<span class="string">"hash"</span>, <span class="string">"field"</span>, <span class="string">"value"</span>);</span><br></pre></td></tr></table></figure><h3 id="2-批量操作"><a href="#2-批量操作" class="headerlink" title="2. 批量操作"></a>2. 批量操作</h3><figure class="highlight csharp"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br></pre></td><td class="code"><pre><span class="line"><span class="comment">// 使用批量操作代替循环单个操作</span></span><br><span class="line"><span class="keyword">var</span> hashEntries = <span class="keyword">new</span> HashEntry[]</span><br><span class="line">{</span><br><span class="line"> <span class="keyword">new</span> HashEntry(<span class="string">"field1"</span>, <span class="string">"value1"</span>),</span><br><span class="line"> <span class="keyword">new</span> HashEntry(<span class="string">"field2"</span>, <span class="string">"value2"</span>),</span><br><span class="line"> <span class="keyword">new</span> HashEntry(<span class="string">"field3"</span>, <span class="string">"value3"</span>)</span><br><span class="line">};</span><br><span class="line"><span class="keyword">await</span> db.HashSetAsync(<span class="string">"myhash"</span>, hashEntries);</span><br></pre></td></tr></table></figure><h2 id="常见问题"><a href="#常见问题" class="headerlink" title="常见问题"></a>常见问题</h2><h3 id="1-缓存雪崩"><a href="#1-缓存雪崩" class="headerlink" title="1. 缓存雪崩"></a>1. 缓存雪崩</h3><p>大量缓存同时过期,导致请求全部打到数据库。</p><p><strong>解决方案</strong>:</p><ul><li>设置随机过期时间</li><li>使用热点数据永不过期</li><li>使用互斥锁</li></ul><figure class="highlight csharp"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br></pre></td><td class="code"><pre><span class="line"><span class="keyword">var</span> expiry = TimeSpan.FromMinutes(<span class="number">30</span> + Random.Shared.Next(<span class="number">0</span>, <span class="number">10</span>));</span><br><span class="line"><span class="keyword">await</span> db.StringSetAsync(key, <span class="keyword">value</span>, expiry);</span><br></pre></td></tr></table></figure><h3 id="2-缓存穿透"><a href="#2-缓存穿透" class="headerlink" title="2. 缓存穿透"></a>2. 缓存穿透</h3><p>查询不存在的数据,缓存和数据库都没有。</p><p><strong>解决方案</strong>:</p><ul><li>缓存空值</li><li>使用布隆过滤器</li></ul><figure class="highlight csharp"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br></pre></td><td class="code"><pre><span class="line"><span class="keyword">var</span> user = <span class="keyword">await</span> GetUserFromDbAsync(userId);</span><br><span class="line"><span class="keyword">if</span> (user == <span class="literal">null</span>)</span><br><span class="line">{</span><br><span class="line"> <span class="comment">// 缓存空值,设置较短过期时间</span></span><br><span class="line"> <span class="keyword">await</span> db.StringSetAsync(cacheKey, <span class="string">"null"</span>, TimeSpan.FromMinutes(<span class="number">5</span>));</span><br><span class="line">}</span><br></pre></td></tr></table></figure><h3 id="3-缓存击穿"><a href="#3-缓存击穿" class="headerlink" title="3. 缓存击穿"></a>3. 缓存击穿</h3><p>热点数据过期瞬间,大量请求同时访问。</p><p><strong>解决方案</strong>:</p><ul><li>使用分布式锁</li><li>热点数据永不过期</li></ul><figure class="highlight csharp"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br><span class="line">25</span><br><span class="line">26</span><br><span class="line">27</span><br><span class="line">28</span><br><span class="line">29</span><br><span class="line">30</span><br><span class="line">31</span><br><span class="line">32</span><br></pre></td><td class="code"><pre><span class="line"><span class="function"><span class="keyword">public</span> <span class="keyword">async</span> Task<User> <span class="title">GetUserWithLockAsync</span>(<span class="params"><span class="built_in">int</span> userId</span>)</span></span><br><span class="line">{</span><br><span class="line"> <span class="keyword">var</span> cacheKey = <span class="string">$"user:<span class="subst">{userId}</span>"</span>;</span><br><span class="line"> <span class="keyword">var</span> cached = <span class="keyword">await</span> db.StringGetAsync(cacheKey);</span><br><span class="line"> </span><br><span class="line"> <span class="keyword">if</span> (cached.HasValue)</span><br><span class="line"> <span class="keyword">return</span> JsonSerializer.Deserialize<User>(cached);</span><br><span class="line"> </span><br><span class="line"> <span class="keyword">var</span> lockKey = <span class="string">$"lock:<span class="subst">{cacheKey}</span>"</span>;</span><br><span class="line"> <span class="keyword">var</span> lockToken = Guid.NewGuid().ToString();</span><br><span class="line"> </span><br><span class="line"> <span class="keyword">if</span> (<span class="keyword">await</span> db.StringSetAsync(lockKey, lockToken, TimeSpan.FromSeconds(<span class="number">10</span>), When.NotExists))</span><br><span class="line"> {</span><br><span class="line"> <span class="keyword">try</span></span><br><span class="line"> {</span><br><span class="line"> <span class="keyword">var</span> user = <span class="keyword">await</span> GetUserFromDbAsync(userId);</span><br><span class="line"> <span class="keyword">if</span> (user != <span class="literal">null</span>)</span><br><span class="line"> {</span><br><span class="line"> <span class="keyword">await</span> db.StringSetAsync(cacheKey, JsonSerializer.Serialize(user), TimeSpan.FromHours(<span class="number">1</span>));</span><br><span class="line"> }</span><br><span class="line"> <span class="keyword">return</span> user;</span><br><span class="line"> }</span><br><span class="line"> <span class="keyword">finally</span></span><br><span class="line"> {</span><br><span class="line"> <span class="keyword">await</span> db.KeyDeleteAsync(lockKey);</span><br><span class="line"> }</span><br><span class="line"> }</span><br><span class="line"> </span><br><span class="line"> <span class="comment">// 等待并重试</span></span><br><span class="line"> <span class="keyword">await</span> Task.Delay(<span class="number">50</span>);</span><br><span class="line"> <span class="keyword">return</span> <span class="keyword">await</span> GetUserWithLockAsync(userId);</span><br><span class="line">}</span><br></pre></td></tr></table></figure><h2 id="参考资源"><a href="#参考资源" class="headerlink" title="参考资源"></a>参考资源</h2><ul><li><a href="https://redis.io/documentation">Redis 官方文档</a></li><li><a href="https://github.com/StackExchange/StackExchange.Redis">StackExchange.Redis GitHub</a></li><li><a href="https://redis.io/commands">Redis 命令参考</a></li><li><a href="https://redis.io/topics/best-practices">Redis 最佳实践</a></li></ul><hr>]]></content>
<summary type="html"><h2 id="前言"><a href="#前言" class="headerlink" title="前言"></a>前言</h2><p>作为 .NET 开发人员,Redis 是一个必须掌握的技术。Redis 是一个开源的内存数据结构存储系统,可以用作数据库、缓存和消息代理。</p></summary>
<category term="编程技术" scheme="https://probieluo.github.io/categories/%E7%BC%96%E7%A8%8B%E6%8A%80%E6%9C%AF/"/>
<category term="Redis" scheme="https://probieluo.github.io/tags/Redis/"/>
</entry>
<entry>
<title>表达式树-动态构建表达式树</title>
<link href="https://probieluo.github.io/posts/expression-trees-01/"/>
<id>https://probieluo.github.io/posts/expression-trees-01/</id>
<published>2025-11-24T15:10:39.000Z</published>
<updated>2026-04-22T09:12:03.923Z</updated>
<content type="html"><![CDATA[<p>上篇post介绍了表达式树,本节介绍如何动态构建表达式树。</p><h3 id="代码动态构建表达式树"><a href="#代码动态构建表达式树" class="headerlink" title="代码动态构建表达式树"></a>代码动态构建表达式树</h3><p>在<code>表达式树(Expression Trees)</code>这一文章中我们编写的代码都是让C#编译器把Lambda表达式转换表达式树,这些表达式是硬编码的。</p><p>我们通过代码编写表达式:<code>Expression<Func<Book,bool>> e = b =>b.Price > 5</code></p><figure class="highlight csharp"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br><span class="line">25</span><br><span class="line">26</span><br><span class="line">27</span><br><span class="line">28</span><br><span class="line">29</span><br><span class="line">30</span><br><span class="line">31</span><br><span class="line">32</span><br><span class="line">33</span><br><span class="line">34</span><br><span class="line">35</span><br><span class="line">36</span><br><span class="line">37</span><br><span class="line">38</span><br><span class="line">39</span><br><span class="line">40</span><br><span class="line">41</span><br><span class="line">42</span><br><span class="line">43</span><br><span class="line">44</span><br><span class="line">45</span><br><span class="line">46</span><br><span class="line">47</span><br><span class="line">48</span><br><span class="line">49</span><br><span class="line">50</span><br><span class="line">51</span><br><span class="line">52</span><br><span class="line">53</span><br><span class="line">54</span><br><span class="line">55</span><br><span class="line">56</span><br><span class="line">57</span><br><span class="line">58</span><br><span class="line">59</span><br><span class="line">60</span><br><span class="line">61</span><br><span class="line">62</span><br></pre></td><td class="code"><pre><span class="line"><span class="comment">// 手动构建:b => b.Price > 5</span></span><br><span class="line"><span class="keyword">static</span> Expression<Func<Book, <span class="built_in">bool</span>>> BuildExpressionManually()</span><br><span class="line">{</span><br><span class="line"><span class="comment">// 1. 创建参数节点:b(类型Book)</span></span><br><span class="line">ParameterExpression paramB = Expression.Parameter(<span class="keyword">typeof</span>(Book), <span class="string">"b"</span>);</span><br><span class="line"></span><br><span class="line"><span class="comment">// 2. 创建左子节点:b.Price</span></span><br><span class="line">MemberExpression priceMember = Expression.MakeMemberAccess(paramB, <span class="keyword">typeof</span>(Book).GetProperty(<span class="string">"Price"</span>));</span><br><span class="line"></span><br><span class="line"><span class="comment">// 3. 创建右子节点:常量5</span></span><br><span class="line">ConstantExpression constant5 = Expression.Constant(<span class="number">5</span>m, <span class="keyword">typeof</span>(<span class="built_in">decimal</span>));</span><br><span class="line"></span><br><span class="line"><span class="comment">// 4. 创建主体节点:b.Price > 5(大于运算)</span></span><br><span class="line">BinaryExpression greaterThanExpr = Expression.GreaterThan(</span><br><span class="line">left: priceMember, <span class="comment">// 左表达式:b.Price</span></span><br><span class="line">right: constant5 <span class="comment">// 右表达式:5m</span></span><br><span class="line">);</span><br><span class="line"></span><br><span class="line"><span class="comment">// 5. 创建Lambda表达式:b => (b.Price > 5)</span></span><br><span class="line">Expression<Func<Book, <span class="built_in">bool</span>>> e = Expression.Lambda<Func<Book, <span class="built_in">bool</span>>>(</span><br><span class="line">body: greaterThanExpr, <span class="comment">// Lambda的主体</span></span><br><span class="line">parameters: paramB <span class="comment">// Lambda的参数(b)</span></span><br><span class="line">);</span><br><span class="line"></span><br><span class="line"><span class="keyword">return</span> e;</span><br><span class="line">}</span><br><span class="line"></span><br><span class="line"><span class="keyword">class</span> <span class="title">Book</span></span><br><span class="line">{</span><br><span class="line"><span class="keyword">public</span> <span class="built_in">string</span> Title { <span class="keyword">get</span>; <span class="keyword">set</span>; }</span><br><span class="line"><span class="keyword">public</span> <span class="built_in">decimal</span> Price { <span class="keyword">get</span>; <span class="keyword">set</span>; } </span><br><span class="line">}</span><br><span class="line"></span><br><span class="line"><span class="comment">//验证1</span></span><br><span class="line"><span class="function"><span class="keyword">private</span> <span class="keyword">static</span> <span class="keyword">void</span> <span class="title">ConstructExpressions</span>()</span></span><br><span class="line">{</span><br><span class="line"><span class="keyword">var</span> e = BuildExpressionManually();</span><br><span class="line">Console.WriteLine(<span class="string">$"手动构建的表达式:<span class="subst">{e}</span>"</span>);</span><br><span class="line">}</span><br><span class="line"></span><br><span class="line"><span class="comment">//验证2</span></span><br><span class="line"><span class="function"><span class="keyword">private</span> <span class="keyword">static</span> <span class="keyword">void</span> <span class="title">UseBuildExpressionManually</span>()</span></span><br><span class="line">{</span><br><span class="line">List<Book> books = <span class="keyword">new</span> List<Book>()</span><br><span class="line">{</span><br><span class="line"><span class="keyword">new</span> Book { Title = <span class="string">"C# 入门"</span>, Price = <span class="number">6</span> },</span><br><span class="line"><span class="keyword">new</span> Book { Title = <span class="string">"LINQ 实战"</span>, Price = <span class="number">2</span> },</span><br><span class="line"><span class="keyword">new</span> Book { Title = <span class="string">"数据结构与算法"</span>, Price = <span class="number">8</span> },</span><br><span class="line">};</span><br><span class="line"></span><br><span class="line"><span class="keyword">var</span> e = BuildExpressionManually();</span><br><span class="line"></span><br><span class="line"><span class="comment">// 编译表达式树为委托</span></span><br><span class="line"><span class="keyword">var</span> predicate = e.Compile();</span><br><span class="line"></span><br><span class="line"><span class="keyword">var</span> b = books.Where(predicate);</span><br><span class="line"></span><br><span class="line"><span class="keyword">foreach</span> (<span class="keyword">var</span> book <span class="keyword">in</span> b)</span><br><span class="line">{</span><br><span class="line">Console.WriteLine(<span class="string">$"符合条件的图书:<span class="subst">{book.Title}</span>,价格:<span class="subst">{book.Price}</span>"</span>);</span><br><span class="line">}</span><br><span class="line">}</span><br></pre></td></tr></table></figure><hr><h4 id="构建表达式树常用工厂方法"><a href="#构建表达式树常用工厂方法" class="headerlink" title="构建表达式树常用工厂方法"></a>构建表达式树常用工厂方法</h4><div class="collapse-box-control"> <div class="collapse-box-header"><div class="collapse-box-icon"><i class="fa fa-plus"></i></div><span>Expression工厂方法</span></div> <div class="collapse-box-content"><div class="inner"> <ol><li><p>基础节点构建(参数、常量、默认值)</p><table><thead><tr><th>工厂方法</th><th>功能描述</th><th>示例代码</th><th>节点类型</th></tr></thead><tbody><tr><td><code>Expression.Parameter</code></td><td>创建参数节点(Lambda 形参)</td><td><code>var b = Expression.Parameter(typeof(Book), "b");</code></td><td><code>ParameterExpression</code></td></tr><tr><td><code>Expression.Constant</code></td><td>创建常量节点(值、字面量)</td><td><code>var five = Expression.Constant(5m, typeof(decimal));</code></td><td><code>ConstantExpression</code></td></tr><tr><td><code>Expression.Default</code></td><td>创建类型默认值</td><td><code>var defaultBook = Expression.Default(typeof(Book));</code></td><td><code>DefaultExpression</code></td></tr></tbody></table></li><li><p>成员访问(属性 / 字段)</p><table><thead><tr><th>工厂方法</th><th>功能描述</th><th>示例代码</th><th>节点类型</th></tr></thead><tbody><tr><td><code>Expression.Property</code></td><td>访问属性</td><td><code>var priceProp = Expression.Property(b, nameof(Book.Price));</code></td><td><code>MemberExpression</code></td></tr><tr><td><code>Expression.PropertyOrField</code></td><td>统一属性或字段访问</td><td><code>var titleMember = Expression.PropertyOrField(b, "Title");</code></td><td><code>MemberExpression</code></td></tr><tr><td><code>Expression.Field</code></td><td>访问字段</td><td><code>var authorField = Expression.Field(b, "_author");</code></td><td><code>MemberExpression</code></td></tr></tbody></table></li><li><p>二元运算(算术 / 比较 / 逻辑)</p><table><thead><tr><th>工厂方法</th><th>功能</th><th>示例</th><th>ExpressionType</th></tr></thead><tbody><tr><td><code>Expression.Add</code></td><td>加法</td><td><code>Expression.Add(priceProp, five)</code></td><td>Add</td></tr><tr><td><code>Expression.Subtract</code></td><td>减法</td><td><code>Expression.Subtract(priceProp, five)</code></td><td>Subtract</td></tr><tr><td><code>Expression.GreaterThan</code></td><td>大于</td><td><code>Expression.GreaterThan(priceProp, five)</code></td><td>GreaterThan</td></tr><tr><td><code>Expression.Equal</code></td><td>等于</td><td><code>Expression.Equal(Expression.Property(b,"Title"), Expression.Constant("C#入门"))</code></td><td>Equal</td></tr><tr><td><code>Expression.AndAlso</code></td><td>逻辑与(短路)</td><td><code>Expression.AndAlso(cond1, cond2)</code></td><td>AndAlso</td></tr><tr><td><code>Expression.OrElse</code></td><td>逻辑或(短路)</td><td><code>Expression.OrElse(cond1, cond2)</code></td><td>OrElse</td></tr><tr><td><code>Expression.MakeBinary</code></td><td>通用入口</td><td><code>Expression.MakeBinary(ExpressionType.GreaterThanOrEqual, priceProp, five)</code></td><td>任意二元</td></tr></tbody></table><p> 组合示例:</p> <figure class="highlight csharp"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br></pre></td><td class="code"><pre><span class="line"><span class="keyword">var</span> b = Expression.Parameter(<span class="keyword">typeof</span>(Book), <span class="string">"b"</span>);</span><br><span class="line"><span class="keyword">var</span> priceProp = Expression.Property(b, <span class="keyword">nameof</span>(Book.Price));</span><br><span class="line"><span class="keyword">var</span> five = Expression.Constant(<span class="number">5</span>m);</span><br><span class="line"><span class="keyword">var</span> cond1 = Expression.GreaterThan(priceProp, five);</span><br><span class="line"><span class="keyword">var</span> cond2 = Expression.Equal(Expression.Property(b, <span class="string">"Title"</span>), Expression.Constant(<span class="string">"C#"</span>));</span><br><span class="line"><span class="keyword">var</span> andExpr = Expression.AndAlso(cond1, cond2);</span><br></pre></td></tr></table></figure></li><li><p>Lambda 表达式包装</p><table><thead><tr><th>工厂方法</th><th>功能描述</th><th>示例</th><th>返回类型</th></tr></thead><tbody><tr><td><code>Expression.Lambda<TDelegate></code></td><td>推荐用法</td><td><code>Expression<Func<Book,bool>> e = Expression.Lambda<Func<Book,bool>>(cond1, b);</code></td><td><code>Expression<Func<Book,bool>></code></td></tr><tr><td><code>Expression.Lambda</code></td><td>非泛型</td><td><code>LambdaExpression raw = Expression.Lambda(cond1, b);</code></td><td><code>LambdaExpression</code></td></tr></tbody></table></li><li><p>方法调用(实例 / 静态)</p><table><thead><tr><th>工厂方法</th><th>功能</th><th>示例(实例 / 静态)</th><th>节点类型</th></tr></thead><tbody><tr><td><code>Expression.Call</code></td><td>调用方法</td><td>见示例</td><td><code>MethodCallExpression</code></td></tr></tbody></table> <figure class="highlight csharp"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br></pre></td><td class="code"><pre><span class="line"><span class="keyword">var</span> b = Expression.Parameter(<span class="keyword">typeof</span>(Book), <span class="string">"b"</span>);</span><br><span class="line"><span class="keyword">var</span> titleProp = Expression.Property(b, <span class="keyword">nameof</span>(Book.Title));</span><br><span class="line"></span><br><span class="line"><span class="comment">// 实例方法 b.Title.Contains("C#")</span></span><br><span class="line"><span class="keyword">var</span> containsMethod = <span class="keyword">typeof</span>(<span class="built_in">string</span>).GetMethod(<span class="keyword">nameof</span>(<span class="built_in">string</span>.Contains), <span class="keyword">new</span>[] { <span class="keyword">typeof</span>(<span class="built_in">string</span>) });</span><br><span class="line"><span class="keyword">var</span> containsCall = Expression.Call(titleProp, containsMethod, Expression.Constant(<span class="string">"C#"</span>));</span><br><span class="line"></span><br><span class="line"><span class="comment">// 静态方法 string.IsNullOrEmpty(b.Title)</span></span><br><span class="line"><span class="keyword">var</span> isNullOrEmptyMethod = <span class="keyword">typeof</span>(<span class="built_in">string</span>).GetMethod(<span class="keyword">nameof</span>(<span class="built_in">string</span>.IsNullOrEmpty), <span class="keyword">new</span>[] { <span class="keyword">typeof</span>(<span class="built_in">string</span>) });</span><br><span class="line"><span class="keyword">var</span> nullCheckCall = Expression.Call(isNullOrEmptyMethod, titleProp);</span><br></pre></td></tr></table></figure></li><li><p>一元运算与类型转换</p><table><thead><tr><th>工厂方法</th><th>功能</th><th>示例</th><th>节点类型</th></tr></thead><tbody><tr><td><code>Expression.Convert</code></td><td>强制转换</td><td><code>Expression.Convert(Expression.Constant(5), typeof(decimal));</code></td><td>UnaryExpression</td></tr><tr><td><code>Expression.Not</code></td><td>逻辑非</td><td><code>Expression.Not(cond1)</code></td><td>UnaryExpression</td></tr><tr><td><code>Expression.TypeAs</code></td><td>安全转换</td><td><code>Expression.TypeAs(Expression.Parameter(typeof(object),"obj"), typeof(Book));</code></td><td>UnaryExpression</td></tr></tbody></table></li><li><p>对象与集合创建</p><table><thead><tr><th>工厂方法</th><th>功能</th><th>示例</th><th>节点类型</th></tr></thead><tbody><tr><td><code>Expression.New</code></td><td>构造对象</td><td>有 / 无参</td><td><code>NewExpression</code></td></tr><tr><td><code>Expression.NewArrayInit</code></td><td>创建数组并初始化</td><td><code>Expression.NewArrayInit(typeof(string), Expression.Constant("A"), Expression.Constant("B"));</code></td><td><code>NewArrayExpression</code></td></tr><tr><td><code>Expression.ArrayIndex</code></td><td>数组索引访问</td><td><code>Expression.ArrayIndex(arrParam, Expression.Constant(0));</code></td><td><code>BinaryExpression</code></td></tr></tbody></table><p> 有参构造示例:</p> <figure class="highlight csharp"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br></pre></td><td class="code"><pre><span class="line"><span class="keyword">var</span> titleConst = Expression.Constant(<span class="string">"C#进阶"</span>);</span><br><span class="line"><span class="keyword">var</span> priceConst = Expression.Constant(<span class="number">79.9</span>m);</span><br><span class="line"><span class="keyword">var</span> ctor = <span class="keyword">typeof</span>(Book).GetConstructor(<span class="keyword">new</span>[] { <span class="keyword">typeof</span>(<span class="built_in">string</span>), <span class="keyword">typeof</span>(<span class="built_in">decimal</span>) });</span><br><span class="line"><span class="keyword">var</span> newBookExpr = Expression.New(ctor, titleConst, priceConst);</span><br></pre></td></tr></table></figure><p> 数组访问示例:</p> <figure class="highlight csharp"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br></pre></td><td class="code"><pre><span class="line"><span class="keyword">var</span> arrParam = Expression.Parameter(<span class="keyword">typeof</span>(<span class="built_in">string</span>[]), <span class="string">"arr"</span>);</span><br><span class="line"><span class="keyword">var</span> first = Expression.ArrayIndex(arrParam, Expression.Constant(<span class="number">0</span>));</span><br></pre></td></tr></table></figure></li></ol> </div></div> </div><hr><h4 id="用ExpressionTreeToString简化动态构建表达式树"><a href="#用ExpressionTreeToString简化动态构建表达式树" class="headerlink" title="用ExpressionTreeToString简化动态构建表达式树"></a>用ExpressionTreeToString简化动态构建表达式树</h4><p>ExpressionTreeToString提供了ToString(“Factory methods”,”C#”),执行会输出类似工厂方法生成表达式树的代码。</p><figure class="highlight csharp"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br></pre></td><td class="code"><pre><span class="line">Expression<Func<Book, <span class="built_in">bool</span>>> e = b => b.Price > <span class="number">5</span>;</span><br><span class="line">Console.WriteLine(e.ToString(<span class="string">"Factory methods"</span>, <span class="string">"C#"</span>));</span><br><span class="line"><span class="comment">/* 输出</span></span><br><span class="line"><span class="comment">// using static System.Linq.Expressions.Expression</span></span><br><span class="line"><span class="comment"></span></span><br><span class="line"><span class="comment">var b = Parameter(</span></span><br><span class="line"><span class="comment"> typeof(Book),</span></span><br><span class="line"><span class="comment"> "b"</span></span><br><span class="line"><span class="comment">);</span></span><br><span class="line"><span class="comment"></span></span><br><span class="line"><span class="comment">Lambda(</span></span><br><span class="line"><span class="comment"> MakeBinary(ExpressionType.GreaterThan,</span></span><br><span class="line"><span class="comment"> MakeMemberAccess(b,</span></span><br><span class="line"><span class="comment"> typeof(Book).GetProperty("Price")</span></span><br><span class="line"><span class="comment"> ),</span></span><br><span class="line"><span class="comment"> Constant(5), false,</span></span><br><span class="line"><span class="comment"> typeof(decimal).GetMethod("op_GreaterThan")</span></span><br><span class="line"><span class="comment"> ),</span></span><br><span class="line"><span class="comment"> b</span></span><br><span class="line"><span class="comment">)</span></span><br><span class="line"><span class="comment">*/</span></span><br></pre></td></tr></table></figure><p>通过<code>ToString("Factory methods","C#")</code>输出动态构建表达式树</p><figure class="highlight csharp"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br></pre></td><td class="code"><pre><span class="line"><span class="keyword">using</span> ExpressionTreeToString;</span><br><span class="line"><span class="keyword">using</span> System.Linq.Expressions;</span><br><span class="line"><span class="keyword">using</span> <span class="keyword">static</span> System.Linq.Expressions.Expression;</span><br><span class="line"></span><br><span class="line"><span class="keyword">var</span> b = Parameter(<span class="keyword">typeof</span>(Book),<span class="string">"b"</span>);</span><br><span class="line"></span><br><span class="line"><span class="keyword">var</span> e = Lambda(</span><br><span class="line">MakeBinary(ExpressionType.GreaterThan,</span><br><span class="line">MakeMemberAccess(b,</span><br><span class="line"><span class="keyword">typeof</span>(Book).GetProperty(<span class="string">"Price"</span>)</span><br><span class="line">),</span><br><span class="line">Constant(<span class="number">5</span>m), <span class="literal">false</span>,</span><br><span class="line"><span class="keyword">typeof</span>(<span class="built_in">decimal</span>).GetMethod(<span class="string">"op_GreaterThan"</span>)</span><br><span class="line">),</span><br><span class="line">b</span><br><span class="line">);</span><br><span class="line"></span><br><span class="line">Console.WriteLine(e);<span class="comment">//b => (b.Price > 5)</span></span><br></pre></td></tr></table></figure><hr><h3 id="让构建”动态”起来"><a href="#让构建”动态”起来" class="headerlink" title="让构建”动态”起来"></a>让构建”动态”起来</h3><p>上面代码动态构建出了表达式树,但逻辑是固定的。既然逻辑是固定的,为什么不直接硬编码Lambda表达式?动态构建表达式树的价值就在于,运行时根据条件的不同生成不同的表达式树。</p><h4 id="场景示例:动态查询条件"><a href="#场景示例:动态查询条件" class="headerlink" title="场景示例:动态查询条件"></a>场景示例:动态查询条件</h4><p>假设有一个图书检索系统,用户可以选择多个筛选条件:</p><figure class="highlight csharp"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br></pre></td><td class="code"><pre><span class="line"><span class="keyword">class</span> <span class="title">BookSearchCriteria</span></span><br><span class="line">{</span><br><span class="line"> <span class="keyword">public</span> <span class="built_in">decimal</span>? MinPrice { <span class="keyword">get</span>; <span class="keyword">set</span>; }</span><br><span class="line"> <span class="keyword">public</span> <span class="built_in">decimal</span>? MaxPrice { <span class="keyword">get</span>; <span class="keyword">set</span>; }</span><br><span class="line"> <span class="keyword">public</span> <span class="built_in">string</span> TitleKeyword { <span class="keyword">get</span>; <span class="keyword">set</span>; }</span><br><span class="line"> <span class="keyword">public</span> <span class="built_in">bool</span>? OnlyInStock { <span class="keyword">get</span>; <span class="keyword">set</span>; }</span><br><span class="line">}</span><br></pre></td></tr></table></figure><h4 id="实现动态表达式构建器"><a href="#实现动态表达式构建器" class="headerlink" title="实现动态表达式构建器"></a>实现动态表达式构建器</h4><figure class="highlight csharp"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br><span class="line">25</span><br><span class="line">26</span><br><span class="line">27</span><br><span class="line">28</span><br><span class="line">29</span><br><span class="line">30</span><br><span class="line">31</span><br><span class="line">32</span><br><span class="line">33</span><br><span class="line">34</span><br><span class="line">35</span><br><span class="line">36</span><br><span class="line">37</span><br><span class="line">38</span><br><span class="line">39</span><br><span class="line">40</span><br><span class="line">41</span><br><span class="line">42</span><br><span class="line">43</span><br></pre></td><td class="code"><pre><span class="line"><span class="keyword">static</span> Expression<Func<Book, <span class="built_in">bool</span>>> BuildDynamicPredicate(BookSearchCriteria criteria)</span><br><span class="line">{</span><br><span class="line"> <span class="keyword">var</span> param = Expression.Parameter(<span class="keyword">typeof</span>(Book), <span class="string">"b"</span>);</span><br><span class="line"> Expression body = Expression.Constant(<span class="literal">true</span>); <span class="comment">// 初始条件:恒真</span></span><br><span class="line"></span><br><span class="line"> <span class="comment">// 动态添加最低价格条件</span></span><br><span class="line"> <span class="keyword">if</span> (criteria.MinPrice.HasValue)</span><br><span class="line"> {</span><br><span class="line"> <span class="keyword">var</span> priceProp = Expression.Property(param, <span class="keyword">nameof</span>(Book.Price));</span><br><span class="line"> <span class="keyword">var</span> minPriceConst = Expression.Constant(criteria.MinPrice.Value);</span><br><span class="line"> <span class="keyword">var</span> condition = Expression.GreaterThanOrEqual(priceProp, minPriceConst);</span><br><span class="line"> body = Expression.AndAlso(body, condition);</span><br><span class="line"> }</span><br><span class="line"></span><br><span class="line"> <span class="comment">// 动态添加最高价格条件</span></span><br><span class="line"> <span class="keyword">if</span> (criteria.MaxPrice.HasValue)</span><br><span class="line"> {</span><br><span class="line"> <span class="keyword">var</span> priceProp = Expression.Property(param, <span class="keyword">nameof</span>(Book.Price));</span><br><span class="line"> <span class="keyword">var</span> maxPriceConst = Expression.Constant(criteria.MaxPrice.Value);</span><br><span class="line"> <span class="keyword">var</span> condition = Expression.LessThanOrEqual(priceProp, maxPriceConst);</span><br><span class="line"> body = Expression.AndAlso(body, condition);</span><br><span class="line"> }</span><br><span class="line"></span><br><span class="line"> <span class="comment">// 动态添加标题关键词条件</span></span><br><span class="line"> <span class="keyword">if</span> (!<span class="built_in">string</span>.IsNullOrEmpty(criteria.TitleKeyword))</span><br><span class="line"> {</span><br><span class="line"> <span class="keyword">var</span> titleProp = Expression.Property(param, <span class="keyword">nameof</span>(Book.Title));</span><br><span class="line"> <span class="keyword">var</span> keywordConst = Expression.Constant(criteria.TitleKeyword);</span><br><span class="line"> <span class="keyword">var</span> containsMethod = <span class="keyword">typeof</span>(<span class="built_in">string</span>).GetMethod(<span class="keyword">nameof</span>(<span class="built_in">string</span>.Contains), <span class="keyword">new</span>[] { <span class="keyword">typeof</span>(<span class="built_in">string</span>) });</span><br><span class="line"> <span class="keyword">var</span> condition = Expression.Call(titleProp, containsMethod, keywordConst);</span><br><span class="line"> body = Expression.AndAlso(body, condition);</span><br><span class="line"> }</span><br><span class="line"></span><br><span class="line"> <span class="comment">// 动态添加库存条件(假设 Book 有 InStock 属性)</span></span><br><span class="line"> <span class="keyword">if</span> (criteria.OnlyInStock.HasValue && criteria.OnlyInStock.Value)</span><br><span class="line"> {</span><br><span class="line"> <span class="keyword">var</span> inStockProp = Expression.Property(param, <span class="string">"InStock"</span>);</span><br><span class="line"> <span class="keyword">var</span> condition = Expression.Equal(inStockProp, Expression.Constant(<span class="literal">true</span>));</span><br><span class="line"> body = Expression.AndAlso(body, condition);</span><br><span class="line"> }</span><br><span class="line"></span><br><span class="line"> <span class="keyword">return</span> Expression.Lambda<Func<Book, <span class="built_in">bool</span>>>(body, param);</span><br><span class="line">}</span><br></pre></td></tr></table></figure><h4 id="使用动态构建的表达式"><a href="#使用动态构建的表达式" class="headerlink" title="使用动态构建的表达式"></a>使用动态构建的表达式</h4><figure class="highlight csharp"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br><span class="line">25</span><br><span class="line">26</span><br><span class="line">27</span><br><span class="line">28</span><br><span class="line">29</span><br><span class="line">30</span><br><span class="line">31</span><br><span class="line">32</span><br><span class="line">33</span><br><span class="line">34</span><br><span class="line">35</span><br><span class="line">36</span><br><span class="line">37</span><br><span class="line">38</span><br><span class="line">39</span><br><span class="line">40</span><br><span class="line">41</span><br><span class="line">42</span><br><span class="line">43</span><br><span class="line">44</span><br><span class="line">45</span><br><span class="line">46</span><br><span class="line">47</span><br><span class="line">48</span><br><span class="line">49</span><br><span class="line">50</span><br><span class="line">51</span><br><span class="line">52</span><br><span class="line">53</span><br></pre></td><td class="code"><pre><span class="line"><span class="function"><span class="keyword">static</span> <span class="keyword">void</span> <span class="title">DynamicQueryExample</span>()</span></span><br><span class="line">{</span><br><span class="line"> <span class="keyword">var</span> books = <span class="keyword">new</span> List<Book></span><br><span class="line"> {</span><br><span class="line"> <span class="keyword">new</span> Book { Title = <span class="string">"C# 入门"</span>, Price = <span class="number">45</span>m, InStock = <span class="literal">true</span> },</span><br><span class="line"> <span class="keyword">new</span> Book { Title = <span class="string">"LINQ 实战"</span>, Price = <span class="number">55</span>m, InStock = <span class="literal">false</span> },</span><br><span class="line"> <span class="keyword">new</span> Book { Title = <span class="string">"C# 高级编程"</span>, Price = <span class="number">89</span>m, InStock = <span class="literal">true</span> },</span><br><span class="line"> <span class="keyword">new</span> Book { Title = <span class="string">"数据结构与算法"</span>, Price = <span class="number">65</span>m, InStock = <span class="literal">true</span> }</span><br><span class="line"> };</span><br><span class="line"></span><br><span class="line"> <span class="comment">// 场景1:只查询价格在 50-100 之间的书</span></span><br><span class="line"> <span class="keyword">var</span> criteria1 = <span class="keyword">new</span> BookSearchCriteria</span><br><span class="line"> {</span><br><span class="line"> MinPrice = <span class="number">50</span>m,</span><br><span class="line"> MaxPrice = <span class="number">100</span>m</span><br><span class="line"> };</span><br><span class="line"> <span class="keyword">var</span> predicate1 = BuildDynamicPredicate(criteria1);</span><br><span class="line"> <span class="keyword">var</span> result1 = books.Where(predicate1.Compile()).ToList();</span><br><span class="line"> Console.WriteLine(<span class="string">$"场景1 生成的表达式:<span class="subst">{predicate1}</span>"</span>);</span><br><span class="line"> Console.WriteLine(<span class="string">$"结果数量:<span class="subst">{result1.Count}</span>"</span>);</span><br><span class="line"> <span class="comment">// 输出:</span></span><br><span class="line"><span class="comment">// 场景1 生成的表达式:b => ((True AndAlso (b.Price >= 50)) AndAlso (b.Price <= 100))</span></span><br><span class="line"><span class="comment">// 结果数量:3</span></span><br><span class="line"></span><br><span class="line"> <span class="comment">// 场景2:查询标题包含"C#"且有库存的书</span></span><br><span class="line"> <span class="keyword">var</span> criteria2 = <span class="keyword">new</span> BookSearchCriteria</span><br><span class="line"> {</span><br><span class="line"> TitleKeyword = <span class="string">"C#"</span>,</span><br><span class="line"> OnlyInStock = <span class="literal">true</span></span><br><span class="line"> };</span><br><span class="line"> <span class="keyword">var</span> predicate2 = BuildDynamicPredicate(criteria2);</span><br><span class="line"> <span class="keyword">var</span> result2 = books.Where(predicate2.Compile()).ToList();</span><br><span class="line"> Console.WriteLine(<span class="string">$"\n场景2 生成的表达式:<span class="subst">{predicate2}</span>"</span>);</span><br><span class="line"> Console.WriteLine(<span class="string">$"结果数量:<span class="subst">{result2.Count}</span>"</span>);</span><br><span class="line"> <span class="comment">// 输出:</span></span><br><span class="line"><span class="comment">// 场景2 生成的表达式:b => ((True AndAlso b.Title.Contains("C#")) AndAlso (b.InStock == True))</span></span><br><span class="line"><span class="comment">// 结果数量:2</span></span><br><span class="line"></span><br><span class="line"> <span class="comment">// 场景3:组合多个条件</span></span><br><span class="line"> <span class="keyword">var</span> criteria3 = <span class="keyword">new</span> BookSearchCriteria</span><br><span class="line"> {</span><br><span class="line"> MinPrice = <span class="number">40</span>m,</span><br><span class="line"> TitleKeyword = <span class="string">"C#"</span>,</span><br><span class="line"> OnlyInStock = <span class="literal">true</span></span><br><span class="line"> };</span><br><span class="line"> <span class="keyword">var</span> predicate3 = BuildDynamicPredicate(criteria3);</span><br><span class="line"> <span class="keyword">var</span> result3 = books.Where(predicate3.Compile()).ToList();</span><br><span class="line"> Console.WriteLine(<span class="string">$"\n场景3 生成的表达式:<span class="subst">{predicate3}</span>"</span>);</span><br><span class="line"> Console.WriteLine(<span class="string">$"结果数量:<span class="subst">{result3.Count}</span>"</span>);</span><br><span class="line"><span class="comment">// 输出:</span></span><br><span class="line"><span class="comment">// 场景3 生成的表达式:b => (((True AndAlso (b.Price >= 40)) AndAlso b.Title.Contains("C#")) AndAlso (b.InStock == True))</span></span><br><span class="line"><span class="comment">// 结果数量:2</span></span><br><span class="line">}</span><br></pre></td></tr></table></figure><h4 id="移除冗余的-True-条件"><a href="#移除冗余的-True-条件" class="headerlink" title="移除冗余的 True 条件"></a>移除冗余的 True 条件</h4><p>上面的实现会产生 <code>(True AndAlso ...)</code> 这样的冗余表达式。可以优化:</p><figure class="highlight csharp"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br><span class="line">25</span><br><span class="line">26</span><br><span class="line">27</span><br><span class="line">28</span><br><span class="line">29</span><br><span class="line">30</span><br><span class="line">31</span><br><span class="line">32</span><br><span class="line">33</span><br><span class="line">34</span><br><span class="line">35</span><br><span class="line">36</span><br><span class="line">37</span><br><span class="line">38</span><br><span class="line">39</span><br><span class="line">40</span><br><span class="line">41</span><br><span class="line">42</span><br><span class="line">43</span><br></pre></td><td class="code"><pre><span class="line"><span class="keyword">static</span> Expression<Func<Book, <span class="built_in">bool</span>>> BuildDynamicPredicateOptimized(BookSearchCriteria criteria)</span><br><span class="line">{</span><br><span class="line"> <span class="keyword">var</span> param = Expression.Parameter(<span class="keyword">typeof</span>(Book), <span class="string">"b"</span>);</span><br><span class="line"> Expression body = <span class="literal">null</span>; <span class="comment">// 初始为 null</span></span><br><span class="line"></span><br><span class="line"> <span class="comment">// 辅助方法:安全添加条件</span></span><br><span class="line"> <span class="function"><span class="keyword">void</span> <span class="title">AddCondition</span>(<span class="params">Expression condition</span>)</span></span><br><span class="line"> {</span><br><span class="line"> body = body == <span class="literal">null</span> ? condition : Expression.AndAlso(body, condition);</span><br><span class="line"> }</span><br><span class="line"></span><br><span class="line"> <span class="comment">// 动态添加条件</span></span><br><span class="line"> <span class="keyword">if</span> (criteria.MinPrice.HasValue)</span><br><span class="line"> {</span><br><span class="line"> <span class="keyword">var</span> priceProp = Expression.Property(param, <span class="keyword">nameof</span>(Book.Price));</span><br><span class="line"> AddCondition(Expression.GreaterThanOrEqual(priceProp, Expression.Constant(criteria.MinPrice.Value)));</span><br><span class="line"> }</span><br><span class="line"></span><br><span class="line"> <span class="keyword">if</span> (criteria.MaxPrice.HasValue)</span><br><span class="line"> {</span><br><span class="line"> <span class="keyword">var</span> priceProp = Expression.Property(param, <span class="keyword">nameof</span>(Book.Price));</span><br><span class="line"> AddCondition(Expression.LessThanOrEqual(priceProp, Expression.Constant(criteria.MaxPrice.Value)));</span><br><span class="line"> }</span><br><span class="line"></span><br><span class="line"> <span class="keyword">if</span> (!<span class="built_in">string</span>.IsNullOrEmpty(criteria.TitleKeyword))</span><br><span class="line"> {</span><br><span class="line"> <span class="keyword">var</span> titleProp = Expression.Property(param, <span class="keyword">nameof</span>(Book.Title));</span><br><span class="line"> <span class="keyword">var</span> containsMethod = <span class="keyword">typeof</span>(<span class="built_in">string</span>).GetMethod(<span class="keyword">nameof</span>(<span class="built_in">string</span>.Contains), <span class="keyword">new</span>[] { <span class="keyword">typeof</span>(<span class="built_in">string</span>) });</span><br><span class="line"> AddCondition(Expression.Call(titleProp, containsMethod, Expression.Constant(criteria.TitleKeyword)));</span><br><span class="line"> }</span><br><span class="line"></span><br><span class="line"> <span class="keyword">if</span> (criteria.OnlyInStock == <span class="literal">true</span>)</span><br><span class="line"> {</span><br><span class="line"> <span class="keyword">var</span> inStockProp = Expression.Property(param, <span class="string">"InStock"</span>);</span><br><span class="line"> AddCondition(Expression.Equal(inStockProp, Expression.Constant(<span class="literal">true</span>)));</span><br><span class="line"> }</span><br><span class="line"></span><br><span class="line"> <span class="comment">// 如果没有任何条件,返回恒真表达式</span></span><br><span class="line"> <span class="keyword">if</span> (body == <span class="literal">null</span>)</span><br><span class="line"> body = Expression.Constant(<span class="literal">true</span>);</span><br><span class="line"></span><br><span class="line"> <span class="keyword">return</span> Expression.Lambda<Func<Book, <span class="built_in">bool</span>>>(body, param);</span><br><span class="line">}</span><br></pre></td></tr></table></figure><p>汇总代码(在program.cs里面粘贴下面代码可直接运行)</p><figure class="highlight csharp"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br><span class="line">25</span><br><span class="line">26</span><br><span class="line">27</span><br><span class="line">28</span><br><span class="line">29</span><br><span class="line">30</span><br><span class="line">31</span><br><span class="line">32</span><br><span class="line">33</span><br><span class="line">34</span><br><span class="line">35</span><br><span class="line">36</span><br><span class="line">37</span><br><span class="line">38</span><br><span class="line">39</span><br><span class="line">40</span><br><span class="line">41</span><br><span class="line">42</span><br><span class="line">43</span><br><span class="line">44</span><br><span class="line">45</span><br><span class="line">46</span><br><span class="line">47</span><br><span class="line">48</span><br><span class="line">49</span><br><span class="line">50</span><br><span class="line">51</span><br><span class="line">52</span><br><span class="line">53</span><br><span class="line">54</span><br><span class="line">55</span><br><span class="line">56</span><br><span class="line">57</span><br><span class="line">58</span><br><span class="line">59</span><br><span class="line">60</span><br><span class="line">61</span><br><span class="line">62</span><br><span class="line">63</span><br><span class="line">64</span><br><span class="line">65</span><br><span class="line">66</span><br><span class="line">67</span><br><span class="line">68</span><br><span class="line">69</span><br><span class="line">70</span><br><span class="line">71</span><br><span class="line">72</span><br><span class="line">73</span><br><span class="line">74</span><br><span class="line">75</span><br><span class="line">76</span><br><span class="line">77</span><br><span class="line">78</span><br><span class="line">79</span><br><span class="line">80</span><br><span class="line">81</span><br><span class="line">82</span><br><span class="line">83</span><br><span class="line">84</span><br><span class="line">85</span><br><span class="line">86</span><br><span class="line">87</span><br><span class="line">88</span><br><span class="line">89</span><br><span class="line">90</span><br><span class="line">91</span><br><span class="line">92</span><br><span class="line">93</span><br><span class="line">94</span><br><span class="line">95</span><br><span class="line">96</span><br><span class="line">97</span><br><span class="line">98</span><br><span class="line">99</span><br><span class="line">100</span><br><span class="line">101</span><br><span class="line">102</span><br><span class="line">103</span><br><span class="line">104</span><br><span class="line">105</span><br><span class="line">106</span><br><span class="line">107</span><br><span class="line">108</span><br><span class="line">109</span><br><span class="line">110</span><br><span class="line">111</span><br><span class="line">112</span><br><span class="line">113</span><br><span class="line">114</span><br><span class="line">115</span><br><span class="line">116</span><br><span class="line">117</span><br><span class="line">118</span><br><span class="line">119</span><br><span class="line">120</span><br><span class="line">121</span><br><span class="line">122</span><br><span class="line">123</span><br><span class="line">124</span><br><span class="line">125</span><br><span class="line">126</span><br><span class="line">127</span><br><span class="line">128</span><br><span class="line">129</span><br><span class="line">130</span><br><span class="line">131</span><br><span class="line">132</span><br><span class="line">133</span><br><span class="line">134</span><br><span class="line">135</span><br><span class="line">136</span><br><span class="line">137</span><br><span class="line">138</span><br><span class="line">139</span><br><span class="line">140</span><br><span class="line">141</span><br><span class="line">142</span><br><span class="line">143</span><br><span class="line">144</span><br><span class="line">145</span><br><span class="line">146</span><br><span class="line">147</span><br><span class="line">148</span><br><span class="line">149</span><br><span class="line">150</span><br><span class="line">151</span><br><span class="line">152</span><br><span class="line">153</span><br><span class="line">154</span><br><span class="line">155</span><br></pre></td><td class="code"><pre><span class="line"><span class="keyword">using</span> System;</span><br><span class="line"><span class="keyword">using</span> System.Collections.Generic;</span><br><span class="line"><span class="keyword">using</span> System.Linq;</span><br><span class="line"><span class="keyword">using</span> System.Linq.Expressions;</span><br><span class="line"><span class="keyword">using</span> System.Reflection;</span><br><span class="line"></span><br><span class="line"><span class="keyword">var</span> books = <span class="keyword">new</span> List<Book></span><br><span class="line"> {</span><br><span class="line"> <span class="keyword">new</span> Book { Title = <span class="string">"C# 入门"</span>, Price = <span class="number">45</span>m, InStock = <span class="literal">true</span> },</span><br><span class="line"> <span class="keyword">new</span> Book { Title = <span class="string">"LINQ 实战"</span>, Price = <span class="number">55</span>m, InStock = <span class="literal">false</span> },</span><br><span class="line"> <span class="keyword">new</span> Book { Title = <span class="string">"C# 高级编程"</span>, Price = <span class="number">89</span>m, InStock = <span class="literal">true</span> },</span><br><span class="line"> <span class="keyword">new</span> Book { Title = <span class="string">"数据结构与算法"</span>, Price = <span class="number">65</span>m, InStock = <span class="literal">true</span> }</span><br><span class="line"> };</span><br><span class="line"></span><br><span class="line"><span class="comment">// 场景1:只查询价格在 50-100 之间的书</span></span><br><span class="line"><span class="keyword">var</span> criteria1 = <span class="keyword">new</span> BookSearchCriteria</span><br><span class="line">{</span><br><span class="line"> MinPrice = <span class="number">50</span>m,</span><br><span class="line"> MaxPrice = <span class="number">100</span>m</span><br><span class="line">};</span><br><span class="line"><span class="keyword">var</span> predicate1 = BuildDynamicPredicate(criteria1);</span><br><span class="line"><span class="keyword">var</span> result1 = books.Where(predicate1.Compile()).ToList();</span><br><span class="line">Console.WriteLine(<span class="string">$"场景1 生成的表达式:<span class="subst">{predicate1}</span>"</span>);</span><br><span class="line">Console.WriteLine(<span class="string">$"结果数量:<span class="subst">{result1.Count}</span>"</span>);</span><br><span class="line"></span><br><span class="line"><span class="comment">// 场景2:查询标题包含"C#"且有库存的书</span></span><br><span class="line"><span class="keyword">var</span> criteria2 = <span class="keyword">new</span> BookSearchCriteria</span><br><span class="line">{</span><br><span class="line"> TitleKeyword = <span class="string">"C#"</span>,</span><br><span class="line"> OnlyInStock = <span class="literal">true</span></span><br><span class="line">};</span><br><span class="line"><span class="keyword">var</span> predicate2 = BuildDynamicPredicate(criteria2);</span><br><span class="line"><span class="keyword">var</span> result2 = books.Where(predicate2.Compile()).ToList();</span><br><span class="line">Console.WriteLine(<span class="string">$"\n场景2 生成的表达式:<span class="subst">{predicate2}</span>"</span>);</span><br><span class="line">Console.WriteLine(<span class="string">$"结果数量:<span class="subst">{result2.Count}</span>"</span>);</span><br><span class="line"></span><br><span class="line"><span class="comment">// 场景3:组合多个条件</span></span><br><span class="line"><span class="keyword">var</span> criteria3 = <span class="keyword">new</span> BookSearchCriteria</span><br><span class="line">{</span><br><span class="line"> MinPrice = <span class="number">40</span>m,</span><br><span class="line"> TitleKeyword = <span class="string">"C#"</span>,</span><br><span class="line"> OnlyInStock = <span class="literal">true</span></span><br><span class="line">};</span><br><span class="line"><span class="keyword">var</span> predicate3 = BuildDynamicPredicate(criteria3);</span><br><span class="line"><span class="keyword">var</span> result3 = books.Where(predicate3.Compile()).ToList();</span><br><span class="line">Console.WriteLine(<span class="string">$"\n场景3 生成的表达式:<span class="subst">{predicate3}</span>"</span>);</span><br><span class="line">Console.WriteLine(<span class="string">$"结果数量:<span class="subst">{result3.Count}</span>"</span>);</span><br><span class="line"></span><br><span class="line"><span class="keyword">var</span> predicate4 = BuildDynamicPredicateOptimized(criteria3);</span><br><span class="line"><span class="keyword">var</span> result4 = books.Where(predicate4.Compile()).ToList();</span><br><span class="line">Console.WriteLine(<span class="string">$"\n场景4 生成的表达式:<span class="subst">{predicate4}</span>"</span>);</span><br><span class="line">Console.WriteLine(<span class="string">$"结果数量:<span class="subst">{result4.Count}</span>"</span>);</span><br><span class="line"></span><br><span class="line"><span class="keyword">static</span> Expression<Func<Book, <span class="built_in">bool</span>>> BuildDynamicPredicate(BookSearchCriteria criteria)</span><br><span class="line">{</span><br><span class="line"> <span class="keyword">var</span> param = Expression.Parameter(<span class="keyword">typeof</span>(Book), <span class="string">"b"</span>);</span><br><span class="line"> Expression body = Expression.Constant(<span class="literal">true</span>); <span class="comment">// 初始条件:恒真</span></span><br><span class="line"></span><br><span class="line"> <span class="comment">// 动态添加最低价格条件</span></span><br><span class="line"> <span class="keyword">if</span> (criteria.MinPrice.HasValue)</span><br><span class="line"> {</span><br><span class="line"> <span class="keyword">var</span> priceProp = Expression.Property(param, <span class="keyword">nameof</span>(Book.Price));</span><br><span class="line"> <span class="keyword">var</span> minPriceConst = Expression.Constant(criteria.MinPrice.Value);</span><br><span class="line"> <span class="keyword">var</span> condition = Expression.GreaterThanOrEqual(priceProp, minPriceConst);</span><br><span class="line"> body = Expression.AndAlso(body, condition);</span><br><span class="line"> }</span><br><span class="line"></span><br><span class="line"> <span class="comment">// 动态添加最高价格条件</span></span><br><span class="line"> <span class="keyword">if</span> (criteria.MaxPrice.HasValue)</span><br><span class="line"> {</span><br><span class="line"> <span class="keyword">var</span> priceProp = Expression.Property(param, <span class="keyword">nameof</span>(Book.Price));</span><br><span class="line"> <span class="keyword">var</span> maxPriceConst = Expression.Constant(criteria.MaxPrice.Value);</span><br><span class="line"> <span class="keyword">var</span> condition = Expression.LessThanOrEqual(priceProp, maxPriceConst);</span><br><span class="line"> body = Expression.AndAlso(body, condition);</span><br><span class="line"> }</span><br><span class="line"></span><br><span class="line"> <span class="comment">// 动态添加标题关键词条件</span></span><br><span class="line"> <span class="keyword">if</span> (!<span class="built_in">string</span>.IsNullOrEmpty(criteria.TitleKeyword))</span><br><span class="line"> {</span><br><span class="line"> <span class="keyword">var</span> titleProp = Expression.Property(param, <span class="keyword">nameof</span>(Book.Title));</span><br><span class="line"> <span class="keyword">var</span> keywordConst = Expression.Constant(criteria.TitleKeyword);</span><br><span class="line"> <span class="keyword">var</span> containsMethod = <span class="keyword">typeof</span>(<span class="built_in">string</span>).GetMethod(<span class="keyword">nameof</span>(<span class="built_in">string</span>.Contains), <span class="keyword">new</span>[] { <span class="keyword">typeof</span>(<span class="built_in">string</span>) });</span><br><span class="line"> <span class="keyword">var</span> condition = Expression.Call(titleProp, containsMethod, keywordConst);</span><br><span class="line"> body = Expression.AndAlso(body, condition);</span><br><span class="line"> }</span><br><span class="line"></span><br><span class="line"> <span class="comment">// 动态添加库存条件(假设 Book 有 InStock 属性)</span></span><br><span class="line"> <span class="keyword">if</span> (criteria.OnlyInStock.HasValue && criteria.OnlyInStock.Value)</span><br><span class="line"> {</span><br><span class="line"> <span class="keyword">var</span> inStockProp = Expression.Property(param, <span class="string">"InStock"</span>);</span><br><span class="line"> <span class="keyword">var</span> condition = Expression.Equal(inStockProp, Expression.Constant(<span class="literal">true</span>));</span><br><span class="line"> body = Expression.AndAlso(body, condition);</span><br><span class="line"> }</span><br><span class="line"></span><br><span class="line"> <span class="keyword">return</span> Expression.Lambda<Func<Book, <span class="built_in">bool</span>>>(body, param);</span><br><span class="line">}</span><br><span class="line"></span><br><span class="line"><span class="keyword">static</span> Expression<Func<Book, <span class="built_in">bool</span>>> BuildDynamicPredicateOptimized(BookSearchCriteria criteria)</span><br><span class="line">{</span><br><span class="line"> <span class="keyword">var</span> param = Expression.Parameter(<span class="keyword">typeof</span>(Book), <span class="string">"b"</span>);</span><br><span class="line"> Expression body = <span class="literal">null</span>; <span class="comment">// 初始为 null</span></span><br><span class="line"></span><br><span class="line"> <span class="comment">// 辅助方法:安全添加条件</span></span><br><span class="line"> <span class="function"><span class="keyword">void</span> <span class="title">AddCondition</span>(<span class="params">Expression condition</span>)</span></span><br><span class="line"> {</span><br><span class="line"> body = body == <span class="literal">null</span> ? condition : Expression.AndAlso(body, condition);</span><br><span class="line"> }</span><br><span class="line"></span><br><span class="line"> <span class="comment">// 动态添加条件</span></span><br><span class="line"> <span class="keyword">if</span> (criteria.MinPrice.HasValue)</span><br><span class="line"> {</span><br><span class="line"> <span class="keyword">var</span> priceProp = Expression.Property(param, <span class="keyword">nameof</span>(Book.Price));</span><br><span class="line"> AddCondition(Expression.GreaterThanOrEqual(priceProp, Expression.Constant(criteria.MinPrice.Value)));</span><br><span class="line"> }</span><br><span class="line"></span><br><span class="line"> <span class="keyword">if</span> (criteria.MaxPrice.HasValue)</span><br><span class="line"> {</span><br><span class="line"> <span class="keyword">var</span> priceProp = Expression.Property(param, <span class="keyword">nameof</span>(Book.Price));</span><br><span class="line"> AddCondition(Expression.LessThanOrEqual(priceProp, Expression.Constant(criteria.MaxPrice.Value)));</span><br><span class="line"> }</span><br><span class="line"></span><br><span class="line"> <span class="keyword">if</span> (!<span class="built_in">string</span>.IsNullOrEmpty(criteria.TitleKeyword))</span><br><span class="line"> {</span><br><span class="line"> <span class="keyword">var</span> titleProp = Expression.Property(param, <span class="keyword">nameof</span>(Book.Title));</span><br><span class="line"> <span class="keyword">var</span> containsMethod = <span class="keyword">typeof</span>(<span class="built_in">string</span>).GetMethod(<span class="keyword">nameof</span>(<span class="built_in">string</span>.Contains), <span class="keyword">new</span>[] { <span class="keyword">typeof</span>(<span class="built_in">string</span>) });</span><br><span class="line"> AddCondition(Expression.Call(titleProp, containsMethod, Expression.Constant(criteria.TitleKeyword)));</span><br><span class="line"> }</span><br><span class="line"></span><br><span class="line"> <span class="keyword">if</span> (criteria.OnlyInStock == <span class="literal">true</span>)</span><br><span class="line"> {</span><br><span class="line"> <span class="keyword">var</span> inStockProp = Expression.Property(param, <span class="string">"InStock"</span>);</span><br><span class="line"> AddCondition(Expression.Equal(inStockProp, Expression.Constant(<span class="literal">true</span>)));</span><br><span class="line"> }</span><br><span class="line"></span><br><span class="line"> <span class="comment">// 如果没有任何条件,返回恒真表达式</span></span><br><span class="line"> <span class="keyword">if</span> (body == <span class="literal">null</span>)</span><br><span class="line"> body = Expression.Constant(<span class="literal">true</span>);</span><br><span class="line"></span><br><span class="line"> <span class="keyword">return</span> Expression.Lambda<Func<Book, <span class="built_in">bool</span>>>(body, param);</span><br><span class="line">}</span><br><span class="line"></span><br><span class="line"><span class="keyword">class</span> <span class="title">BookSearchCriteria</span></span><br><span class="line">{</span><br><span class="line"> <span class="keyword">public</span> <span class="built_in">decimal</span>? MinPrice { <span class="keyword">get</span>; <span class="keyword">set</span>; }</span><br><span class="line"> <span class="keyword">public</span> <span class="built_in">decimal</span>? MaxPrice { <span class="keyword">get</span>; <span class="keyword">set</span>; }</span><br><span class="line"> <span class="keyword">public</span> <span class="built_in">string</span> TitleKeyword { <span class="keyword">get</span>; <span class="keyword">set</span>; }</span><br><span class="line"> <span class="keyword">public</span> <span class="built_in">bool</span>? OnlyInStock { <span class="keyword">get</span>; <span class="keyword">set</span>; }</span><br><span class="line">}</span><br><span class="line"></span><br><span class="line"><span class="keyword">class</span> <span class="title">Book</span></span><br><span class="line">{</span><br><span class="line"> <span class="keyword">public</span> <span class="built_in">string</span> Title { <span class="keyword">get</span>; <span class="keyword">set</span>; }</span><br><span class="line"> <span class="keyword">public</span> <span class="built_in">decimal</span> Price { <span class="keyword">get</span>; <span class="keyword">set</span>; }</span><br><span class="line"> <span class="keyword">public</span> <span class="built_in">bool</span> InStock { <span class="keyword">get</span>; <span class="keyword">set</span>; }</span><br><span class="line">}</span><br></pre></td></tr></table></figure><p>执行dotnet run 输出以下结果</p><figure class="highlight bash"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br></pre></td><td class="code"><pre><span class="line">PS D:\<span class="built_in">source</span>\repo\ConsoleApp1> dotnet run --project <span class="string">"ConsoleApp1"</span></span><br><span class="line">场景1 生成的表达式:b => ((True AndAlso (b.Price >= <span class="number">50</span>)) AndAlso (b.Price <= 100))</span><br><span class="line">结果数量:3</span><br><span class="line"></span><br><span class="line">场景2 生成的表达式:b => ((True AndAlso b.Title.Contains("C#")) AndAlso (b.InStock == True))</span><br><span class="line">结果数量:2</span><br><span class="line"></span><br><span class="line">场景3 生成的表达式:b => (((True AndAlso (b.Price >= <span class="number">40</span>)) AndAlso b.Title.Contains(<span class="string">"C#"</span>)) AndAlso (b.InStock == True))</span><br><span class="line">结果数量:2</span><br><span class="line"></span><br><span class="line">场景4 生成的表达式:b => (((b.Price >= <span class="number">40</span>) AndAlso b.Title.Contains("C#")) AndAlso (b.InStock == True))</span><br><span class="line">结果数量:2</span><br></pre></td></tr></table></figure><h4 id="动态构建的优势总结"><a href="#动态构建的优势总结" class="headerlink" title="动态构建的优势总结"></a>动态构建的优势总结</h4><table><thead><tr><th>场景</th><th>传统方式</th><th>动态表达式树</th></tr></thead><tbody><tr><td>固定条件查询</td><td><code>books.Where(b => b.Price > 50)</code></td><td>无需使用,直接 Lambda 即可</td></tr><tr><td>用户可选条件</td><td>需要写大量 if-else 拼接 SQL/LINQ</td><td>根据条件动态组合表达式</td></tr><tr><td>规则引擎</td><td>硬编码规则</td><td>配置驱动,动态生成规则</td></tr><tr><td>权限过滤</td><td>在业务层手动过滤</td><td>统一在查询层注入权限表达式</td></tr><tr><td>低代码平台</td><td>无法实现</td><td>可视化拖拽转表达式树</td></tr></tbody></table><h4 id="性能注意事项"><a href="#性能注意事项" class="headerlink" title="性能注意事项"></a>性能注意事项</h4><ol><li><p><strong>缓存编译结果</strong>:表达式编译 (<code>Compile()</code>) 成本较高</p><figure class="highlight csharp"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br></pre></td><td class="code"><pre><span class="line"><span class="keyword">private</span> <span class="keyword">static</span> <span class="keyword">readonly</span> Dictionary<<span class="built_in">string</span>, Func<Book, <span class="built_in">bool</span>>> _compiledCache = <span class="keyword">new</span>();</span><br><span class="line"></span><br><span class="line"><span class="keyword">var</span> key = predicate.ToString();</span><br><span class="line"><span class="keyword">if</span> (!_compiledCache.ContainsKey(key))</span><br><span class="line"> _compiledCache[key] = predicate.Compile();</span><br><span class="line"><span class="keyword">var</span> func = _compiledCache[key];</span><br></pre></td></tr></table></figure></li><li><p><strong>避免过度使用</strong>:简单固定查询直接用 Lambda</p></li><li><p><strong>EF Core 翻译限制</strong>:确保表达式可以被翻译成 SQL(避免调用本地方法)</p></li></ol><hr>]]></content>
<summary type="html"><p>上篇post介绍了表达式树,本节介绍如何动态构建表达式树。</p>
<h3 id="代码动态构建表达式树"><a href="#代码动态构建表达式树" class="headerlink" title="代码动态构建表达式树"></a>代码动态构建表达式树</h3><p>在</summary>
<category term="编程技术" scheme="https://probieluo.github.io/categories/%E7%BC%96%E7%A8%8B%E6%8A%80%E6%9C%AF/"/>
<category term="C#" scheme="https://probieluo.github.io/tags/C/"/>
<category term="LINQ" scheme="https://probieluo.github.io/tags/LINQ/"/>
</entry>
<entry>
<title>表达式树</title>
<link href="https://probieluo.github.io/posts/expression-trees-00/"/>
<id>https://probieluo.github.io/posts/expression-trees-00/</id>
<published>2025-11-23T14:45:42.000Z</published>
<updated>2026-04-22T09:11:59.489Z</updated>
<content type="html"><![CDATA[<p>在 .NET 开发中,表达式树是一个强大但容易被忽视的特性。如果你使用过 Entity Framework 或 LINQ to SQL,其实你已经在间接使用表达式树了。</p><span id="more"></span><hr><h2 id="什么是表达式树?"><a href="#什么是表达式树?" class="headerlink" title="什么是表达式树?"></a>什么是表达式树?</h2><p><strong>表达式树(Expression Tree)</strong> 是用树形数据结构来表示代码逻辑运算的技术,它让我们可以在运行时访问逻辑运算结构。表达式树在.net中对应<code>Expression<TDelegate></code>类型。</p><h3 id="通俗理解"><a href="#通俗理解" class="headerlink" title="通俗理解"></a>通俗理解</h3><ul><li><strong>普通委托</strong>:是一段可执行的代码</li><li><strong>表达式树</strong>:是描述代码结构的数据,可以被分析、修改、翻译成其他形式(如 SQL)</li></ul><h3 id="一个简单的例子"><a href="#一个简单的例子" class="headerlink" title="一个简单的例子"></a>一个简单的例子</h3><figure class="highlight csharp"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br></pre></td><td class="code"><pre><span class="line"><span class="comment">// 普通委托:直接执行</span></span><br><span class="line">Func<<span class="built_in">int</span>, <span class="built_in">bool</span>> func = x => x > <span class="number">5</span>;</span><br><span class="line"><span class="built_in">bool</span> result = func(<span class="number">10</span>); <span class="comment">// 直接执行,返回 true</span></span><br><span class="line"></span><br><span class="line"><span class="comment">// 表达式树:代码的数据表示</span></span><br><span class="line">Expression<Func<<span class="built_in">int</span>, <span class="built_in">bool</span>>> expr = x => x > <span class="number">5</span>;</span><br><span class="line"><span class="comment">// expr 不是可执行代码,而是一个树形结构,描述了"x > 5"这个表达式</span></span><br></pre></td></tr></table></figure><h3 id="为什么需要表达式树?"><a href="#为什么需要表达式树?" class="headerlink" title="为什么需要表达式树?"></a>为什么需要表达式树?</h3><p>表达式树的主要用途是<strong>将 C# 代码翻译成其他形式</strong>:</p><ol><li><strong>LINQ to SQL / Entity Framework</strong>:将 C# 查询翻译成 SQL 语句</li><li><strong>动态查询构建</strong>:根据用户输入动态生成查询条件</li><li><strong>代码分析</strong>:检查代码结构、提取元数据</li><li><strong>序列化/反序列化</strong>:将代码逻辑保存或传输</li></ol><hr><h2 id="Expression-和-Func-的区别"><a href="#Expression-和-Func-的区别" class="headerlink" title="Expression 和 Func 的区别"></a>Expression 和 Func 的区别</h2><h3 id="Func-委托"><a href="#Func-委托" class="headerlink" title="Func<T> - 委托"></a><code>Func<T></code> - 委托</h3><p><code>Func<T></code> 是一个委托类型,代表<strong>可执行的代码</strong>。</p><figure class="highlight csharp"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br></pre></td><td class="code"><pre><span class="line">Func<<span class="built_in">int</span>, <span class="built_in">int</span>, <span class="built_in">int</span>> <span class="keyword">add</span> = (a, b) => a + b;</span><br><span class="line"><span class="built_in">int</span> result = <span class="keyword">add</span>(<span class="number">3</span>, <span class="number">5</span>); <span class="comment">// 执行代码,返回 8</span></span><br></pre></td></tr></table></figure><p><strong>特点</strong>:</p><ul><li>编译后直接生成 IL 代码</li><li>可以直接调用执行</li><li>无法查看内部结构</li></ul><h3 id="Expression-表达式树"><a href="#Expression-表达式树" class="headerlink" title="Expression<Func<T>> - 表达式树"></a><code>Expression<Func<T>></code> - 表达式树</h3><p><code>Expression<Func<T>></code> 是一个表达式树类型,代表<strong>代码的数据结构</strong>。</p><figure class="highlight csharp"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br></pre></td><td class="code"><pre><span class="line">Expression<Func<<span class="built_in">int</span>, <span class="built_in">int</span>, <span class="built_in">int</span>>> addExpr = (a, b) => a + b;</span><br><span class="line"><span class="comment">// addExpr 是一个树形结构,不能直接执行</span></span><br></pre></td></tr></table></figure><p><strong>特点</strong>:</p><ul><li>编译后生成表达式树对象(包含 Body、Parameters 等属性)</li><li>不能直接调用,需要先编译成委托</li><li>可以分析、修改、翻译</li></ul><h3 id="对比表格"><a href="#对比表格" class="headerlink" title="对比表格"></a>对比表格</h3><table><thead><tr><th>维度</th><th>Func<T></th><th>Expression<Func<T>></th></tr></thead><tbody><tr><td>本质</td><td>委托(可执行代码)</td><td>表达式树(代码的数据结构)</td></tr><tr><td>是否可执行</td><td>✅ 直接执行</td><td>❌ 需要先编译成委托</td></tr><tr><td>是否可分析</td><td>❌ 无法查看内部结构</td><td>✅ 可以访问树形结构</td></tr><tr><td>典型用途</td><td>LINQ to Objects、回调函数</td><td>LINQ to SQL、动态查询</td></tr></tbody></table><h3 id="实际应用场景对比"><a href="#实际应用场景对比" class="headerlink" title="实际应用场景对比"></a>实际应用场景对比</h3><figure class="highlight csharp"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br></pre></td><td class="code"><pre><span class="line"><span class="comment">// LINQ to Objects:使用 Func(在内存中执行)</span></span><br><span class="line">List<<span class="built_in">int</span>> numbers = <span class="keyword">new</span> List<<span class="built_in">int</span>> { <span class="number">1</span>, <span class="number">2</span>, <span class="number">3</span>, <span class="number">4</span>, <span class="number">5</span> };</span><br><span class="line"><span class="keyword">var</span> result1 = numbers.Where(x => x > <span class="number">3</span>); <span class="comment">// 直接在内存中过滤</span></span><br><span class="line"></span><br><span class="line"><span class="comment">// LINQ to Entities:使用 Expression(翻译成 SQL)</span></span><br><span class="line">DbSet<User> users = dbContext.Users;</span><br><span class="line"><span class="keyword">var</span> result2 = users.Where(x => x.Age > <span class="number">18</span>); <span class="comment">// 翻译成 SQL: SELECT * FROM Users WHERE Age > 18</span></span><br></pre></td></tr></table></figure><h3 id="总结"><a href="#总结" class="headerlink" title="总结"></a>总结</h3><p>Expression类似于源代码,Func类似编译后的二进制程序。</p><hr><h2 id="查看表达式树的结构"><a href="#查看表达式树的结构" class="headerlink" title="查看表达式树的结构"></a>查看表达式树的结构</h2><p>表达式树是一个树形结构,可以通过其属性查看内部组成。</p><h3 id="Visual-Studio通过自动窗口查看"><a href="#Visual-Studio通过自动窗口查看" class="headerlink" title="Visual Studio通过自动窗口查看"></a>Visual Studio通过自动窗口查看</h3><figure class="highlight csharp"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line">Expression<Func<<span class="built_in">int</span>, <span class="built_in">bool</span>>> expr = x => x > <span class="number">5</span>;</span><br></pre></td></tr></table></figure><p><img src="/../images/%E5%B1%8F%E5%B9%95%E6%88%AA%E5%9B%BE%202025-11-24%20221604.png"></p><p>可以看到expr NodeType是Lambda,Body下面有Left {x}和Right {5} ,Body的 NodeType 是 GreaterThan,它是 二元运算符节点类型 BinaryExpression ,GreaterThan表示大于</p><h3 id="代码查看表达式树"><a href="#代码查看表达式树" class="headerlink" title="代码查看表达式树"></a>代码查看表达式树</h3><p>使用包ExpressionTreeToString</p><figure class="highlight csharp"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br></pre></td><td class="code"><pre><span class="line">Expression<Func<<span class="built_in">int</span>,<span class="built_in">bool</span>>> expr = x => x > <span class="number">5</span>;</span><br><span class="line">Console.WriteLine(expr);</span><br><span class="line">Console.WriteLine(expr.Body);</span><br><span class="line"></span><br><span class="line">Console.WriteLine(expr.ToString(<span class="string">"Object notation"</span>,<span class="string">"C#"</span>));</span><br></pre></td></tr></table></figure><p><img src="/../images/image%20copy.png"></p><p>可以看到,表达式树不同类型的节点对应不同类型,这些类型直接间接继承Expression。如上面Body就是二元运算符节点类型 BinaryExpression、Lambda 表达式 LambdaExpression(如 x => x > 10)、方法调用 MethodCallExpression(如 obj.ToString())、成员访问 MemberExpression (如 obj.Name、x.Age)、常量 ConstantExpression 、参数 ParameterExpression。</p><p>而NodeType表示标识当前表达式节点的类型,是ExpressionType枚举值。</p><div class="collapse-box-control"> <div class="collapse-box-header"><div class="collapse-box-icon"><i class="fa fa-plus"></i></div><span>ExpressionType</span></div> <div class="collapse-box-content"><div class="inner"> <figure class="highlight csharp"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br><span class="line">25</span><br><span class="line">26</span><br><span class="line">27</span><br><span class="line">28</span><br><span class="line">29</span><br><span class="line">30</span><br><span class="line">31</span><br><span class="line">32</span><br><span class="line">33</span><br><span class="line">34</span><br><span class="line">35</span><br><span class="line">36</span><br><span class="line">37</span><br><span class="line">38</span><br><span class="line">39</span><br><span class="line">40</span><br><span class="line">41</span><br><span class="line">42</span><br><span class="line">43</span><br><span class="line">44</span><br><span class="line">45</span><br><span class="line">46</span><br><span class="line">47</span><br><span class="line">48</span><br><span class="line">49</span><br><span class="line">50</span><br><span class="line">51</span><br><span class="line">52</span><br><span class="line">53</span><br><span class="line">54</span><br><span class="line">55</span><br><span class="line">56</span><br><span class="line">57</span><br><span class="line">58</span><br><span class="line">59</span><br><span class="line">60</span><br><span class="line">61</span><br><span class="line">62</span><br><span class="line">63</span><br><span class="line">64</span><br><span class="line">65</span><br><span class="line">66</span><br><span class="line">67</span><br><span class="line">68</span><br><span class="line">69</span><br><span class="line">70</span><br><span class="line">71</span><br><span class="line">72</span><br><span class="line">73</span><br><span class="line">74</span><br><span class="line">75</span><br><span class="line">76</span><br><span class="line">77</span><br><span class="line">78</span><br><span class="line">79</span><br><span class="line">80</span><br><span class="line">81</span><br><span class="line">82</span><br><span class="line">83</span><br><span class="line">84</span><br><span class="line">85</span><br><span class="line">86</span><br><span class="line">87</span><br><span class="line">88</span><br><span class="line">89</span><br><span class="line">90</span><br><span class="line">91</span><br><span class="line">92</span><br><span class="line">93</span><br><span class="line">94</span><br><span class="line">95</span><br><span class="line">96</span><br><span class="line">97</span><br><span class="line">98</span><br><span class="line">99</span><br><span class="line">100</span><br><span class="line">101</span><br><span class="line">102</span><br><span class="line">103</span><br><span class="line">104</span><br><span class="line">105</span><br><span class="line">106</span><br><span class="line">107</span><br><span class="line">108</span><br><span class="line">109</span><br><span class="line">110</span><br><span class="line">111</span><br><span class="line">112</span><br><span class="line">113</span><br><span class="line">114</span><br><span class="line">115</span><br><span class="line">116</span><br><span class="line">117</span><br><span class="line">118</span><br><span class="line">119</span><br><span class="line">120</span><br><span class="line">121</span><br><span class="line">122</span><br><span class="line">123</span><br><span class="line">124</span><br><span class="line">125</span><br><span class="line">126</span><br><span class="line">127</span><br><span class="line">128</span><br><span class="line">129</span><br><span class="line">130</span><br><span class="line">131</span><br><span class="line">132</span><br><span class="line">133</span><br><span class="line">134</span><br><span class="line">135</span><br><span class="line">136</span><br><span class="line">137</span><br><span class="line">138</span><br><span class="line">139</span><br><span class="line">140</span><br><span class="line">141</span><br><span class="line">142</span><br><span class="line">143</span><br><span class="line">144</span><br><span class="line">145</span><br><span class="line">146</span><br><span class="line">147</span><br><span class="line">148</span><br><span class="line">149</span><br><span class="line">150</span><br><span class="line">151</span><br><span class="line">152</span><br><span class="line">153</span><br><span class="line">154</span><br><span class="line">155</span><br><span class="line">156</span><br><span class="line">157</span><br><span class="line">158</span><br><span class="line">159</span><br><span class="line">160</span><br><span class="line">161</span><br><span class="line">162</span><br><span class="line">163</span><br><span class="line">164</span><br><span class="line">165</span><br><span class="line">166</span><br><span class="line">167</span><br><span class="line">168</span><br><span class="line">169</span><br><span class="line">170</span><br><span class="line">171</span><br><span class="line">172</span><br><span class="line">173</span><br><span class="line">174</span><br><span class="line">175</span><br><span class="line">176</span><br><span class="line">177</span><br><span class="line">178</span><br><span class="line">179</span><br><span class="line">180</span><br><span class="line">181</span><br><span class="line">182</span><br><span class="line">183</span><br><span class="line">184</span><br><span class="line">185</span><br><span class="line">186</span><br><span class="line">187</span><br><span class="line">188</span><br><span class="line">189</span><br><span class="line">190</span><br><span class="line">191</span><br><span class="line">192</span><br><span class="line">193</span><br><span class="line">194</span><br><span class="line">195</span><br><span class="line">196</span><br><span class="line">197</span><br><span class="line">198</span><br><span class="line">199</span><br><span class="line">200</span><br><span class="line">201</span><br><span class="line">202</span><br><span class="line">203</span><br><span class="line">204</span><br><span class="line">205</span><br><span class="line">206</span><br><span class="line">207</span><br><span class="line">208</span><br><span class="line">209</span><br><span class="line">210</span><br><span class="line">211</span><br><span class="line">212</span><br><span class="line">213</span><br><span class="line">214</span><br><span class="line">215</span><br><span class="line">216</span><br><span class="line">217</span><br><span class="line">218</span><br><span class="line">219</span><br><span class="line">220</span><br><span class="line">221</span><br><span class="line">222</span><br><span class="line">223</span><br><span class="line">224</span><br><span class="line">225</span><br><span class="line">226</span><br><span class="line">227</span><br><span class="line">228</span><br><span class="line">229</span><br><span class="line">230</span><br><span class="line">231</span><br><span class="line">232</span><br><span class="line">233</span><br><span class="line">234</span><br><span class="line">235</span><br><span class="line">236</span><br><span class="line">237</span><br><span class="line">238</span><br><span class="line">239</span><br><span class="line">240</span><br><span class="line">241</span><br><span class="line">242</span><br><span class="line">243</span><br><span class="line">244</span><br><span class="line">245</span><br><span class="line">246</span><br><span class="line">247</span><br><span class="line">248</span><br><span class="line">249</span><br><span class="line">250</span><br><span class="line">251</span><br><span class="line">252</span><br><span class="line">253</span><br><span class="line">254</span><br><span class="line">255</span><br><span class="line">256</span><br><span class="line">257</span><br><span class="line">258</span><br><span class="line">259</span><br><span class="line">260</span><br><span class="line">261</span><br><span class="line">262</span><br><span class="line">263</span><br><span class="line">264</span><br><span class="line">265</span><br><span class="line">266</span><br><span class="line">267</span><br><span class="line">268</span><br><span class="line">269</span><br><span class="line">270</span><br><span class="line">271</span><br><span class="line">272</span><br><span class="line">273</span><br><span class="line">274</span><br><span class="line">275</span><br><span class="line">276</span><br><span class="line">277</span><br><span class="line">278</span><br><span class="line">279</span><br><span class="line">280</span><br><span class="line">281</span><br><span class="line">282</span><br><span class="line">283</span><br><span class="line">284</span><br><span class="line">285</span><br><span class="line">286</span><br><span class="line">287</span><br><span class="line">288</span><br><span class="line">289</span><br><span class="line">290</span><br><span class="line">291</span><br><span class="line">292</span><br><span class="line">293</span><br><span class="line">294</span><br><span class="line">295</span><br><span class="line">296</span><br><span class="line">297</span><br><span class="line">298</span><br><span class="line">299</span><br><span class="line">300</span><br><span class="line">301</span><br><span class="line">302</span><br><span class="line">303</span><br><span class="line">304</span><br><span class="line">305</span><br><span class="line">306</span><br><span class="line">307</span><br><span class="line">308</span><br><span class="line">309</span><br><span class="line">310</span><br><span class="line">311</span><br><span class="line">312</span><br><span class="line">313</span><br><span class="line">314</span><br><span class="line">315</span><br><span class="line">316</span><br><span class="line">317</span><br><span class="line">318</span><br><span class="line">319</span><br><span class="line">320</span><br><span class="line">321</span><br><span class="line">322</span><br><span class="line">323</span><br><span class="line">324</span><br><span class="line">325</span><br><span class="line">326</span><br><span class="line">327</span><br><span class="line">328</span><br><span class="line">329</span><br><span class="line">330</span><br><span class="line">331</span><br><span class="line">332</span><br><span class="line">333</span><br><span class="line">334</span><br><span class="line">335</span><br><span class="line">336</span><br><span class="line">337</span><br><span class="line">338</span><br><span class="line">339</span><br><span class="line">340</span><br><span class="line">341</span><br><span class="line">342</span><br><span class="line">343</span><br><span class="line">344</span><br><span class="line">345</span><br><span class="line">346</span><br><span class="line">347</span><br><span class="line">348</span><br><span class="line">349</span><br><span class="line">350</span><br><span class="line">351</span><br><span class="line">352</span><br><span class="line">353</span><br><span class="line">354</span><br><span class="line">355</span><br><span class="line">356</span><br><span class="line">357</span><br><span class="line">358</span><br><span class="line">359</span><br><span class="line">360</span><br><span class="line">361</span><br><span class="line">362</span><br><span class="line">363</span><br><span class="line">364</span><br><span class="line">365</span><br><span class="line">366</span><br><span class="line">367</span><br><span class="line">368</span><br><span class="line">369</span><br><span class="line">370</span><br><span class="line">371</span><br><span class="line">372</span><br><span class="line">373</span><br><span class="line">374</span><br><span class="line">375</span><br><span class="line">376</span><br><span class="line">377</span><br><span class="line">378</span><br><span class="line">379</span><br><span class="line">380</span><br><span class="line">381</span><br><span class="line">382</span><br><span class="line">383</span><br><span class="line">384</span><br><span class="line">385</span><br><span class="line">386</span><br><span class="line">387</span><br><span class="line">388</span><br><span class="line">389</span><br><span class="line">390</span><br><span class="line">391</span><br><span class="line">392</span><br><span class="line">393</span><br><span class="line">394</span><br><span class="line">395</span><br><span class="line">396</span><br><span class="line">397</span><br><span class="line">398</span><br><span class="line">399</span><br><span class="line">400</span><br><span class="line">401</span><br><span class="line">402</span><br><span class="line">403</span><br><span class="line">404</span><br><span class="line">405</span><br><span class="line">406</span><br></pre></td><td class="code"><pre><span class="line"><span class="meta">#<span class="keyword">region</span> 程序集 System.Linq.Expressions, Version=10.0.0.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a</span></span><br><span class="line"><span class="comment">// C:\Program Files\dotnet\packs\Microsoft.NETCore.App.Ref\10.0.0\ref\net10.0\System.Linq.Expressions.dll</span></span><br><span class="line"><span class="meta">#<span class="keyword">endregion</span></span></span><br><span class="line"></span><br><span class="line"><span class="keyword">namespace</span> <span class="title">System.Linq.Expressions</span></span><br><span class="line">{</span><br><span class="line"><span class="comment">//</span></span><br><span class="line"><span class="comment">// 摘要:</span></span><br><span class="line"><span class="comment">// Describes the node types for the nodes of an expression tree.</span></span><br><span class="line"><span class="keyword">public</span> <span class="built_in">enum</span> ExpressionType</span><br><span class="line">{</span><br><span class="line"><span class="comment">//</span></span><br><span class="line"><span class="comment">// 摘要:</span></span><br><span class="line"><span class="comment">// An addition operation, such as a + b, without overflow checking, for numeric</span></span><br><span class="line"><span class="comment">// operands.</span></span><br><span class="line">Add = <span class="number">0</span>,</span><br><span class="line"><span class="comment">//</span></span><br><span class="line"><span class="comment">// 摘要:</span></span><br><span class="line"><span class="comment">// An addition operation, such as (a + b), with overflow checking, for numeric operands.</span></span><br><span class="line">AddChecked = <span class="number">1</span>,</span><br><span class="line"><span class="comment">//</span></span><br><span class="line"><span class="comment">// 摘要:</span></span><br><span class="line"><span class="comment">// A bitwise or logical AND operation, such as (a & b) in C# and (a And b) in Visual</span></span><br><span class="line"><span class="comment">// Basic.</span></span><br><span class="line">And = <span class="number">2</span>,</span><br><span class="line"><span class="comment">//</span></span><br><span class="line"><span class="comment">// 摘要:</span></span><br><span class="line"><span class="comment">// A conditional AND operation that evaluates the second operand only if the first</span></span><br><span class="line"><span class="comment">// operand evaluates to true. It corresponds to (a && b) in C# and (a AndAlso b)</span></span><br><span class="line"><span class="comment">// in Visual Basic.</span></span><br><span class="line">AndAlso = <span class="number">3</span>,</span><br><span class="line"><span class="comment">//</span></span><br><span class="line"><span class="comment">// 摘要:</span></span><br><span class="line"><span class="comment">// An operation that obtains the length of a one-dimensional array, such as array.Length.</span></span><br><span class="line">ArrayLength = <span class="number">4</span>,</span><br><span class="line"><span class="comment">//</span></span><br><span class="line"><span class="comment">// 摘要:</span></span><br><span class="line"><span class="comment">// An indexing operation in a one-dimensional array, such as array[index] in C#</span></span><br><span class="line"><span class="comment">// or array(index) in Visual Basic.</span></span><br><span class="line">ArrayIndex = <span class="number">5</span>,</span><br><span class="line"><span class="comment">//</span></span><br><span class="line"><span class="comment">// 摘要:</span></span><br><span class="line"><span class="comment">// A method call, such as in the obj.sampleMethod() expression.</span></span><br><span class="line">Call = <span class="number">6</span>,</span><br><span class="line"><span class="comment">//</span></span><br><span class="line"><span class="comment">// 摘要:</span></span><br><span class="line"><span class="comment">// A node that represents a null coalescing operation, such as (a ?? b) in C# or</span></span><br><span class="line"><span class="comment">// If(a, b) in Visual Basic.</span></span><br><span class="line">Coalesce = <span class="number">7</span>,</span><br><span class="line"><span class="comment">//</span></span><br><span class="line"><span class="comment">// 摘要:</span></span><br><span class="line"><span class="comment">// A conditional operation, such as a > b ? a : b in C# or If(a > b, a, b) in Visual</span></span><br><span class="line"><span class="comment">// Basic.</span></span><br><span class="line">Conditional = <span class="number">8</span>,</span><br><span class="line"><span class="comment">//</span></span><br><span class="line"><span class="comment">// 摘要:</span></span><br><span class="line"><span class="comment">// A constant value.</span></span><br><span class="line">Constant = <span class="number">9</span>,</span><br><span class="line"><span class="comment">//</span></span><br><span class="line"><span class="comment">// 摘要:</span></span><br><span class="line"><span class="comment">// A cast or conversion operation, such as (SampleType)obj in C#or CType(obj, SampleType)</span></span><br><span class="line"><span class="comment">// in Visual Basic. For a numeric conversion, if the converted value is too large</span></span><br><span class="line"><span class="comment">// for the destination type, no exception is thrown.</span></span><br><span class="line">Convert = <span class="number">10</span>,</span><br><span class="line"><span class="comment">//</span></span><br><span class="line"><span class="comment">// 摘要:</span></span><br><span class="line"><span class="comment">// A cast or conversion operation, such as (SampleType)obj in C#or CType(obj, SampleType)</span></span><br><span class="line"><span class="comment">// in Visual Basic. For a numeric conversion, if the converted value does not fit</span></span><br><span class="line"><span class="comment">// the destination type, an exception is thrown.</span></span><br><span class="line">ConvertChecked = <span class="number">11</span>,</span><br><span class="line"><span class="comment">//</span></span><br><span class="line"><span class="comment">// 摘要:</span></span><br><span class="line"><span class="comment">// A division operation, such as (a / b), for numeric operands.</span></span><br><span class="line">Divide = <span class="number">12</span>,</span><br><span class="line"><span class="comment">//</span></span><br><span class="line"><span class="comment">// 摘要:</span></span><br><span class="line"><span class="comment">// A node that represents an equality comparison, such as (a == b) in C# or (a =</span></span><br><span class="line"><span class="comment">// b) in Visual Basic.</span></span><br><span class="line">Equal = <span class="number">13</span>,</span><br><span class="line"><span class="comment">//</span></span><br><span class="line"><span class="comment">// 摘要:</span></span><br><span class="line"><span class="comment">// A bitwise or logical XOR operation, such as (a ^ b) in C# or (a Xor b) in Visual</span></span><br><span class="line"><span class="comment">// Basic.</span></span><br><span class="line">ExclusiveOr = <span class="number">14</span>,</span><br><span class="line"><span class="comment">//</span></span><br><span class="line"><span class="comment">// 摘要:</span></span><br><span class="line"><span class="comment">// A "greater than" comparison, such as (a > b).</span></span><br><span class="line">GreaterThan = <span class="number">15</span>,</span><br><span class="line"><span class="comment">//</span></span><br><span class="line"><span class="comment">// 摘要:</span></span><br><span class="line"><span class="comment">// A "greater than or equal to" comparison, such as (a >= b).</span></span><br><span class="line">GreaterThanOrEqual = <span class="number">16</span>,</span><br><span class="line"><span class="comment">//</span></span><br><span class="line"><span class="comment">// 摘要:</span></span><br><span class="line"><span class="comment">// An operation that invokes a delegate or lambda expression, such as sampleDelegate.Invoke().</span></span><br><span class="line">Invoke = <span class="number">17</span>,</span><br><span class="line"><span class="comment">//</span></span><br><span class="line"><span class="comment">// 摘要:</span></span><br><span class="line"><span class="comment">// A lambda expression, such as a => a + a in C# or Function(a) a + a in Visual</span></span><br><span class="line"><span class="comment">// Basic.</span></span><br><span class="line">Lambda = <span class="number">18</span>,</span><br><span class="line"><span class="comment">//</span></span><br><span class="line"><span class="comment">// 摘要:</span></span><br><span class="line"><span class="comment">// A bitwise left-shift operation, such as (a << b).</span></span><br><span class="line">LeftShift = <span class="number">19</span>,</span><br><span class="line"><span class="comment">//</span></span><br><span class="line"><span class="comment">// 摘要:</span></span><br><span class="line"><span class="comment">// A "less than" comparison, such as (a < b).</span></span><br><span class="line">LessThan = <span class="number">20</span>,</span><br><span class="line"><span class="comment">//</span></span><br><span class="line"><span class="comment">// 摘要:</span></span><br><span class="line"><span class="comment">// A "less than or equal to" comparison, such as (a <= b).</span></span><br><span class="line">LessThanOrEqual = <span class="number">21</span>,</span><br><span class="line"><span class="comment">//</span></span><br><span class="line"><span class="comment">// 摘要:</span></span><br><span class="line"><span class="comment">// An operation that creates a new System.Collections.IEnumerable object and initializes</span></span><br><span class="line"><span class="comment">// it from a list of elements, such as new List<SampleType>(){ a, b, c } in C# or</span></span><br><span class="line"><span class="comment">// Dim sampleList = { a, b, c } in Visual Basic.</span></span><br><span class="line">ListInit = <span class="number">22</span>,</span><br><span class="line"><span class="comment">//</span></span><br><span class="line"><span class="comment">// 摘要:</span></span><br><span class="line"><span class="comment">// An operation that reads from a field or property, such as obj.SampleProperty.</span></span><br><span class="line">MemberAccess = <span class="number">23</span>,</span><br><span class="line"><span class="comment">//</span></span><br><span class="line"><span class="comment">// 摘要:</span></span><br><span class="line"><span class="comment">// An operation that creates a new object and initializes one or more of its members,</span></span><br><span class="line"><span class="comment">// such as new Point { X = 1, Y = 2 } in C# or New Point With {.X = 1, .Y = 2} in</span></span><br><span class="line"><span class="comment">// Visual Basic.</span></span><br><span class="line">MemberInit = <span class="number">24</span>,</span><br><span class="line"><span class="comment">//</span></span><br><span class="line"><span class="comment">// 摘要:</span></span><br><span class="line"><span class="comment">// An arithmetic remainder operation, such as (a % b) in C# or (a Mod b) in Visual</span></span><br><span class="line"><span class="comment">// Basic.</span></span><br><span class="line">Modulo = <span class="number">25</span>,</span><br><span class="line"><span class="comment">//</span></span><br><span class="line"><span class="comment">// 摘要:</span></span><br><span class="line"><span class="comment">// A multiplication operation, such as (a * b), without overflow checking, for numeric</span></span><br><span class="line"><span class="comment">// operands.</span></span><br><span class="line">Multiply = <span class="number">26</span>,</span><br><span class="line"><span class="comment">//</span></span><br><span class="line"><span class="comment">// 摘要:</span></span><br><span class="line"><span class="comment">// An multiplication operation, such as (a * b), that has overflow checking, for</span></span><br><span class="line"><span class="comment">// numeric operands.</span></span><br><span class="line">MultiplyChecked = <span class="number">27</span>,</span><br><span class="line"><span class="comment">//</span></span><br><span class="line"><span class="comment">// 摘要:</span></span><br><span class="line"><span class="comment">// An arithmetic negation operation, such as (-a). The object a should not be modified</span></span><br><span class="line"><span class="comment">// in place.</span></span><br><span class="line">Negate = <span class="number">28</span>,</span><br><span class="line"><span class="comment">//</span></span><br><span class="line"><span class="comment">// 摘要:</span></span><br><span class="line"><span class="comment">// A unary plus operation, such as (+a). The result of a predefined unary plus operation</span></span><br><span class="line"><span class="comment">// is the value of the operand, but user-defined implementations might have unusual</span></span><br><span class="line"><span class="comment">// results.</span></span><br><span class="line">UnaryPlus = <span class="number">29</span>,</span><br><span class="line"><span class="comment">//</span></span><br><span class="line"><span class="comment">// 摘要:</span></span><br><span class="line"><span class="comment">// An arithmetic negation operation, such as (-a), that has overflow checking. The</span></span><br><span class="line"><span class="comment">// object a should not be modified in place.</span></span><br><span class="line">NegateChecked = <span class="number">30</span>,</span><br><span class="line"><span class="comment">//</span></span><br><span class="line"><span class="comment">// 摘要:</span></span><br><span class="line"><span class="comment">// An operation that calls a constructor to create a new object, such as new SampleType().</span></span><br><span class="line">New = <span class="number">31</span>,</span><br><span class="line"><span class="comment">//</span></span><br><span class="line"><span class="comment">// 摘要:</span></span><br><span class="line"><span class="comment">// An operation that creates a new one-dimensional array and initializes it from</span></span><br><span class="line"><span class="comment">// a list of elements, such as new SampleType[]{a, b, c} in C# or New SampleType(){a,</span></span><br><span class="line"><span class="comment">// b, c} in Visual Basic.</span></span><br><span class="line">NewArrayInit = <span class="number">32</span>,</span><br><span class="line"><span class="comment">//</span></span><br><span class="line"><span class="comment">// 摘要:</span></span><br><span class="line"><span class="comment">// An operation that creates a new array, in which the bounds for each dimension</span></span><br><span class="line"><span class="comment">// are specified, such as new SampleType[dim1, dim2] in C# or New SampleType(dim1,</span></span><br><span class="line"><span class="comment">// dim2) in Visual Basic.</span></span><br><span class="line">NewArrayBounds = <span class="number">33</span>,</span><br><span class="line"><span class="comment">//</span></span><br><span class="line"><span class="comment">// 摘要:</span></span><br><span class="line"><span class="comment">// A bitwise complement or logical negation operation. In C#, it is equivalent to</span></span><br><span class="line"><span class="comment">// (~a) for integral types and to (!a) for Boolean values. In Visual Basic, it is</span></span><br><span class="line"><span class="comment">// equivalent to (Not a). The object a should not be modified in place.</span></span><br><span class="line">Not = <span class="number">34</span>,</span><br><span class="line"><span class="comment">//</span></span><br><span class="line"><span class="comment">// 摘要:</span></span><br><span class="line"><span class="comment">// An inequality comparison, such as (a != b) in C# or (a <> b) in Visual Basic.</span></span><br><span class="line">NotEqual = <span class="number">35</span>,</span><br><span class="line"><span class="comment">//</span></span><br><span class="line"><span class="comment">// 摘要:</span></span><br><span class="line"><span class="comment">// A bitwise or logical OR operation, such as (a | b) in C# or (a Or b) in Visual</span></span><br><span class="line"><span class="comment">// Basic.</span></span><br><span class="line">Or = <span class="number">36</span>,</span><br><span class="line"><span class="comment">//</span></span><br><span class="line"><span class="comment">// 摘要:</span></span><br><span class="line"><span class="comment">// A short-circuiting conditional OR operation, such as (a || b) in C# or (a OrElse</span></span><br><span class="line"><span class="comment">// b) in Visual Basic.</span></span><br><span class="line">OrElse = <span class="number">37</span>,</span><br><span class="line"><span class="comment">//</span></span><br><span class="line"><span class="comment">// 摘要:</span></span><br><span class="line"><span class="comment">// A reference to a parameter or variable that is defined in the context of the</span></span><br><span class="line"><span class="comment">// expression. For more information, see System.Linq.Expressions.ParameterExpression.</span></span><br><span class="line">Parameter = <span class="number">38</span>,</span><br><span class="line"><span class="comment">//</span></span><br><span class="line"><span class="comment">// 摘要:</span></span><br><span class="line"><span class="comment">// A mathematical operation that raises a number to a power, such as (a ^ b) in</span></span><br><span class="line"><span class="comment">// Visual Basic.</span></span><br><span class="line">Power = <span class="number">39</span>,</span><br><span class="line"><span class="comment">//</span></span><br><span class="line"><span class="comment">// 摘要:</span></span><br><span class="line"><span class="comment">// An expression that has a constant value of type System.Linq.Expressions.Expression.</span></span><br><span class="line"><span class="comment">// A System.Linq.Expressions.ExpressionType.Quote node can contain references to</span></span><br><span class="line"><span class="comment">// parameters that are defined in the context of the expression it represents.</span></span><br><span class="line">Quote = <span class="number">40</span>,</span><br><span class="line"><span class="comment">//</span></span><br><span class="line"><span class="comment">// 摘要:</span></span><br><span class="line"><span class="comment">// A bitwise right-shift operation, such as (a >> b).</span></span><br><span class="line">RightShift = <span class="number">41</span>,</span><br><span class="line"><span class="comment">//</span></span><br><span class="line"><span class="comment">// 摘要:</span></span><br><span class="line"><span class="comment">// A subtraction operation, such as (a - b), without overflow checking, for numeric</span></span><br><span class="line"><span class="comment">// operands.</span></span><br><span class="line">Subtract = <span class="number">42</span>,</span><br><span class="line"><span class="comment">//</span></span><br><span class="line"><span class="comment">// 摘要:</span></span><br><span class="line"><span class="comment">// An arithmetic subtraction operation, such as (a - b), that has overflow checking,</span></span><br><span class="line"><span class="comment">// for numeric operands.</span></span><br><span class="line">SubtractChecked = <span class="number">43</span>,</span><br><span class="line"><span class="comment">//</span></span><br><span class="line"><span class="comment">// 摘要:</span></span><br><span class="line"><span class="comment">// An explicit reference or boxing conversion in which null is supplied if the conversion</span></span><br><span class="line"><span class="comment">// fails, such as (obj as SampleType) in C# or TryCast(obj, SampleType) in Visual</span></span><br><span class="line"><span class="comment">// Basic.</span></span><br><span class="line">TypeAs = <span class="number">44</span>,</span><br><span class="line"><span class="comment">//</span></span><br><span class="line"><span class="comment">// 摘要:</span></span><br><span class="line"><span class="comment">// A type test, such as obj is SampleType in C# or TypeOf obj is SampleType in Visual</span></span><br><span class="line"><span class="comment">// Basic.</span></span><br><span class="line">TypeIs = <span class="number">45</span>,</span><br><span class="line"><span class="comment">//</span></span><br><span class="line"><span class="comment">// 摘要:</span></span><br><span class="line"><span class="comment">// An assignment operation, such as (a = b).</span></span><br><span class="line">Assign = <span class="number">46</span>,</span><br><span class="line"><span class="comment">//</span></span><br><span class="line"><span class="comment">// 摘要:</span></span><br><span class="line"><span class="comment">// A block of expressions.</span></span><br><span class="line">Block = <span class="number">47</span>,</span><br><span class="line"><span class="comment">//</span></span><br><span class="line"><span class="comment">// 摘要:</span></span><br><span class="line"><span class="comment">// Debugging information.</span></span><br><span class="line">DebugInfo = <span class="number">48</span>,</span><br><span class="line"><span class="comment">//</span></span><br><span class="line"><span class="comment">// 摘要:</span></span><br><span class="line"><span class="comment">// A unary decrement operation, such as (a - 1) in C# and Visual Basic. The object</span></span><br><span class="line"><span class="comment">// a should not be modified in place.</span></span><br><span class="line">Decrement = <span class="number">49</span>,</span><br><span class="line"><span class="comment">//</span></span><br><span class="line"><span class="comment">// 摘要:</span></span><br><span class="line"><span class="comment">// A dynamic operation.</span></span><br><span class="line">Dynamic = <span class="number">50</span>,</span><br><span class="line"><span class="comment">//</span></span><br><span class="line"><span class="comment">// 摘要:</span></span><br><span class="line"><span class="comment">// A default value.</span></span><br><span class="line">Default = <span class="number">51</span>,</span><br><span class="line"><span class="comment">//</span></span><br><span class="line"><span class="comment">// 摘要:</span></span><br><span class="line"><span class="comment">// An extension expression.</span></span><br><span class="line">Extension = <span class="number">52</span>,</span><br><span class="line"><span class="comment">//</span></span><br><span class="line"><span class="comment">// 摘要:</span></span><br><span class="line"><span class="comment">// A "go to" expression, such as goto Label in C# or GoTo Label in Visual Basic.</span></span><br><span class="line">Goto = <span class="number">53</span>,</span><br><span class="line"><span class="comment">//</span></span><br><span class="line"><span class="comment">// 摘要:</span></span><br><span class="line"><span class="comment">// A unary increment operation, such as (a + 1) in C# and Visual Basic. The object</span></span><br><span class="line"><span class="comment">// a should not be modified in place.</span></span><br><span class="line">Increment = <span class="number">54</span>,</span><br><span class="line"><span class="comment">//</span></span><br><span class="line"><span class="comment">// 摘要:</span></span><br><span class="line"><span class="comment">// An index operation or an operation that accesses a property that takes arguments.</span></span><br><span class="line">Index = <span class="number">55</span>,</span><br><span class="line"><span class="comment">//</span></span><br><span class="line"><span class="comment">// 摘要:</span></span><br><span class="line"><span class="comment">// A label.</span></span><br><span class="line">Label = <span class="number">56</span>,</span><br><span class="line"><span class="comment">//</span></span><br><span class="line"><span class="comment">// 摘要:</span></span><br><span class="line"><span class="comment">// A list of run-time variables. For more information, see System.Linq.Expressions.RuntimeVariablesExpression.</span></span><br><span class="line">RuntimeVariables = <span class="number">57</span>,</span><br><span class="line"><span class="comment">//</span></span><br><span class="line"><span class="comment">// 摘要:</span></span><br><span class="line"><span class="comment">// A loop, such as for or while.</span></span><br><span class="line">Loop = <span class="number">58</span>,</span><br><span class="line"><span class="comment">//</span></span><br><span class="line"><span class="comment">// 摘要:</span></span><br><span class="line"><span class="comment">// A switch operation, such as switch in C# or Select Case in Visual Basic.</span></span><br><span class="line">Switch = <span class="number">59</span>,</span><br><span class="line"><span class="comment">//</span></span><br><span class="line"><span class="comment">// 摘要:</span></span><br><span class="line"><span class="comment">// An operation that throws an exception, such as throw new Exception().</span></span><br><span class="line">Throw = <span class="number">60</span>,</span><br><span class="line"><span class="comment">//</span></span><br><span class="line"><span class="comment">// 摘要:</span></span><br><span class="line"><span class="comment">// A try-catch expression.</span></span><br><span class="line">Try = <span class="number">61</span>,</span><br><span class="line"><span class="comment">//</span></span><br><span class="line"><span class="comment">// 摘要:</span></span><br><span class="line"><span class="comment">// An unbox value type operation, such as unbox and unbox.any instructions in MSIL.</span></span><br><span class="line">Unbox = <span class="number">62</span>,</span><br><span class="line"><span class="comment">//</span></span><br><span class="line"><span class="comment">// 摘要:</span></span><br><span class="line"><span class="comment">// An addition compound assignment operation, such as (a += b), without overflow</span></span><br><span class="line"><span class="comment">// checking, for numeric operands.</span></span><br><span class="line">AddAssign = <span class="number">63</span>,</span><br><span class="line"><span class="comment">//</span></span><br><span class="line"><span class="comment">// 摘要:</span></span><br><span class="line"><span class="comment">// A bitwise or logical AND compound assignment operation, such as (a &= b) in C#.</span></span><br><span class="line">AndAssign = <span class="number">64</span>,</span><br><span class="line"><span class="comment">//</span></span><br><span class="line"><span class="comment">// 摘要:</span></span><br><span class="line"><span class="comment">// An division compound assignment operation, such as (a /= b), for numeric operands.</span></span><br><span class="line">DivideAssign = <span class="number">65</span>,</span><br><span class="line"><span class="comment">//</span></span><br><span class="line"><span class="comment">// 摘要:</span></span><br><span class="line"><span class="comment">// A bitwise or logical XOR compound assignment operation, such as (a ^= b) in C#.</span></span><br><span class="line">ExclusiveOrAssign = <span class="number">66</span>,</span><br><span class="line"><span class="comment">//</span></span><br><span class="line"><span class="comment">// 摘要:</span></span><br><span class="line"><span class="comment">// A bitwise left-shift compound assignment, such as (a <<= b).</span></span><br><span class="line">LeftShiftAssign = <span class="number">67</span>,</span><br><span class="line"><span class="comment">//</span></span><br><span class="line"><span class="comment">// 摘要:</span></span><br><span class="line"><span class="comment">// An arithmetic remainder compound assignment operation, such as (a %= b) in C#.</span></span><br><span class="line">ModuloAssign = <span class="number">68</span>,</span><br><span class="line"><span class="comment">//</span></span><br><span class="line"><span class="comment">// 摘要:</span></span><br><span class="line"><span class="comment">// A multiplication compound assignment operation, such as (a *= b), without overflow</span></span><br><span class="line"><span class="comment">// checking, for numeric operands.</span></span><br><span class="line">MultiplyAssign = <span class="number">69</span>,</span><br><span class="line"><span class="comment">//</span></span><br><span class="line"><span class="comment">// 摘要:</span></span><br><span class="line"><span class="comment">// A bitwise or logical OR compound assignment, such as (a |= b) in C#.</span></span><br><span class="line">OrAssign = <span class="number">70</span>,</span><br><span class="line"><span class="comment">//</span></span><br><span class="line"><span class="comment">// 摘要:</span></span><br><span class="line"><span class="comment">// A compound assignment operation that raises a number to a power, such as (a ^=</span></span><br><span class="line"><span class="comment">// b) in Visual Basic.</span></span><br><span class="line">PowerAssign = <span class="number">71</span>,</span><br><span class="line"><span class="comment">//</span></span><br><span class="line"><span class="comment">// 摘要:</span></span><br><span class="line"><span class="comment">// A bitwise right-shift compound assignment operation, such as (a >>= b).</span></span><br><span class="line">RightShiftAssign = <span class="number">72</span>,</span><br><span class="line"><span class="comment">//</span></span><br><span class="line"><span class="comment">// 摘要:</span></span><br><span class="line"><span class="comment">// A subtraction compound assignment operation, such as (a -= b), without overflow</span></span><br><span class="line"><span class="comment">// checking, for numeric operands.</span></span><br><span class="line">SubtractAssign = <span class="number">73</span>,</span><br><span class="line"><span class="comment">//</span></span><br><span class="line"><span class="comment">// 摘要:</span></span><br><span class="line"><span class="comment">// An addition compound assignment operation, such as (a += b), with overflow checking,</span></span><br><span class="line"><span class="comment">// for numeric operands.</span></span><br><span class="line">AddAssignChecked = <span class="number">74</span>,</span><br><span class="line"><span class="comment">//</span></span><br><span class="line"><span class="comment">// 摘要:</span></span><br><span class="line"><span class="comment">// A multiplication compound assignment operation, such as (a *= b), that has overflow</span></span><br><span class="line"><span class="comment">// checking, for numeric operands.</span></span><br><span class="line">MultiplyAssignChecked = <span class="number">75</span>,</span><br><span class="line"><span class="comment">//</span></span><br><span class="line"><span class="comment">// 摘要:</span></span><br><span class="line"><span class="comment">// A subtraction compound assignment operation, such as (a -= b), that has overflow</span></span><br><span class="line"><span class="comment">// checking, for numeric operands.</span></span><br><span class="line">SubtractAssignChecked = <span class="number">76</span>,</span><br><span class="line"><span class="comment">//</span></span><br><span class="line"><span class="comment">// 摘要:</span></span><br><span class="line"><span class="comment">// A unary prefix increment, such as (++a). The object a should be modified in place.</span></span><br><span class="line">PreIncrementAssign = <span class="number">77</span>,</span><br><span class="line"><span class="comment">//</span></span><br><span class="line"><span class="comment">// 摘要:</span></span><br><span class="line"><span class="comment">// A unary prefix decrement, such as (--a). The object a should be modified in place.</span></span><br><span class="line">PreDecrementAssign = <span class="number">78</span>,</span><br><span class="line"><span class="comment">//</span></span><br><span class="line"><span class="comment">// 摘要:</span></span><br><span class="line"><span class="comment">// A unary postfix increment, such as (a++). The object a should be modified in</span></span><br><span class="line"><span class="comment">// place.</span></span><br><span class="line">PostIncrementAssign = <span class="number">79</span>,</span><br><span class="line"><span class="comment">//</span></span><br><span class="line"><span class="comment">// 摘要:</span></span><br><span class="line"><span class="comment">// A unary postfix decrement, such as (a--). The object a should be modified in</span></span><br><span class="line"><span class="comment">// place.</span></span><br><span class="line">PostDecrementAssign = <span class="number">80</span>,</span><br><span class="line"><span class="comment">//</span></span><br><span class="line"><span class="comment">// 摘要:</span></span><br><span class="line"><span class="comment">// An exact type test.</span></span><br><span class="line">TypeEqual = <span class="number">81</span>,</span><br><span class="line"><span class="comment">//</span></span><br><span class="line"><span class="comment">// 摘要:</span></span><br><span class="line"><span class="comment">// A ones complement operation, such as (~a) in C#.</span></span><br><span class="line">OnesComplement = <span class="number">82</span>,</span><br><span class="line"><span class="comment">//</span></span><br><span class="line"><span class="comment">// 摘要:</span></span><br><span class="line"><span class="comment">// A true condition value.</span></span><br><span class="line">IsTrue = <span class="number">83</span>,</span><br><span class="line"><span class="comment">//</span></span><br><span class="line"><span class="comment">// 摘要:</span></span><br><span class="line"><span class="comment">// A false condition value.</span></span><br><span class="line">IsFalse = <span class="number">84</span></span><br><span class="line">}</span><br><span class="line">}</span><br></pre></td></tr></table></figure> </div></div> </div><h3 id="递归遍历表达式树"><a href="#递归遍历表达式树" class="headerlink" title="递归遍历表达式树"></a>递归遍历表达式树</h3><figure class="highlight csharp"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br><span class="line">25</span><br><span class="line">26</span><br><span class="line">27</span><br><span class="line">28</span><br><span class="line">29</span><br><span class="line">30</span><br><span class="line">31</span><br><span class="line">32</span><br><span class="line">33</span><br><span class="line">34</span><br><span class="line">35</span><br><span class="line">36</span><br><span class="line">37</span><br><span class="line">38</span><br><span class="line">39</span><br><span class="line">40</span><br><span class="line">41</span><br><span class="line">42</span><br><span class="line">43</span><br><span class="line">44</span><br><span class="line">45</span><br><span class="line">46</span><br><span class="line">47</span><br><span class="line">48</span><br><span class="line">49</span><br><span class="line">50</span><br><span class="line">51</span><br><span class="line">52</span><br><span class="line">53</span><br><span class="line">54</span><br><span class="line">55</span><br><span class="line">56</span><br><span class="line">57</span><br><span class="line">58</span><br><span class="line">59</span><br><span class="line">60</span><br><span class="line">61</span><br><span class="line">62</span><br><span class="line">63</span><br><span class="line">64</span><br><span class="line">65</span><br><span class="line">66</span><br><span class="line">67</span><br><span class="line">68</span><br><span class="line">69</span><br><span class="line">70</span><br><span class="line">71</span><br><span class="line">72</span><br><span class="line">73</span><br><span class="line">74</span><br><span class="line">75</span><br><span class="line">76</span><br><span class="line">77</span><br><span class="line">78</span><br><span class="line">79</span><br><span class="line">80</span><br><span class="line">81</span><br><span class="line">82</span><br><span class="line">83</span><br><span class="line">84</span><br><span class="line">85</span><br><span class="line">86</span><br><span class="line">87</span><br><span class="line">88</span><br><span class="line">89</span><br><span class="line">90</span><br><span class="line">91</span><br><span class="line">92</span><br><span class="line">93</span><br><span class="line">94</span><br><span class="line">95</span><br><span class="line">96</span><br><span class="line">97</span><br><span class="line">98</span><br><span class="line">99</span><br><span class="line">100</span><br><span class="line">101</span><br><span class="line">102</span><br></pre></td><td class="code"><pre><span class="line"><span class="keyword">using</span> System.Linq.Expressions;</span><br><span class="line"></span><br><span class="line"><span class="keyword">namespace</span> <span class="title">ExpressionTreeDemo</span></span><br><span class="line">{</span><br><span class="line"><span class="keyword">internal</span> <span class="keyword">class</span> <span class="title">Program</span></span><br><span class="line">{</span><br><span class="line"><span class="function"><span class="keyword">static</span> <span class="keyword">void</span> <span class="title">Main</span>(<span class="params"><span class="built_in">string</span>[] <span class="keyword">args</span></span>)</span></span><br><span class="line">{</span><br><span class="line"><span class="comment">// 构建示例表达式树:p => p.Age > 18 && p.Salary >= 10000m</span></span><br><span class="line">Expression<Func<Person, <span class="built_in">bool</span>>> expr = p => p.Age > <span class="number">18</span> && p.Salary >= <span class="number">10000</span>m;</span><br><span class="line"></span><br><span class="line">Console.WriteLine(<span class="string">"=== 递归遍历表达式树 ==="</span>);</span><br><span class="line">TraverseExpression(expr, depth: <span class="number">0</span>); <span class="comment">// depth 用于缩进,直观显示层级</span></span><br><span class="line">}</span><br><span class="line"></span><br><span class="line"><span class="comment"><span class="doctag">///</span> <span class="doctag"><summary></span></span></span><br><span class="line"><span class="comment"><span class="doctag">///</span> 递归遍历表达式树,打印所有节点信息</span></span><br><span class="line"><span class="comment"><span class="doctag">///</span> <span class="doctag"></summary></span></span></span><br><span class="line"><span class="comment"><span class="doctag">///</span> <span class="doctag"><param name="node"></span>当前要遍历的节点<span class="doctag"></param></span></span></span><br><span class="line"><span class="comment"><span class="doctag">///</span> <span class="doctag"><param name="depth"></span>当前节点的层级(用于缩进)<span class="doctag"></param></span></span></span><br><span class="line"><span class="function"><span class="keyword">static</span> <span class="keyword">void</span> <span class="title">TraverseExpression</span>(<span class="params">Expression node, <span class="built_in">int</span> depth</span>)</span></span><br><span class="line">{</span><br><span class="line"><span class="keyword">if</span> (node == <span class="literal">null</span>) <span class="keyword">return</span>;</span><br><span class="line"></span><br><span class="line"><span class="comment">// 缩进:层级越深,缩进越多(直观显示树形结构)</span></span><br><span class="line"><span class="built_in">string</span> indent = <span class="keyword">new</span> <span class="built_in">string</span>(<span class="string">' '</span>, depth * <span class="number">2</span>);</span><br><span class="line"></span><br><span class="line"><span class="built_in">string</span> nodeInfo = GetNodeInfo(node);</span><br><span class="line">Console.WriteLine(<span class="string">$"<span class="subst">{indent}</span>[<span class="subst">{depth}</span>] <span class="subst">{node.NodeType}</span> → <span class="subst">{nodeInfo}</span>"</span>);</span><br><span class="line"></span><br><span class="line"><span class="comment">// 根据节点类型,递归处理子节点</span></span><br><span class="line"><span class="keyword">switch</span> (node)</span><br><span class="line">{</span><br><span class="line"><span class="comment">// 1. 二元表达式(如 GreaterThan、AndAlso、OrElse)</span></span><br><span class="line"><span class="keyword">case</span> BinaryExpression binaryExpr:</span><br><span class="line">TraverseExpression(binaryExpr.Left, depth + <span class="number">1</span>); <span class="comment">// 递归左子节点</span></span><br><span class="line">TraverseExpression(binaryExpr.Right, depth + <span class="number">1</span>); <span class="comment">// 递归右子节点</span></span><br><span class="line"><span class="keyword">break</span>;</span><br><span class="line"></span><br><span class="line"><span class="comment">// 2. 属性访问表达式(如 p.Age、p.Salary)</span></span><br><span class="line"><span class="keyword">case</span> MemberExpression memberExpr:</span><br><span class="line">TraverseExpression(memberExpr.Expression, depth + <span class="number">1</span>);</span><br><span class="line"><span class="keyword">break</span>;</span><br><span class="line"></span><br><span class="line"><span class="comment">// 3. Lambda 表达式</span></span><br><span class="line"><span class="keyword">case</span> LambdaExpression lambdaExpr:</span><br><span class="line"><span class="keyword">foreach</span> (<span class="keyword">var</span> param <span class="keyword">in</span> lambdaExpr.Parameters)</span><br><span class="line">{</span><br><span class="line">TraverseExpression(param, depth + <span class="number">1</span>); <span class="comment">// 递归参数</span></span><br><span class="line">}</span><br><span class="line">TraverseExpression(lambdaExpr.Body, depth + <span class="number">1</span>); <span class="comment">// 递归表达式体</span></span><br><span class="line"><span class="keyword">break</span>;</span><br><span class="line"></span><br><span class="line"><span class="comment">// 4. 方法调用表达式</span></span><br><span class="line"><span class="keyword">case</span> MethodCallExpression methodExpr:</span><br><span class="line">TraverseExpression(methodExpr.Object, depth + <span class="number">1</span>); <span class="comment">// 递归调用对象(静态方法为 null)</span></span><br><span class="line"><span class="keyword">foreach</span> (<span class="keyword">var</span> arg <span class="keyword">in</span> methodExpr.Arguments)</span><br><span class="line">{</span><br><span class="line">TraverseExpression(arg, depth + <span class="number">1</span>); <span class="comment">// 递归所有参数</span></span><br><span class="line">}</span><br><span class="line"><span class="keyword">break</span>;</span><br><span class="line"></span><br><span class="line"><span class="comment">// 5. 常量表达式</span></span><br><span class="line"><span class="keyword">case</span> ConstantExpression _:</span><br><span class="line"><span class="keyword">break</span>;</span><br><span class="line"></span><br><span class="line"><span class="comment">// 6. 参数表达式</span></span><br><span class="line"><span class="keyword">case</span> ParameterExpression _:</span><br><span class="line"><span class="keyword">break</span>;</span><br><span class="line"></span><br><span class="line"><span class="comment">// 其他节点类型(如 UnaryExpression、NewExpression )</span></span><br><span class="line"><span class="literal">default</span>:</span><br><span class="line">Console.WriteLine(<span class="string">$"<span class="subst">{indent}</span>[<span class="subst">{depth}</span>] 未处理的节点类型:<span class="subst">{node.GetType().Name}</span>"</span>);</span><br><span class="line"><span class="keyword">break</span>;</span><br><span class="line">}</span><br><span class="line">}</span><br><span class="line"></span><br><span class="line"><span class="comment"><span class="doctag">///</span> <span class="doctag"><summary></span></span></span><br><span class="line"><span class="comment"><span class="doctag">///</span> 获取节点的关键信息</span></span><br><span class="line"><span class="comment"><span class="doctag">///</span> <span class="doctag"></summary></span></span></span><br><span class="line"><span class="function"><span class="keyword">static</span> <span class="built_in">string</span> <span class="title">GetNodeInfo</span>(<span class="params">Expression node</span>)</span></span><br><span class="line">{</span><br><span class="line"><span class="keyword">return</span> node <span class="keyword">switch</span></span><br><span class="line">{</span><br><span class="line">ParameterExpression param => <span class="string">$"参数:<span class="subst">{param.Name}</span>(类型:<span class="subst">{param.Type.Name}</span>)"</span>,</span><br><span class="line">ConstantExpression constant => <span class="string">$"常量:<span class="subst">{constant.Value}</span>(类型:<span class="subst">{constant.Type.Name}</span>)"</span>,</span><br><span class="line">MemberExpression member => <span class="string">$"属性:<span class="subst">{member.Member.Name}</span>(所属类型:<span class="subst">{member.Member.DeclaringType.Name}</span>)"</span>,</span><br><span class="line">BinaryExpression binary => <span class="string">$"二元运算:<span class="subst">{binary.NodeType}</span>(返回类型:<span class="subst">{binary.Type.Name}</span>)"</span>,</span><br><span class="line">LambdaExpression lambda => <span class="string">$"Lambda 表达式(参数数:<span class="subst">{lambda.Parameters.Count}</span>,返回类型:<span class="subst">{lambda.ReturnType.Name}</span>)"</span>,</span><br><span class="line">MethodCallExpression method => <span class="string">$"方法调用:<span class="subst">{method.Method.Name}</span>(参数数:<span class="subst">{method.Arguments.Count}</span>)"</span>,</span><br><span class="line">_ => <span class="string">$"类型:<span class="subst">{node.GetType().Name}</span>"</span></span><br><span class="line">};</span><br><span class="line">}</span><br><span class="line">}</span><br><span class="line"><span class="keyword">class</span> <span class="title">Person</span></span><br><span class="line">{</span><br><span class="line"><span class="keyword">public</span> <span class="built_in">string</span> Name { <span class="keyword">get</span>; <span class="keyword">set</span>; }</span><br><span class="line"><span class="keyword">public</span> <span class="built_in">int</span> Age { <span class="keyword">get</span>; <span class="keyword">set</span>; }</span><br><span class="line"><span class="keyword">public</span> <span class="built_in">decimal</span> Salary { <span class="keyword">get</span>; <span class="keyword">set</span>; }</span><br><span class="line">}</span><br><span class="line">}</span><br><span class="line"></span><br></pre></td></tr></table></figure><p><img src="/../images/%E5%B1%8F%E5%B9%95%E6%88%AA%E5%9B%BE%202025-11-24%20224959.png"></p><hr><h2 id="总结-1"><a href="#总结-1" class="headerlink" title="总结"></a>总结</h2><table><thead><tr><th>概念</th><th>说明</th></tr></thead><tbody><tr><td><strong>表达式树</strong></td><td>代码的树形数据结构,可以被分析和翻译</td></tr><tr><td><strong>Func<T></strong></td><td>可执行的委托,用于 LINQ to Objects</td></tr><tr><td><strong>Expression<Func<T>></strong></td><td>表达式树,用于 LINQ to SQL/EF</td></tr></tbody></table><h3 id="关键要点"><a href="#关键要点" class="headerlink" title="关键要点"></a>关键要点</h3><ol><li>表达式树是”代码的数据”,不是”可执行代码”</li><li><code>Expression<Func<T>></code> 可以被翻译成 SQL 或其他语言</li><li>手动构建表达式树适用于动态场景,但不要过度使用</li><li>优先使用 Lambda 表达式,保持代码简洁</li></ol><h3 id="推荐阅读"><a href="#推荐阅读" class="headerlink" title="推荐阅读"></a>推荐阅读</h3><ul><li><a href="https://learn.microsoft.com/zh-cn/dotnet/csharp/programming-guide/concepts/expression-trees/">Microsoft 官方文档:表达式树</a></li><li><a href="https://learn.microsoft.com/zh-cn/ef/core/">Entity Framework 如何工作</a></li></ul><hr>]]></content>
<summary type="html"><p>在 .NET 开发中,表达式树是一个强大但容易被忽视的特性。如果你使用过 Entity Framework 或 LINQ to SQL,其实你已经在间接使用表达式树了。</p></summary>
<category term="编程技术" scheme="https://probieluo.github.io/categories/%E7%BC%96%E7%A8%8B%E6%8A%80%E6%9C%AF/"/>
<category term="C#" scheme="https://probieluo.github.io/tags/C/"/>
<category term="LINQ" scheme="https://probieluo.github.io/tags/LINQ/"/>
</entry>
</feed>