<?xml version="1.0" encoding="UTF-8"?>
<rss version="2.0">
  <channel>
    <title>信息部技术社区</title>
    <link>https://xxb.lttc.cn/</link>
    <description>信息部技术社区社区最新发帖.</description>
    <language>en-us</language>
    <item>
      <title>正则表达式整理</title>
      <description>&lt;h2 id="正则表达式"&gt;正则表达式&lt;/h2&gt;&lt;h2 id="正则表达式到底是什么东西？"&gt;正则表达式到底是什么东西？&lt;/h2&gt;
&lt;p&gt;在编写处理字符串的程序或网页时，经常会有查找符合某些复杂规则的字符串的需要。&lt;strong&gt;正则表达式&lt;/strong&gt;就是用于描述这些规则的工具。换句话说，正则表达式就是记录文本规则的代码。&lt;/p&gt;

&lt;p&gt;很可能你使用过 Windows/Dos 下用于文件查找的&lt;strong&gt;通配符 (wildcard)&lt;/strong&gt;，也就是&lt;span style="color:blue;"&gt;*&lt;/span&gt;和&lt;span style="color:blue;"&gt;?&lt;/span&gt;。如果你想查找某个目录下的所有的 Word 文档的话，你会搜索&lt;span style="color:red;"&gt;*.doc&lt;/span&gt;。在这里，&lt;span style="color:blue;"&gt;*&lt;/span&gt;会被解释成任意的字符串。和通配符类似，正则表达式也是用来进行文本匹配的工具，只不过比起通配符，它能更精确地描述你的需求——当然，代价就是更复杂——比如你可以编写一个正则表达式，用来查找&lt;u&gt;所有以 0 开头，后面跟着 2-3 个数字，然后是一个连字号 “-”，最后是 7 或 8 位数字的字符串&lt;/u&gt;(像 010-12345678 或 0376-7654321)。&lt;/p&gt;
&lt;h2 id="入门"&gt;入门&lt;/h2&gt;
&lt;p&gt;学习正则表达式的最好方法是从例子开始，理解例子之后再自己对例子进行修改，实验。下面给出了不少简单的例子，并对它们作了详细的说明。&lt;/p&gt;

&lt;p&gt;假设你在一篇英文小说里查找&lt;u&gt;hi&lt;/u&gt;，你可以使用正则表达式&lt;span style="color:red;"&gt;hi&lt;/span&gt;。&lt;/p&gt;

&lt;p&gt;这几乎是最简单的正则表达式了，它可以精确匹配这样的字符串：&lt;u&gt;由两个字符组成，前一个字符是 h,后一个是 i&lt;/u&gt;。通常，处理正则表达式的工具会提供一个忽略大小写的选项，如果选中了这个选项，它可以匹配 hi,HI,Hi,hI 这四种情况中的任意一种。&lt;/p&gt;

&lt;p&gt;不幸的是，很多单词里包含 hi 这两个连续的字符，比如 him,history,high 等等。用&lt;span style="color:red;"&gt;hi&lt;/span&gt;来查找的话，这里边的 hi 也会被找出来。如果要精确地查找 hi 这个单词的话，我们应该使用&lt;span style="color:red;"&gt;\bhi\b&lt;/span&gt;。&lt;/p&gt;

&lt;p&gt;\b 是正则表达式规定的一个特殊代码（某些人叫它&lt;strong&gt;元字符&lt;/strong&gt;，&lt;strong&gt;metacharacter&lt;/strong&gt;），代表着&lt;u&gt;单词的开头或结尾，也就是单词的分界处&lt;/u&gt;。虽然通常英文的单词是由空格，标点符号或者换行来分隔的，但是&lt;span style="color:blue;"&gt;\b&lt;/span&gt;并不匹配这些单词分隔字符中的任何一个，它&lt;strong&gt;只匹配一个位置&lt;/strong&gt;。&lt;/p&gt;

&lt;p&gt;假如你要找的是&lt;u&gt;hi 后面不远处跟着一个 Lucy&lt;/u&gt;，你应该用&lt;span style="color:red;"&gt;\bhi\b.*\bLucy\b&lt;/span&gt;。&lt;/p&gt;

&lt;p&gt;这里，.是另一个元字符，匹配除了&lt;u&gt;换行符以外的任意字符&lt;/u&gt;。* 同样是元字符，不过它代表的不是字符，也不是位置，而是数量——它指定 *&lt;u&gt;前边的内容可以连续重复使用任意次以使整个表达式得到匹配&lt;/u&gt;.。因此，.* 连在一起就意味着任意数量的不包含换行的字符。现在&lt;span style="color:red;"&gt;\bhi\b._\bLucy\b&lt;/span&gt;的意思就很明显了：&lt;u&gt;先是一个单词 hi,然后是任意个任意字符 (但不能是换行)，最后是 Lucy 这个单词&lt;/u&gt;。&lt;/p&gt;

&lt;p&gt;如果同时使用其它元字符，我们就能构造出功能更强大的正则表达式。比如下面这个例子：&lt;/p&gt;

&lt;p&gt;&lt;span style="color:red;"&gt;0\d\d-\d\d\d\d\d\d\d\d&lt;/span&gt;匹配这样的字符串：&lt;u&gt;以 0 开头，然后是两个数字，然后是一个连字号 “-”，最后是 8 个数字&lt;/u&gt;(也就是中国的电话号码。当然，这个例子只能匹配区号为 3 位的情形)。&lt;/p&gt;

&lt;p&gt;这里的&lt;span style="color:green;"&gt;\d&lt;/span&gt;是个新的元字符，匹配&lt;u&gt;一位数字 (0，或 1，或 2，或……&lt;/u&gt;)。-不是元字符，只匹配它本身——连字符 (或者减号，或者中横线，或者随你怎么称呼它)。&lt;/p&gt;

&lt;p&gt;为了避免那么多烦人的重复，我们也可以这样写这个表达式：&lt;span style="color:red;"&gt;0\d{2}-\d{8}&lt;/span&gt;。这里&lt;span style="color:green;"&gt;\d&lt;/span&gt;后面的&lt;span style="color:green;"&gt;{2}&lt;/span&gt;(&lt;span style="color:green;"&gt;{8}&lt;/span&gt;) 的意思是前面&lt;span style="color:green;"&gt;\d&lt;/span&gt;&lt;u&gt;必须连续重复匹配 2 次 (8 次)&lt;/u&gt;。&lt;/p&gt;
&lt;h2 id="元字符"&gt;元字符&lt;/h2&gt;
&lt;p&gt;现在你已经知道几个很有用的元字符了，如&lt;span style="color:blue;"&gt;\b,.,*&lt;/span&gt;，还有&lt;span style="color:blue;"&gt;\d&lt;/span&gt;.正则表达式里还有更多的元字符，比如&lt;span style="color:blue;"&gt;\s&lt;/span&gt;匹配&lt;u&gt;任意的空白符，包括空格，制表符 (Tab)，换行符，中文全角空格&lt;/u&gt;等。&lt;span style="color:blue;"&gt;\w&lt;/span&gt;匹配&lt;u&gt;字母或数字或下划线或汉字&lt;/u&gt;等。&lt;/p&gt;

&lt;p&gt;下面来看看更多的例子：&lt;/p&gt;

&lt;p&gt;&lt;span style="color:red;"&gt;\ba\w*\b&lt;/span&gt;匹配以字母&lt;u&gt;&lt;span style="color:green;"&gt;a&lt;/span&gt;开头的单词——先是某个单词开始处 (&lt;span style="color:green;"&gt;\b&lt;/span&gt;)，然后是字母 a,然后是任意数量的字母或数字 (&lt;span style="color:green;"&gt;\w*&lt;/span&gt;)，最后是单词结束处 (&lt;span style="color:green;"&gt;\b&lt;/span&gt;)&lt;/u&gt;。&lt;/p&gt;

&lt;p&gt;&lt;span style="color:red;"&gt;\d+&lt;/span&gt;匹配&lt;u&gt;1 个或更多连续的数字&lt;/u&gt;。这里的&lt;span style="color:green;"&gt;+&lt;/span&gt;是和&lt;span style="color:green;"&gt;*&lt;/span&gt;类似的元字符，不同的是&lt;span style="color:blue;"&gt;*&lt;/span&gt;&lt;u&gt;匹配重复任意次 (可能是 0 次)&lt;/u&gt;，而&lt;span style="color:blue;"&gt;+&lt;/span&gt;则匹配&lt;u&gt;重复 1 次或更多次&lt;/u&gt;。&lt;/p&gt;

&lt;p&gt;&lt;span style="color:red;"&gt;\b\w{6}\b&lt;/span&gt; 匹配&lt;u&gt;刚好 6 个字符的单词&lt;/u&gt;。&lt;/p&gt;

&lt;p&gt;好吧，现在我们说说正则表达式里的单词是什么意思吧：就是不少于一个的连续的\w。不错，这与学习英文时要背的成千上万个同名的东西的确关系不大 :)&lt;/p&gt;

&lt;p&gt;表 1.常用的元字符&lt;/p&gt;
&lt;table class="table table-bordered table-striped"&gt;
&lt;tr&gt;
&lt;th&gt;代码&lt;/th&gt;
&lt;th&gt;说明&lt;/th&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;.&lt;/td&gt;
&lt;td&gt;匹配除换行符以外的任意字符&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;\w&lt;/td&gt;
&lt;td&gt;匹配字母或数字或下划线或汉字&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;\s&lt;/td&gt;
&lt;td&gt;匹配任意的空白符&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;\d&lt;/td&gt;
&lt;td&gt;匹配数字&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;\b&lt;/td&gt;
&lt;td&gt;匹配单词的开始或结束&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;^&lt;/td&gt;
&lt;td&gt;匹配字符串的开始&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;$&lt;/td&gt;
&lt;td&gt;匹配字符串的结束&lt;/td&gt;
&lt;/tr&gt;
&lt;/table&gt;
&lt;p&gt;元字符&lt;span style="color:blue;"&gt;^&lt;/span&gt;（和数字 6 在同一个键位上的符号）和&lt;span style="color:blue;"&gt;\$&lt;/span&gt;都匹配一个位置，这和&lt;span style="color:blue;"&gt;\b&lt;/span&gt;有点类似。&lt;span style="color:blue;"&gt;^&lt;/span&gt;匹配你要用来查找的字符串的开头，&lt;span style="color:blue;"&gt;\$&lt;/span&gt;匹配结尾。这两个代码在验证输入的内容时非常有用，比如一个网站如果要求你填写的 QQ 号必须为 5 位到 12 位数字时，可以使用：&lt;span style="color:red;"&gt;^\d{5,12}\$&lt;/span&gt;。&lt;/p&gt;

&lt;p&gt;这里的&lt;span style="color:green;"&gt;{5,12}&lt;/span&gt;和前面介绍过的&lt;span style="color:green;"&gt;{2}&lt;/span&gt;是类似的，只不过&lt;span style="color:green;"&gt;{2}&lt;/span&gt;匹配&lt;u&gt;只能不多不少重复 2 次&lt;/u&gt;，&lt;span style="color:green;"&gt;{5,12}&lt;/span&gt;则是&lt;u&gt;重复的次数不能少于 5 次，不能多于 12 次&lt;/u&gt;，否则都不匹配。&lt;/p&gt;

&lt;p&gt;因为使用了&lt;span style="color:green;"&gt;^&lt;/span&gt;和&lt;span style="color:green;"&gt;\$&lt;/span&gt;，所以输入的整个字符串都要用来和&lt;span style="color:green;"&gt;\d{5,12}&lt;/span&gt;来匹配，也就是说整个输入&lt;u&gt;必须是 5 到 12 个数字&lt;/u&gt;，因此如果输入的 QQ 号能匹配这个正则表达式的话，那就符合要求了。&lt;/p&gt;

&lt;p&gt;和忽略大小写的选项类似，有些正则表达式处理工具还有一个处理多行的选项。如果选中了这个选项，&lt;span style="color:blue;"&gt;^&lt;/span&gt;和&lt;span style="color:blue;"&gt;\$&lt;/span&gt;的意义就变成了&lt;u&gt;匹配行的开始处和结束处&lt;/u&gt;。&lt;/p&gt;
&lt;h2 id="字符转义"&gt;字符转义&lt;/h2&gt;
&lt;p&gt;如果你想查找元字符本身的话，比如你查找.,或者 *,就出现了问题：你没办法指定它们，因为它们会被解释成别的意思。这时你就得使用\来取消这些字符的特殊意义。因此，你应该使用&lt;span style="color:red;"&gt;\.&lt;/span&gt;和&lt;span style="color:red;"&gt;\*&lt;/span&gt;。当然，要查找\本身，你也得用&lt;span style="color:red;"&gt;\\&lt;/span&gt;.&lt;/p&gt;

&lt;p&gt;例如：&lt;span style="color:red;"&gt;deerchao\.cn&lt;/span&gt;匹配&lt;u&gt;deerchao.cn&lt;/u&gt;，&lt;span style="color:red;"&gt;C:\\Windows&lt;/span&gt;匹配&lt;u&gt;C:\Windows&lt;/u&gt;。&lt;/p&gt;
&lt;h2 id="重复"&gt;重复&lt;/h2&gt;
&lt;p&gt;你已经看过了前面的&lt;span style="color:blue;"&gt;&lt;em&gt;,+,{2},{5,12}&lt;/em&gt;&lt;/span&gt;这几个匹配重复的方式了。下面是正则表达式中所有的限定符 (指定数量的代码，例如,{5,12}等)：&lt;/p&gt;

&lt;p&gt;表 2.常用的限定符&lt;/p&gt;
&lt;table class="table table-bordered table-striped"&gt;
&lt;tr&gt;
&lt;th&gt;代码/语法&lt;/th&gt;
&lt;th&gt;说明&lt;/th&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;*&lt;/td&gt;
&lt;td&gt;重复零次或更多次&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;+&lt;/td&gt;
&lt;td&gt;重复一次或更多次&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;?&lt;/td&gt;
&lt;td&gt;重复零次或一次&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;{n}&lt;/td&gt;
&lt;td&gt;重复 n 次&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;{n,}&lt;/td&gt;
&lt;td&gt;重复 n 次或更多次&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;{n,m}&lt;/td&gt;
&lt;td&gt;重复 n 到 m 次&lt;/td&gt;
&lt;/tr&gt;
&lt;/table&gt;
&lt;p&gt;下面是一些使用重复的例子：&lt;/p&gt;

&lt;p&gt;&lt;span style="color:red;"&gt;Windows\d+&lt;/span&gt;匹配&lt;u&gt;Windows 后面跟 1 个或更多数字&lt;/u&gt;&lt;/p&gt;

&lt;p&gt;&lt;span style="color:red;"&gt;^\w+&lt;/span&gt;匹配&lt;u&gt;一行的第一个单词 (或整个字符串的第一个单词，具体匹配哪个意思得看选项设置)&lt;/u&gt;&lt;/p&gt;
&lt;h2 id="字符类"&gt;字符类&lt;/h2&gt;
&lt;p&gt;要想查找数字，字母或数字，空白是很简单的，因为已经有了对应这些字符集合的元字符，但是如果你想匹配没有预定义元字符的字符集合 (比如元音字母 a,e,i,o,u),应该怎么办？&lt;/p&gt;

&lt;p&gt;很简单，你只需要在方括号里列出它们就行了，像&lt;span style="color:red;"&gt;[aeiou]&lt;/span&gt;就匹配任何一个英文元音字母，&lt;span style="color:red;"&gt;[.?!]&lt;/span&gt;匹配&lt;u&gt;标点符号 (.或?或!)&lt;/u&gt;。&lt;/p&gt;

&lt;p&gt;我们也可以轻松地指定一个字符&lt;strong&gt;范围&lt;/strong&gt;，像&lt;span style="color:red;"&gt;[0-9]&lt;/span&gt;代表的含意与&lt;span style="color:red;"&gt;\d&lt;/span&gt;就是完全一致的：&lt;u&gt;一位数字&lt;/u&gt;；同理&lt;span style="color:red;"&gt;[a-z0-9A-Z_]&lt;/span&gt;也完全等同于&lt;span style="color:blue;"&gt;\w&lt;/span&gt;（如果只考虑英文的话）。&lt;/p&gt;

&lt;p&gt;下面是一个更复杂的表达式：&lt;span style="color:red;"&gt;\(?0\d{2}[) -]?\d{8}&lt;/span&gt;。&lt;/p&gt;

&lt;p&gt;这个表达式可以匹配几种格式的电话号码，像 (010) 88886666，或 022-22334455，或 02912345678 等。我们对它进行一些分析吧：首先是一个转义字符&lt;span style="color:green;"&gt;\(&lt;/span&gt;,它能出现 0 次或 1 次 (&lt;span style="color:green;"&gt;?&lt;/span&gt;),然后是一个&lt;span style="color:green;"&gt;0&lt;/span&gt;，后面跟着 2 个数字 (&lt;span style="color:green;"&gt;\d{2}&lt;/span&gt;)，然后是&lt;span style="color:green;"&gt;)&lt;/span&gt;或&lt;span style="color:green;"&gt;-&lt;/span&gt;或&lt;span style="color:green;"&gt;空格&lt;/span&gt;中的一个，它出现 1 次或不出现 (&lt;span style="color:green;"&gt;?&lt;/span&gt;)，最后是 8 个数字 (&lt;span style="color:green;"&gt;\d{8}&lt;/span&gt;)。&lt;/p&gt;
&lt;h2 id="分枝条件"&gt;分枝条件&lt;/h2&gt;
&lt;p&gt;不幸的是，刚才那个表达式也能匹配 010) 12345678 或 (022-87654321 这样的 “不正确” 的格式。要解决这个问题，我们需要用到&lt;strong&gt;分枝条件&lt;/strong&gt;。正则表达式里的&lt;strong&gt;分枝条件&lt;/strong&gt;指的是有几种规则，如果满足其中任意一种规则都应该当成匹配，具体方法是用&lt;span style="color:blue;"&gt;|&lt;/span&gt;把不同的规则分隔开。听不明白？没关系，看例子：&lt;/p&gt;

&lt;p&gt;&lt;span style="color:red;"&gt;0\d{2}-\d{8}|0\d{3}-\d{7}&lt;/span&gt;这个表达式能匹配&lt;u&gt;两种以连字号分隔的电话号码：一种是三位区号，8 位本地号 (如 010-12345678)，一种是 4 位区号，7 位本地号 (0376-2233445)&lt;/u&gt;。&lt;/p&gt;

&lt;p&gt;&lt;span style="color:red;"&gt;\(0\d{2}\)[- ]?\d{8}|0\d{2}[- ]?\d{8}&lt;/span&gt;这个表达式&lt;u&gt;匹配 3 位区号的电话号码，其中区号可以用小括号括起来，也可以不用，区号与本地号间可以用连字号或空格间隔，也可以没有间隔&lt;/u&gt;。你可以试试用分枝条件把这个表达式扩展成也支持 4 位区号的。&lt;/p&gt;

&lt;p&gt;&lt;span style="color:red;"&gt;\d{5}-\d{4}|\d{5}&lt;/span&gt;这个表达式用于匹配美国的邮政编码。美国邮编的规则是 5 位数字，或者用连字号间隔的 9 位数字。之所以要给出这个例子是因为它能说明一个问题：&lt;strong&gt;使用分枝条件时，要注意各个条件的顺序&lt;/strong&gt;。如果你把它改成&lt;span style="color:red;"&gt;\d{5}|\d{5}-\d{4}&lt;/span&gt;的话，那么就只会匹配 5 位的邮编 (以及 9 位邮编的前 5 位)。原因是匹配分枝条件时，将会从左到右地测试每个条件，如果满足了某个分枝的话，就不会去再管其它的条件了。&lt;/p&gt;
&lt;h2 id="分组"&gt;分组&lt;/h2&gt;
&lt;p&gt;我们已经提到了怎么重复单个字符（直接在字符后面加上限定符就行了）；但如果想要重复多个字符又该怎么办？你可以用小括号来指定子&lt;strong&gt;表达式&lt;/strong&gt;(也叫做&lt;strong&gt;分组&lt;/strong&gt;)，然后你就可以指定这个子表达式的重复次数了，你也可以对子表达式进行其它一些操作 (后面会有介绍)。&lt;/p&gt;

&lt;p&gt;&lt;span style="color:red;"&gt;(\d{1,3}\.){3}\d{1,3}&lt;/span&gt;是一个&lt;u&gt;简单的 IP 地址匹配&lt;/u&gt;表达式。要理解这个表达式，请按下列顺序分析它：&lt;span style="color:green;"&gt;\d{1,3}&lt;/span&gt;匹配 1 到 3 位的数字，&lt;span style="color:green;"&gt;(\d{1,3}\.){3}&lt;/span&gt;匹配&lt;u&gt;三位数字加上一个英文句号 (这个整体也就是这个&lt;strong&gt;分组&lt;/strong&gt;) 重复 3 次&lt;/u&gt;，最后再加上&lt;u&gt;一个一到三位的数字&lt;/u&gt;(&lt;span style="color:red;"&gt;\d{1,3}&lt;/span&gt;)。&lt;/p&gt;

&lt;p&gt;不幸的是，它也将匹配 256.300.888.999 这种不可能存在的 IP 地址。如果能使用算术比较的话，或许能简单地解决这个问题，但是正则表达式中并不提供关于数学的任何功能，所以只能使用冗长的分组，选择，字符类来描述一个正确的 IP 地址：&lt;span style="color:red;"&gt;((2[0-4]\d|25[0-5]|[01]?\d\d?)\.){3}(2[0-4]\d|25[0-5]|[01]?\d\d?)&lt;/span&gt;。&lt;/p&gt;

&lt;p&gt;理解这个表达式的关键是理解&lt;span style="color:green;"&gt;2[0-4]\d|25[0-5]|[01]?\d\d?&lt;/span&gt;，这里我就不细说了，你自己应该能分析得出来它的意义。&lt;/p&gt;
&lt;h2 id="反义"&gt;反义&lt;/h2&gt;
&lt;p&gt;有时需要查找不属于某个能简单定义的字符类的字符。比如想查找除了数字以外，其它任意字符都行的情况，这时需要用到&lt;strong&gt;反义&lt;/strong&gt;：&lt;/p&gt;

&lt;p&gt;表 3.常用的反义代码&lt;/p&gt;
&lt;table class="table table-bordered table-striped"&gt;
&lt;tr&gt;
&lt;th&gt;代码/语法&lt;/th&gt;
&lt;th&gt;说明&lt;/th&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;\W&lt;/td&gt;
&lt;td&gt;匹配任意不是字母，数字，下划线，汉字的字符&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;\S&lt;/td&gt;
&lt;td&gt;匹配任意不是空白符的字符&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;\D&lt;/td&gt;
&lt;td&gt;匹配任意非数字的字符&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;\B&lt;/td&gt;
&lt;td&gt;匹配不是单词开头或结束的位置&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;[^x]&lt;/td&gt;
&lt;td&gt;匹配除了 x 以外的任意字符&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;[^aeiou]&lt;/td&gt;
&lt;td&gt;匹配除了 aeiou 这几个字母以外的任意字符&lt;/td&gt;
&lt;/tr&gt;
&lt;/table&gt;
&lt;p&gt;例子：&lt;span style="color:red;"&gt;\S+&lt;/span&gt;匹配&lt;u&gt;不包含空白符的字符串&lt;/u&gt;。&lt;/p&gt;

&lt;p&gt;&lt;span style="color:red;"&gt;&lt;a&gt;]+&amp;gt;&lt;/a&gt;&lt;/span&gt;匹配&lt;u&gt;用尖括号括起来的以 a 开头的字符串&lt;/u&gt;。&lt;/p&gt;
&lt;h2 id="后向引用"&gt;后向引用&lt;/h2&gt;
&lt;p&gt;使用小括号指定一个子表达式后，&lt;strong&gt;匹配这个子表达式的文本&lt;/strong&gt;(也就是此分组捕获的内容) 可以在表达式或其它程序中作进一步的处理。默认情况下，每个分组会自动拥有一个&lt;strong&gt;组号&lt;/strong&gt;，规则是：从左向右，以分组的左括号为标志，第一个出现的分组的组号为 1，第二个为 2，以此类推。&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;后向引用&lt;/strong&gt;用于重复搜索前面某个分组匹配的文本。例如，&lt;span style="color:green;"&gt;\1&lt;/span&gt;代表分组 1 匹配的文本。难以理解？请看示例：&lt;/p&gt;

&lt;p&gt;&lt;span style="color:red;"&gt;\b(\w+)\b\s+\1\b&lt;/span&gt;可以用来匹配重复的单词，像 go go, 或者 kitty kitty。这个表达式首先是&lt;u&gt;一个单词&lt;/u&gt;，也就是&lt;u&gt;单词开始处和结束处之间的多于一个的字母或数字&lt;/u&gt;(&lt;span style="color:green;"&gt;\b(\w+)\b&lt;/span&gt;)，这个单词会被捕获到编号为 1 的分组中，然后是&lt;u&gt;1 个或几个空白符 (&lt;span style="color:green;"&gt;\s+&lt;/span&gt;)，最后是&lt;u&gt;分组 1 中捕获的内容（也就是前面匹配的那个单词）&lt;/u&gt;(&lt;span style="color:green;"&gt;\1&lt;/span&gt;)。&lt;/u&gt;&lt;/p&gt;

&lt;p&gt;你也可以自己指定子表达式的&lt;strong&gt;组名&lt;/strong&gt;。要指定一个子表达式的组名，请使用这样的语法：&lt;span style="color:blue;"&gt;(?\w+)&lt;/span&gt;(或者把尖括号换成'也行：&lt;span style="color:blue;"&gt;(?'Word'\w+))&lt;/span&gt;,这样就把&lt;span style="color:green;"&gt;\w+&lt;/span&gt;的组名指定为&lt;span style="color:green;"&gt;Word&lt;/span&gt;了。要反向引用这个分组&lt;strong&gt;捕获&lt;/strong&gt;的内容，你可以使用\k,所以上一个例子也可以写成这样：&lt;span style="color:red;"&gt;\b(?\w+)\b\s+\k\b&lt;/span&gt;。&lt;/p&gt;

&lt;p&gt;使用小括号的时候，还有很多特定用途的语法。下面列出了最常用的一些：&lt;/p&gt;

&lt;p&gt;表 4.常用分组语法&lt;/p&gt;

&lt;table&gt;
    &lt;tr&gt;
        &lt;td&gt;分类&lt;/td&gt;
        &lt;td&gt;代码/语法&lt;/td&gt;
        &lt;td&gt;说明&lt;/td&gt;
    &lt;/tr&gt;
    &lt;tr&gt;
        &lt;td&gt;捕获&lt;/td&gt;
        &lt;td&gt;(exp)&lt;/td&gt;
        &lt;td&gt;匹配 exp,并捕获文本到自动命名的组里&lt;/td&gt;
    &lt;/tr&gt;
    &lt;tr&gt;
        &lt;td&gt;(?&amp;lt;name&amp;gt;exp)&lt;/td&gt;
        &lt;td&gt;匹配 exp,并捕获文本到名称为 name 的组里，也可以写成 (?'name'exp)&lt;/td&gt;
    &lt;/tr&gt;
    &lt;tr&gt;
        &lt;td&gt;(?:exp)&lt;/td&gt;
        &lt;td&gt;匹配 exp,不捕获匹配的文本，也不给此分组分配组号&lt;/td&gt;
    &lt;/tr&gt;
    &lt;tr&gt;
        &lt;td&gt;零宽断言&lt;/td&gt;
        &lt;td&gt;(?=exp)&lt;/td&gt;
        &lt;td&gt;匹配 exp 前面的位置&lt;/td&gt;
    &lt;/tr&gt;
    &lt;tr&gt;
        &lt;td&gt;(?&amp;lt;=exp)&lt;/td&gt;
        &lt;td&gt;匹配 exp 后面的位置&lt;/td&gt;
    &lt;/tr&gt;
    &lt;tr&gt;
        &lt;td&gt;(?! exp)&lt;/td&gt;
        &lt;td&gt;匹配后面跟的不是 exp 的位置&lt;/td&gt;
    &lt;/tr&gt;
    &lt;tr&gt;
        &lt;td&gt;(?&amp;lt;! exp)&lt;/td&gt;
        &lt;td&gt;匹配前面不是 exp 的位置&lt;/td&gt;
    &lt;/tr&gt;
    &lt;tr&gt;
        &lt;td&gt;注释&lt;/td&gt;
        &lt;td&gt;(?#comment)&lt;/td&gt;
        &lt;td&gt;这种类型的分组不对正则表达式的处理产生任何影响，用于提供注释让人阅读&lt;/td&gt;
    &lt;/tr&gt;
&lt;/table&gt;

&lt;p&gt;我们已经讨论了前两种语法。第三个&lt;span style="color:green;"&gt;(?:exp)&lt;/span&gt;不会改变正则表达式的处理方式，只是这样的组匹配的内容&lt;u&gt;不会像前两种那样被捕获到某个组里面，也不会拥有组号&lt;/u&gt;。“我为什么会想要这样做？”——好问题，你觉得为什么呢？&lt;/p&gt;
&lt;h2 id="零宽断言"&gt;零宽断言&lt;/h2&gt;
&lt;p&gt;接下来的四个用于查找在某些内容 (但并不包括这些内容) 之前或之后的东西，也就是说它们像&lt;span style="color:blue;"&gt;\b,^,\$&lt;/span&gt;那样用于指定一个位置，这个位置应该满足一定的条件 (即断言)，因此它们也被称为&lt;strong&gt;零宽断言&lt;/strong&gt;。最好还是拿例子来说明吧：&lt;/p&gt;

&lt;p&gt;&lt;span style="color:blue;"&gt;(?=exp)&lt;/span&gt;也叫&lt;strong&gt;零宽度正预测先行断言&lt;/strong&gt;，它&lt;u&gt;断言自身出现的位置的后面能匹配表达式 exp&lt;/u&gt;。比如&lt;span style="color:red;"&gt;\b\w+(?=ing\b)&lt;/span&gt;，匹配&lt;u&gt;以 ing 结尾的单词的前面部分 (除了 ing 以外的部分)&lt;/u&gt;，如查找 I'm singing while you're dancing.时，它会匹配&lt;u&gt;sing&lt;/u&gt;和&lt;u&gt;danc&lt;/u&gt;。&lt;/p&gt;

&lt;p&gt;&lt;span style="color:blue;"&gt;(?&amp;lt;=exp)&lt;/span&gt;也叫&lt;strong&gt;零宽度正回顾后发断言&lt;/strong&gt;，它&lt;u&gt;断言自身出现的位置的前面能匹配表达式 exp&lt;/u&gt;。比如&lt;span style="color:red;"&gt;(?&amp;lt;=\bre)\w+\b&lt;/span&gt;会匹配&lt;u&gt;以 re 开头的单词的后半部分 (除了 re 以外的部分&lt;/u&gt;)，例如在查找 reading a book 时，它匹配&lt;u&gt;ading&lt;/u&gt;。&lt;/p&gt;

&lt;p&gt;假如你想要给一个很长的数字中每三位间加一个逗号 (当然是从右边加起了)，你可以这样查找需要在前面和里面添加逗号的部分：&lt;span style="color:red;"&gt;((?&amp;lt;=\d)\d{3})+\b&lt;/span&gt;，用它对 1234567890 进行查找时结果是&lt;u&gt;234567890&lt;/u&gt;。&lt;/p&gt;

&lt;p&gt;下面这个例子同时使用了这两种断言：&lt;span style="color:red;"&gt;(?&amp;lt;=\s)\d+(?=\s)&lt;/span&gt;匹配以空白符间隔的数字 (&lt;u&gt;再次强调，不包括这些空白符&lt;/u&gt;)。&lt;/p&gt;
&lt;h2 id="负向零宽断言"&gt;负向零宽断言&lt;/h2&gt;
&lt;p&gt;前面我们提到过怎么查找&lt;strong&gt;不是某个字符或不在某个字符类里&lt;/strong&gt;的字符的方法 (反义)。但是如果我们只是想要&lt;strong&gt;确保某个字符没有出现，但并不想去匹配它&lt;/strong&gt;时怎么办？例如，如果我们想查找这样的单词 -- 它里面出现了字母 q,但是 q 后面跟的不是字母 u,我们可以尝试这样：&lt;/p&gt;

&lt;p&gt;&lt;span style="color:red;"&gt;\b\w_q[^u]\w_\b&lt;/span&gt;匹配包含&lt;strong&gt;后面不是字母 u 的字母 q&lt;/strong&gt;的单词。但是如果多做测试 (或者你思维足够敏锐，直接就观察出来了)，你会发现，如果 q 出现在单词的结尾的话，像&lt;strong&gt;Iraq&lt;/strong&gt;,&lt;strong&gt;Benq&lt;/strong&gt;，这个表达式就会出错。这是因为&lt;span style="color:green;"&gt;[^u]&lt;/span&gt;总要匹配一个字符，所以如果 q 是单词的最后一个字符的话，后面的&lt;span style="color:green;"&gt;[^u]&lt;/span&gt;将会匹配 q 后面的单词分隔符 (可能是空格，或者是句号或其它的什么)，后面的&lt;span style="color:green;"&gt;\w*\b&lt;/span&gt;将会匹配下一个单词，于是&lt;span style="color:red;"&gt;\b\w_q[^u]\w_\b&lt;/span&gt;就能匹配整个&lt;em&gt;Iraq fighting&lt;/em&gt;。&lt;strong&gt;负向零宽断言&lt;/strong&gt;能解决这样的问题，因为它只匹配一个位置，并不&lt;strong&gt;消费&lt;/strong&gt;任何字符。现在，我们可以这样来解决这个问题：&lt;span style="color:red;"&gt;\b\w_q(?! u)\w_\b&lt;/span&gt;。&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;零宽度负预测先行断言&lt;/strong&gt;&lt;span style="color:red;"&gt;(?! exp)&lt;/span&gt;，&lt;u&gt;断言此位置的后面不能匹配表达式 exp&lt;/u&gt;。例如：&lt;span style="color:red;"&gt;\d{3}(?!\d)&lt;/span&gt;匹配&lt;u&gt;三位数字，而且这三位数字的后面不能是数字&lt;/u&gt;；&lt;span style="color:red;"&gt;\b((?! abc)\w)+\b&lt;/span&gt;匹配&lt;u&gt;不包含连续字符串 abc 的单词&lt;/u&gt;。&lt;/p&gt;

&lt;p&gt;同理，我们可以用&lt;span style="color:blue;"&gt;(?&amp;lt;! exp)&lt;/span&gt;,&lt;strong&gt;零宽度负回顾后发断言&lt;/strong&gt;来&lt;u&gt;断言此位置的前面不能匹配表达式 exp&lt;/u&gt;：&lt;span style="color:red;"&gt;(?&amp;lt;![a-z])\d{7}&lt;/span&gt;匹配&lt;u&gt;前面不是小写字母的七位数字&lt;/u&gt;。&lt;/p&gt;

&lt;p&gt;一个更复杂的例子：&lt;span style="color:red;"&gt;(?&amp;lt;=&amp;amp;lt(\w+)&amp;gt;).*(?=&amp;lt;\/\1&amp;gt;)&lt;/span&gt;匹配&lt;u&gt;不包含属性的简单 HTML 标签内里的内容&lt;/u&gt;。&lt;span style="color:blue;"&gt;(?&amp;lt;=&amp;lt;(\w+)&amp;gt;)&lt;/span&gt;指定了这样的&lt;strong&gt;前缀&lt;/strong&gt;：&lt;u&gt;被尖括号括起来的单词&lt;/u&gt;(比如可能是&amp;lt;b&amp;gt;)，然后是&lt;span style="color:green;"&gt;.*&lt;/span&gt;(任意的字符串),最后是一个后缀&lt;span style="color:green;"&gt;(?=&amp;lt;\/\1&amp;gt;)&lt;/span&gt;。注意后缀里的&lt;span style="color:green;"&gt;\/&lt;/span&gt;，它用到了前面提过的字符转义；&lt;span style="color:green;"&gt;\1&lt;/span&gt;则是一个反向引用，引用的正是&lt;u&gt;捕获的第一组&lt;/u&gt;，前面的&lt;span style="color:green;"&gt;(\w+)&lt;/span&gt;匹配的内容，这样如果前缀实际上是&amp;lt;b&amp;gt;的话，后缀就是&amp;lt;b&amp;gt;了。整个表达式匹配的是&amp;lt;b&amp;gt;和&amp;lt;b&amp;gt;之间的内容 (再次提醒，不包括前缀和后缀本身)。&lt;/p&gt;
&lt;h2 id="贪婪与懒惰"&gt;贪婪与懒惰&lt;/h2&gt;
&lt;p&gt;当正则表达式中包含能接受重复的限定符时，通常的行为是（在使整个表达式能得到匹配的前提下）匹配&lt;strong&gt;尽可能多&lt;/strong&gt;的字符。以这个表达式为例：&lt;span style="color:red;"&gt;a.&lt;em&gt;b&lt;/em&gt;&lt;/span&gt;，它将会匹配&lt;u&gt;最长的以 a 开始，以 b 结束的字符串&lt;/u&gt;。如果用它来搜索 *aabab 的话，它会匹配整个字符串&lt;u&gt;aabab&lt;/u&gt;。这被称为&lt;strong&gt;贪婪&lt;/strong&gt;匹配。&lt;/p&gt;

&lt;p&gt;有时，我们更需要&lt;strong&gt;懒惰&lt;/strong&gt;匹配，也就是匹配&lt;strong&gt;尽可能少&lt;/strong&gt;的字符。前面给出的限定符都可以被转化为懒惰匹配模式，只要在它后面加上一个问号&lt;span style="color:blue;"&gt;?&lt;/span&gt;。这样&lt;span style="color:red;"&gt;.*?&lt;/span&gt;就意味着&lt;u&gt;匹配任意数量的重复，但是在能使整个匹配成功的前提下使用最少的重复&lt;/u&gt;。现在看看懒惰版的例子吧：&lt;/p&gt;

&lt;p&gt;&lt;span style="color:red;"&gt;a.*?b&lt;/span&gt;匹配&lt;u&gt;最短的，以 a 开始，以 b 结束的字符串&lt;/u&gt;。如果把它应用于 aabab 的话，它会匹配&lt;u&gt;aab（第一到第三个字符）和 ab（第四到第五个字符）&lt;/u&gt;。&lt;/p&gt;

&lt;p&gt;表 5.懒惰限定符&lt;/p&gt;
&lt;table class="table table-bordered table-striped"&gt;
&lt;tr&gt;
&lt;th&gt;代码/语法&lt;/th&gt;
&lt;th&gt;说明&lt;/th&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;*?&lt;/td&gt;
&lt;td&gt;重复任意次，但尽可能少重复&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;+?&lt;/td&gt;
&lt;td&gt;重复 1 次或更多次，但尽可能少重复&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;??&lt;/td&gt;
&lt;td&gt;重复 0 次或 1 次，但尽可能少重复&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;{n,m}?&lt;/td&gt;
&lt;td&gt;重复 n 到 m 次，但尽可能少重复&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;{n,}?&lt;/td&gt;
&lt;td&gt;重复 n 次以上，但尽可能少重复&lt;/td&gt;
&lt;/tr&gt;
&lt;/table&gt;&lt;h2 id="处理选项"&gt;处理选项&lt;/h2&gt;
&lt;p&gt;上面介绍了几个选项如忽略大小写，处理多行等，这些选项能用来改变处理正则表达式的方式。下面是.Net 中常用的正则表达式选项：&lt;/p&gt;

&lt;p&gt;表 6.常用的处理选项&lt;/p&gt;
&lt;table class="table table-bordered table-striped"&gt;
&lt;tr&gt;
&lt;th&gt;名称&lt;/th&gt;
&lt;th&gt;说明&lt;/th&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;IgnoreCase(忽略大小写)&lt;/td&gt;
&lt;td&gt;匹配时不区分大小写。&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Multiline(多行模式)&lt;/td&gt;
&lt;td&gt;更改^和\$ 的含义，使它们分别在任意一行的行首和行尾匹配，而不仅仅在整个字符串的开头和结尾匹配。(在此模式下,\$ 的精确含意是:匹配\n 之前的位置以及字符串结束前的位置.)&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Singleline(单行模式)&lt;/td&gt;
&lt;td&gt;更改.的含义，使它与每一个字符匹配（包括换行符\n）。&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;IgnorePatternWhitespace(忽略空白)&lt;/td&gt;
&lt;td&gt;忽略表达式中的非转义空白并启用由 # 标记的注释。&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;ExplicitCapture(显式捕获)&lt;/td&gt;
&lt;td&gt;仅捕获已被显式命名的组。&lt;/td&gt;
&lt;/tr&gt;
&lt;/table&gt;
&lt;p&gt;一个经常被问到的问题是：是不是只能同时使用多行模式和单行模式中的一种？答案是：不是。这两个选项之间没有任何关系，除了它们的名字比较相似（以至于让人感到疑惑）以外。事实上，为了避免混淆，在最新的 JavaScript 中，单行模式其实名叫 dotAll，意为点可以匹配所有字符，然而在指定该选项时，用的还是 Singleline 的首字母 s.&lt;/p&gt;</description>
      <author>admin</author>
      <pubDate>Tue, 11 Jan 2022 14:31:29 +0800</pubDate>
      <link>https://xxb.lttc.cn/topics/241</link>
      <guid>https://xxb.lttc.cn/topics/241</guid>
    </item>
    <item>
      <title>视图套视图，隐患毒毒毒</title>
      <description>&lt;p&gt;为了开发便利，现阶段大家用了 N 多视图套视图的复杂套娃技术。虽然比较炫酷，但隐患风险也比较多。下面结合最近的实际案例，给大家提醒下，注意自家后院是否会起火。&lt;/p&gt;

&lt;p&gt;例如，在遥远的 [链接服务器] 上的 [XX 数据库] 建立了 1 个 AAA(视图)；然后在 10.200.0.92 上也建立了 1 个 AAA(视图)，视图内容为 “select *  from [链接服务器].[XX 数据库].AAA(视图)”。如果遥远的 [链接服务器] 上的 AAA(视图) 中添加了新字段时，10.200.0.92 上的 AAA(视图) 的数据结构不会同步更新；即在 [链接服务器] 上的 AAA(视图) 的字段列表的开始位置增加了 ZD1、ZD2，整体的字段列表由 10 个字段扩充为 12 个字段，如在 10.200.0.92 上执行 “select *  from [链接服务器].[XX 数据库].AAA(视图)” 时，仍然只获取到未扩充前的 10 个字段，而且这 10 个字段的第一个字段的值变成了新增字段 ZD1 的字段值，第二个字段的值变成了新增字段 ZD2 的字段值，第三个字段的值变成了 12 个字段列表中第 3 个字段的值（即原来的 10 个字段列表中的第一个字段的值），以此类推，未扩充前的 10 个字段中的最后一个字段的值变成了 12 个字段列表中第 10 个字段的值（即原来的 10 个字段列表中的第 8 个字段的值）。&lt;/p&gt;

&lt;p&gt;以上具体情形请脑补。&lt;/p&gt;

&lt;p&gt;一旦发生上述问题，有极大可能会造成我们的系统发生莫名其妙的错误，且问题隐藏极深，不易排查。&lt;/p&gt;

&lt;p&gt;不管 10.200.0.92 上的视图写成 “select * from .......” 还是 “select 字段名 1, 字段名 2，字段名 3 ... from .......”，结果都是一样的，无法规避上述问题。&lt;/p&gt;

&lt;p&gt;现阶段，建议大家给视图增加字段时，将新字段添加到字段列表的最后，能够避免一部分问题，但也治标不治本，不能完全解决隐患。原大家提高警惕，共防视图遗毒。&lt;/p&gt;</description>
      <author>houning</author>
      <pubDate>Thu, 30 Dec 2021 17:36:44 +0800</pubDate>
      <link>https://xxb.lttc.cn/topics/240</link>
      <guid>https://xxb.lttc.cn/topics/240</guid>
    </item>
    <item>
      <title># Visual Studio 远程调试</title>
      <description>&lt;h2 id="Visual Studio 远程调试"&gt;Visual Studio 远程调试&lt;/h2&gt;
&lt;p&gt;&lt;strong&gt;在可以远程对方电脑，并且只有在对方电脑上出现 BUG 时，可以使用远程调试功能&lt;/strong&gt;&lt;/p&gt;
&lt;h2 id="原理"&gt;原理&lt;/h2&gt;
&lt;p&gt;&lt;img src="/uploads/photo/wangbaochen/5a8b18df-aa88-4d3f-9112-9f3a19c66762.png!large" title="" alt=""&gt;&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;目标机：&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;负责执行目标程序。安装和运行远程工具（Remote Debugger），等待来自开发机的连接请求。&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;开发机：&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;将编译好的程序部署到目标机器上执行。配置 VS 工程，建立与目标机的连接，开始远程调试。&lt;/p&gt;
&lt;h2 id="环境要求"&gt;环境要求&lt;/h2&gt;
&lt;p&gt;&lt;strong&gt;目标机必须满足：&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;Windows 7 以后或者 Windows Server 2008 Service Pack 2 以后。&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;网络必须满足：&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;目标机和开发机必须通过网络、工作组、家庭组连接，或者通过网线直连。&lt;/p&gt;
&lt;h2 id="安装 Remote Debugger"&gt;安装 Remote Debugger&lt;/h2&gt;
&lt;p&gt;在你本地的 VS 安装目录下找到 msvsmon.exe，此文件就是 Remote Debugger。&lt;/p&gt;

&lt;p&gt;它的位置在：&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;C:\Program Files (x86)\Microsoft Visual Studio\2017\Enterprise\Common7\IDE\Remote Debugger\x86\msvsmon.exe&lt;br&gt;
C:\Program Files (x86)\Microsoft Visual Studio\2017\Enterprise\Common7\IDE\Remote Debugger\x64\msvsmon.exe&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;可以共享该目录，在目标机上直接运行 Share Folder 中的 msvsmon.exe 即可。&lt;/p&gt;

&lt;p&gt;也可以把 msvsmon.exe 所在的整个目录 Copy 到目标机上。&lt;/p&gt;

&lt;p&gt;这里同样需要注意，要与目标机的操作系统位数匹配。&lt;/p&gt;
&lt;h2 id="配置 Remote Debugger"&gt;配置 Remote Debugger&lt;/h2&gt;
&lt;p&gt;这一步还是在目标机上。&lt;/p&gt;

&lt;p&gt;所在登陆账户，一定要拥有管理员权限。&lt;/p&gt;

&lt;p&gt;打开安装好的 Remote Debugger (msvsmon.exe)，首次打开时，会出现配置窗口，提醒配置一些参数。&lt;/p&gt;

&lt;p&gt;只需要配置一次，再次打开时，不会弹出该窗口。&lt;/p&gt;

&lt;p&gt;&lt;img src="/uploads/photo/wangbaochen/9c562fc8-4352-47fa-b940-1df5a7f7f23f.png!large" title="" alt=""&gt;&lt;/p&gt;

&lt;p&gt;如果你不清楚你的网络连接方式，比较保险的做法是把所有框都勾选上。&lt;/p&gt;

&lt;p&gt;点击配置远程调试按钮之后，会出现如下窗口，代表 Remote Debugger 正在等待来自开发机的连接。&lt;/p&gt;

&lt;p&gt;&lt;img src="/uploads/photo/wangbaochen/ffcec1c7-d4d8-4abc-9f99-4fafafb01817.png!large" title="" alt=""&gt;&lt;/p&gt;

&lt;p&gt;这里可以点击 工具 &amp;gt; 选项 菜单，做一些额外的参数配置。&lt;strong&gt;建议选择无身份验证，防止连接失败&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;&lt;img src="/uploads/photo/wangbaochen/db143850-1f60-4485-af2d-99338ce2fa53.png!large" title="" alt=""&gt;&lt;/p&gt;

&lt;p&gt;有时可能会由于&lt;strong&gt;防火墙或者认证&lt;/strong&gt;等原因，导致连接不上的问题，最极端的方法是完全关闭它们。虽然官方出于安全考虑，并不建议这么做。&lt;/p&gt;

&lt;p&gt;假如你确定网络没有任何风险，那么可以关闭两边机器的防火墙。上面窗口中，也可以选择无认证方式。&lt;/p&gt;

&lt;p&gt;最大空闲时间设置为 0 代表永远不会超时，一直处于等待状态。&lt;/p&gt;

&lt;p&gt;TCP/IP 端口号这里默认给出的是 4022，你可以更改，但一般无需这样做。&lt;/p&gt;
&lt;h2 id="调试"&gt;调试&lt;/h2&gt;
&lt;p&gt;连接类型：选择&lt;strong&gt;远程 (无身份验证)&lt;/strong&gt;&lt;br&gt;
连接目标：目标机 IP:端口号&lt;/p&gt;

&lt;p&gt;点击查找，找到目标用户并连接，然后附加到进程&lt;/p&gt;

&lt;p&gt;&lt;img src="/uploads/photo/wangbaochen/ded3be63-855a-4a10-acb6-4f1dedd7c32f.png!large" title="" alt=""&gt;&lt;/p&gt;

&lt;p&gt;连接到目标机后，选则需要调试的进程，如上图：&lt;/p&gt;

&lt;p&gt;&lt;img src="/uploads/photo/wangbaochen/034d6fcd-9877-442c-af45-ec6dd36943a1.png!large" title="" alt=""&gt;&lt;/p&gt;

&lt;p&gt;连接成功后，目标机会显示，开发机用户。&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;要调试的 DLL 必须时 DEBUG 模式，否则不能击中断点&lt;/strong&gt;&lt;/p&gt;</description>
      <author>wangbaochen</author>
      <pubDate>Wed, 29 Dec 2021 08:21:01 +0800</pubDate>
      <link>https://xxb.lttc.cn/topics/239</link>
      <guid>https://xxb.lttc.cn/topics/239</guid>
    </item>
    <item>
      <title>WnGrid 添加 Image 列</title>
      <description>&lt;h2 id="WnGrid添加ImageColumn"&gt;WnGrid 添加 ImageColumn&lt;/h2&gt;
&lt;ol&gt;
&lt;li&gt;修改类型为 byte[] 列为 Image 列，此前为 Text 列。添加 ImageShowMode 属性。&lt;/li&gt;
&lt;li&gt;修改 WnImage 单值控件存储数据逻辑，添加 ImageShowInWnGrid 属性。&lt;/li&gt;
&lt;li&gt;修改 ExportData 方法，添加 IsExistImageColumn 属性&lt;/li&gt;
&lt;/ol&gt;
&lt;h2 id="ImageShowMode"&gt;ImageShowMode&lt;/h2&gt;
&lt;p&gt;图片展示模式（PictureSizeMode）枚举类型  &lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;Clip：图片没有被拉伸，原图，若图片需要导出推荐使用此模式&lt;br&gt;
Stretch：图片被拉伸，以适应编辑器&lt;br&gt;
Zoom：图片被按比例拉长。图片至少在一个方向上适合编辑器，若要当作缩略图显示，推荐此模式&lt;br&gt;
StretchHorizontal：图片被水平拉长。其高度保持不变&lt;br&gt;
StretchVertical：图片被垂直拉长。其宽度保持不变&lt;br&gt;
Squeeze：图像的实际尺寸小于容器的尺寸，则按原样显示。如果图像的尺寸大于容器的尺寸，图像将按比例缩小以适应容器的边界&lt;/p&gt;
&lt;/blockquote&gt;
&lt;h2 id="ImageShowInWnGrid"&gt;ImageShowInWnGrid&lt;/h2&gt;
&lt;p&gt;是否在 WnGrid 中展示图片（bool）&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;为兼容此前的数据存储模式添加，此前 Image 通过 BinaryFormatter 序列化为 byte[]，如果需要在 WnGrid 中展示图片则 Image 不能被序列化，需要原始 byte[]。&lt;/p&gt;
&lt;/blockquote&gt;
&lt;h2 id="IsExistImageColumn"&gt;IsExistImageColumn&lt;/h2&gt;
&lt;p&gt;导出的文件中是否存在图片 (bool)&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;DefaultExportType 为 WYSIWYG 可以导出图片，默认为 Default。&lt;/p&gt;
&lt;/blockquote&gt;</description>
      <author>wangbaochen</author>
      <pubDate>Mon, 20 Dec 2021 09:57:10 +0800</pubDate>
      <link>https://xxb.lttc.cn/topics/238</link>
      <guid>https://xxb.lttc.cn/topics/238</guid>
    </item>
    <item>
      <title>WnForm 扩展工具 (Visual Studio 扩展)</title>
      <description>&lt;h2 id="WnFormExt"&gt;WnFormExt&lt;/h2&gt;
&lt;p&gt;WnForm 扩展工具&lt;/p&gt;
&lt;h2 id="编译条件"&gt;编译条件&lt;/h2&gt;
&lt;ul&gt;
&lt;li&gt;Visual Studio 2022 Preview&lt;/li&gt;
&lt;li&gt;Extensibility Essentials 2022&lt;/li&gt;
&lt;/ul&gt;
&lt;h2 id="安装"&gt;安装&lt;/h2&gt;
&lt;p&gt;自行编译为 vsix 安装即可。&lt;/p&gt;
&lt;h2 id="界面展示"&gt;界面展示&lt;/h2&gt;
&lt;p&gt;设置
&lt;img src="/uploads/photo/wangbaochen/de7c1c93-7f46-40f7-9df3-89647e032d1e.png!large" title="" alt=""&gt;&lt;/p&gt;
&lt;table class="table table-bordered table-striped"&gt;
&lt;tr&gt;
&lt;th style="text-align: left;"&gt;安装完毕后在工具列表中展示&lt;/th&gt;
&lt;th style="text-align: left;"&gt;打开之后在右侧显示（若没有修改过界面布局）&lt;/th&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td style="text-align: left;"&gt;&lt;img src="/uploads/photo/wangbaochen/2092578a-769b-4581-b8cb-b92a460dbb3d.png!large" title="" alt=""&gt;&lt;/td&gt;
&lt;td style="text-align: left;"&gt;&lt;img src="/uploads/photo/wangbaochen/57554de9-17e1-426a-9474-b92638d6af48.png!large" title="" alt=""&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;/table&gt;
&lt;p&gt;添加代码片段&lt;br&gt;
&lt;img src="/uploads/photo/wangbaochen/a445a4cc-34c0-4b3d-b394-f1145497fa3e.png!large" title="" alt=""&gt;&lt;/p&gt;

&lt;p&gt;查找代码片段&lt;br&gt;
&lt;img src="/uploads/photo/wangbaochen/b1fd8f02-cd3d-4571-a7ea-cf5e0eb10fcf.png!large" title="" alt=""&gt;&lt;/p&gt;
&lt;h2 id="主要功能"&gt;主要功能&lt;/h2&gt;
&lt;ol&gt;
&lt;li&gt;引用替换&lt;/li&gt;
&lt;li&gt;拷贝&lt;/li&gt;
&lt;li&gt;代码片段管理&lt;/li&gt;
&lt;/ol&gt;
&lt;h3 id="引用"&gt;引用&lt;/h3&gt;
&lt;p&gt;项目引用的程序集位置发生变更时，需要重新手动更换，引用替换功能可以根据指定的引用地址一键替换。&lt;/p&gt;

&lt;blockquote&gt;
&lt;ol&gt;
&lt;li&gt;引用地址存在两处，工具&amp;gt;选项&amp;gt;WnForm 扩展工具&amp;gt;常规和工具界面，界面的优先级&amp;gt;常规&lt;/li&gt;
&lt;li&gt;替换完毕后需要重新加载项目&lt;/li&gt;
&lt;/ol&gt;
&lt;/blockquote&gt;

&lt;p&gt;&lt;img src="/uploads/photo/wangbaochen/e2be908e-f874-43b2-b359-ebd2ce1c21ce.gif!large" title="" alt=""&gt;&lt;/p&gt;
&lt;h3 id="关闭并拷贝"&gt;关闭并拷贝&lt;/h3&gt;
&lt;p&gt;&lt;del&gt;先关闭打开时间最近的 WnForm 程序&lt;/del&gt;
先将占用拷贝文件的线程关闭，
再将项目生成的类库拷贝到指定目录&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;拷贝目录存在两处，工具&amp;gt;选项&amp;gt;WnForm 扩展工具&amp;gt;常规和工具界面，界面的优先级&amp;gt;常规&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;&lt;img src="/uploads/photo/wangbaochen/4436c8c1-3593-4ca3-97da-d18b6f7d14de.gif!large" title="" alt=""&gt;&lt;/p&gt;
&lt;h3 id="代码片段管理"&gt;代码片段管理&lt;/h3&gt;
&lt;p&gt;代码片段是小块可重用代码，可通过右键单击菜单（关联菜单）命令或热键组合插入到代码文件中。 代码片段通常包含常用的代码块（如 try-finally 或 if-else 块），可用于插入整个类或方法。&lt;/p&gt;
&lt;h4 id="添加代码片段"&gt;添加代码片段&lt;/h4&gt;
&lt;ul&gt;
&lt;li&gt;首先在设置中指定自定义代码片段位置 (一次即可)&lt;/li&gt;
&lt;li&gt;在工具&amp;gt;代码片段管理器&amp;gt;选择语言 (eg. CSharp)&amp;gt;添加自定义代码片段位置 (一次即可)&lt;/li&gt;
&lt;li&gt;选中要添加的代码&lt;/li&gt;
&lt;li&gt;填写名称、作者、快捷键、详情等信息&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;添加完毕之后，输入快捷方式，按下两次 Tab 键，加载代码&lt;/p&gt;

&lt;p&gt;&lt;img src="/uploads/photo/wangbaochen/d6a18f0e-2b8a-40de-96e2-80d6b4f14e43.gif!large" title="" alt=""&gt;&lt;/p&gt;
&lt;h4 id="查找代码片段"&gt;查找代码片段&lt;/h4&gt;
&lt;ul&gt;
&lt;li&gt;解析，会将 Visual Studio 自带的代码片段和自定义的代码片段解析到 SQLite 中方便查询&lt;/li&gt;
&lt;li&gt;根据名称、详细信息、代码查找输入的内容并以树展示，输入内容为空则查找所有&lt;/li&gt;
&lt;li&gt;展开树节点后，双击显示详细信息&lt;/li&gt;
&lt;/ul&gt;

&lt;blockquote&gt;
&lt;p&gt;解析文件存放位置：C:\Users{name}\AppData\Local\MySQLiteDB\MyDB.sqlite&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;&lt;img src="/uploads/photo/wangbaochen/0724d903-e11a-4266-b155-22aa7e4b472e.gif!large" title="" alt=""&gt;&lt;/p&gt;
&lt;h3 id="代码链接"&gt;代码链接&lt;/h3&gt;
&lt;p&gt;&lt;a href="https://git.xxb.lttc.cn/wangbc/WnFormExt" rel="nofollow" target="_blank"&gt;https://git.xxb.lttc.cn/wangbc/WnFormExt&lt;/a&gt;&lt;/p&gt;</description>
      <author>wangbaochen</author>
      <pubDate>Fri, 22 Oct 2021 17:08:31 +0800</pubDate>
      <link>https://xxb.lttc.cn/topics/237</link>
      <guid>https://xxb.lttc.cn/topics/237</guid>
    </item>
    <item>
      <title>打印完毕自动关闭预览脚本</title>
      <description>&lt;h2 id="引用"&gt;引用&lt;/h2&gt;
&lt;p&gt;在脚本引用中添加&lt;/p&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;DevExpress.XtraPrinting.v18.1.dll
DevExpress.Utils.v18.1.dll
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;&lt;img src="/uploads/photo/wangbaochen/9a5a7eb5-69e5-4f47-9d92-896cec79e750.png!large" title="" alt=""&gt;&lt;/p&gt;
&lt;h2 id="编写脚本"&gt;编写脚本&lt;/h2&gt;
&lt;p&gt;XtraReport 通过 PrintingSystemBase 基类进行打印，在打印前记录当前 XtraReport ，在打印文件发送到打印机后（EndPrint 事件），关闭预览界面。&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;&lt;a href="/zhangrui" class="user-mention" title="@zhangrui"&gt;&lt;i&gt;@&lt;/i&gt;zhangrui&lt;/a&gt; 张锐提供不要直接复制黏贴代码，需要手动选择当前报表 (PrintObject) 的 BeforePrint 事件，否则无法触发 BeforePrint 事件
&lt;img src="/uploads/photo/wangbaochen/b751c541-81be-41b8-9aa7-0d5a35af64ee.png!large" title="" alt=""&gt;  &lt;/p&gt;
&lt;/blockquote&gt;
&lt;pre class="highlight csharp"&gt;&lt;code&gt;&lt;span class="n"&gt;XtraReport&lt;/span&gt; &lt;span class="n"&gt;report&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="k"&gt;private&lt;/span&gt; &lt;span class="k"&gt;void&lt;/span&gt; &lt;span class="nf"&gt;PrintObject_BeforePrint&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="kt"&gt;object&lt;/span&gt; &lt;span class="n"&gt;sender&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;System&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Drawing&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Printing&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;PrintEventArgs&lt;/span&gt; &lt;span class="n"&gt;e&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
 &lt;span class="n"&gt;report&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;sender&lt;/span&gt; &lt;span class="k"&gt;as&lt;/span&gt; &lt;span class="n"&gt;XtraReport&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt; 
 &lt;span class="n"&gt;report&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;PrintingSystem&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;EndPrint&lt;/span&gt; &lt;span class="p"&gt;+=&lt;/span&gt; &lt;span class="n"&gt;PrintingSystem_EndPrint&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;  
&lt;span class="p"&gt;}&lt;/span&gt;

&lt;span class="k"&gt;private&lt;/span&gt; &lt;span class="k"&gt;void&lt;/span&gt; &lt;span class="nf"&gt;PrintingSystem_EndPrint&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="kt"&gt;object&lt;/span&gt; &lt;span class="n"&gt;sender&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;EventArgs&lt;/span&gt; &lt;span class="n"&gt;e&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;  
 &lt;span class="n"&gt;report&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;ClosePreview&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;  
&lt;span class="p"&gt;}&lt;/span&gt;  

&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;&lt;img src="/uploads/photo/wangbaochen/44aa43ba-f7db-40f2-acc2-6763bdf16884.png!large" title="" alt=""&gt;&lt;/p&gt;</description>
      <author>wangbaochen</author>
      <pubDate>Sat, 04 Sep 2021 09:54:20 +0800</pubDate>
      <link>https://xxb.lttc.cn/topics/236</link>
      <guid>https://xxb.lttc.cn/topics/236</guid>
    </item>
    <item>
      <title>C# 调用 exe 文件</title>
      <description>&lt;h2 id="WinForm系统方案"&gt;WinForm 系统方案&lt;/h2&gt;
&lt;p&gt;通过配置通用模块【APP.WnEmbeddedForm】打开 exe 文件，此方案无法传参,需将文件放入 WinForm 所在文件夹中。&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;配置参数：ParamInfo&lt;br&gt;
Folder:exe 文件所在的文件夹&lt;br&gt;
File:exe 文件名称&lt;br&gt;
ModuleName:模块打开时所展示的名称&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;e.g.:&lt;br&gt;
&lt;img src="/uploads/photo/wangbaochen/d85839b2-16f1-4bd2-945b-40e983ad6235.png!large" title="" alt=""&gt;&lt;/p&gt;
&lt;h2 id="代码方案"&gt;代码方案&lt;/h2&gt;
&lt;p&gt;调用外部 exe 并且传参，无返回值。&lt;/p&gt;
&lt;pre class="highlight csharp"&gt;&lt;code&gt;
&lt;span class="k"&gt;public&lt;/span&gt; &lt;span class="k"&gt;static&lt;/span&gt; &lt;span class="k"&gt;void&lt;/span&gt; &lt;span class="nf"&gt;RunExeByProcess&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="kt"&gt;string&lt;/span&gt; &lt;span class="n"&gt;exePath&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="kt"&gt;string&lt;/span&gt; &lt;span class="n"&gt;argument&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="p"&gt;{&lt;/span&gt;
 &lt;span class="c1"&gt;//创建进程&lt;/span&gt;
 &lt;span class="n"&gt;System&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Diagnostics&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Process&lt;/span&gt; &lt;span class="n"&gt;process&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="k"&gt;new&lt;/span&gt; &lt;span class="n"&gt;System&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Diagnostics&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;Process&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;

 &lt;span class="c1"&gt;//调用的exe的名称&lt;/span&gt;
 &lt;span class="n"&gt;process&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;StartInfo&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;FileName&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="n"&gt;exePath&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

 &lt;span class="c1"&gt;//传递进exe的参数 -- 参数以空格分隔，如果某个参数为空，可以传入”” &lt;/span&gt;
 &lt;span class="n"&gt;process&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;StartInfo&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Arguments&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="n"&gt;argument&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

 &lt;span class="c1"&gt;//是否需要系统shell调用程序&lt;/span&gt;
 &lt;span class="n"&gt;process&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;StartInfo&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;UseShellExecute&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="k"&gt;false&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

 &lt;span class="c1"&gt;//不显示exe的界面&lt;/span&gt;
 &lt;span class="n"&gt;process&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;StartInfo&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;CreateNoWindow&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="k"&gt;true&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

 &lt;span class="c1"&gt;//输出&lt;/span&gt;
 &lt;span class="n"&gt;process&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;StartInfo&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;RedirectStandardOutput&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="k"&gt;true&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
 &lt;span class="n"&gt;process&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;StartInfo&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;RedirectStandardInput&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="k"&gt;true&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
 &lt;span class="n"&gt;process&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;Start&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;

 &lt;span class="n"&gt;process&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;StandardInput&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;AutoFlush&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="k"&gt;true&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

 &lt;span class="c1"&gt;//阻塞等待调用结束&lt;/span&gt;
 &lt;span class="n"&gt;process&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;WaitForExit&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;

&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;调用外部 exe 并且传参，有返回值。&lt;/p&gt;
&lt;pre class="highlight csharp"&gt;&lt;code&gt;&lt;span class="k"&gt;public&lt;/span&gt; &lt;span class="k"&gt;static&lt;/span&gt; &lt;span class="kt"&gt;string&lt;/span&gt; &lt;span class="nf"&gt;RunExeByProcess&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="kt"&gt;string&lt;/span&gt; &lt;span class="n"&gt;exePath&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="kt"&gt;string&lt;/span&gt; &lt;span class="n"&gt;argument&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
  &lt;span class="p"&gt;{&lt;/span&gt;
   &lt;span class="c1"&gt;//创建进程&lt;/span&gt;
   &lt;span class="n"&gt;System&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Diagnostics&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Process&lt;/span&gt; &lt;span class="n"&gt;process&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="k"&gt;new&lt;/span&gt; &lt;span class="n"&gt;System&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Diagnostics&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;Process&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;

   &lt;span class="c1"&gt;//调用的exe的名称&lt;/span&gt;
   &lt;span class="n"&gt;process&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;StartInfo&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;FileName&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="n"&gt;exePath&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

   &lt;span class="c1"&gt;//传递进exe的参数&lt;/span&gt;
   &lt;span class="n"&gt;process&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;StartInfo&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Arguments&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="n"&gt;argument&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
   &lt;span class="n"&gt;process&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;StartInfo&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;UseShellExecute&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="k"&gt;false&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

   &lt;span class="c1"&gt;//不显示exe的界面&lt;/span&gt;
   &lt;span class="n"&gt;process&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;StartInfo&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;CreateNoWindow&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="k"&gt;true&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
   &lt;span class="n"&gt;process&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;StartInfo&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;RedirectStandardOutput&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="k"&gt;true&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
   &lt;span class="n"&gt;process&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;StartInfo&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;RedirectStandardInput&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="k"&gt;true&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
   &lt;span class="n"&gt;process&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;Start&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;

   &lt;span class="n"&gt;process&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;StandardInput&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;AutoFlush&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="k"&gt;true&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

   &lt;span class="kt"&gt;string&lt;/span&gt; &lt;span class="n"&gt;result&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="k"&gt;null&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
   &lt;span class="k"&gt;while&lt;/span&gt; &lt;span class="p"&gt;(!&lt;/span&gt;&lt;span class="n"&gt;process&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;StandardOutput&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;EndOfStream&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
   &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="n"&gt;result&lt;/span&gt; &lt;span class="p"&gt;+=&lt;/span&gt; &lt;span class="n"&gt;process&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;StandardOutput&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;ReadLine&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="p"&gt;+&lt;/span&gt; &lt;span class="n"&gt;Environment&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;NewLine&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
   &lt;span class="p"&gt;}&lt;/span&gt;

   &lt;span class="n"&gt;process&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;WaitForExit&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;
   &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="n"&gt;result&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
  &lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;</description>
      <author>wangbaochen</author>
      <pubDate>Sat, 04 Sep 2021 09:52:58 +0800</pubDate>
      <link>https://xxb.lttc.cn/topics/235</link>
      <guid>https://xxb.lttc.cn/topics/235</guid>
    </item>
    <item>
      <title> AOP 编程思想</title>
      <description>&lt;h2 id="AOP编程思想"&gt;AOP 编程思想&lt;/h2&gt;&lt;h2 id="一、什么是AOP"&gt;一、什么是 AOP&lt;/h2&gt;
&lt;p&gt;AOP：Aspect Oriented Programming 的缩写，意为面向切面编程，通过预编译方式和运行期间动态代理实现程序功能的统一维护的一种技术。AOP 是 OOP 思想的延续。利用 AOP 可以对业务逻辑的各个部分进行隔离，从而使得业务逻辑各部分之间的耦合度降低，提高程序的可重用性，同时提高了开发的效率。&lt;/p&gt;
&lt;h2 id="二、编程思想的发展路线"&gt;二、编程思想的发展路线&lt;/h2&gt;&lt;h3 id="1、POP"&gt;1、POP&lt;/h3&gt;
&lt;p&gt;POP：Procedure Oriented Programming 的缩写，即面向过程编程，是一种以过程为中心的编程思想。&lt;/p&gt;

&lt;p&gt;面向过程是分析出解决问题的步骤，然后用函数或者方法，把这些步骤一步一步的实现，使用的时候在一个一个的一次调用函数或者方法，这就是面向过程编程。最开始的时候都是面向过程编程。面向过程是最为实际的一种思考方式。就算是面向对象编程，里面也是包含有面向过程的编程思想，因为面向过程是一种基础的编程思考方式，它从实际出发来考虑如何实现需求。&lt;/p&gt;

&lt;p&gt;POP 的不足：面向过程编程，只能处理一些简单的问题，无法处理一些复杂的问题。如果问题很复杂，全部以流程来思考的话，会发现流程很混乱，甚至流程都不能进行下去。&lt;/p&gt;
&lt;h3 id="2、OOP"&gt;2、OOP&lt;/h3&gt;
&lt;p&gt;OOP：Object Oriented Programming 的缩写，即面向对象编程。&lt;/p&gt;

&lt;p&gt;早期的计算机编程都是面向过程的，因为早期的编程都比较简单。但是随着时间的发展，需求处理的问题越来越多，问题就会越来越复杂，这时就不能简单的使用面向过程的编程了，就出现了面向对象编程。在计算机里面，把所有的东西都想象成一种事物。现实世界中的对象都有一些属性和行为，也就对应计算机中的属性和方法。&lt;/p&gt;

&lt;p&gt;面向对象编程就是把构成问题的事物分解成各个对象，建立对象的目的不是为了完成一个步骤，而是为了描述某个事物在整个解决问题的步骤中的行为。&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;OOP 的不足：产生新的需求会导致程序代码不断的进行修改，容易造成程序的不稳定。&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;如果非常了解 OOP 的话，那么我们应该知道，从对象的组织角度来讲，分类方法都是以继承关系为主线的，我们称为纵向。如果只使用 OOP 思想的话，会带来两个问题：&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;共性问题。&lt;/li&gt;
&lt;li&gt;扩展问题，需要对先有类进行扩展时就比较困难了。&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;OOP 与 POP 的区别：&lt;/p&gt;

&lt;p&gt;在对比面向过程的时候，面向对象的方法是把事物最小化为对象，包括属性和方法。当程序的规模比较小的时候，面向过程编程还是有一些优势的，因为这时候程序的流程是比较容易梳理清楚的。以早上去上班为例，过程就是起床、穿衣、刷牙洗脸、去公司。每一步都是按照顺序完成的，我们只需要按照步骤去一步一步的实现里面的方法就行了，最后在依次调用实现的方法即可，这就是面向过程开发。&lt;/p&gt;

&lt;p&gt;如果使用面向对象编程，我们就需要抽象出来一个员工类，该员工具有起床、穿衣、刷牙洗脸、去公司的四个方法。但是，最终要实现早上去上班的这个需求的话，还是要按照顺序依次来调用四个方法。最开始的时候，我们是按照面向过程的思想来思考该需求，然后在按照面向对象的思想来抽象出几个方法，最终要实现这个需求，还是要按照面向过程的顺序来实现。&lt;/p&gt;

&lt;p&gt;面向对象和面向过程的区别仅仅是在思考问题方式上面的不同。最终你会发现，在你实现这个需求的时候，即使使用了面向对象的思想抽象出来了员工类，但是最后还是要使用面向过程来实现这个需求。&lt;/p&gt;
&lt;h2 id="3、AOP"&gt;3、AOP&lt;/h2&gt;
&lt;p&gt;AOP：Aspect Oriented Programming 的缩写，即面向切面编程。是对 OOP 的一种补充，在不修改原始类的情况下，给程序动态添加统一功能的一种技术。&lt;/p&gt;

&lt;p&gt;OOP 关注的是将需求功能划分为不同的并且相对独立、封装良好的类，依靠继承和多态来定义彼此的关系。AOP 能够将通用需求功能从不相关的类中分离出来，很多类共享一个行为，一旦发生变化，不需要去修改很多类，只需要去修改这一个类即可。&lt;/p&gt;

&lt;p&gt;AOP 中的切面是指什么呢？切面指的是横切关注点。看下面一张图：&lt;/p&gt;

&lt;p&gt;&lt;img src="/uploads/photo/wangbaochen/4cdf2dab-60a0-4a01-86aa-81a387f78bae.png!large" title="" alt=""&gt;&lt;/p&gt;

&lt;p&gt;OOP 是为了将状态和行为进行模块化。上图是一个商场系统，我们使用 OOP 将该系统纵向分为订单管理、商品管理、库存管理模块。在该系统里面，我们要进行授权验证。像订单、商品、库存都是业务逻辑功能，但是这三个模块都需要一些共有的功能，比如说授权验证、日志记录等。我们不可能在每个模块里面都去写授权验证，而且授权验证也不属于具体的业务，它其实属于功能性模块，并且会横跨多个业务模块。可以看到这里是横向的，这就是所谓的切面。通俗的将，AOP 就是将公用的功能给提取出来，如果以后这些公用的功能发生了变化，我们只需要修改这些公用功能的代码即可，其它的地方就不需要去更改了。所谓的切面，就是只关注通用功能，而不关注业务逻辑，而且不修改原有的类。&lt;/p&gt;

&lt;p&gt;AOP 优势：&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;将通用功能从业务逻辑中抽离出来，提高代码复用性，有利于后期的维护和扩展。&lt;/li&gt;
&lt;li&gt;软件设计时，抽出通用功能（切面），有利于软件设计的模块化，降低软件架构的复杂度。&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;AOP 的劣势：&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;AOP 的对 OOP 思想的一种补充，它无法单独存在。如果说单独使用 AOP 去设计一套系统是不可能的。在设计系统的时候，如果系统比较简单，那么可以只使用 POP 或者 OOP 来设计。如果系统很复杂，就需要使用 AOP 思想。首先要使用 POP 来梳理整个业务流程，然后根据 POP 的流程，去整理类和模块，最后在使用 AOP 来抽取通用功能。&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;AOP 和 OOP 的区别：&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;面向目标不同：OOP 是面向名词领域 (抽象出来一个事物，比如学生、员工，这些都是名词)。AOP 是面向动词领域 (比如鉴权、记录日志，这些都是动作或行为)。&lt;/li&gt;
&lt;li&gt;思想结构不同：OOP 是纵向的 (以继承为主线，所以是纵向的)。AOP 是横向的。&lt;/li&gt;
&lt;li&gt;注重方面不同：OOP 是注重业务逻辑单元的划分，AOP 偏重业务处理过程中的某个步骤或阶段。&lt;/li&gt;
&lt;/ol&gt;

&lt;blockquote&gt;
&lt;p&gt;POP、OOP、AOP 三种思想是相互补充的。在一个系统的开发过程中，这三种编程思想是不可或缺的。&lt;/p&gt;
&lt;/blockquote&gt;
&lt;h2 id="三、实现AOP"&gt;三、实现 AOP&lt;/h2&gt;
&lt;p&gt;实现 AOP 有两种方式：&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;静态代理实现。所谓静态代理，就是我们自己来写代理对象。&lt;/li&gt;
&lt;li&gt;动态代理实现。所谓动态代理，就是在程序运行时，去生成一个代理对象。&lt;/li&gt;
&lt;/ol&gt;
&lt;h3 id="1、静态代理"&gt;1、静态代理&lt;/h3&gt;
&lt;p&gt;实现静态代理需要使用到两种设计模式：装饰器模式和代理模式。&lt;/p&gt;

&lt;p&gt;装饰器模式：允许向一个现有的对象添加新的功能，同时又不改变这个现有对象的结构。属于结构型设计模式，它是作为现有类的一种包装。首先会创建一个装饰类，用来包装原有的类，并在保持类的完整性的前提下，提供额外的功能。看下面的例子。&lt;/p&gt;

&lt;p&gt;我们首先创建一个 User 类：&lt;/p&gt;
&lt;pre class="highlight csharp"&gt;&lt;code&gt;&lt;span class="k"&gt;using&lt;/span&gt; &lt;span class="nn"&gt;System&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="k"&gt;using&lt;/span&gt; &lt;span class="nn"&gt;System.Collections.Generic&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="k"&gt;using&lt;/span&gt; &lt;span class="nn"&gt;System.Linq&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="k"&gt;using&lt;/span&gt; &lt;span class="nn"&gt;System.Text&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="k"&gt;using&lt;/span&gt; &lt;span class="nn"&gt;System.Threading.Tasks&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

&lt;span class="k"&gt;namespace&lt;/span&gt; &lt;span class="nn"&gt;StaticDemo.Model&lt;/span&gt;
&lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="k"&gt;public&lt;/span&gt; &lt;span class="k"&gt;class&lt;/span&gt; &lt;span class="nc"&gt;User&lt;/span&gt;
    &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="k"&gt;public&lt;/span&gt; &lt;span class="kt"&gt;string&lt;/span&gt; &lt;span class="n"&gt;Name&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="k"&gt;get&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt; &lt;span class="k"&gt;set&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt;
        &lt;span class="k"&gt;public&lt;/span&gt; &lt;span class="kt"&gt;string&lt;/span&gt; &lt;span class="n"&gt;Password&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="k"&gt;get&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt; &lt;span class="k"&gt;set&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt;
    &lt;span class="p"&gt;}&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;接着我们创建一个账号服务的接口，里面有一个方法，用来注册一个用户：&lt;/p&gt;
&lt;pre class="highlight csharp"&gt;&lt;code&gt;&lt;span class="k"&gt;using&lt;/span&gt; &lt;span class="nn"&gt;StaticDemo.Model&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

&lt;span class="k"&gt;namespace&lt;/span&gt; &lt;span class="nn"&gt;StaticDemo.Services&lt;/span&gt;
&lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="c1"&gt;/// &amp;lt;summary&amp;gt;&lt;/span&gt;
    &lt;span class="c1"&gt;/// 接口&lt;/span&gt;
    &lt;span class="c1"&gt;/// &amp;lt;/summary&amp;gt;&lt;/span&gt;
    &lt;span class="k"&gt;public&lt;/span&gt; &lt;span class="k"&gt;interface&lt;/span&gt; &lt;span class="nc"&gt;IAccountService&lt;/span&gt;
    &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="c1"&gt;/// &amp;lt;summary&amp;gt;&lt;/span&gt;
        &lt;span class="c1"&gt;/// 注册用户&lt;/span&gt;
        &lt;span class="c1"&gt;/// &amp;lt;/summary&amp;gt;&lt;/span&gt;
        &lt;span class="c1"&gt;/// &amp;lt;param name="user"&amp;gt;&amp;lt;/param&amp;gt;&lt;/span&gt;
        &lt;span class="k"&gt;void&lt;/span&gt; &lt;span class="nf"&gt;Reg&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;User&lt;/span&gt; &lt;span class="n"&gt;user&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
    &lt;span class="p"&gt;}&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;然后创建一个类来实现上面的接口：&lt;/p&gt;
&lt;pre class="highlight csharp"&gt;&lt;code&gt;&lt;span class="k"&gt;using&lt;/span&gt; &lt;span class="nn"&gt;StaticDemo.Model&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="k"&gt;using&lt;/span&gt; &lt;span class="nn"&gt;System&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

&lt;span class="k"&gt;namespace&lt;/span&gt; &lt;span class="nn"&gt;StaticDemo.Services&lt;/span&gt;
&lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="c1"&gt;/// &amp;lt;summary&amp;gt;&lt;/span&gt;
    &lt;span class="c1"&gt;/// 实现IAccountService接口&lt;/span&gt;
    &lt;span class="c1"&gt;/// &amp;lt;/summary&amp;gt;&lt;/span&gt;
    &lt;span class="k"&gt;public&lt;/span&gt; &lt;span class="k"&gt;class&lt;/span&gt; &lt;span class="nc"&gt;AccountService&lt;/span&gt; &lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="n"&gt;IAccountService&lt;/span&gt;
    &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="k"&gt;public&lt;/span&gt; &lt;span class="k"&gt;void&lt;/span&gt; &lt;span class="nf"&gt;Reg&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;User&lt;/span&gt; &lt;span class="n"&gt;user&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
        &lt;span class="p"&gt;{&lt;/span&gt;
            &lt;span class="c1"&gt;// 业务代码 之前 或者之后执行一些其它的逻辑&lt;/span&gt;
            &lt;span class="n"&gt;Console&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;WriteLine&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;$"&lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="n"&gt;user&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Name&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="s"&gt;注册成功"&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
        &lt;span class="p"&gt;}&lt;/span&gt;
    &lt;span class="p"&gt;}&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;我们在创建一个&lt;a href="https://www.cnblogs.com/JiYF/p/6439435.html" rel="nofollow" target="_blank" title=""&gt;装饰器&lt;/a&gt;类：&lt;/p&gt;
&lt;pre class="highlight csharp"&gt;&lt;code&gt;&lt;span class="k"&gt;using&lt;/span&gt; &lt;span class="nn"&gt;StaticDemo.Model&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="k"&gt;using&lt;/span&gt; &lt;span class="nn"&gt;StaticDemo.Services&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="k"&gt;using&lt;/span&gt; &lt;span class="nn"&gt;System&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

&lt;span class="k"&gt;namespace&lt;/span&gt; &lt;span class="nn"&gt;StaticDemo&lt;/span&gt;
&lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="c1"&gt;/// &amp;lt;summary&amp;gt;&lt;/span&gt;
    &lt;span class="c1"&gt;/// 装饰器类&lt;/span&gt;
    &lt;span class="c1"&gt;/// &amp;lt;/summary&amp;gt;&lt;/span&gt;
    &lt;span class="k"&gt;public&lt;/span&gt; &lt;span class="k"&gt;class&lt;/span&gt; &lt;span class="nc"&gt;AccountDecorator&lt;/span&gt; &lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="n"&gt;IAccountService&lt;/span&gt;
    &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="k"&gt;private&lt;/span&gt; &lt;span class="k"&gt;readonly&lt;/span&gt; &lt;span class="n"&gt;IAccountService&lt;/span&gt; &lt;span class="n"&gt;_accountService&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

        &lt;span class="k"&gt;public&lt;/span&gt; &lt;span class="nf"&gt;AccountDecorator&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;IAccountService&lt;/span&gt; &lt;span class="n"&gt;accountService&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
        &lt;span class="p"&gt;{&lt;/span&gt;
            &lt;span class="n"&gt;_accountService&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="n"&gt;accountService&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
        &lt;span class="p"&gt;}&lt;/span&gt;

        &lt;span class="k"&gt;public&lt;/span&gt; &lt;span class="k"&gt;void&lt;/span&gt; &lt;span class="nf"&gt;Reg&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;User&lt;/span&gt; &lt;span class="n"&gt;user&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
        &lt;span class="p"&gt;{&lt;/span&gt;
            &lt;span class="nf"&gt;Before&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;
            &lt;span class="c1"&gt;// 这里调用注册的方法，原有类里面的逻辑不会改变&lt;/span&gt;
            &lt;span class="c1"&gt;// 在逻辑前面和后面分别添加其他逻辑&lt;/span&gt;
            &lt;span class="n"&gt;_accountService&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;Reg&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;user&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
            &lt;span class="nf"&gt;After&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;
        &lt;span class="p"&gt;}&lt;/span&gt;

        &lt;span class="k"&gt;private&lt;/span&gt; &lt;span class="k"&gt;void&lt;/span&gt; &lt;span class="nf"&gt;Before&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;
        &lt;span class="p"&gt;{&lt;/span&gt;
            &lt;span class="n"&gt;Console&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;WriteLine&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"注册之前的逻辑"&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
        &lt;span class="p"&gt;}&lt;/span&gt;

        &lt;span class="k"&gt;private&lt;/span&gt; &lt;span class="k"&gt;void&lt;/span&gt; &lt;span class="nf"&gt;After&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;
        &lt;span class="p"&gt;{&lt;/span&gt;
            &lt;span class="n"&gt;Console&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;WriteLine&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"注册之后的逻辑"&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
        &lt;span class="p"&gt;}&lt;/span&gt;
    &lt;span class="p"&gt;}&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;我们会发现装饰器类同样实现了 IAccountService 接口。最后我们在 Main 方法里面调用：&lt;/p&gt;
&lt;pre class="highlight csharp"&gt;&lt;code&gt;&lt;span class="k"&gt;using&lt;/span&gt; &lt;span class="nn"&gt;StaticDemo.Model&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="k"&gt;using&lt;/span&gt; &lt;span class="nn"&gt;StaticDemo.Services&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="k"&gt;using&lt;/span&gt; &lt;span class="nn"&gt;System&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

&lt;span class="k"&gt;namespace&lt;/span&gt; &lt;span class="nn"&gt;StaticDemo&lt;/span&gt;
&lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="k"&gt;class&lt;/span&gt; &lt;span class="nc"&gt;Program&lt;/span&gt;
    &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="k"&gt;static&lt;/span&gt; &lt;span class="k"&gt;void&lt;/span&gt; &lt;span class="nf"&gt;Main&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="kt"&gt;string&lt;/span&gt;&lt;span class="p"&gt;[]&lt;/span&gt; &lt;span class="n"&gt;args&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
        &lt;span class="p"&gt;{&lt;/span&gt;
            &lt;span class="c1"&gt;// 实例化对象&lt;/span&gt;
            &lt;span class="n"&gt;IAccountService&lt;/span&gt; &lt;span class="n"&gt;accountService&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="k"&gt;new&lt;/span&gt; &lt;span class="nf"&gt;AccountService&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;
            &lt;span class="c1"&gt;// 实例化装饰器类，并用上面的实例给构造方法传值&lt;/span&gt;
            &lt;span class="kt"&gt;var&lt;/span&gt; &lt;span class="n"&gt;account&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="k"&gt;new&lt;/span&gt; &lt;span class="nf"&gt;AccountDecorator&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;accountService&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
            &lt;span class="kt"&gt;var&lt;/span&gt; &lt;span class="n"&gt;user&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="k"&gt;new&lt;/span&gt; &lt;span class="n"&gt;User&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="n"&gt;Name&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="s"&gt;"Rick"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;Password&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="s"&gt;"12345678"&lt;/span&gt; &lt;span class="p"&gt;};&lt;/span&gt;
            &lt;span class="c1"&gt;// 调用装饰器类的注册方法，相当于调用实例化对象的注册方法&lt;/span&gt;
            &lt;span class="n"&gt;account&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;Reg&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;user&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;

            &lt;span class="n"&gt;Console&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;ReadKey&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;
        &lt;span class="p"&gt;}&lt;/span&gt;
    &lt;span class="p"&gt;}&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;运行结果：&lt;/p&gt;

&lt;p&gt;&lt;img src="/uploads/photo/wangbaochen/93daa0dd-72e2-4740-ad42-a54134317b26.png!large" title="" alt=""&gt;&lt;/p&gt;

&lt;p&gt;下面我们在来看看如何使用&lt;a href="https://www.cnblogs.com/zhili/p/proxypattern.html" rel="nofollow" target="_blank" title=""&gt;代理模式&lt;/a&gt;实现。&lt;/p&gt;
&lt;pre class="highlight csharp"&gt;&lt;code&gt;&lt;span class="k"&gt;using&lt;/span&gt; &lt;span class="nn"&gt;StaticDemo.Model&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="k"&gt;using&lt;/span&gt; &lt;span class="nn"&gt;StaticDemo.Services&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="k"&gt;using&lt;/span&gt; &lt;span class="nn"&gt;System&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

&lt;span class="k"&gt;namespace&lt;/span&gt; &lt;span class="nn"&gt;StaticDemo&lt;/span&gt;
&lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="c1"&gt;/// &amp;lt;summary&amp;gt;&lt;/span&gt;
    &lt;span class="c1"&gt;/// 代理类&lt;/span&gt;
    &lt;span class="c1"&gt;/// &amp;lt;/summary&amp;gt;&lt;/span&gt;
    &lt;span class="k"&gt;public&lt;/span&gt; &lt;span class="k"&gt;class&lt;/span&gt; &lt;span class="nc"&gt;ProxyAccount&lt;/span&gt; &lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="n"&gt;IAccountService&lt;/span&gt;
    &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="k"&gt;private&lt;/span&gt; &lt;span class="k"&gt;readonly&lt;/span&gt; &lt;span class="n"&gt;IAccountService&lt;/span&gt; &lt;span class="n"&gt;_accountService&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

        &lt;span class="c1"&gt;/// &amp;lt;summary&amp;gt;&lt;/span&gt;
        &lt;span class="c1"&gt;/// 构造函数没有参数&lt;/span&gt;
        &lt;span class="c1"&gt;/// 直接在里面创建了AccountService类&lt;/span&gt;
        &lt;span class="c1"&gt;/// &amp;lt;/summary&amp;gt;&lt;/span&gt;
        &lt;span class="k"&gt;public&lt;/span&gt; &lt;span class="nf"&gt;ProxyAccount&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;
        &lt;span class="p"&gt;{&lt;/span&gt;
            &lt;span class="n"&gt;_accountService&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="k"&gt;new&lt;/span&gt; &lt;span class="nf"&gt;AccountService&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;
        &lt;span class="p"&gt;}&lt;/span&gt;

        &lt;span class="k"&gt;public&lt;/span&gt; &lt;span class="k"&gt;void&lt;/span&gt; &lt;span class="nf"&gt;Reg&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;User&lt;/span&gt; &lt;span class="n"&gt;user&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
        &lt;span class="p"&gt;{&lt;/span&gt;
            &lt;span class="nf"&gt;before&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;
            &lt;span class="n"&gt;_accountService&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;Reg&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;user&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
            &lt;span class="nf"&gt;after&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;
        &lt;span class="p"&gt;}&lt;/span&gt;

        &lt;span class="k"&gt;private&lt;/span&gt; &lt;span class="k"&gt;void&lt;/span&gt; &lt;span class="nf"&gt;before&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;
        &lt;span class="p"&gt;{&lt;/span&gt;
            &lt;span class="n"&gt;Console&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;WriteLine&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"代理：注册之前的逻辑"&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
        &lt;span class="p"&gt;}&lt;/span&gt;

        &lt;span class="k"&gt;private&lt;/span&gt; &lt;span class="k"&gt;void&lt;/span&gt; &lt;span class="nf"&gt;after&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;
        &lt;span class="p"&gt;{&lt;/span&gt;
            &lt;span class="n"&gt;Console&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;WriteLine&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"代理：注册之后的逻辑"&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
        &lt;span class="p"&gt;}&lt;/span&gt;
    &lt;span class="p"&gt;}&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Main 方法里面调用：&lt;/p&gt;
&lt;pre class="highlight csharp"&gt;&lt;code&gt;&lt;span class="k"&gt;using&lt;/span&gt; &lt;span class="nn"&gt;StaticDemo.Model&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="k"&gt;using&lt;/span&gt; &lt;span class="nn"&gt;StaticDemo.Services&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="k"&gt;using&lt;/span&gt; &lt;span class="nn"&gt;System&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

&lt;span class="k"&gt;namespace&lt;/span&gt; &lt;span class="nn"&gt;StaticDemo&lt;/span&gt;
&lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="k"&gt;class&lt;/span&gt; &lt;span class="nc"&gt;Program&lt;/span&gt;
    &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="k"&gt;static&lt;/span&gt; &lt;span class="k"&gt;void&lt;/span&gt; &lt;span class="nf"&gt;Main&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="kt"&gt;string&lt;/span&gt;&lt;span class="p"&gt;[]&lt;/span&gt; &lt;span class="n"&gt;args&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
        &lt;span class="p"&gt;{&lt;/span&gt;
            &lt;span class="err"&gt;#&lt;/span&gt;&lt;span class="n"&gt;region&lt;/span&gt; &lt;span class="err"&gt;装饰器模式&lt;/span&gt;
            &lt;span class="c1"&gt;//// 实例化对象&lt;/span&gt;
            &lt;span class="c1"&gt;//IAccountService accountService = new AccountService();&lt;/span&gt;
            &lt;span class="c1"&gt;//// 实例化装饰器类，并用上面的实例给构造方法传值&lt;/span&gt;
            &lt;span class="c1"&gt;//var account = new AccountDecorator(accountService);&lt;/span&gt;
            &lt;span class="c1"&gt;//var user = new User { Name = "Rick", Password = "12345678" };&lt;/span&gt;
            &lt;span class="c1"&gt;//// 调用装饰器类的注册方法，相当于调用实例化对象的注册方法&lt;/span&gt;
            &lt;span class="c1"&gt;//account.Reg(user);&lt;/span&gt;
            &lt;span class="err"&gt;#&lt;/span&gt;&lt;span class="n"&gt;endregion&lt;/span&gt;

            &lt;span class="err"&gt;#&lt;/span&gt;&lt;span class="n"&gt;region&lt;/span&gt; &lt;span class="err"&gt;代理模式&lt;/span&gt;
            &lt;span class="kt"&gt;var&lt;/span&gt; &lt;span class="n"&gt;account&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="k"&gt;new&lt;/span&gt; &lt;span class="nf"&gt;ProxyAccount&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;
            &lt;span class="kt"&gt;var&lt;/span&gt; &lt;span class="n"&gt;user&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="k"&gt;new&lt;/span&gt; &lt;span class="n"&gt;User&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="n"&gt;Name&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="s"&gt;"Tom"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;Password&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="s"&gt;"12345678"&lt;/span&gt; &lt;span class="p"&gt;};&lt;/span&gt;
            &lt;span class="n"&gt;account&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;Reg&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;user&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
            &lt;span class="err"&gt;#&lt;/span&gt;&lt;span class="n"&gt;endregion&lt;/span&gt;

            &lt;span class="n"&gt;Console&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;ReadKey&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;
        &lt;span class="p"&gt;}&lt;/span&gt;
    &lt;span class="p"&gt;}&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;运行结果：&lt;/p&gt;

&lt;p&gt;&lt;img src="/uploads/photo/wangbaochen/a9f61b99-5d05-4a82-a4da-f49474122b9d.png!large" title="" alt=""&gt;&lt;/p&gt;

&lt;p&gt;可能有的人会发现，装饰器类和代理类很相像，功能也一模一样，仅仅是构造函数不同。那么装饰器模式和代理模式有区别吗？有些东西，形式上看起来区别很小，但实际上他们区别很大。它们在形式上确实一样，不管是装饰器类还是代理类，它们都要实现相同的接口，但是它们在运用的时候还是有区别的。&lt;/p&gt;

&lt;p&gt;装饰器模式关注于在一个对象上动态添加方法，而代理模式关注于控制对象的访问。简单来说，使用代理模式，我们的代理类可以隐藏一个类的具体信息。var account = new ProxyAccount();仅看这段代码不看源码，不知道里面代理的是谁。&lt;/p&gt;

&lt;p&gt;当使用代理模式的时候，我们常常是在代理类中去创建一个对象的实例： _accountService = new AccountService()。而当我们使用装饰器模式的时候，我们通常是将原始对象作为一个参数传递给装饰器的构造函数。简单来说，在使用装饰器模式的时候，我们可以明确地知道装饰的是谁，而且更重要的是，代理类里面是写死的，在编译的时候就确定了关系。而装饰器是在运行时来确定的。&lt;/p&gt;
&lt;h3 id="2、动态代理"&gt;2、动态代理&lt;/h3&gt;
&lt;p&gt;动态代理实现也有两种方式；&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;通过代码织入的方式。例如 PostSharp 第三方插件。我们知道.NET 程序最终会编译成 IL 中间语言，在编译程序的时候，PostSharp 会动态的去修改 IL，在 IL 里面添加代码，这就是代码织入的方式。&lt;/li&gt;
&lt;li&gt;通过反射的方式实现。通过反射实现的方法非常多，也有很多实现了 AOP 的框架，例如 Unity、MVC 过滤器、Autofac 等&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;我们先来看看如何使用 PostSharp 实现动态代理。PostSharp 是一款收费的第三方插件。&lt;/p&gt;

&lt;p&gt;首先新创建一个控制台应用程序，然后创建一个订单业务类：&lt;/p&gt;
&lt;pre class="highlight csharp"&gt;&lt;code&gt;&lt;span class="k"&gt;using&lt;/span&gt; &lt;span class="nn"&gt;System&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

&lt;span class="k"&gt;namespace&lt;/span&gt; &lt;span class="nn"&gt;PostSharpDemo&lt;/span&gt;
&lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="c1"&gt;/// &amp;lt;summary&amp;gt;&lt;/span&gt;
    &lt;span class="c1"&gt;/// 订单业务类&lt;/span&gt;
    &lt;span class="c1"&gt;/// &amp;lt;/summary&amp;gt;&lt;/span&gt;
    &lt;span class="k"&gt;public&lt;/span&gt; &lt;span class="k"&gt;class&lt;/span&gt; &lt;span class="nc"&gt;OrderBusiness&lt;/span&gt;
    &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="k"&gt;public&lt;/span&gt; &lt;span class="k"&gt;void&lt;/span&gt; &lt;span class="nf"&gt;DoWork&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;
        &lt;span class="p"&gt;{&lt;/span&gt;
            &lt;span class="n"&gt;Console&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;WriteLine&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"执行订单业务"&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
        &lt;span class="p"&gt;}&lt;/span&gt;
    &lt;span class="p"&gt;}&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;接着在 Main 方法里面调用：&lt;/p&gt;
&lt;pre class="highlight csharp"&gt;&lt;code&gt;&lt;span class="k"&gt;using&lt;/span&gt; &lt;span class="nn"&gt;System&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

&lt;span class="k"&gt;namespace&lt;/span&gt; &lt;span class="nn"&gt;PostSharpDemo&lt;/span&gt;
&lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="k"&gt;class&lt;/span&gt; &lt;span class="nc"&gt;Program&lt;/span&gt;
    &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="k"&gt;static&lt;/span&gt; &lt;span class="k"&gt;void&lt;/span&gt; &lt;span class="nf"&gt;Main&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="kt"&gt;string&lt;/span&gt;&lt;span class="p"&gt;[]&lt;/span&gt; &lt;span class="n"&gt;args&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
        &lt;span class="p"&gt;{&lt;/span&gt;
            &lt;span class="n"&gt;OrderBusiness&lt;/span&gt; &lt;span class="n"&gt;order&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="k"&gt;new&lt;/span&gt; &lt;span class="nf"&gt;OrderBusiness&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;
            &lt;span class="c1"&gt;// 调用方法&lt;/span&gt;
            &lt;span class="n"&gt;order&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;DoWork&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;
            &lt;span class="n"&gt;Console&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;ReadKey&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;
        &lt;span class="p"&gt;}&lt;/span&gt;
    &lt;span class="p"&gt;}&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;运行结果：&lt;/p&gt;

&lt;p&gt;&lt;img src="/uploads/photo/wangbaochen/54482247-74b5-4a19-af36-af3546e0c80f.png!large" title="" alt=""&gt;&lt;/p&gt;

&lt;p&gt;这时又提出了一个新的需求，要去添加一个日志功能，记录业务的执行情况，按照以前的办法，需要定义一个日志帮助类：&lt;/p&gt;
&lt;pre class="highlight csharp"&gt;&lt;code&gt;&lt;span class="k"&gt;using&lt;/span&gt; &lt;span class="nn"&gt;System&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="k"&gt;using&lt;/span&gt; &lt;span class="nn"&gt;System.IO&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

&lt;span class="k"&gt;namespace&lt;/span&gt; &lt;span class="nn"&gt;PostSharpDemo&lt;/span&gt;
&lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="k"&gt;public&lt;/span&gt; &lt;span class="k"&gt;class&lt;/span&gt; &lt;span class="nc"&gt;LgoHelper&lt;/span&gt;
    &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="k"&gt;public&lt;/span&gt; &lt;span class="k"&gt;static&lt;/span&gt; &lt;span class="k"&gt;void&lt;/span&gt; &lt;span class="nf"&gt;RecoreLog&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="kt"&gt;string&lt;/span&gt; &lt;span class="n"&gt;message&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
        &lt;span class="p"&gt;{&lt;/span&gt;
            &lt;span class="kt"&gt;string&lt;/span&gt; &lt;span class="n"&gt;strPath&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="n"&gt;AppDomain&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;CurrentDomain&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;BaseDirectory&lt;/span&gt;&lt;span class="p"&gt;+&lt;/span&gt;&lt;span class="s"&gt;"\\log.txt"&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
            &lt;span class="k"&gt;using&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;StreamWriter&lt;/span&gt; &lt;span class="n"&gt;sw&lt;/span&gt;&lt;span class="p"&gt;=&lt;/span&gt;&lt;span class="k"&gt;new&lt;/span&gt; &lt;span class="nf"&gt;StreamWriter&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;strPath&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="k"&gt;true&lt;/span&gt;&lt;span class="p"&gt;))&lt;/span&gt;
            &lt;span class="p"&gt;{&lt;/span&gt;
                &lt;span class="n"&gt;sw&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;WriteLine&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;message&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
                &lt;span class="n"&gt;sw&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;Close&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;
            &lt;span class="p"&gt;}&lt;/span&gt;
        &lt;span class="p"&gt;}&lt;/span&gt;
    &lt;span class="p"&gt;}&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;如果不使用 AOP，我们就需要在记录日志的地方实例化 Loghelper 对象，然后记录日志：&lt;/p&gt;
&lt;pre class="highlight csharp"&gt;&lt;code&gt;&lt;span class="k"&gt;using&lt;/span&gt; &lt;span class="nn"&gt;System&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

&lt;span class="k"&gt;namespace&lt;/span&gt; &lt;span class="nn"&gt;PostSharpDemo&lt;/span&gt;
&lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="c1"&gt;/// &amp;lt;summary&amp;gt;&lt;/span&gt;
    &lt;span class="c1"&gt;/// 订单业务类&lt;/span&gt;
    &lt;span class="c1"&gt;/// &amp;lt;/summary&amp;gt;&lt;/span&gt;
    &lt;span class="k"&gt;public&lt;/span&gt; &lt;span class="k"&gt;class&lt;/span&gt; &lt;span class="nc"&gt;OrderBusiness&lt;/span&gt;
    &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="k"&gt;public&lt;/span&gt; &lt;span class="k"&gt;void&lt;/span&gt; &lt;span class="nf"&gt;DoWork&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;
        &lt;span class="p"&gt;{&lt;/span&gt;
            &lt;span class="c1"&gt;// 记录日志&lt;/span&gt;
            &lt;span class="n"&gt;LgoHelper&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;RecoreLog&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"执行业务前"&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
            &lt;span class="n"&gt;Console&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;WriteLine&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"执行订单业务"&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
            &lt;span class="n"&gt;LgoHelper&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;RecoreLog&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"执行业务后"&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
        &lt;span class="p"&gt;}&lt;/span&gt;
    &lt;span class="p"&gt;}&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;我们再次运行程序，查看结果：&lt;/p&gt;

&lt;p&gt;&lt;img src="/uploads/photo/wangbaochen/aa7f6636-f98f-46dc-84b5-1b5c1bf73372.png!large" title="" alt=""&gt;&lt;/p&gt;

&lt;p&gt;我们看看日志内容：&lt;/p&gt;

&lt;p&gt;&lt;img src="/uploads/photo/wangbaochen/dd08ba1c-f6b4-466a-942d-70dd8752bd25.png!large" title="" alt=""&gt;&lt;/p&gt;

&lt;p&gt;这样修改可以实现记录日志的功能。但是上面的方法会修改原先已有的代码，这就违反了开闭原则。而且添加日志也不是业务需求的变动，不应该去修改业务代码。下面使用 AOP 来实现。首先安装 PostSharp，直接在 NuGet 里面搜索，然后安装即可：&lt;/p&gt;

&lt;p&gt;然后定义一个 LogAttribute 类，继承自 OnMethodBoundaryAspect。这个 Aspect 提供了进入、退出函数等连接点方法。另外，Aspect 上必须设置 “[Serializable] ”，这与 PostSharp 内部对 Aspect 的生命周期管理有关：&lt;/p&gt;
&lt;pre class="highlight csharp"&gt;&lt;code&gt;&lt;span class="k"&gt;using&lt;/span&gt; &lt;span class="nn"&gt;PostSharp.Aspects&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="k"&gt;using&lt;/span&gt; &lt;span class="nn"&gt;System&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

&lt;span class="k"&gt;namespace&lt;/span&gt; &lt;span class="nn"&gt;PostSharpDemo&lt;/span&gt;
&lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="n"&gt;Serializable&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;
    &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="nf"&gt;AttributeUsage&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;AttributeTargets&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Method&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;AllowMultiple&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="k"&gt;false&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;Inherited&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="k"&gt;true&lt;/span&gt;&lt;span class="p"&gt;)]&lt;/span&gt;
    &lt;span class="k"&gt;public&lt;/span&gt; &lt;span class="k"&gt;class&lt;/span&gt; &lt;span class="nc"&gt;LogAttribute&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="n"&gt;OnMethodBoundaryAspect&lt;/span&gt;
    &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="k"&gt;public&lt;/span&gt; &lt;span class="kt"&gt;string&lt;/span&gt; &lt;span class="n"&gt;ActionName&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="k"&gt;get&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt; &lt;span class="k"&gt;set&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt;
        &lt;span class="k"&gt;public&lt;/span&gt; &lt;span class="k"&gt;override&lt;/span&gt; &lt;span class="k"&gt;void&lt;/span&gt; &lt;span class="nf"&gt;OnEntry&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;MethodExecutionArgs&lt;/span&gt; &lt;span class="n"&gt;eventArgs&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
        &lt;span class="p"&gt;{&lt;/span&gt;
            &lt;span class="n"&gt;LgoHelper&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;RecoreLog&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;ActionName&lt;/span&gt; &lt;span class="p"&gt;+&lt;/span&gt; &lt;span class="s"&gt;"开始执行业务前"&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
        &lt;span class="p"&gt;}&lt;/span&gt;

        &lt;span class="k"&gt;public&lt;/span&gt; &lt;span class="k"&gt;override&lt;/span&gt; &lt;span class="k"&gt;void&lt;/span&gt; &lt;span class="nf"&gt;OnExit&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;MethodExecutionArgs&lt;/span&gt; &lt;span class="n"&gt;eventArgs&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
        &lt;span class="p"&gt;{&lt;/span&gt;
            &lt;span class="n"&gt;LgoHelper&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;RecoreLog&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;ActionName&lt;/span&gt; &lt;span class="p"&gt;+&lt;/span&gt; &lt;span class="s"&gt;"业务执行完成后"&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
        &lt;span class="p"&gt;}&lt;/span&gt;
    &lt;span class="p"&gt;}&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;然后 Log 特性应用到 DoWork 函数上面：&lt;/p&gt;
&lt;pre class="highlight csharp"&gt;&lt;code&gt;&lt;span class="k"&gt;using&lt;/span&gt; &lt;span class="nn"&gt;System&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

&lt;span class="k"&gt;namespace&lt;/span&gt; &lt;span class="nn"&gt;PostSharpDemo&lt;/span&gt;
&lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="c1"&gt;/// &amp;lt;summary&amp;gt;&lt;/span&gt;
    &lt;span class="c1"&gt;/// 订单业务类&lt;/span&gt;
    &lt;span class="c1"&gt;/// &amp;lt;/summary&amp;gt;&lt;/span&gt;
    &lt;span class="k"&gt;public&lt;/span&gt; &lt;span class="k"&gt;class&lt;/span&gt; &lt;span class="nc"&gt;OrderBusiness&lt;/span&gt;
    &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="nf"&gt;Log&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;ActionName&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt;&lt;span class="s"&gt;"DoWork"&lt;/span&gt;&lt;span class="p"&gt;)]&lt;/span&gt;
        &lt;span class="k"&gt;public&lt;/span&gt; &lt;span class="k"&gt;void&lt;/span&gt; &lt;span class="nf"&gt;DoWork&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;
        &lt;span class="p"&gt;{&lt;/span&gt;
            &lt;span class="c1"&gt;// 记录日志&lt;/span&gt;
            &lt;span class="c1"&gt;// LgoHelper.RecoreLog("执行业务前");&lt;/span&gt;
            &lt;span class="n"&gt;Console&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;WriteLine&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"执行订单业务"&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
            &lt;span class="c1"&gt;// LgoHelper.RecoreLog("执行业务后");&lt;/span&gt;
        &lt;span class="p"&gt;}&lt;/span&gt;
    &lt;span class="p"&gt;}&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;这样修改以后，只需要在方法上面添加一个特性，以前记录日志的代码就可以注释掉了，这样就不会再修改业务逻辑代码了，运行程序：&lt;/p&gt;

&lt;p&gt;&lt;img src="/uploads/photo/wangbaochen/0388bdce-dcea-4fc2-85d8-00955786d09d.png!large" title="" alt=""&gt;&lt;/p&gt;

&lt;p&gt;在看看日志：&lt;/p&gt;

&lt;p&gt;&lt;img src="/uploads/photo/wangbaochen/9f203ed0-676e-4316-8d7d-fba27380c126.png!large" title="" alt=""&gt;&lt;/p&gt;

&lt;p&gt;这样就实现了 AOP 功能。&lt;/p&gt;

&lt;p&gt;我们在看看使用 Remoting 来实现动态代理。&lt;/p&gt;

&lt;p&gt;首先还是创建一个 User 实体类：&lt;/p&gt;
&lt;pre class="highlight csharp"&gt;&lt;code&gt;&lt;span class="k"&gt;namespace&lt;/span&gt; &lt;span class="nn"&gt;DynamicProxy.Model&lt;/span&gt;
&lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="k"&gt;public&lt;/span&gt; &lt;span class="k"&gt;class&lt;/span&gt; &lt;span class="nc"&gt;User&lt;/span&gt;
    &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="k"&gt;public&lt;/span&gt; &lt;span class="kt"&gt;string&lt;/span&gt; &lt;span class="n"&gt;Name&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="k"&gt;get&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt; &lt;span class="k"&gt;set&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt;
        &lt;span class="k"&gt;public&lt;/span&gt; &lt;span class="kt"&gt;string&lt;/span&gt; &lt;span class="n"&gt;Password&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="k"&gt;get&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt; &lt;span class="k"&gt;set&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt;
    &lt;span class="p"&gt;}&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;然后创建一个接口，里面有一个注册方法：&lt;/p&gt;
&lt;pre class="highlight csharp"&gt;&lt;code&gt;&lt;span class="k"&gt;using&lt;/span&gt; &lt;span class="nn"&gt;DynamicProxy.Model&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

&lt;span class="k"&gt;namespace&lt;/span&gt; &lt;span class="nn"&gt;DynamicProxy.Services&lt;/span&gt;
&lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="k"&gt;public&lt;/span&gt; &lt;span class="k"&gt;interface&lt;/span&gt; &lt;span class="nc"&gt;IAccountService&lt;/span&gt;
    &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="k"&gt;void&lt;/span&gt; &lt;span class="nf"&gt;Reg&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;User&lt;/span&gt; &lt;span class="n"&gt;user&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
    &lt;span class="p"&gt;}&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;然后创建接口的实现类：&lt;/p&gt;
&lt;pre class="highlight csharp"&gt;&lt;code&gt;&lt;span class="k"&gt;using&lt;/span&gt; &lt;span class="nn"&gt;DynamicProxy.Model&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="k"&gt;using&lt;/span&gt; &lt;span class="nn"&gt;System&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

&lt;span class="k"&gt;namespace&lt;/span&gt; &lt;span class="nn"&gt;DynamicProxy.Services&lt;/span&gt;
&lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="k"&gt;public&lt;/span&gt; &lt;span class="k"&gt;class&lt;/span&gt; &lt;span class="nc"&gt;AccountService&lt;/span&gt; &lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="n"&gt;MarshalByRefObject&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;IAccountService&lt;/span&gt;
    &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="k"&gt;public&lt;/span&gt; &lt;span class="k"&gt;void&lt;/span&gt; &lt;span class="nf"&gt;Reg&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;User&lt;/span&gt; &lt;span class="n"&gt;user&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
        &lt;span class="p"&gt;{&lt;/span&gt;
            &lt;span class="n"&gt;Console&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;WriteLine&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;$"&lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="n"&gt;user&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Name&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="s"&gt;注册成功"&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
        &lt;span class="p"&gt;}&lt;/span&gt;
    &lt;span class="p"&gt;}&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;然后创建一个泛型的动态代理类：&lt;/p&gt;
&lt;pre class="highlight csharp"&gt;&lt;code&gt;&lt;span class="k"&gt;using&lt;/span&gt; &lt;span class="nn"&gt;System&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="k"&gt;using&lt;/span&gt; &lt;span class="nn"&gt;System.Runtime.Remoting&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="k"&gt;using&lt;/span&gt; &lt;span class="nn"&gt;System.Runtime.Remoting.Messaging&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="k"&gt;using&lt;/span&gt; &lt;span class="nn"&gt;System.Runtime.Remoting.Proxies&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

&lt;span class="k"&gt;namespace&lt;/span&gt; &lt;span class="nn"&gt;DynamicProxy&lt;/span&gt;
&lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="k"&gt;public&lt;/span&gt; &lt;span class="k"&gt;class&lt;/span&gt; &lt;span class="nc"&gt;DynamicProxy&lt;/span&gt;&lt;span class="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="n"&gt;T&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="n"&gt;RealProxy&lt;/span&gt;
    &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="k"&gt;private&lt;/span&gt; &lt;span class="k"&gt;readonly&lt;/span&gt; &lt;span class="n"&gt;T&lt;/span&gt; &lt;span class="n"&gt;_target&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

        &lt;span class="c1"&gt;// 执行之前&lt;/span&gt;
        &lt;span class="k"&gt;public&lt;/span&gt; &lt;span class="n"&gt;Action&lt;/span&gt; &lt;span class="n"&gt;BeforeAction&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="k"&gt;get&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt; &lt;span class="k"&gt;set&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt;

        &lt;span class="c1"&gt;// 执行之后&lt;/span&gt;
        &lt;span class="k"&gt;public&lt;/span&gt; &lt;span class="n"&gt;Action&lt;/span&gt; &lt;span class="n"&gt;AfterAction&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="k"&gt;get&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt; &lt;span class="k"&gt;set&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt;

        &lt;span class="c1"&gt;// 被代理泛型类&lt;/span&gt;
        &lt;span class="k"&gt;public&lt;/span&gt; &lt;span class="nf"&gt;DynamicProxy&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;T&lt;/span&gt; &lt;span class="n"&gt;target&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="k"&gt;base&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="k"&gt;typeof&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;T&lt;/span&gt;&lt;span class="p"&gt;))&lt;/span&gt;
        &lt;span class="p"&gt;{&lt;/span&gt;
            &lt;span class="n"&gt;_target&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="n"&gt;target&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
        &lt;span class="p"&gt;}&lt;/span&gt;

        &lt;span class="c1"&gt;// 代理类调用方法&lt;/span&gt;
        &lt;span class="k"&gt;public&lt;/span&gt; &lt;span class="k"&gt;override&lt;/span&gt; &lt;span class="n"&gt;IMessage&lt;/span&gt; &lt;span class="nf"&gt;Invoke&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;IMessage&lt;/span&gt; &lt;span class="n"&gt;msg&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
        &lt;span class="p"&gt;{&lt;/span&gt;
            &lt;span class="kt"&gt;var&lt;/span&gt; &lt;span class="n"&gt;reqMsg&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="n"&gt;msg&lt;/span&gt; &lt;span class="k"&gt;as&lt;/span&gt; &lt;span class="n"&gt;IMethodCallMessage&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
            &lt;span class="kt"&gt;var&lt;/span&gt; &lt;span class="n"&gt;target&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="n"&gt;_target&lt;/span&gt; &lt;span class="k"&gt;as&lt;/span&gt; &lt;span class="n"&gt;MarshalByRefObject&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

            &lt;span class="nf"&gt;BeforeAction&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;
            &lt;span class="c1"&gt;// 这里才真正去执行代理类里面的方法&lt;/span&gt;
            &lt;span class="c1"&gt;// target表示被代理的对象，reqMsg表示要执行的方法&lt;/span&gt;
            &lt;span class="kt"&gt;var&lt;/span&gt; &lt;span class="n"&gt;result&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="n"&gt;RemotingServices&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;ExecuteMessage&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;target&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;reqMsg&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
            &lt;span class="nf"&gt;AfterAction&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;
            &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="n"&gt;result&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
        &lt;span class="p"&gt;}&lt;/span&gt;

    &lt;span class="p"&gt;}&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;我们看到，这个泛型动态代理类里面有两个泛型委托：BeforeAction、AfterAction。通过构造函数把代理泛型类传递进去。最后调用 Invoke 方法执行代理类的方法。&lt;/p&gt;

&lt;p&gt;最后我们还要创建一个代理工厂类，用来创建代理对象，通过调用动态代理来创建动态代理对象：&lt;/p&gt;
&lt;pre class="highlight csharp"&gt;&lt;code&gt;&lt;span class="k"&gt;using&lt;/span&gt; &lt;span class="nn"&gt;System&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

&lt;span class="k"&gt;namespace&lt;/span&gt; &lt;span class="nn"&gt;DynamicProxy&lt;/span&gt;
&lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="c1"&gt;/// &amp;lt;summary&amp;gt;&lt;/span&gt;
    &lt;span class="c1"&gt;/// 动态代理工厂类&lt;/span&gt;
    &lt;span class="c1"&gt;/// &amp;lt;/summary&amp;gt;&lt;/span&gt;
    &lt;span class="k"&gt;public&lt;/span&gt; &lt;span class="k"&gt;static&lt;/span&gt; &lt;span class="k"&gt;class&lt;/span&gt; &lt;span class="nc"&gt;ProxyFactory&lt;/span&gt;
    &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="k"&gt;public&lt;/span&gt; &lt;span class="k"&gt;static&lt;/span&gt; &lt;span class="n"&gt;T&lt;/span&gt; &lt;span class="n"&gt;Create&lt;/span&gt;&lt;span class="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="n"&gt;T&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;(&lt;/span&gt;&lt;span class="n"&gt;Action&lt;/span&gt; &lt;span class="n"&gt;before&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;Action&lt;/span&gt; &lt;span class="n"&gt;after&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
        &lt;span class="p"&gt;{&lt;/span&gt;
            &lt;span class="c1"&gt;// 实例化被代理泛型对象&lt;/span&gt;
            &lt;span class="n"&gt;T&lt;/span&gt; &lt;span class="n"&gt;instance&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="n"&gt;Activator&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;CreateInstance&lt;/span&gt;&lt;span class="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="n"&gt;T&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;();&lt;/span&gt;
            &lt;span class="c1"&gt;// 实例化动态代理，创建动态代理对象&lt;/span&gt;
            &lt;span class="kt"&gt;var&lt;/span&gt; &lt;span class="n"&gt;proxy&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="k"&gt;new&lt;/span&gt; &lt;span class="n"&gt;DynamicProxy&lt;/span&gt;&lt;span class="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="n"&gt;T&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;(&lt;/span&gt;&lt;span class="n"&gt;instance&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="n"&gt;BeforeAction&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="n"&gt;before&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;AfterAction&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="n"&gt;after&lt;/span&gt; &lt;span class="p"&gt;};&lt;/span&gt;
            &lt;span class="c1"&gt;// 返回透明代理对象&lt;/span&gt;
            &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;T&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="n"&gt;proxy&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;GetTransparentProxy&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;
        &lt;span class="p"&gt;}&lt;/span&gt;
    &lt;span class="p"&gt;}&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;我们最后在 Main 方法里面调用：&lt;/p&gt;
&lt;pre class="highlight csharp"&gt;&lt;code&gt;&lt;span class="k"&gt;using&lt;/span&gt; &lt;span class="nn"&gt;DynamicProxy.Model&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="k"&gt;using&lt;/span&gt; &lt;span class="nn"&gt;DynamicProxy.Services&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="k"&gt;using&lt;/span&gt; &lt;span class="nn"&gt;System&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

&lt;span class="k"&gt;namespace&lt;/span&gt; &lt;span class="nn"&gt;DynamicProxy&lt;/span&gt;
&lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="k"&gt;class&lt;/span&gt; &lt;span class="nc"&gt;Program&lt;/span&gt;
    &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="k"&gt;static&lt;/span&gt; &lt;span class="k"&gt;void&lt;/span&gt; &lt;span class="nf"&gt;Main&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="kt"&gt;string&lt;/span&gt;&lt;span class="p"&gt;[]&lt;/span&gt; &lt;span class="n"&gt;args&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
        &lt;span class="p"&gt;{&lt;/span&gt;
            &lt;span class="c1"&gt;// 调用动态代理工厂类创建动态代理对象，传递AccountService，并且传递两个委托&lt;/span&gt;
            &lt;span class="kt"&gt;var&lt;/span&gt; &lt;span class="n"&gt;acount&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="n"&gt;ProxyFactory&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Create&lt;/span&gt;&lt;span class="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="n"&gt;AccountService&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;(&lt;/span&gt;&lt;span class="n"&gt;before&lt;/span&gt;&lt;span class="p"&gt;:()&lt;/span&gt; &lt;span class="p"&gt;=&amp;gt;&lt;/span&gt;
            &lt;span class="p"&gt;{&lt;/span&gt;
                &lt;span class="n"&gt;Console&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;WriteLine&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"注册之前"&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
            &lt;span class="p"&gt;},&lt;/span&gt; &lt;span class="n"&gt;after&lt;/span&gt;&lt;span class="p"&gt;:()&lt;/span&gt; &lt;span class="p"&gt;=&amp;gt;&lt;/span&gt;
            &lt;span class="p"&gt;{&lt;/span&gt;
                &lt;span class="n"&gt;Console&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;WriteLine&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"注册之后"&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
            &lt;span class="p"&gt;});&lt;/span&gt;

            &lt;span class="n"&gt;User&lt;/span&gt; &lt;span class="n"&gt;user&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="k"&gt;new&lt;/span&gt; &lt;span class="nf"&gt;User&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt; 
            &lt;span class="p"&gt;{&lt;/span&gt;
             &lt;span class="n"&gt;Name&lt;/span&gt;&lt;span class="p"&gt;=&lt;/span&gt;&lt;span class="s"&gt;"张三"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
             &lt;span class="n"&gt;Password&lt;/span&gt;&lt;span class="p"&gt;=&lt;/span&gt;&lt;span class="s"&gt;"123456"&lt;/span&gt;
            &lt;span class="p"&gt;};&lt;/span&gt;
            &lt;span class="c1"&gt;// 调用注册方法&lt;/span&gt;
            &lt;span class="n"&gt;acount&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;Reg&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;user&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;

            &lt;span class="n"&gt;Console&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;ReadKey&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;
        &lt;span class="p"&gt;}&lt;/span&gt;
    &lt;span class="p"&gt;}&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;程序运行结果：&lt;/p&gt;

&lt;p&gt;&lt;img src="/uploads/photo/wangbaochen/8b2d6282-ec47-4ccd-9b78-f7d5114f8bd7.png!large" title="" alt=""&gt;&lt;/p&gt;

&lt;p&gt;这样就利用 Remoting 实现了动态代理。&lt;/p&gt;</description>
      <author>wangbaochen</author>
      <pubDate>Tue, 13 Jul 2021 11:25:12 +0800</pubDate>
      <link>https://xxb.lttc.cn/topics/234</link>
      <guid>https://xxb.lttc.cn/topics/234</guid>
    </item>
    <item>
      <title>关于越南语中 Decimal 转换错误的问题以及解决方案</title>
      <description>&lt;p&gt;由于服务器上的地区和文化设置是以 ，作为小数，.作为数字分组。所以在越南语中转换小数时失败。&lt;/p&gt;

&lt;p&gt;在越南语环境中可以使用以下语法：&lt;/p&gt;
&lt;pre class="highlight csharp"&gt;&lt;code&gt;&lt;span class="kt"&gt;var&lt;/span&gt; &lt;span class="n"&gt;decimalValue&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="n"&gt;Convert&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;ToDecimal&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;stringValue&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;CultureInfo&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;InvariantCulture&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;

&lt;span class="err"&gt;或&lt;/span&gt;

&lt;span class="kt"&gt;decimal&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;Parse&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;s&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;CultureInfo&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;InvariantCulture&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;

&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;或者设置区域与语言&lt;/p&gt;

&lt;p&gt;&lt;img src="/uploads/photo/wangbaochen/27e82f2d-88c6-4d56-bce0-8ddcc6a61dae.png!large" title="" alt=""&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;&lt;p&gt;失败&lt;br&gt;
&lt;img src="/uploads/photo/wangbaochen/9f9c6bf5-4281-4106-bd72-0cf35078fac6.png!large" title="" alt=""&gt;&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;成功&lt;br&gt;
&lt;img src="/uploads/photo/wangbaochen/f9074ef2-3504-4a8b-be03-3de3662776e2.png!large" title="" alt=""&gt;&lt;/p&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;img src="/uploads/photo/wangbaochen/3a7c8445-f6b3-4042-895d-9ea53c4ac33f.png!large" title="" alt=""&gt;&lt;/p&gt;</description>
      <author>wangbaochen</author>
      <pubDate>Thu, 10 Jun 2021 14:22:36 +0800</pubDate>
      <link>https://xxb.lttc.cn/topics/232</link>
      <guid>https://xxb.lttc.cn/topics/232</guid>
    </item>
    <item>
      <title>时间复杂度和空间复杂度以及空间换时间小例</title>
      <description>&lt;h2 id="时间复杂度和空间复杂度"&gt;时间复杂度和空间复杂度&lt;/h2&gt;
&lt;p&gt;通常不同的算法虽然结果一样但是消耗资源和时间却会有很大差别，衡量不同算法之间的优劣就要用到时间复杂度和空间复杂度&lt;/p&gt;
&lt;h2 id="时间复杂度"&gt;时间复杂度&lt;/h2&gt;
&lt;p&gt;时间指运行算法所需要的时间，那我们运行用 C# 程序看一下运行时间不得了，但是这个性能好的电脑和性能不好的电脑时间肯定是不同的。所以有了大 O 符号表示法 T（n）=O(f(n))&lt;/p&gt;

&lt;p&gt;下面这个 for 循环运行需要&lt;br&gt;
for (int i = 0; i &amp;lt; n; i++) 需要一个单位时间&lt;br&gt;
j = i;N 次需要 N 个单位时间&lt;br&gt;
j++;也是 N 次&lt;br&gt;
所以时间复杂度为 T（n）=（1+2n）* 单位时间&lt;br&gt;
简化为 O（n） &lt;/p&gt;
&lt;pre class="highlight csharp"&gt;&lt;code&gt;&lt;span class="k"&gt;for&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="kt"&gt;int&lt;/span&gt; &lt;span class="n"&gt;i&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="m"&gt;0&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt; &lt;span class="n"&gt;i&lt;/span&gt; &lt;span class="p"&gt;&amp;lt;&lt;/span&gt; &lt;span class="n"&gt;n&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt; &lt;span class="n"&gt;i&lt;/span&gt;&lt;span class="p"&gt;++)&lt;/span&gt;
&lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="n"&gt;j&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="n"&gt;i&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
    &lt;span class="n"&gt;j&lt;/span&gt;&lt;span class="p"&gt;++;&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;h3 id="常见的时间复杂度量级"&gt;常见的时间复杂度量级&lt;/h3&gt;
&lt;ol&gt;
&lt;li&gt;
&lt;p&gt;常数阶 O（1）&lt;/p&gt;

&lt;p&gt;就是没有复杂循环结构 不管执行多少行几万十几万行都按 O(1) 算&lt;/p&gt;
&lt;pre class="highlight csharp"&gt;&lt;code&gt;&lt;span class="n"&gt;j&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="n"&gt;i&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="n"&gt;j&lt;/span&gt;&lt;span class="p"&gt;++;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;线性阶 O(n)&lt;/p&gt;

&lt;p&gt;就是上面的 for 循环 消耗时间随着 N 变化而变化&lt;/p&gt;
&lt;pre class="highlight csharp"&gt;&lt;code&gt;&lt;span class="k"&gt;for&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="kt"&gt;int&lt;/span&gt; &lt;span class="n"&gt;i&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="m"&gt;0&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt; &lt;span class="n"&gt;i&lt;/span&gt; &lt;span class="p"&gt;&amp;lt;&lt;/span&gt; &lt;span class="n"&gt;n&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt; &lt;span class="n"&gt;i&lt;/span&gt;&lt;span class="p"&gt;++)&lt;/span&gt;
&lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="n"&gt;j&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="n"&gt;i&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
    &lt;span class="n"&gt;j&lt;/span&gt;&lt;span class="p"&gt;++;&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;对数阶 O(logN)&lt;/p&gt;

&lt;p&gt;下面的循环就按 i*2 何时=N，就是 2 的多少次方是 N，log2^N&lt;/p&gt;
&lt;pre class="highlight csharp"&gt;&lt;code&gt;&lt;span class="kt"&gt;int&lt;/span&gt; &lt;span class="n"&gt;i&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="m"&gt;1&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="k"&gt;while&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;i&lt;/span&gt;&lt;span class="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="n"&gt;n&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="n"&gt;i&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="n"&gt;i&lt;/span&gt; &lt;span class="p"&gt;*&lt;/span&gt; &lt;span class="m"&gt;2&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;线性对数阶 O(nlogN)&lt;/p&gt;

&lt;p&gt;就是 for 循环套 while&lt;/p&gt;
&lt;pre class="highlight csharp"&gt;&lt;code&gt;&lt;span class="k"&gt;for&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;m&lt;/span&gt;&lt;span class="p"&gt;=&lt;/span&gt;&lt;span class="m"&gt;1&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt; &lt;span class="n"&gt;m&lt;/span&gt;&lt;span class="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="n"&gt;n&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt; &lt;span class="n"&gt;m&lt;/span&gt;&lt;span class="p"&gt;++)&lt;/span&gt;
&lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="n"&gt;i&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="m"&gt;1&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
    &lt;span class="k"&gt;while&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;i&lt;/span&gt;&lt;span class="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="n"&gt;n&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="n"&gt;i&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="n"&gt;i&lt;/span&gt; &lt;span class="p"&gt;*&lt;/span&gt; &lt;span class="m"&gt;2&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
    &lt;span class="p"&gt;}&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;平方阶 O(n²)&lt;/p&gt;

&lt;p&gt;就是双重 for 循环
如果把里面一层改成 m 那么复杂度就是 O(m*n)&lt;/p&gt;
&lt;pre class="highlight csharp"&gt;&lt;code&gt;&lt;span class="k"&gt;for&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="kt"&gt;int&lt;/span&gt; &lt;span class="n"&gt;i&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="m"&gt;0&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt; &lt;span class="n"&gt;i&lt;/span&gt; &lt;span class="p"&gt;&amp;lt;&lt;/span&gt; &lt;span class="n"&gt;n&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt; &lt;span class="n"&gt;i&lt;/span&gt;&lt;span class="p"&gt;++)&lt;/span&gt;
&lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="k"&gt;for&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="kt"&gt;int&lt;/span&gt; &lt;span class="n"&gt;j&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="m"&gt;0&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt; &lt;span class="n"&gt;j&lt;/span&gt; &lt;span class="p"&gt;&amp;lt;&lt;/span&gt; &lt;span class="n"&gt;n&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt; &lt;span class="n"&gt;j&lt;/span&gt;&lt;span class="p"&gt;++)&lt;/span&gt;
    &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="n"&gt;j&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="n"&gt;i&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
        &lt;span class="n"&gt;j&lt;/span&gt;&lt;span class="p"&gt;++;&lt;/span&gt;
    &lt;span class="p"&gt;}&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;
&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;然后 for 越多就 K 次方阶 O(n^k) 这样子&lt;/p&gt;
&lt;h2 id="空间复杂度"&gt;空间复杂度&lt;/h2&gt;
&lt;ol&gt;
&lt;li&gt;
&lt;p&gt;O(1)&lt;/p&gt;

&lt;p&gt;就是算法执行临时空间不会随着 N 发生变化 都算成一个常量&lt;/p&gt;
&lt;pre class="highlight csharp"&gt;&lt;code&gt;&lt;span class="n"&gt;j&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="n"&gt;i&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="n"&gt;j&lt;/span&gt;&lt;span class="p"&gt;++;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;O(n)&lt;/p&gt;

&lt;p&gt;int[] m = new int[n] new 出来了 N 个所以是 N 下面循环并没有再开临时变量&lt;/p&gt;
&lt;pre class="highlight csharp"&gt;&lt;code&gt;&lt;span class="kt"&gt;int&lt;/span&gt;&lt;span class="p"&gt;[]&lt;/span&gt; &lt;span class="n"&gt;m&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="k"&gt;new&lt;/span&gt; &lt;span class="kt"&gt;int&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="n"&gt;n&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;
&lt;span class="k"&gt;for&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;i&lt;/span&gt;&lt;span class="p"&gt;=&lt;/span&gt;&lt;span class="m"&gt;1&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt; &lt;span class="n"&gt;i&lt;/span&gt;&lt;span class="p"&gt;&amp;lt;=&lt;/span&gt;&lt;span class="n"&gt;n&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt; &lt;span class="p"&gt;++&lt;/span&gt;&lt;span class="n"&gt;i&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="n"&gt;j&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="n"&gt;i&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
    &lt;span class="n"&gt;j&lt;/span&gt;&lt;span class="p"&gt;++;&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;
&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;空间复杂度其实和时间复杂度判断标准差不多主要是看开了多少个临时变量是否跟 N 有一定的线性关系&lt;/p&gt;

&lt;p&gt;这都是一些简单的如果是复杂的怎么计算呢 下面都计算时间复杂度为例子  &lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;T(n) = n + 29 一般说是 O(n) 因为常数项影响函数增长很小&lt;br&gt;
&lt;/li&gt;
&lt;li&gt;T(n) = n^3 + n^2 + 29 一般说为 O(n3)，n3 的增长速度是远超 n^2 的，同时 n^2 的增长速度是远超 n 的，所以有了高次项可以忽略低次项&lt;br&gt;
&lt;/li&gt;
&lt;li&gt;T(n) = 3n^3 一般说为 O(n^3) 因为阶数比乘数影响增长速度最显著我们通常忽略
比如这个时间复杂度为 max(O(n^2), O(n)) 及 O（n^2）&lt;br&gt;
&lt;/li&gt;
&lt;/ol&gt;
&lt;pre class="highlight csharp"&gt;&lt;code&gt; &lt;span class="k"&gt;void&lt;/span&gt; &lt;span class="nf"&gt;aFunc&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="kt"&gt;int&lt;/span&gt; &lt;span class="n"&gt;n&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="c1"&gt;// 第一部分时间复杂度为 O(n^2)&lt;/span&gt;
    &lt;span class="k"&gt;for&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="kt"&gt;int&lt;/span&gt; &lt;span class="n"&gt;i&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="m"&gt;0&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt; &lt;span class="n"&gt;i&lt;/span&gt; &lt;span class="p"&gt;&amp;lt;&lt;/span&gt; &lt;span class="n"&gt;n&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt; &lt;span class="n"&gt;i&lt;/span&gt;&lt;span class="p"&gt;++)&lt;/span&gt;
    &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="k"&gt;for&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="kt"&gt;int&lt;/span&gt; &lt;span class="n"&gt;j&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="m"&gt;0&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt; &lt;span class="n"&gt;j&lt;/span&gt; &lt;span class="p"&gt;&amp;lt;&lt;/span&gt; &lt;span class="n"&gt;n&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt; &lt;span class="n"&gt;j&lt;/span&gt;&lt;span class="p"&gt;++)&lt;/span&gt;
        &lt;span class="p"&gt;{&lt;/span&gt;
            &lt;span class="nf"&gt;printf&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"Hello, World!\n"&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;&lt;span class="c1"&gt;//时间复杂度为O(1)&lt;/span&gt;
        &lt;span class="p"&gt;}&lt;/span&gt;
    &lt;span class="p"&gt;}&lt;/span&gt;

    &lt;span class="c1"&gt;// 第二部分时间复杂度为 O(n)&lt;/span&gt;
    &lt;span class="k"&gt;for&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="kt"&gt;int&lt;/span&gt; &lt;span class="n"&gt;j&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="m"&gt;0&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt; &lt;span class="n"&gt;j&lt;/span&gt; &lt;span class="p"&gt;&amp;lt;&lt;/span&gt; &lt;span class="n"&gt;n&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt; &lt;span class="n"&gt;j&lt;/span&gt;&lt;span class="p"&gt;++)&lt;/span&gt;
    &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="nf"&gt;printf&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"Hello, World!\n"&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
    &lt;span class="p"&gt;}&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;下面这个 T（0）=T（1）=1&lt;br&gt;
T（n）=T(n-1)+T(n-2)+1 加一因为加法算一次执行&lt;br&gt;
会发现这就是个裴波那契数列&lt;/p&gt;
&lt;pre class="highlight csharp"&gt;&lt;code&gt;&lt;span class="kt"&gt;long&lt;/span&gt; &lt;span class="nf"&gt;aFunc&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="kt"&gt;int&lt;/span&gt; &lt;span class="n"&gt;n&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;n&lt;/span&gt; &lt;span class="p"&gt;&amp;lt;=&lt;/span&gt; &lt;span class="m"&gt;1&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="m"&gt;1&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
    &lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="k"&gt;else&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="nf"&gt;aFunc&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;n&lt;/span&gt; &lt;span class="p"&gt;-&lt;/span&gt; &lt;span class="m"&gt;1&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;+&lt;/span&gt; &lt;span class="nf"&gt;aFunc&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;n&lt;/span&gt; &lt;span class="p"&gt;-&lt;/span&gt; &lt;span class="m"&gt;2&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
    &lt;span class="p"&gt;}&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;&lt;img src="/uploads/photo/wangbaochen/aecf73a5-4ffa-45cd-a21c-c406acab314c.png!large" title="" alt=""&gt;&lt;/p&gt;

&lt;p&gt;就是每多一层计算就要一次单位时间&lt;br&gt;
就按第五层来算就是 2^3+1=7 次计算&lt;br&gt;
N 才需要 2^(n-2)+1 次计算&lt;br&gt;
那么时间复杂度就是 O（2(n-2)+1）常数项忽略就是 O（2n） &lt;/p&gt;
&lt;h3 id="空间换时间案例"&gt;空间换时间案例&lt;/h3&gt;
&lt;p&gt;在执行多次循环时，可以通过键值对的方式替换循环。达到空间换时间，提高运行效率。&lt;/p&gt;
&lt;pre class="highlight csharp"&gt;&lt;code&gt;&lt;span class="cp"&gt;#region 获取两个数组中名称相同的值的和
&lt;/span&gt;
&lt;span class="cp"&gt;#region 写法一
&lt;/span&gt;
&lt;span class="n"&gt;DateTime&lt;/span&gt; &lt;span class="n"&gt;time1&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="n"&gt;DateTime&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Now&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="kt"&gt;int&lt;/span&gt; &lt;span class="n"&gt;sum1&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="m"&gt;0&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="kt"&gt;int&lt;/span&gt; &lt;span class="n"&gt;num1&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="m"&gt;0&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="k"&gt;foreach&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="kt"&gt;var&lt;/span&gt; &lt;span class="n"&gt;class1&lt;/span&gt; &lt;span class="k"&gt;in&lt;/span&gt; &lt;span class="n"&gt;class1s&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="k"&gt;foreach&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="kt"&gt;var&lt;/span&gt; &lt;span class="n"&gt;class2&lt;/span&gt; &lt;span class="k"&gt;in&lt;/span&gt; &lt;span class="n"&gt;class2s&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;class1&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Name&lt;/span&gt; &lt;span class="p"&gt;==&lt;/span&gt; &lt;span class="n"&gt;class2&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Name&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
        &lt;span class="p"&gt;{&lt;/span&gt;
            &lt;span class="n"&gt;sum1&lt;/span&gt; &lt;span class="p"&gt;+=&lt;/span&gt; &lt;span class="n"&gt;class1&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Value&lt;/span&gt; &lt;span class="p"&gt;+&lt;/span&gt; &lt;span class="n"&gt;class2&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Value&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
        &lt;span class="p"&gt;}&lt;/span&gt;
        &lt;span class="n"&gt;num1&lt;/span&gt; &lt;span class="p"&gt;+=&lt;/span&gt; &lt;span class="m"&gt;1&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
    &lt;span class="p"&gt;}&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;span class="n"&gt;DateTime&lt;/span&gt; &lt;span class="n"&gt;time2&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="n"&gt;DateTime&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Now&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="n"&gt;Console&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;WriteLine&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;$"写法一：总和为&lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="n"&gt;sum1&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="s"&gt;，循环&lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="n"&gt;num1&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="s"&gt;次，耗时：&lt;/span&gt;&lt;span class="p"&gt;{(&lt;/span&gt;&lt;span class="n"&gt;time2&lt;/span&gt; &lt;span class="p"&gt;-&lt;/span&gt; &lt;span class="n"&gt;time1&lt;/span&gt;&lt;span class="p"&gt;).&lt;/span&gt;&lt;span class="n"&gt;TotalMilliseconds&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="s"&gt;毫秒"&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;

&lt;span class="cp"&gt;#endregion
&lt;/span&gt;
&lt;span class="cp"&gt;#region 写法二
&lt;/span&gt;
&lt;span class="n"&gt;DateTime&lt;/span&gt; &lt;span class="n"&gt;time3&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="n"&gt;DateTime&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Now&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="kt"&gt;int&lt;/span&gt; &lt;span class="n"&gt;sum2&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="m"&gt;0&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="kt"&gt;int&lt;/span&gt; &lt;span class="n"&gt;num2&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="m"&gt;0&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="k"&gt;foreach&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="kt"&gt;var&lt;/span&gt; &lt;span class="n"&gt;class1&lt;/span&gt; &lt;span class="k"&gt;in&lt;/span&gt; &lt;span class="n"&gt;class1s&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="k"&gt;foreach&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="kt"&gt;var&lt;/span&gt; &lt;span class="n"&gt;class2&lt;/span&gt; &lt;span class="k"&gt;in&lt;/span&gt; &lt;span class="n"&gt;class2s&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="n"&gt;num2&lt;/span&gt; &lt;span class="p"&gt;+=&lt;/span&gt; &lt;span class="m"&gt;1&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
        &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;class1&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Name&lt;/span&gt; &lt;span class="p"&gt;==&lt;/span&gt; &lt;span class="n"&gt;class2&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Name&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
        &lt;span class="p"&gt;{&lt;/span&gt;
            &lt;span class="n"&gt;sum2&lt;/span&gt; &lt;span class="p"&gt;+=&lt;/span&gt; &lt;span class="n"&gt;class1&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Value&lt;/span&gt; &lt;span class="p"&gt;+&lt;/span&gt; &lt;span class="n"&gt;class2&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Value&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
            &lt;span class="k"&gt;break&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
        &lt;span class="p"&gt;}&lt;/span&gt;
    &lt;span class="p"&gt;}&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;span class="n"&gt;DateTime&lt;/span&gt; &lt;span class="n"&gt;time4&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="n"&gt;DateTime&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Now&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="n"&gt;Console&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;WriteLine&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;$"写法二：总和为&lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="n"&gt;sum2&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="s"&gt;，循环&lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="n"&gt;num2&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="s"&gt;次，耗时：&lt;/span&gt;&lt;span class="p"&gt;{(&lt;/span&gt;&lt;span class="n"&gt;time4&lt;/span&gt; &lt;span class="p"&gt;-&lt;/span&gt; &lt;span class="n"&gt;time3&lt;/span&gt;&lt;span class="p"&gt;).&lt;/span&gt;&lt;span class="n"&gt;TotalMilliseconds&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="s"&gt;毫秒"&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;

&lt;span class="cp"&gt;#endregion
&lt;/span&gt;
&lt;span class="cp"&gt;#region 写法三
&lt;/span&gt;
&lt;span class="n"&gt;DateTime&lt;/span&gt; &lt;span class="n"&gt;time5&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="n"&gt;DateTime&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Now&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="kt"&gt;int&lt;/span&gt; &lt;span class="n"&gt;num3&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="m"&gt;0&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="n"&gt;Dictionary&lt;/span&gt;&lt;span class="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="kt"&gt;string&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="kt"&gt;int&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&lt;/span&gt; &lt;span class="n"&gt;pairs&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="k"&gt;new&lt;/span&gt; &lt;span class="n"&gt;Dictionary&lt;/span&gt;&lt;span class="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="kt"&gt;string&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="kt"&gt;int&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;();&lt;/span&gt;
&lt;span class="k"&gt;foreach&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="kt"&gt;var&lt;/span&gt; &lt;span class="n"&gt;class2&lt;/span&gt; &lt;span class="k"&gt;in&lt;/span&gt; &lt;span class="n"&gt;class2s&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="n"&gt;pairs&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;Add&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;class2&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Name&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;class2&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Value&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
    &lt;span class="n"&gt;num3&lt;/span&gt; &lt;span class="p"&gt;+=&lt;/span&gt; &lt;span class="m"&gt;1&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;

&lt;span class="kt"&gt;int&lt;/span&gt; &lt;span class="n"&gt;sum3&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="m"&gt;0&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="k"&gt;foreach&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="kt"&gt;var&lt;/span&gt; &lt;span class="n"&gt;class1&lt;/span&gt; &lt;span class="k"&gt;in&lt;/span&gt; &lt;span class="n"&gt;class1s&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;pairs&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;ContainsKey&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;class1&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Name&lt;/span&gt;&lt;span class="p"&gt;))&lt;/span&gt;
        &lt;span class="n"&gt;sum3&lt;/span&gt; &lt;span class="p"&gt;+=&lt;/span&gt; &lt;span class="n"&gt;class1&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Value&lt;/span&gt; &lt;span class="p"&gt;+&lt;/span&gt; &lt;span class="n"&gt;pairs&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="n"&gt;class1&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Name&lt;/span&gt;&lt;span class="p"&gt;];&lt;/span&gt;
    &lt;span class="n"&gt;num3&lt;/span&gt; &lt;span class="p"&gt;+=&lt;/span&gt; &lt;span class="m"&gt;1&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;span class="n"&gt;DateTime&lt;/span&gt; &lt;span class="n"&gt;time6&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="n"&gt;DateTime&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Now&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="n"&gt;Console&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;WriteLine&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;$"写法三：总和为&lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="n"&gt;sum3&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="s"&gt;，循环&lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="n"&gt;num3&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="s"&gt;次，耗时：&lt;/span&gt;&lt;span class="p"&gt;{(&lt;/span&gt;&lt;span class="n"&gt;time6&lt;/span&gt; &lt;span class="p"&gt;-&lt;/span&gt; &lt;span class="n"&gt;time5&lt;/span&gt;&lt;span class="p"&gt;).&lt;/span&gt;&lt;span class="n"&gt;TotalMilliseconds&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="s"&gt;毫秒"&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;span class="n"&gt;Console&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;ReadLine&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;

&lt;span class="cp"&gt;#endregion
&lt;/span&gt;
&lt;span class="cp"&gt;#endregion
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;&lt;img src="/uploads/photo/wangbaochen/d256015b-fcb7-4fab-9bd9-074d1421164e.png!large" title="" alt=""&gt;&lt;/p&gt;

&lt;p&gt;源码地址：&lt;a href="https://git.xxb.lttc.cn/wangbc/Demo/src/master/ONDemo" rel="nofollow" target="_blank"&gt;https://git.xxb.lttc.cn/wangbc/Demo/src/master/ONDemo&lt;/a&gt;&lt;/p&gt;</description>
      <author>wangbaochen</author>
      <pubDate>Fri, 04 Jun 2021 14:44:13 +0800</pubDate>
      <link>https://xxb.lttc.cn/topics/231</link>
      <guid>https://xxb.lttc.cn/topics/231</guid>
    </item>
    <item>
      <title>报表平台</title>
      <description>&lt;h2 id="报表平台"&gt;报表平台&lt;/h2&gt;
&lt;p&gt;为了满足日益增加的需求，在不改变现有报表的基础上进行升级。&lt;/p&gt;
&lt;h2 id="报表创建工具"&gt;报表创建工具&lt;/h2&gt;&lt;h3 id="报表定义"&gt;报表定义&lt;/h3&gt;
&lt;p&gt;增加以下功能：
&lt;img src="/uploads/photo/wangbaochen/1cca4661-cbb6-4ffd-8f97-37075fa82443.png!large" title="" alt=""&gt;&lt;/p&gt;
&lt;h4 id="是否手动选列"&gt;是否手动选列&lt;/h4&gt;
&lt;p&gt;勾选之后用户可以在方案管理界面中手动选择所需要获取的列。&lt;br&gt;
此属性需要配合报表配置信息和数据源一起使用。&lt;br&gt;
配置信息中需要配置查询字段，数据源中需要将列字段替换为关键字 {ColumnString}&lt;/p&gt;
&lt;h4 id="是否分页、默认每页行数"&gt;是否分页、默认每页行数&lt;/h4&gt;
&lt;p&gt;现在平面报表中增加分页功能，获取大量数据时，分次获取，减少等待时间。&lt;br&gt;
是否分页勾选之后，默认每页行数必须填写&lt;/p&gt;

&lt;p&gt;&lt;img src="/uploads/photo/wangbaochen/eeaa6ef3-1a53-4f21-9c1f-4008c2e7f8f4.gif!large" title="" alt=""&gt;&lt;/p&gt;
&lt;h4 id="平面报表展示模式"&gt;平面报表展示模式&lt;/h4&gt;
&lt;p&gt;在平面报表行双击可以将整行展示在一个 Group 中，方便多列时查看，第一次双击初始化比较慢。  &lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;行：默认选择，以行的形式显示&lt;/li&gt;
&lt;li&gt;浮动表格：双击当前行，在当前行附近展示浮动窗口。&lt;br&gt;
&lt;img src="/uploads/photo/wangbaochen/1b26e0cf-ebd3-4356-b4e6-c5ffe805d147.png!large" title="" alt=""&gt;
&lt;/li&gt;
&lt;li&gt;内嵌表格：双击当前行，在当前行下展示嵌入的窗口。
&lt;img src="/uploads/photo/wangbaochen/89c19d1c-2b3e-420f-9d34-196ecd4e213b.png!large" title="" alt=""&gt;
&lt;/li&gt;
&lt;li&gt;无行内嵌表格：双击当前行，隐藏当前行，在当前行位置显示嵌入的窗口。
&lt;img src="/uploads/photo/wangbaochen/3a418104-b691-4aea-9959-dcbe3bcfb5d1.png!large" title="" alt=""&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;h4 id="是否显示自由报表"&gt;是否显示自由报表&lt;/h4&gt;
&lt;p&gt;根据用户需求展示设计的界面，方便用户查看。&lt;/p&gt;

&lt;p&gt;&lt;img src="/uploads/photo/wangbaochen/fa282de7-1377-41fd-aa29-bb3b4954b061.png!large" title="" alt=""&gt;&lt;/p&gt;
&lt;h4 id="是否显示电子表格"&gt;是否显示电子表格&lt;/h4&gt;
&lt;p&gt;为了满足用户数据分析需求，添加一个基础的 EXCEL 电子表格，方便用户自己分析数据。&lt;br&gt;
由于电子表格控件比较多，加载会慢。&lt;/p&gt;

&lt;p&gt;&lt;img src="/uploads/photo/wangbaochen/af130a3c-b989-4312-a4d7-1fe81cb43d00.png!large" title="" alt=""&gt;&lt;/p&gt;
&lt;h4 id="是否显示仪表盘"&gt;是否显示仪表盘&lt;/h4&gt;
&lt;p&gt;为了满足用户数据展示需求，添加仪表盘报表，方便用户查看数据。&lt;/p&gt;

&lt;p&gt;&lt;img src="/uploads/photo/wangbaochen/fbe0edcb-0879-401f-ab5b-7ac75668bfa1.png!large" title="" alt=""&gt;&lt;/p&gt;
&lt;h4 id="报表数据源"&gt;报表数据源&lt;/h4&gt;
&lt;p&gt;为了配合报表功能，现添加如下关键字：&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;{ColumnString} :列信息，勾选手动选列时使用，还需配合列配置信息使用。&lt;/li&gt;
&lt;li&gt;{FilterString} :分页时使用，过滤或排序时使用此关键字。&lt;/li&gt;
&lt;li&gt;{GroupString} :，分组关键字，手动选列时使用，还需配合列配置信息使用，转译为 group by xxx,xxx&lt;/li&gt;
&lt;li&gt;{SortString} :排序关键字，手动选列时使用，还需配合列配置信息使用，转译为 order by xxx,xxx&lt;/li&gt;
&lt;li&gt;{PaginationString} :分页时使用，转义为 offset pageIndex row fetch next pageSize row only&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;img src="/uploads/photo/wangbaochen/f20fdd8c-007f-46cb-b0e4-9a0e3eeb153d.png!large" title="" alt=""&gt;&lt;/p&gt;
&lt;h3 id="报表配置信息"&gt;报表配置信息&lt;/h3&gt;
&lt;p&gt;在列配置信息中添加一下字段来满足报表需求：&lt;/p&gt;

&lt;p&gt;&lt;img src="/uploads/photo/wangbaochen/73200e58-ea35-4834-94a1-e019c90cdaf0.png!large" title="" alt=""&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;查询字段：配合列信息关键字使用，根据用户选择列拼接到 SQL 语句中。&lt;/li&gt;
&lt;li&gt;分组字段：配合分组关键字使用，根据用户选择列拼接到 SQL 语句中。&lt;/li&gt;
&lt;li&gt;排序字段：配合排序关键字使用，根据用户选择列拼接到 SQL 语句中。&lt;/li&gt;
&lt;li&gt;列过滤模式：根据配置的不同在平面报表列过滤时，显示不同的配置。

&lt;ul&gt;
&lt;li&gt;Excel：&lt;br&gt;
&lt;img src="/uploads/photo/wangbaochen/254679c9-9598-44d3-97c9-8016c26b5b85.png!large" title="" alt=""&gt;
&lt;/li&gt;
&lt;li&gt;List:&lt;br&gt;
&lt;img src="/uploads/photo/wangbaochen/d2ac5751-7049-4d12-bde9-09acbd854dc2.png!large" title="" alt=""&gt;
&lt;/li&gt;
&lt;li&gt;CheckedList:&lt;br&gt;
&lt;img src="/uploads/photo/wangbaochen/b6702571-8dc6-4149-8dec-d2c61f37fcc0.png!large" title="" alt=""&gt;
&lt;/li&gt;
&lt;li&gt;Date:&lt;br&gt;
&lt;img src="/uploads/photo/wangbaochen/8b39f4a4-1739-401a-ac46-258b54d2000f.png!large" title="" alt=""&gt;
&lt;/li&gt;
&lt;li&gt;DateSmart:&lt;br&gt;
&lt;img src="/uploads/photo/wangbaochen/7c562800-02fe-4b66-837c-31cc9e666cb0.png!large" title="" alt=""&gt;
&lt;/li&gt;
&lt;li&gt;DateAlt:&lt;br&gt;
&lt;img src="/uploads/photo/wangbaochen/7ea88444-626c-416e-be9a-c8a7ce40c26e.png!large" title="" alt=""&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;是否必取列：勾选之后，此列在用户手动选列时，不可选择，此列必取。&lt;/li&gt;
&lt;/ul&gt;
&lt;h2 id="报表"&gt;报表&lt;/h2&gt;&lt;h3 id="方案管理"&gt;方案管理&lt;/h3&gt;
&lt;p&gt;根据需求添加选择报表和选择列 Group.&lt;/p&gt;

&lt;p&gt;&lt;img src="/uploads/photo/wangbaochen/a2f8579d-612e-4f56-a500-8c9c1048ce6d.png!large" title="" alt=""&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;选择报表：展示报表设计中选择的报表类型，用户根据自身需求勾选需要实例化的报表。防止报表过多加载过慢。&lt;/li&gt;
&lt;li&gt;列选择：用户可以根据自身需要勾选获取的列，以及设置列顺序。&lt;/li&gt;
&lt;/ul&gt;
&lt;h2 id="数据库直连"&gt;数据库直连&lt;/h2&gt;
&lt;p&gt;现在报表支持数据库直连，设置方式和模块设置方式相同。&lt;/p&gt;

&lt;p&gt;&lt;img src="/uploads/photo/wangbaochen/3d6f1f1f-5c71-4ab3-97f1-f0c64db043c0.png!large" title="" alt=""&gt;&lt;/p&gt;

&lt;p&gt;&lt;img src="/uploads/photo/wangbaochen/be916289-4b7b-4b2d-8c9e-8345d6ed1133.png!large" title="" alt=""&gt;&lt;/p&gt;

&lt;p&gt;两处都需要勾选&lt;/p&gt;</description>
      <author>wangbaochen</author>
      <pubDate>Wed, 02 Jun 2021 15:25:32 +0800</pubDate>
      <link>https://xxb.lttc.cn/topics/230</link>
      <guid>https://xxb.lttc.cn/topics/230</guid>
    </item>
    <item>
      <title>ERP 导入 Excel 报错：System.Data.OleDb.OleDbException: 未指定的错误</title>
      <description>&lt;h2 id="问题描述"&gt;问题描述&lt;/h2&gt;
&lt;p&gt;ERP 导入 Excel 报错：&lt;/p&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;System.Data.OleDb.OleDbException: 未指定的错误
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;&lt;img src="/uploads/photo/xinggang/1e0c6105-1b03-4660-94bb-6482410ea8e2.png!large" title="" alt=""&gt;&lt;/p&gt;
&lt;h2 id="解决方案"&gt;解决方案&lt;/h2&gt;
&lt;p&gt;安装：&lt;a href="https://pan.lttc.cn/s/xxb?path=%2F01-%E5%B8%B8%E7%94%A8%E5%B7%A5%E5%85%B7%2FOffice" rel="nofollow" target="_blank" title=""&gt;AccessDatabaseEngine2007.exe&lt;/a&gt;&lt;/p&gt;</description>
      <author>xinggang</author>
      <pubDate>Wed, 28 Apr 2021 14:52:25 +0800</pubDate>
      <link>https://xxb.lttc.cn/topics/229</link>
      <guid>https://xxb.lttc.cn/topics/229</guid>
    </item>
    <item>
      <title>Docker 入门，万字详解</title>
      <description>&lt;h2 id="容器简介"&gt;容器简介&lt;/h2&gt;&lt;h3 id="什么是 Linux 容器"&gt;&lt;strong&gt;什么是 Linux 容器&lt;/strong&gt;&lt;/h3&gt;
&lt;p&gt;Linux 容器是与系统其他部分隔离开的一系列进程，从另一个镜像运行，并由该镜像提供支持进程所需的全部文件。&lt;/p&gt;

&lt;p&gt;容器提供的镜像包含了应用的所有依赖项，因而在从开发到测试再到生产的整个过程中，它都具有可移植性和一致性。&lt;/p&gt;

&lt;p&gt;&lt;img src="/uploads/photo/wangbaochen/03b39244-6354-47ca-9b38-fdeeb0d1f80f.png!large" title="" alt=""&gt;&lt;/p&gt;

&lt;p&gt;更加详细地来说，请您假定您在开发一个应用。您使用的是一台笔记本电脑，而且您的开发环境具有特定的配置。其他开发人员身处的环境配置可能稍有不同。您正在开发的应用依赖于您当前的配置，还要依赖于某些特定文件。&lt;/p&gt;

&lt;p&gt;与此同时，您的企业还拥有标准化的测试和生产环境，且具有自身的配置和一系列支持文件。&lt;/p&gt;

&lt;p&gt;您希望尽可能多在本地模拟这些环境，而不产生重新创建服务器环境的开销。&lt;/p&gt;

&lt;p&gt;因此，您要如何确保应用能够在这些环境中运行和通过质量检测，并且在部署过程中不出现令人头疼的问题，也无需重新编写代码和进行故障修复？答案就是使用容器。&lt;/p&gt;

&lt;p&gt;容器可以确保您的应用拥有必需的配置和文件，使得这些应用能够在从开发到测试、再到生产的整个流程中顺利运行，而不出现任何不良问题。这样可以避免危机，做到皆大欢喜。&lt;/p&gt;

&lt;p&gt;虽然这只是简化的示例，但在需要很高的可移植性、可配置性和隔离的情况下，我们可以利用 Linux 容器通过很多方式解决难题。&lt;/p&gt;

&lt;p&gt;无论基础架构是在企业内部还是在云端，或者混合使用两者，容器都能满足您的需求。&lt;/p&gt;
&lt;h3 id="容器不就是虚拟化吗"&gt;&lt;strong&gt;容器不就是虚拟化吗&lt;/strong&gt;&lt;/h3&gt;
&lt;p&gt;是，但也不竟然。我们用一种简单方式来思考一下：&lt;/p&gt;

&lt;p&gt;虚拟化使得许多操作系统可同时在单个系统上运行。&lt;/p&gt;

&lt;p&gt;容器则可共享同一个操作系统内核，将应用进程与系统其他部分隔离开。&lt;/p&gt;

&lt;p&gt;&lt;img src="/uploads/photo/wangbaochen/e981401f-f740-41d6-8071-1c0f130a9ce6.png!large" title="" alt=""&gt;&lt;/p&gt;

&lt;p&gt;这意味着什么？首先，让多个操作系统在单个虚拟机监控程序上运行以实现虚拟化，并不能达成和使用容器同等的轻量级效果。&lt;/p&gt;

&lt;p&gt;事实上，在仅拥有容量有限的有限资源时，您需要能够可以进行密集部署的轻量级应用。&lt;/p&gt;

&lt;p&gt;Linux 容器可从单个操作系统运行，在所有容器中共享该操作系统，因此应用和服务能够保持轻量级，并行快速运行。&lt;/p&gt;
&lt;h2 id="什么是 Docker？"&gt;什么是 Docker？&lt;/h2&gt;
&lt;p&gt;“Docker” 一词指代多种事物，包括开源社区项目、开源项目使用的工具、主导支持此类项目的公司 Docker Inc. 以及该公司官方支持的工具。技术产品和公司使用同一名称，的确让人有点困惑。&lt;/p&gt;

&lt;p&gt;我们来简单说明一下：&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;&lt;p&gt;🎍 IT 软件中所说的 “Docker” ，是指容器化技术，用于支持创建和使用 Linux 容器。&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;🎍 开源 Docker 社区致力于改进这类技术，并免费提供给所有用户，使之获益。&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;🎍 Docker Inc. 公司凭借 Docker 社区产品起家，它主要负责提升社区版本的安全性，并将改进后的版本与更广泛的技术社区分享。此外，它还专门对这些技术产品进行完善和安全固化，以服务于企业客户。&lt;/p&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;借助 Docker ，您可将容器当做重量轻、模块化的虚拟机使用。同时，您还将获得高度的灵活性，从而实现对容器的高效创建、部署及复制，并能将其从一个环境顺利迁移至另一个环境。&lt;/p&gt;
&lt;h3 id="Docker 如何工作？"&gt;&lt;strong&gt;Docker 如何工作？&lt;/strong&gt;&lt;/h3&gt;
&lt;p&gt;Docker 技术使用 Linux 内核和内核功能（例如 Cgroups 和 namespaces）来分隔进程，以便各进程相互独立运行。&lt;/p&gt;

&lt;p&gt;这种独立性正是采用容器的目的所在；它可以独立运行多种进程、多个应用程序，更加充分地发挥基础设施的作用，同时保持各个独立系统的安全性。&lt;/p&gt;

&lt;p&gt;容器工具（包括 Docker）可提供基于镜像的部署模式。这使得它能够轻松跨多种环境，与其依赖程序共享应用或服务组。Docker 还可在这一容器环境中自动部署应用程序（或者合并多种流程，以构建单个应用程序）。&lt;/p&gt;

&lt;p&gt;此外，由于这些工具基于 Linux 容器构建，使得 Docker 既易于使用，又别具一格 —— 它可为用户提供前所未有的高度应用程访问权限、快速部署以及版本控制和分发能力。&lt;/p&gt;
&lt;h3 id="Docker 技术是否与传统的 Linux 容器相同？"&gt;&lt;strong&gt;Docker 技术是否与传统的 Linux 容器相同？&lt;/strong&gt;&lt;/h3&gt;
&lt;p&gt;否。Docker 技术最初是基于 LXC 技术构建（大多数人都会将这一技术与 “传统的” Linux 容器联系在一起），但后来它逐渐摆脱了对这种技术的依赖。&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;就轻量级 虚拟化 这一功能来看，LXC 非常有用&lt;/strong&gt;，但它无法提供出色的开发人员或用户体验。除了运行容器之外，Docker 技术还具备其他多项功能，包括简化用于构建容器、传输镜像以及控制镜像版本的流程。&lt;/p&gt;

&lt;p&gt;&lt;img src="/uploads/photo/wangbaochen/eeec9b99-5fda-4995-8167-d2ad5ffe8cb8.png!large" title="" alt=""&gt;&lt;/p&gt;

&lt;p&gt;传统的 Linux 容器使用 init 系统来管理多种进程。这意味着，所有应用程序都作为一个整体运行。与此相反，Docker 技术鼓励应用程序各自独立运行其进程，并提供相应工具以实现这一功能。这种精细化运作模式自有其优势。&lt;/p&gt;
&lt;h3 id="docker 的目标"&gt;&lt;strong&gt;docker 的目标&lt;/strong&gt;&lt;/h3&gt;
&lt;p&gt;docker 的主要目标是 "Build,Ship and Run any App,Angwhere" 构建，运输，处处运行&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;构建：&lt;/strong&gt; 做一个 docker 镜像&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;运输：&lt;/strong&gt; docker pull&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;运行：&lt;/strong&gt; 启动一个容器&lt;/p&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;每一个容器，他都有自己的文件系统 rootfs.&lt;/p&gt;
&lt;h2 id="安装 Docker"&gt;安装 Docker&lt;/h2&gt;
&lt;p&gt;环境说明&lt;/p&gt;
&lt;pre class="highlight docker"&gt;&lt;code&gt;&lt;span class="c"&gt;# 需要两台几点进行安装&lt;/span&gt;
[root@docker01 ~]# cat /etc/redhat-release 
CentOS Linux release 7.2.1511 (Core) 
[root@docker01 ~]# uname  -r 
3.10.0-327.el7.x86_64
[root@docker01 ~]# hostname -I
10.0.0.100 172.16.1.100 
[root@docker02 ~]# hostname -I
10.0.0.101 172.16.1.101
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;在&lt;strong&gt;两个节点&lt;/strong&gt;上都进行操作&lt;/p&gt;
&lt;pre class="highlight docker"&gt;&lt;code&gt;wget -O /etc/yum.repos.d/docker-ce.repo https://mirrors.ustc.edu.cn/docker-ce/linux/centos/docker-ce.repo
sed -i 's&lt;span class="c"&gt;#download.docker.com#mirrors.ustc.edu.cn/docker-ce#g' /etc/yum.repos.d/docker-ce.repo&lt;/span&gt;
yum install docker-ce -y
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;修改在 docker01 配置：&lt;/p&gt;
&lt;pre class="highlight docker"&gt;&lt;code&gt;&lt;span class="c"&gt;# 修改启动文件，监听远程端口&lt;/span&gt;
vim /usr/lib/systemd/system/docker.service
ExecStart=/usr/bin/dockerd -H unix:///var/run/docker.sock -H tcp://10.0.0.100:2375
systemctl daemon-reload
systemctl enable docker.service 
systemctl restart docker.service
&lt;span class="c"&gt;# ps -ef检查进行，是否启动&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;在 docker02 测试&lt;/p&gt;
&lt;pre class="highlight docker"&gt;&lt;code&gt;[root@docker02 ~]# docker -H 10.0.0.100 info
Containers: 0
 Running: 0
 Paused: 0
 Stopped: 0
Images: 0
Server Version: 17.12.0-ce
Storage Driver: devicemapper
···
&lt;/code&gt;&lt;/pre&gt;&lt;h3 id="Docker 基础命令操作"&gt;&lt;strong&gt;Docker 基础命令操作&lt;/strong&gt;&lt;/h3&gt;
&lt;p&gt;查看 docker 相关信息&lt;/p&gt;
&lt;pre class="highlight docker"&gt;&lt;code&gt;[root@docker01 ~]#  docker version  
Client:
 Version:    17.12.0-ce
 API version:    1.35
 Go version:    go1.9.2
 Git commit:    c97c6d6
 Built:    Wed Dec 27 20:10:14 2017
 OS/Arch:    linux/amd64
Server:
 Engine:
  Version:    17.12.0-ce
  API version:    1.35 (minimum version 1.12)
  Go version:    go1.9.2
  Git commit:    c97c6d6
  Built:    Wed Dec 27 20:12:46 2017
  OS/Arch:    linux/amd64
  Experimental:    false
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;配置 docker 镜像加速&lt;/p&gt;
&lt;pre class="highlight json"&gt;&lt;code&gt;&lt;span class="err"&gt;vi&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="err"&gt;/etc/docker/daemon.json&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"registry-mirrors"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="s2"&gt;"https://registry.docker-cn.com"&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;   
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;h3 id="启动第一个容器"&gt;&lt;strong&gt;启动第一个容器&lt;/strong&gt;&lt;/h3&gt;&lt;pre class="highlight docker"&gt;&lt;code&gt;[root@docker01 ~]# docker run -d -p 80:80 nginx
Unable to find image 'nginx:latest' locally
latest: Pulling from library/nginx
e7bb522d92ff: Pull complete 
6edc05228666: Pull complete 
cd866a17e81f: Pull complete 
Digest: sha256:285b49d42c703fdf257d1e2422765c4ba9d3e37768d6ea83d7fe2043dad6e63d
Status: Downloaded newer image for nginx:latest
8d8f81da12b5c10af6ba1a5d07f4abc041cb95b01f3d632c3d638922800b0b4d
&lt;span class="c"&gt;# 容器启动后，在浏览器进行访问测试 &lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;参数说明&lt;/p&gt;

&lt;p&gt;&lt;img src="/uploads/photo/wangbaochen/14b1802b-49b4-41e3-abc9-d35a0b58f9f3.png!large" title="" alt=""&gt;&lt;/p&gt;
&lt;h3 id="Docker 镜像生命周期"&gt;&lt;strong&gt;Docker 镜像生命周期&lt;/strong&gt;&lt;/h3&gt;
&lt;p&gt;&lt;img src="/uploads/photo/wangbaochen/1308fbed-764d-4ccd-b067-43be9bebb68a.png!large" title="" alt=""&gt;&lt;/p&gt;
&lt;h2 id="docker 镜像相关操作"&gt;docker 镜像相关操作&lt;/h2&gt;&lt;h4 id="搜索官方仓库镜像"&gt;&lt;strong&gt;搜索官方仓库镜像&lt;/strong&gt;&lt;/h4&gt;&lt;pre class="highlight docker"&gt;&lt;code&gt;[root@docker01 ~]#  docker search centos
NAME                      DESCRIPTION                    STARS    OFFICIAL               AUTOMATED
centos                    The official build of CentOS.  3992     [OK]      
ansible/centos7-ansible   Ansible on Centos7             105                              [OK]
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;列表说明&lt;/p&gt;

&lt;p&gt;&lt;img src="/uploads/photo/wangbaochen/fa821c13-744a-41ee-aeea-470146b72024.png!large" title="" alt=""&gt;&lt;/p&gt;
&lt;h4 id="获取镜像"&gt;获取镜像&lt;/h4&gt;
&lt;p&gt;根据镜像名称拉取镜像&lt;/p&gt;
&lt;pre class="highlight docker"&gt;&lt;code&gt;[root@docker01 ~]# docker pull centos
Using default tag: latest
latest: Pulling from library/centos
af4b0a2388c6: Downloading  34.65MB/73.67MB
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;查看当前主机镜像列表&lt;/p&gt;
&lt;pre class="highlight docker"&gt;&lt;code&gt;[root@docker01 ~]# docker image list 
REPOSITORY          TAG                 IMAGE ID            CREATED             SIZE
centos              latest              ff426288ea90        3 weeks ago         207MB
nginx               latest              3f8a4339aadd        5 weeks ago         108MB
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;拉第三方镜像方法&lt;/p&gt;
&lt;pre class="highlight docker"&gt;&lt;code&gt;docker pull index.tenxcloud.com/tenxcloud/httpd
&lt;/code&gt;&lt;/pre&gt;&lt;h4 id="导出镜像"&gt;导出镜像&lt;/h4&gt;&lt;pre class="highlight docker"&gt;&lt;code&gt;[root@docker01 ~]# docker image list 
REPOSITORY          TAG                 IMAGE ID            CREATED             SIZE
centos              latest              ff426288ea90        3 weeks ago         207MB
nginx               latest              3f8a4339aadd        5 weeks ago         108MB
&lt;span class="c"&gt;# 导出&lt;/span&gt;
[root@docker01 ~]# docker image save centos &amp;gt; docker-centos.tar.gz
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;#### &lt;strong&gt;删除镜像&lt;/strong&gt;&lt;/p&gt;
&lt;pre class="highlight docker"&gt;&lt;code&gt;[root@docker01 ~]# docker image rm centos:latest
[root@docker01 ~]# docker image list 
REPOSITORY          TAG                 IMAGE ID            CREATED             SIZE
nginx               latest              3f8a4339aadd        5 weeks ago         108MB
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;#### &lt;strong&gt;导入镜像&lt;/strong&gt;&lt;/p&gt;
&lt;pre class="highlight docker"&gt;&lt;code&gt;[root@docker01 ~]# docker image load -i docker-centos.tar.gz  
e15afa4858b6: Loading layer  215.8MB/215.8MB
Loaded image: centos:latest
[root@docker01 ~]# docker image list 
REPOSITORY          TAG                 IMAGE ID            CREATED             SIZE
centos              latest              ff426288ea90        3 weeks ago         207MB
nginx               latest              3f8a4339aadd        5 weeks ago         108MB
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;#### &lt;strong&gt;查看镜像的详细信息&lt;/strong&gt;&lt;/p&gt;
&lt;pre class="highlight docker"&gt;&lt;code&gt;[root@docker01 ~]# docker image inspect centos
&lt;/code&gt;&lt;/pre&gt;&lt;h2 id="容器的日常管理"&gt;容器的日常管理&lt;/h2&gt;
&lt;p&gt;#### &lt;strong&gt;容器的起/停&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;最简单的运行一个容器&lt;/p&gt;
&lt;pre class="highlight docker"&gt;&lt;code&gt;[root@docker01 ~]# docker run nginx
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;创建容器，两步走（不常用）&lt;/p&gt;
&lt;pre class="highlight docker"&gt;&lt;code&gt;[root@docker01 ~]# docker create centos:latest  /bin/bash
bb7f32368ecf0492adb59e20032ab2e6cf6a563a0e6751e58930ee5f7aaef204
[root@docker01 ~]# docker start stupefied_nobel
stupefied_nobel
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;快速启动容器方法&lt;/p&gt;
&lt;pre class="highlight docker"&gt;&lt;code&gt;[root@docker01 ~]# docker run  centos:latest  /usr/bin/sleep 20;
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;&lt;strong&gt;容器内的第一个进程必须一直处于运行的状态，否则这个容器，就会处于退出状态！&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;查看正在运行的容器&lt;/p&gt;
&lt;pre class="highlight docker"&gt;&lt;code&gt;[root@docker01 ~]# docker container ls
    或
[root@docker01 ~]# docker ps 
CONTAINER ID        IMAGE               COMMAND                  CREATED             STATUS              PORTS               NAMES
8708e93fd767        nginx               "nginx -g 'daemon of…"   6 seconds ago       Up 4 seconds        80/tcp              keen_lewi
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;查看你容器详细信息/ip&lt;/p&gt;
&lt;pre class="highlight docker"&gt;&lt;code&gt;[root@docker01 ~]# docker container  inspect  容器名称/id
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;查看你所有容器（包括未运行的）&lt;/p&gt;
&lt;pre class="highlight docker"&gt;&lt;code&gt;[root@docker01 ~]# docker ps -a
CONTAINER ID        IMAGE               COMMAND                  CREATED             STATUS                      PORTS               NAMES
8708e93fd767        nginx               "nginx -g 'daemon of…"   4 minutes ago       Exited (0) 59 seconds ago                       keen_lewin
f9f3e6af7508        nginx               "nginx -g 'daemon of…"   5 minutes ago       Exited (0) 5 minutes ago                        optimistic_haibt
8d8f81da12b5        nginx               "nginx -g 'daemon of…"   3 hours ago         Exited (0) 3 hours ago                          lucid_bohr
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;停止容器&lt;/p&gt;
&lt;pre class="highlight docker"&gt;&lt;code&gt;[root@docker01 ~]# docker stop 容器名称/id 
或
[root@docker01 ~]# docker container  kill  容器名称/id
&lt;/code&gt;&lt;/pre&gt;&lt;h3 id="进入容器方法"&gt;&lt;strong&gt;进入容器方法&lt;/strong&gt;&lt;/h3&gt;
&lt;p&gt;&lt;strong&gt;启动时进去方法&lt;/strong&gt;&lt;/p&gt;
&lt;pre class="highlight docker"&gt;&lt;code&gt;[root@docker01 ~]# docker run -it &lt;span class="c"&gt;#参数：-it 可交互终端&lt;/span&gt;
[root@docker01 ~]# docker run -it nginx:latest  /bin/bash
root@79241093859e:/#
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;退出/离开容器&lt;/p&gt;
&lt;pre class="highlight docker"&gt;&lt;code&gt;ctrl+p &amp;amp; ctrl+q
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;&lt;strong&gt;启动后进入容器的方法&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;启动一个 docker&lt;/p&gt;
&lt;pre class="highlight docker"&gt;&lt;code&gt;[root@docker01 ~]# docker run -it centos:latest 
[root@1bf0f43c4d2f /]# ps -ef 
UID         PID   PPID  C STIME TTY          TIME CMD
root          1      0  0 15:47 pts/0    00:00:00 /bin/bash
root         13      1  0 15:47 pts/0    00:00:00 ps -ef
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;attach 进入容器，使用 pts/0 ，会让所用通过此方法进如放入用户看到同样的操作。&lt;/p&gt;
&lt;pre class="highlight docker"&gt;&lt;code&gt;[root@docker01 ~]# docker attach 1bf0f43c4d2f
[root@1bf0f43c4d2f /]# ps -ef 
UID         PID   PPID  C STIME TTY          TIME CMD
root          1      0  0 15:47 pts/0    00:00:00 /bin/bash
root         14      1  0 15:49 pts/0    00:00:00 ps -ef
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;自命名启动一个容器 --name&lt;/p&gt;
&lt;pre class="highlight docker"&gt;&lt;code&gt;[root@docker01 ~]# docker attach 1bf0f43c4d2f
[root@1bf0f43c4d2f /]# ps -ef 
UID         PID   PPID  C STIME TTY          TIME CMD
root          1      0  0 15:47 pts/0    00:00:00 /bin/bash
root         14      1  0 15:49 pts/0    00:00:00 ps -ef
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;exec 进入容器方法（推荐使用）&lt;/p&gt;
&lt;pre class="highlight docker"&gt;&lt;code&gt;[root@docker01 ~]# docker exec -it clsn1  /bin/bash 
[root@b20fa75b4b40 /]# 重新分配一个终端
[root@b20fa75b4b40 /]# ps -ef 
UID         PID   PPID  C STIME TTY          TIME CMD
root          1      0  0 16:11 pts/0    00:00:00 /bin/bash
root         13      0  0 16:14 pts/1    00:00:00 /bin/bash
root         26     13  0 16:14 pts/1    00:00:00 ps -ef
&lt;/code&gt;&lt;/pre&gt;&lt;h3 id="删除所有容器"&gt;&lt;strong&gt;删除所有容器&lt;/strong&gt;&lt;/h3&gt;&lt;pre class="highlight docker"&gt;&lt;code&gt;[root@docker01 ~]# docker rm -f  `docker ps -a -q`
# -f 强制删除
&lt;/code&gt;&lt;/pre&gt;&lt;h3 id="启动时进行端口映射"&gt;&lt;strong&gt;启动时进行端口映射&lt;/strong&gt;&lt;/h3&gt;
&lt;p&gt;-p 参数端口映射&lt;/p&gt;
&lt;pre class="highlight docker"&gt;&lt;code&gt;[root@docker01 ~]# docker run -d -p 8888:80  nginx:latest 
287bec5c60263166c03e1fc5b0b8262fe76507be3dfae4ce5cd2ee2d1e8a89a9
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;不同指定映射方法&lt;/p&gt;

&lt;p&gt;&lt;img src="/uploads/photo/wangbaochen/3ef430e6-0758-4b14-a91d-2f247dbbd657.png!large" title="" alt=""&gt;&lt;/p&gt;

&lt;p&gt;随机映射&lt;/p&gt;
&lt;pre class="highlight docker"&gt;&lt;code&gt;docker run -P （大P）# 需要镜像支持
&lt;/code&gt;&lt;/pre&gt;&lt;h2 id="Docker 数据卷的管理"&gt;Docker 数据卷的管理&lt;/h2&gt;&lt;h3 id="挂载时创建卷"&gt;&lt;strong&gt;挂载时创建卷&lt;/strong&gt;&lt;/h3&gt;
&lt;p&gt;挂载卷&lt;/p&gt;
&lt;pre class="highlight docker"&gt;&lt;code&gt;[root@docker01 ~]# docker run -d -p 80:80 -v /data:/usr/share/nginx/html nginx:latest
079786c1e297b5c5031e7a841160c74e91d4ad06516505043c60dbb78a259d09
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;容器内站点目录: /usr/share/nginx/html&lt;/p&gt;

&lt;p&gt;在宿主机写入数据，查看&lt;/p&gt;
&lt;pre class="highlight docker"&gt;&lt;code&gt;[root@docker01 ~]# echo "http://www.nmtui.com" &amp;gt;/data/index.html
[root@docker01 ~]# curl 10.0.0.100
http://www.nmtui.com
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;设置共享卷，使用同一个卷启动一个新的容器&lt;/p&gt;
&lt;pre class="highlight docker"&gt;&lt;code&gt;[root@docker01 ~]# docker run -d -p 8080:80 -v /data:/usr/share/nginx/html nginx:latest 
351f0bd78d273604bd0971b186979aa0f3cbf45247274493d2490527babb4e42
[root@docker01 ~]# curl 10.0.0.100:8080
http://www.nmtui.com
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;查看卷列表&lt;/p&gt;
&lt;pre class="highlight docker"&gt;&lt;code&gt;[root@docker01 ~]# docker volume ls
DRIVER              VOLUME NAME
&lt;/code&gt;&lt;/pre&gt;&lt;h3 id="创建卷后挂载"&gt;&lt;strong&gt;创建卷后挂载&lt;/strong&gt;&lt;/h3&gt;
&lt;p&gt;创建一个卷&lt;/p&gt;
&lt;pre class="highlight docker"&gt;&lt;code&gt;[root@docker01 ~]# docker volume create 
f3b95f7bd17da220e63d4e70850b8d7fb3e20f8ad02043423a39fdd072b83521
[root@docker01 ~]# docker volume ls 
DRIVER              VOLUME NAME
local               f3b95f7bd17da220e63d4e70850b8d7fb3e20f8ad02043423a39fdd072b83521
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;指定卷名&lt;/p&gt;
&lt;pre class="highlight docker"&gt;&lt;code&gt;[root@docker01 ~]# docker volume ls 
DRIVER              VOLUME NAME
local               clsn
local               f3b95f7bd17da220e63d4e70850b8d7fb3e20f8ad02043423a39fdd072b83521
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;查看卷路径&lt;/p&gt;
&lt;pre class="highlight docker"&gt;&lt;code&gt;[root@docker01 ~]# docker volume inspect clsn 
[
    {
        "CreatedAt": "2018-02-01T00:39:25+08:00",
        "Driver": "local",
        "Labels": {},
        "Mountpoint": "/var/lib/docker/volumes/clsn/_data",
        "Name": "clsn",
        "Options": {},
        "Scope": "local"
    }
]
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;使用卷创建&lt;/p&gt;
&lt;pre class="highlight docker"&gt;&lt;code&gt;[root@docker01 ~]# docker run -d -p 9000:80 -v clsn:/usr/share/nginx/html nginx:latest 
1434559cff996162da7ce71820ed8f5937fb7c02113bbc84e965845c219d3503
&lt;span class="c"&gt;# 宿主机测试&lt;/span&gt;
[root@docker01 ~]# echo 'blog.nmtui.com' &amp;gt;/var/lib/docker/volumes/clsn/_data/index.html 
[root@docker01 ~]# curl 10.0.0.100:9000
blog.nmtui.com
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;设置卷&lt;/p&gt;
&lt;pre class="highlight docker"&gt;&lt;code&gt;[root@docker01 ~]# docker run  -d  -P  --volumes-from 079786c1e297 nginx:latest 
b54b9c9930b417ab3257c6e4a8280b54fae57043c0b76b9dc60b4788e92369fb
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;查看使用的端口&lt;/p&gt;
&lt;pre class="highlight docker"&gt;&lt;code&gt;[root@docker01 ~]# netstat -lntup 
Active Internet connections (only servers)
Proto Recv-Q Send-Q Local Address           Foreign Address         State       PID/Program name    
tcp        0      0 0.0.0.0:22              0.0.0.0:*               LISTEN      1400/sshd           
tcp        0      0 10.0.0.100:2375         0.0.0.0:*               LISTEN      26218/dockerd       
tcp6       0      0 :::9000                 :::*                    LISTEN      32015/docker-proxy  
tcp6       0      0 :::8080                 :::*                    LISTEN      31853/docker-proxy  
tcp6       0      0 :::80                   :::*                    LISTEN      31752/docker-proxy  
tcp6       0      0 :::22                   :::*                    LISTEN      1400/sshd           
tcp6       0      0 :::32769                :::*                    LISTEN      32300/docker-proxy  
[root@docker01 ~]# curl 10.0.0.100:32769
http://www.nmtui.com
&lt;/code&gt;&lt;/pre&gt;&lt;h3 id="手动将容器保存为镜像"&gt;&lt;strong&gt;手动将容器保存为镜像&lt;/strong&gt;&lt;/h3&gt;
&lt;p&gt;本次是基于 docker 官方 centos 6.8 镜像创建&lt;/p&gt;

&lt;p&gt;官方镜像列表：&lt;a href="https://hub.docker.com/explore/" rel="nofollow" target="_blank"&gt;https://hub.docker.com/explore/&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;启动一个 centos6.8 的镜像&lt;/p&gt;
&lt;pre class="highlight docker"&gt;&lt;code&gt;[root@docker01 ~]# docker pull  centos:6.8
[root@docker01 ~]# docker run -it -p 1022:22 centos:6.8  /bin/bash
&lt;span class="c"&gt;# 在容器种安装sshd服务，并修改系统密码&lt;/span&gt;
[root@582051b2b92b ~]# yum install  openssh-server -y 
[root@582051b2b92b ~]# echo "root:123456" |chpasswd
[root@582051b2b92b ~]#  /etc/init.d/sshd start
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;启动完成后镜像 ssh 连接测试&lt;/p&gt;

&lt;p&gt;将容器提交为镜像&lt;/p&gt;
&lt;pre class="highlight docker"&gt;&lt;code&gt;[root@docker01 ~]# docker commit brave_mcclintock  centos6-ssh
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;使用新的镜像启动容器&lt;/p&gt;
&lt;pre class="highlight docker"&gt;&lt;code&gt;[root@docker01 ~]# docker run -d  -p 1122:22  centos6-ssh:latest  /usr/sbin/sshd -D 
5b8161fda2a9f2c39c196c67e2eb9274977e7723fe51c4f08a0190217ae93094
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;在容器安装 httpd 服务&lt;/p&gt;
&lt;pre class="highlight docker"&gt;&lt;code&gt;[root@5b8161fda2a9 /]#  yum install httpd -y
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;编写启动脚本脚本&lt;/p&gt;
&lt;pre class="highlight docker"&gt;&lt;code&gt;[root@5b8161fda2a9 /]# cat  init.sh 
&lt;span class="c"&gt;#!/bin/bash &lt;/span&gt;
/etc/init.d/httpd start 
/usr/sbin/sshd -D
[root@5b8161fda2a9 /]# chmod +x init.sh 
&lt;span class="c"&gt;# 注意执行权限&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;再次提交为新的镜像&lt;/p&gt;
&lt;pre class="highlight docker"&gt;&lt;code&gt;[root@docker01 ~]# docker commit  5b8161fda2a9 centos6-httpd 
sha256:705d67a786cac040800b8485cf046fd57b1828b805c515377fc3e9cea3a481c1
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;启动镜像，做好端口映射。并在浏览器中测试访问&lt;/p&gt;
&lt;pre class="highlight docker"&gt;&lt;code&gt;[root@docker01 ~]# docker run -d -p 1222:22 -p 80:80  centos6-httpd /init.sh 
46fa6a06644e31701dc019fb3a8c3b6ef008d4c2c10d46662a97664f838d8c2c
&lt;/code&gt;&lt;/pre&gt;&lt;h2 id="Dockerfile 自动构建 docker 镜像"&gt;Dockerfile 自动构建 docker 镜像&lt;/h2&gt;
&lt;p&gt;#### &lt;strong&gt;Dockerfile 指令集&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;dockerfile 主要组成部分：&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;&lt;p&gt;基础镜像信息 FROM centos:6.8&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;制作镜像操作指令 RUN yum insatll openssh-server -y&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;容器启动时执行指令 CMD ["/bin/bash"]&lt;/p&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;dockerfile 常用指令：&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;&lt;p&gt;FROM 这个镜像的妈妈是谁？（指定基础镜像）&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;MAINTAINER 告诉别人，谁负责养它？（指定维护者信息，可以没有）&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;RUN 你想让它干啥（在命令前面加上 RUN 即可）&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;ADD 给它点创业资金（COPY 文件，会自动解压）&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;WORKDIR 我是 cd，今天刚化了妆（设置当前工作目录）&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;VOLUME 给它一个存放行李的地方（设置卷，挂载主机目录）&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;EXPOSE 它要打开的门是啥（指定对外的端口）&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;CMD 奔跑吧，兄弟！（指定容器启动后的要干的事情）&lt;/p&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;dockerfile 其他指令：&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;&lt;p&gt;COPY 复制文件&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;ENV  环境变量&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;ENTRYPOINT  容器启动后执行的命令&lt;/p&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;#### &lt;strong&gt;创建一个 Dockerfile&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;创建第一个 Dockerfile 文件&lt;/p&gt;
&lt;pre class="highlight docker"&gt;&lt;code&gt;&lt;span class="c"&gt;# 创建目录&lt;/span&gt;
[root@docker01 base]# cd /opt/base
&lt;span class="c"&gt;# 创建Dcokerfile文件，注意大小写&lt;/span&gt;
[root@docker01 base]# vim Dockerfile
&lt;span class="k"&gt;FROM&lt;/span&gt;&lt;span class="s"&gt; centos:6.8&lt;/span&gt;
&lt;span class="k"&gt;RUN &lt;/span&gt;yum &lt;span class="nb"&gt;install &lt;/span&gt;openssh-server &lt;span class="nt"&gt;-y&lt;/span&gt; 
&lt;span class="k"&gt;RUN &lt;/span&gt;&lt;span class="nb"&gt;echo&lt;/span&gt; &lt;span class="s2"&gt;"root:123456"&lt;/span&gt; |chpasswd
&lt;span class="k"&gt;RUN &lt;/span&gt;/etc/init.d/sshd start 
&lt;span class="k"&gt;CMD&lt;/span&gt;&lt;span class="s"&gt; ["/usr/sbin/sshd","-D"]&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;构建 docker 镜像&lt;/p&gt;
&lt;pre class="highlight docker"&gt;&lt;code&gt;[root@docker01 base]# docker image build  -t centos6.8-ssh . 
-t 为镜像标签打标签  . 表示当前路径
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;使用自构建的镜像启动&lt;/p&gt;
&lt;pre class="highlight docker"&gt;&lt;code&gt;[root@docker01 base]# docker run  -d -p 2022:22 centos6.8-ssh-b 
dc3027d3c15dac881e8e2aeff80724216f3ac725f142daa66484f7cb5d074e7a
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;#### &lt;strong&gt;使用 Dcokerfile 安装 kodexplorer&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;Dockerfile 文件内容&lt;/p&gt;
&lt;pre class="highlight docker"&gt;&lt;code&gt;&lt;span class="k"&gt;FROM&lt;/span&gt;&lt;span class="s"&gt; centos:6.8&lt;/span&gt;
&lt;span class="k"&gt;RUN &lt;/span&gt;yum &lt;span class="nb"&gt;install &lt;/span&gt;wget unzip php php-gd php-mbstring &lt;span class="nt"&gt;-y&lt;/span&gt; &lt;span class="o"&gt;&amp;amp;&amp;amp;&lt;/span&gt; yum clean all
&lt;span class="c"&gt;# 设置工作目录，之后的操作都在这个目录中&lt;/span&gt;
&lt;span class="k"&gt;WORKDIR&lt;/span&gt;&lt;span class="s"&gt; /var/www/html/&lt;/span&gt;
&lt;span class="k"&gt;RUN &lt;/span&gt;wget &lt;span class="nt"&gt;-c&lt;/span&gt; http://static.kodcloud.com/update/download/kodexplorer4.25.zip
&lt;span class="k"&gt;RUN &lt;/span&gt;unzip kodexplorer4.25.zip &lt;span class="o"&gt;&amp;amp;&amp;amp;&lt;/span&gt; &lt;span class="nb"&gt;rm&lt;/span&gt; &lt;span class="nt"&gt;-f&lt;/span&gt; kodexplorer4.25.zip
&lt;span class="k"&gt;RUN &lt;/span&gt;&lt;span class="nb"&gt;chown&lt;/span&gt; &lt;span class="nt"&gt;-R&lt;/span&gt; apache.apache .
&lt;span class="k"&gt;CMD&lt;/span&gt;&lt;span class="s"&gt; ["/usr/sbin/apachectl","-D","FOREGROUND"]&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;更多的 Dockerfile 可以参考官方方法。&lt;/p&gt;
&lt;h2 id="Docker 中的镜像分层"&gt;Docker 中的镜像分层&lt;/h2&gt;
&lt;p&gt;Docker 支持通过扩展现有镜像，创建新的镜像。实际上，Docker Hub 中 99% 的镜像都是通过在 base 镜像中安装和配置需要的软件构建出来的。&lt;/p&gt;

&lt;p&gt;&lt;img src="/uploads/photo/wangbaochen/8fa9f6cb-b88f-485c-869e-940c3da4fcb3.png!large" title="" alt=""&gt;&lt;/p&gt;

&lt;p&gt;从上图可以看到，新镜像是从 base 镜像一层一层叠加生成的。每安装一个软件，就在现有镜像的基础上增加一层。&lt;/p&gt;

&lt;p&gt;### &lt;strong&gt;Docker 镜像为什么分层&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;镜像分层最大的一个好处就是共享资源。&lt;/p&gt;

&lt;p&gt;比如说有多个镜像都从相同的 base 镜像构建而来，那么 Docker Host 只需在磁盘上保存一份 base 镜像；同时内存中也只需加载一份 base 镜像，就可以为所有容器服务了。而且镜像的每一层都可以被共享。&lt;/p&gt;

&lt;p&gt;如果多个容器共享一份基础镜像，当某个容器修改了基础镜像的内容，比如 /etc 下的文件，这时其他容器的 /etc 是不会被修改的，修改只会被限制在单个容器内。这就是容器 Copy-on-Write 特性。&lt;/p&gt;
&lt;h3 id="可写的容器层"&gt;&lt;strong&gt;可写的容器层&lt;/strong&gt;&lt;/h3&gt;
&lt;p&gt;当容器启动时，一个新的可写层被加载到镜像的顶部。这一层通常被称作 “容器层”，“容器层” 之下的都叫 “镜像层”。&lt;/p&gt;

&lt;p&gt;&lt;img src="/uploads/photo/wangbaochen/4245c0fa-66fc-43e1-86db-61657e4e5130.png!large" title="" alt=""&gt;&lt;/p&gt;

&lt;p&gt;所有对容器的改动 - 无论添加、删除、还是修改文件都只会发生在容器层中。只有&lt;strong&gt;容器层是可写的，容器层下面的所有镜像层都是只读的。&lt;/strong&gt;&lt;/p&gt;
&lt;h3 id="容器层的细节说明"&gt;&lt;strong&gt;容器层的细节说明&lt;/strong&gt;&lt;/h3&gt;
&lt;p&gt;镜像层数量可能会很多，所有镜像层会联合在一起组成一个统一的文件系统。如果不同层中有一个相同路径的文件，比如 /a，上层的 /a 会覆盖下层的 /a，也就是说用户只能访问到上层中的文件 /a。在容器层中，用户看到的是一个叠加之后的文件系统。&lt;/p&gt;

&lt;p&gt;文件操作：&lt;/p&gt;

&lt;p&gt;&lt;img src="/uploads/photo/wangbaochen/8ebdf5f6-fe89-415c-8fa4-f16877b4da22.png!large" title="" alt=""&gt;&lt;/p&gt;

&lt;p&gt;只有当需要修改时才复制一份数据，这种特性被称作 Copy-on-Write。可见，容器层保存的是镜像变化的部分，不会对镜像本身进行任何修改。&lt;/p&gt;

&lt;p&gt;这样就解释了我们前面提出的问题：容器层记录对镜像的修改，所有镜像层都是只读的，不会被容器修改，所以镜像可以被多个容器共享。&lt;/p&gt;
&lt;h2 id="使用 docker 运行 zabbix-server"&gt;使用 docker 运行 zabbix-server&lt;/h2&gt;&lt;h3 id="容器间的互联"&gt;&lt;strong&gt;容器间的互联&lt;/strong&gt;&lt;/h3&gt;
&lt;p&gt;在运行 zabbix 之前务必要了解容器间互联的方法&lt;/p&gt;
&lt;pre class="highlight docker"&gt;&lt;code&gt;&lt;span class="c"&gt;# 创建一个nginx容器&lt;/span&gt;
docker run -d -p 80:80 nginx
&lt;span class="c"&gt;# 创建容器，做link，并进入容器中&lt;/span&gt;
docker run -it --link quirky_brown:web01 centos-ssh /bin/bash
&lt;span class="c"&gt;# 在容器中访问nginx容器可以ping通&lt;/span&gt;
ping web01 
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;命令执行过程&lt;/p&gt;
&lt;pre class="highlight docker"&gt;&lt;code&gt;&lt;span class="c"&gt;# 启动apache容器&lt;/span&gt;
[root@docker01 ~]# docker run -d httpd:2.4  
3f1f7fc554720424327286bd2b04aeab1b084a3fb011a785b0deab6a34e56955
^[[A[root@docker01 docker ps -a
CONTAINER ID        IMAGE               COMMAND              CREATED             STATUS              PORTS               NAMES
3f1f7fc55472        httpd:2.4           "httpd-foreground"   6 seconds ago       Up 5 seconds        80/tcp              determined_clarke
&lt;span class="c"&gt;# 拉取一个busybox 镜像&lt;/span&gt;
[root@docker01 ~]# docker pull busybox 
&lt;span class="c"&gt;# 启动容器&lt;/span&gt;
[root@docker01 ~]# docker run -it  --link determined_clarke:web busybox:latest   /bin/sh 
/ # 
# 使用新的容器访问最初的web容器
/ # ping web 
PING web (172.17.0.2): 56 data bytes
64 bytes from 172.17.0.2: seq=0 ttl=64 time=0.058 ms
^C
--- web ping statistics ---
1 packets transmitted, 1 packets received, 0% packet loss
round-trip min/avg/max = 0.058/0.058/0.058 ms   
&lt;/code&gt;&lt;/pre&gt;&lt;h3 id="启动 zabbix 容器"&gt;&lt;strong&gt;启动 zabbix 容器&lt;/strong&gt;&lt;/h3&gt;
&lt;p&gt;1、启动一个 mysql 的容器&lt;/p&gt;
&lt;pre class="highlight docker"&gt;&lt;code&gt;docker run --name mysql-server -t \
      -e MYSQL_DATABASE="zabbix" \
      -e MYSQL_USER="zabbix" \
      -e MYSQL_PASSWORD="zabbix_pwd" \
      -e MYSQL_ROOT_PASSWORD="root_pwd" \
      -d mysql:5.7 \
      --character-set-server=utf8 --collation-server=utf8_bin 
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;2、启动 java-gateway 容器监控 java 服务&lt;/p&gt;
&lt;pre class="highlight docker"&gt;&lt;code&gt;docker run --name zabbix-java-gateway -t \
      -d zabbix/zabbix-java-gateway:latest
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;3、启动 zabbix-mysql 容器使用 link 连接 mysql 与 java-gateway。&lt;/p&gt;
&lt;pre class="highlight docker"&gt;&lt;code&gt;docker run --name zabbix-server-mysql -t \
      -e DB_SERVER_HOST="mysql-server" \
      -e MYSQL_DATABASE="zabbix" \
      -e MYSQL_USER="zabbix" \
      -e MYSQL_PASSWORD="zabbix_pwd" \
      -e MYSQL_ROOT_PASSWORD="root_pwd" \
      -e ZBX_JAVAGATEWAY="zabbix-java-gateway" \
      --link mysql-server:mysql \
      --link zabbix-java-gateway:zabbix-java-gateway \
      -p 10051:10051 \
      -d zabbix/zabbix-server-mysql:latest
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;4、启动 zabbix web 显示，使用 link 连接 zabbix-mysql 与 mysql。&lt;/p&gt;
&lt;pre class="highlight docker"&gt;&lt;code&gt;docker run --name zabbix-web-nginx-mysql -t \
      -e DB_SERVER_HOST="mysql-server" \
      -e MYSQL_DATABASE="zabbix" \
      -e MYSQL_USER="zabbix" \
      -e MYSQL_PASSWORD="zabbix_pwd" \
      -e MYSQL_ROOT_PASSWORD="root_pwd" \
      --link mysql-server:mysql \
      --link zabbix-server-mysql:zabbix-server \
      -p 80:80 \
      -d zabbix/zabbix-web-nginx-mysql:latest
&lt;/code&gt;&lt;/pre&gt;&lt;h3 id="关于 zabbix API"&gt;&lt;strong&gt;关于 zabbix API&lt;/strong&gt;&lt;/h3&gt;
&lt;p&gt;关于 zabbix API 可以参考官方文档，获取 token 方法&lt;/p&gt;
&lt;pre class="highlight docker"&gt;&lt;code&gt;&lt;span class="c"&gt;# 获取token&lt;/span&gt;
[root@docker02 ~]# curl -s -X POST -H 'Content-Type:application/json' -d '
{
"jsonrpc": "2.0",
"method": "user.login",
"params": {
"user": "Admin",
"password": "zabbix"
},
"id": 1
}' http://10.0.0.100/api_jsonrpc.php
{"jsonrpc":"2.0","result":"d3be707f9e866ec5d0d1c242292cbebd","id":1}
&lt;/code&gt;&lt;/pre&gt;&lt;h2 id="docker 仓库（registry）"&gt;docker 仓库（registry）&lt;/h2&gt;&lt;h3 id="创建一个普通仓库"&gt;&lt;strong&gt;创建一个普通仓库&lt;/strong&gt;&lt;/h3&gt;
&lt;p&gt;1、创建仓库&lt;/p&gt;
&lt;pre class="highlight docker"&gt;&lt;code&gt;docker run -d -p 5000:5000 --restart=always --name registry -v /opt/myregistry:/var/lib/registry  registry 
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;2、修改配置文件，使之支持 http&lt;/p&gt;
&lt;pre class="highlight docker"&gt;&lt;code&gt;[root@docker01 ~]# cat  /etc/docker/daemon.json 
{
  "registry-mirrors": ["https://registry.docker-cn.com"],
  "insecure-registries": ["10.0.0.100:5000"]
}
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;重启 docker 让修改生效&lt;/p&gt;
&lt;pre class="highlight docker"&gt;&lt;code&gt;[root@docker01 ~]# systemctl restart  docker.service
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;3、修改镜像标签&lt;/p&gt;
&lt;pre class="highlight docker"&gt;&lt;code&gt;[root@docker01 ~]# docker tag  busybox:latest  10.0.0.100:5000/clsn/busybox:1.0
[root@docker01 ~]# docker images
REPOSITORY                      TAG                 IMAGE ID            CREATED             SIZE
centos6-ssh                     latest              3c2b1e57a0f5        18 hours ago        393MB
httpd                           2.4                 2e202f453940        6 days ago          179MB
10.0.0.100:5000/clsn/busybox    1.0                 5b0d59026729        8 days ago          1.15MB
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;4、将新打标签的镜像上传镜像到仓库&lt;/p&gt;
&lt;pre class="highlight docker"&gt;&lt;code&gt;[root@docker01 ~]# docker push   10.0.0.100:5000/clsn/busybox
&lt;/code&gt;&lt;/pre&gt;&lt;h3 id="带 basic 认证的仓库"&gt;&lt;strong&gt;带 basic 认证的仓库&lt;/strong&gt;&lt;/h3&gt;
&lt;p&gt;1、安装加密工具&lt;/p&gt;
&lt;pre class="highlight docker"&gt;&lt;code&gt;[root@docker01 clsn]# yum install httpd-tools  -y
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;2、设置认证密码&lt;/p&gt;
&lt;pre class="highlight docker"&gt;&lt;code&gt;mkdir /opt/registry-var/auth/ -p
htpasswd  -Bbn clsn 123456  &amp;gt; /opt/registry-var/auth/htpasswd
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;3、启动容器，在启动时传入认证参数&lt;/p&gt;
&lt;pre class="highlight docker"&gt;&lt;code&gt;docker run -d -p 5000:5000 -v /opt/registry-var/auth/:/auth/ -e "REGISTRY_AUTH=htpasswd" -e "REGISTRY_AUTH_HTPASSWD_REALM=Registry Realm" -e REGISTRY_AUTH_HTPASSWD_PATH=/auth/htpasswd registry
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;4、使用验证用户测试&lt;/p&gt;
&lt;pre class="highlight docker"&gt;&lt;code&gt;&lt;span class="c"&gt;# 登陆用户&lt;/span&gt;
[root@docker01 ~]# docker login 10.0.0.100:5000 
Username: clsn  
Password: 123456
Login Succeeded
&lt;span class="c"&gt;# 推送镜像到仓库&lt;/span&gt;
[root@docker01 ~]# docker push 10.0.0.100:5000/clsn/busybox 
The push refers to repository [10.0.0.100:5000/clsn/busybox]
4febd3792a1f: Pushed 
1.0: digest: sha256:4cee1979ba0bf7db9fc5d28fb7b798ca69ae95a47c5fecf46327720df4ff352d size: 527
&lt;span class="c"&gt;#认证文件的保存位置&lt;/span&gt;
[root@docker01 ~]# cat .docker/config.json 
{
    "auths": {
        "10.0.0.100:5000": {
            "auth": "Y2xzbjoxMjM0NTY="
        },
        "https://index.docker.io/v1/": {
            "auth": "Y2xzbjpIenNAMTk5Ng=="
        }
    },
    "HttpHeaders": {
        "User-Agent": "Docker-Client/17.12.0-ce (linux)"
    }
}
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;至此，一个简单的 docker 镜像仓库搭建完成&lt;/p&gt;
&lt;h2 id="docker-compose 编排工具"&gt;docker-compose 编排工具&lt;/h2&gt;&lt;h3 id="安装 docker-compose"&gt;&lt;strong&gt;安装 docker-compose&lt;/strong&gt;&lt;/h3&gt;
&lt;p&gt;安装 docker-compose&lt;/p&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="c"&gt;# 下载pip软件&lt;/span&gt;
yum &lt;span class="nb"&gt;install&lt;/span&gt; &lt;span class="nt"&gt;-y&lt;/span&gt; python2-pip
&lt;span class="c"&gt;# 下载 docker-compose&lt;/span&gt;
pip &lt;span class="nb"&gt;install &lt;/span&gt;docker-compose
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;国内开启 pip 下载加速：&lt;a href="https://mirrors.aliyun.com/help/pypi" rel="nofollow" target="_blank"&gt;https://mirrors.aliyun.com/help/pypi&lt;/a&gt;&lt;/p&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="nb"&gt;mkdir&lt;/span&gt; ~/.pip/
&lt;span class="nb"&gt;cat&lt;/span&gt; &lt;span class="o"&gt;&amp;gt;&lt;/span&gt; ~/.pip/pip.conf &lt;span class="o"&gt;&amp;lt;&amp;lt;&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="no"&gt;EOF&lt;/span&gt;&lt;span class="sh"&gt;'
[global]
index-url = https://mirrors.aliyun.com/pypi/simple/
[install]
trusted-host=mirrors.aliyun.com
&lt;/span&gt;&lt;span class="no"&gt;EOF
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;h3 id="编排启动镜像"&gt;&lt;strong&gt;编排启动镜像&lt;/strong&gt;&lt;/h3&gt;
&lt;p&gt;1、创建文件目录&lt;/p&gt;
&lt;pre class="highlight docker"&gt;&lt;code&gt;[root@docker01 ~]# mkdir /opt/my_wordpress/
[root@docker01 ~]# cd /opt/my_wordpress/
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;2、编写编排文件&lt;/p&gt;
&lt;pre class="highlight docker"&gt;&lt;code&gt;[root@docker01 my_wordpress]# vim docker-compose.yml
version: '3'
services:
   db:
     image: mysql:5.7
     volumes:
       - /data/db_data:/var/lib/mysql
     restart: always
     environment:
       MYSQL_ROOT_PASSWORD: somewordpress
       MYSQL_DATABASE: wordpress
       MYSQL_USER: wordpress
       MYSQL_PASSWORD: wordpress
   wordpress:
     depends_on:
       - db
     image: wordpress:latest
     volumes:
       - /data/web_data:/var/www/html
     ports: 
       - "8000:80"
     restart: always
     environment:
       WORDPRESS_DB_HOST: db:3306
       WORDPRESS_DB_USER: wordpress
       WORDPRESS_DB_PASSWORD: wordpress
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;3、启动&lt;/p&gt;
&lt;pre class="highlight docker"&gt;&lt;code&gt;[root@docker01 my_wordpress]# docker-compose up
　　#启动方法：docker-compose up
　　#后台启动方法：docker-compose up -d
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;4、浏览器上访问 &lt;a href="http://10.0.0.100:8000" rel="nofollow" target="_blank"&gt;http://10.0.0.100:8000&lt;/a&gt;，进行 wordpress 的安装即可&lt;/p&gt;
&lt;h3 id="haproxy 代理后端 docker 容器"&gt;&lt;strong&gt;haproxy 代理后端 docker 容器&lt;/strong&gt;&lt;/h3&gt;
&lt;p&gt;1、修改编排脚本&lt;/p&gt;
&lt;pre class="highlight yaml"&gt;&lt;code&gt;&lt;span class="pi"&gt;[&lt;/span&gt;&lt;span class="nv"&gt;root@docker01 my_wordpress&lt;/span&gt;&lt;span class="pi"&gt;]&lt;/span&gt;&lt;span class="c1"&gt;# cat docker-compose.yml &lt;/span&gt;
&lt;span class="na"&gt;version&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s1"&gt;'&lt;/span&gt;&lt;span class="s"&gt;3'&lt;/span&gt;
&lt;span class="na"&gt;services&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
   &lt;span class="na"&gt;db&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
     &lt;span class="na"&gt;image&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;mysql:5.7&lt;/span&gt;
     &lt;span class="na"&gt;volumes&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
       &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="s"&gt;/data/db_data:/var/lib/mysql&lt;/span&gt;
     &lt;span class="na"&gt;restart&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;always&lt;/span&gt;
     &lt;span class="na"&gt;environment&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
       &lt;span class="na"&gt;MYSQL_ROOT_PASSWORD&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;somewordpress&lt;/span&gt;
       &lt;span class="na"&gt;MYSQL_DATABASE&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;wordpress&lt;/span&gt;
       &lt;span class="na"&gt;MYSQL_USER&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;wordpress&lt;/span&gt;
       &lt;span class="na"&gt;MYSQL_PASSWORD&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;wordpress&lt;/span&gt;
   &lt;span class="na"&gt;wordpress&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
     &lt;span class="na"&gt;depends_on&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
       &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="s"&gt;db&lt;/span&gt;
     &lt;span class="na"&gt;image&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;wordpress:latest&lt;/span&gt;
     &lt;span class="na"&gt;volumes&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
       &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="s"&gt;/data/web_data:/var/www/html&lt;/span&gt;
     &lt;span class="na"&gt;ports&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; 
       &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="s"&gt;80"&lt;/span&gt;
     &lt;span class="na"&gt;restart&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;always&lt;/span&gt;
     &lt;span class="na"&gt;environment&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
       &lt;span class="na"&gt;WORDPRESS_DB_HOST&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;db:3306&lt;/span&gt;
       &lt;span class="na"&gt;WORDPRESS_DB_USER&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;wordpress&lt;/span&gt;
       &lt;span class="na"&gt;WORDPRESS_DB_PASSWORD&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;wordpress&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;2、同时启动两台 wordpress&lt;/p&gt;
&lt;pre class="highlight docker"&gt;&lt;code&gt;[root@docker01 my_wordpress]# docker-compose scale wordpress=2 
WARNING: The scale command is deprecated. Use the up command with the --scale flag instead.
Starting mywordpress_wordpress_1 ... done
Creating mywordpress_wordpress_2 ... done
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;3、安装 haproxy&lt;/p&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="o"&gt;[&lt;/span&gt;root@docker01 ~]# yum &lt;span class="nb"&gt;install &lt;/span&gt;haproxy &lt;span class="nt"&gt;-y&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;4、修改 haproxy 配置文件&lt;/p&gt;
&lt;pre class="highlight viml"&gt;&lt;code&gt;&lt;span class="p"&gt;[&lt;/span&gt;root@docker01 &lt;span class="p"&gt;~]&lt;/span&gt;#&lt;span class="k"&gt;cp&lt;/span&gt; &lt;span class="sr"&gt;/etc/&lt;/span&gt;haproxy/haproxy&lt;span class="p"&gt;.&lt;/span&gt;cfg&lt;span class="p"&gt;{,.&lt;/span&gt;bak&lt;span class="p"&gt;}&lt;/span&gt;
&lt;span class="p"&gt;[&lt;/span&gt;root@docker01 &lt;span class="p"&gt;~]&lt;/span&gt;# &lt;span class="k"&gt;vim&lt;/span&gt; &lt;span class="sr"&gt;/etc/&lt;/span&gt;haproxy/haproxy&lt;span class="p"&gt;.&lt;/span&gt;cfg
global
    &lt;span class="nb"&gt;log&lt;/span&gt;         &lt;span class="m"&gt;127&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="m"&gt;0&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="m"&gt;0&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="m"&gt;1&lt;/span&gt; local2
    chroot      &lt;span class="sr"&gt;/var/&lt;/span&gt;lib/haproxy
    pidfile     &lt;span class="sr"&gt;/var/&lt;/span&gt;run/haproxy&lt;span class="p"&gt;.&lt;/span&gt;pid
    maxconn     &lt;span class="m"&gt;4000&lt;/span&gt;
    user        haproxy
    group       haproxy
    daemon
    stats socket &lt;span class="sr"&gt;/var/&lt;/span&gt;lib&lt;span class="sr"&gt;/haproxy/&lt;/span&gt;stats level admin  #支持命令行控制
defaults
    &lt;span class="k"&gt;mode&lt;/span&gt;                    http
    &lt;span class="nb"&gt;log&lt;/span&gt;                     global
    option                  httplog
    option                  dontlognull
    option http&lt;span class="p"&gt;-&lt;/span&gt;server&lt;span class="p"&gt;-&lt;/span&gt;&lt;span class="k"&gt;close&lt;/span&gt;
    option forwardfor       except &lt;span class="m"&gt;127&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="m"&gt;0&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="m"&gt;0&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="m"&gt;0&lt;/span&gt;/&lt;span class="m"&gt;8&lt;/span&gt;
    option                  redispatch
    retries                 &lt;span class="m"&gt;3&lt;/span&gt;
    &lt;span class="nb"&gt;timeout&lt;/span&gt; http&lt;span class="p"&gt;-&lt;/span&gt;request    &lt;span class="m"&gt;10&lt;/span&gt;s
    &lt;span class="nb"&gt;timeout&lt;/span&gt; queue           &lt;span class="m"&gt;1&lt;/span&gt;&lt;span class="k"&gt;m&lt;/span&gt;
    &lt;span class="nb"&gt;timeout&lt;/span&gt; connect         &lt;span class="m"&gt;10&lt;/span&gt;s
    &lt;span class="nb"&gt;timeout&lt;/span&gt; client          &lt;span class="m"&gt;1&lt;/span&gt;&lt;span class="k"&gt;m&lt;/span&gt;
    &lt;span class="nb"&gt;timeout&lt;/span&gt; server          &lt;span class="m"&gt;1&lt;/span&gt;&lt;span class="k"&gt;m&lt;/span&gt;
    &lt;span class="nb"&gt;timeout&lt;/span&gt; http&lt;span class="p"&gt;-&lt;/span&gt;keep&lt;span class="p"&gt;-&lt;/span&gt;alive &lt;span class="m"&gt;10&lt;/span&gt;s
    &lt;span class="nb"&gt;timeout&lt;/span&gt; check           &lt;span class="m"&gt;10&lt;/span&gt;s
    maxconn                 &lt;span class="m"&gt;3000&lt;/span&gt;
listen stats
    &lt;span class="k"&gt;mode&lt;/span&gt; http
    bind &lt;span class="m"&gt;0&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="m"&gt;0&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="m"&gt;0&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="m"&gt;0&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="m"&gt;8888&lt;/span&gt;
    stats enable
    stats uri     /haproxy&lt;span class="p"&gt;-&lt;/span&gt;status 
    stats auth    admin&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="m"&gt;123456&lt;/span&gt;
frontend frontend_www_example_com
    bind &lt;span class="m"&gt;10&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="m"&gt;0&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="m"&gt;0&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="m"&gt;100&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="m"&gt;8000&lt;/span&gt;
    &lt;span class="k"&gt;mode&lt;/span&gt; http
    option httplog
    &lt;span class="nb"&gt;log&lt;/span&gt; global
    default_backend backend_www_example_com
backend backend_www_example_com
    option forwardfor header X&lt;span class="p"&gt;-&lt;/span&gt;REAL&lt;span class="p"&gt;-&lt;/span&gt;IP
    option httpchk HEAD &lt;span class="sr"&gt;/ HTTP/&lt;/span&gt;&lt;span class="m"&gt;1&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="m"&gt;0&lt;/span&gt;
    balance roundrobin
    server web&lt;span class="p"&gt;-&lt;/span&gt;node1  &lt;span class="m"&gt;10&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="m"&gt;0&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="m"&gt;0&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="m"&gt;100&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="m"&gt;32768&lt;/span&gt; check inter &lt;span class="m"&gt;2000&lt;/span&gt; rise &lt;span class="m"&gt;30&lt;/span&gt; fall &lt;span class="m"&gt;15&lt;/span&gt;
    server web&lt;span class="p"&gt;-&lt;/span&gt;node2  &lt;span class="m"&gt;10&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="m"&gt;0&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="m"&gt;0&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="m"&gt;100&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="m"&gt;32769&lt;/span&gt; check inter &lt;span class="m"&gt;2000&lt;/span&gt; rise &lt;span class="m"&gt;30&lt;/span&gt; fall &lt;span class="m"&gt;15&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;5、启动 haproxy&lt;/p&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;systemctl start haproxy
systemctl &lt;span class="nb"&gt;enable &lt;/span&gt;haproxy
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;6、使用浏览器访问 hapeoxy 监听的 8000 端口可以看到负载的情况&lt;/p&gt;

&lt;p&gt;&lt;img src="/uploads/photo/wangbaochen/d5e9fb20-eb29-40e6-9ea1-00513b7e8fab.png!large" title="" alt=""&gt;&lt;/p&gt;

&lt;p&gt;7、使用浏览器访问：&lt;a href="http://10.0.0.100:8888/haproxy-status" rel="nofollow" target="_blank"&gt;http://10.0.0.100:8888/haproxy-status&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;可以看到后端节点的监控状况&lt;/p&gt;

&lt;p&gt;&lt;img src="/uploads/photo/wangbaochen/33a4ffab-ca43-45cb-a99f-e5eabb30ea4f.png!large" title="" alt=""&gt;&lt;/p&gt;
&lt;h3 id="安装 socat 直接操作 socket 控制 haproxy"&gt;&lt;strong&gt;安装 socat 直接操作 socket 控制 haproxy&lt;/strong&gt;&lt;/h3&gt;
&lt;p&gt;1、安装软件&lt;/p&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;yum &lt;span class="nb"&gt;install &lt;/span&gt;socat.x86_64 &lt;span class="nt"&gt;-y&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;2、查看帮助&lt;/p&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="o"&gt;[&lt;/span&gt;root@docker01 web_data]# &lt;span class="nb"&gt;echo&lt;/span&gt; &lt;span class="s2"&gt;"help"&lt;/span&gt;|socat stdio /var/lib/haproxy/stats
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;3、下线后端节点&lt;/p&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="nb"&gt;echo&lt;/span&gt; &lt;span class="s2"&gt;"disable server backend_www_example_com/web-node2"&lt;/span&gt;|socat stdio /var/lib/haproxy/stats
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;4、上线后端节点&lt;/p&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="nb"&gt;echo&lt;/span&gt; &lt;span class="s2"&gt;"enable server backend_www_example_com/web-node3"&lt;/span&gt;|socat stdio /var/lib/haproxy/stats
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;5、编写 php 测试页，放到 /data/web_data 下，在浏览器中访问可以查看当前的节点&lt;/p&gt;
&lt;pre class="highlight php"&gt;&lt;code&gt;[root@docker01 web_data]# vim check.php
&lt;span class="nt"&gt;&amp;lt;html&amp;gt;&lt;/span&gt;
    &lt;span class="nt"&gt;&amp;lt;head&amp;gt;&lt;/span&gt;
        &lt;span class="nt"&gt;&amp;lt;title&amp;gt;&lt;/span&gt;PHP测试&lt;span class="nt"&gt;&amp;lt;/title&amp;gt;&lt;/span&gt;
    &lt;span class="nt"&gt;&amp;lt;/head&amp;gt;&lt;/span&gt;
    &lt;span class="nt"&gt;&amp;lt;body&amp;gt;&lt;/span&gt;
        &lt;span class="cp"&gt;&amp;lt;?php  echo '&amp;lt;p&amp;gt;Hello World &amp;lt;/p&amp;gt;'; ?&amp;gt;&lt;/span&gt;
        &lt;span class="cp"&gt;&amp;lt;?php&lt;/span&gt;  &lt;span class="k"&gt;echo&lt;/span&gt; &lt;span class="s2"&gt;"访问的服务器地址是:"&lt;/span&gt;&lt;span class="mf"&gt;.&lt;/span&gt;&lt;span class="s2"&gt;"&amp;lt;fontcolor=red&amp;gt;"&lt;/span&gt;&lt;span class="mf"&gt;.&lt;/span&gt;&lt;span class="nv"&gt;$_SERVER&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="s1"&gt;'SERVER_ADDR'&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;&lt;span class="mf"&gt;.&lt;/span&gt;&lt;span class="s2"&gt;"&amp;lt;/font&amp;gt;"&lt;/span&gt;&lt;span class="mf"&gt;.&lt;/span&gt;&lt;span class="s2"&gt;"&amp;lt;br&amp;gt;"&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
        &lt;span class="k"&gt;echo&lt;/span&gt;&lt;span class="s2"&gt;"访问的服务器域名是:"&lt;/span&gt;&lt;span class="mf"&gt;.&lt;/span&gt;&lt;span class="s2"&gt;"&amp;lt;fontcolor=red&amp;gt;"&lt;/span&gt;&lt;span class="mf"&gt;.&lt;/span&gt;&lt;span class="nv"&gt;$_SERVER&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="s1"&gt;'SERVER_NAME'&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;&lt;span class="mf"&gt;.&lt;/span&gt;&lt;span class="s2"&gt;"&amp;lt;/font&amp;gt;"&lt;/span&gt;&lt;span class="mf"&gt;.&lt;/span&gt;&lt;span class="s2"&gt;"&amp;lt;br&amp;gt;"&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
        &lt;span class="cp"&gt;?&amp;gt;&lt;/span&gt;
    &lt;span class="nt"&gt;&amp;lt;/body&amp;gt;&lt;/span&gt;
&lt;span class="nt"&gt;&amp;lt;/html&amp;gt;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;h2 id="重启 docker 服务，容器全部退出的解决办法"&gt;重启 docker 服务，容器全部退出的解决办法&lt;/h2&gt;&lt;h3 id="在启动是指定自动重启"&gt;&lt;strong&gt;在启动是指定自动重启&lt;/strong&gt;&lt;/h3&gt;&lt;pre class="highlight docker"&gt;&lt;code&gt;docker run  --restart=always
&lt;/code&gt;&lt;/pre&gt;&lt;h3 id="修改 docker 默认配置文件"&gt;&lt;strong&gt;修改 docker 默认配置文件&lt;/strong&gt;&lt;/h3&gt;&lt;pre class="highlight json"&gt;&lt;code&gt;&lt;span class="err"&gt;#&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="err"&gt;添加上下面这行&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;span class="nl"&gt;"live-restore"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="kc"&gt;true&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;docker server 配置文件 /etc/docker/daemon.json 参考&lt;/p&gt;
&lt;pre class="highlight json"&gt;&lt;code&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="err"&gt;root@docker&lt;/span&gt;&lt;span class="mi"&gt;02&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="err"&gt;~&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;&lt;span class="err"&gt;#&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="err"&gt;cat&lt;/span&gt;&lt;span class="w"&gt;  &lt;/span&gt;&lt;span class="err"&gt;/etc/docker/daemon.json&lt;/span&gt;&lt;span class="w"&gt; 
&lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"registry-mirrors"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="s2"&gt;"https://registry.docker-cn.com"&lt;/span&gt;&lt;span class="p"&gt;],&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"graph"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"/opt/mydocker"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="err"&gt;#&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="err"&gt;修改数据的存放目录到/opt/mydocker/，原/var/lib/docker/&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"insecure-registries"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="s2"&gt;"10.0.0.100:5000"&lt;/span&gt;&lt;span class="p"&gt;],&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"live-restore"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="kc"&gt;true&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;重启生效，只对在此之后启动的容器生效&lt;/p&gt;
&lt;pre class="highlight docker"&gt;&lt;code&gt;[root@docker01 ~]# systemctl restart  docker.service
&lt;/code&gt;&lt;/pre&gt;&lt;h2 id="Docker 网络类型"&gt;Docker 网络类型&lt;/h2&gt;
&lt;p&gt;&lt;img src="/uploads/photo/wangbaochen/70d50add-38a5-40b1-8d8a-98a015e4d04c.png!large" title="" alt=""&gt;&lt;/p&gt;
&lt;h3 id="docker 的网络类型"&gt;&lt;strong&gt;docker 的网络类型&lt;/strong&gt;&lt;/h3&gt;
&lt;p&gt;&lt;img src="/uploads/photo/wangbaochen/e21a3f39-3729-4c85-9735-cd576009e947.png!large" title="" alt=""&gt;&lt;/p&gt;

&lt;p&gt;Bridge 默认 docker 网络隔离基于网络命名空间，在物理机上创建 docker 容器时会为每一个 docker 容器分配网络命名空间，并且把容器 IP 桥接到物理机的虚拟网桥上。&lt;/p&gt;
&lt;h3 id="不为容器配置网络功能"&gt;&lt;strong&gt;不为容器配置网络功能&lt;/strong&gt;&lt;/h3&gt;
&lt;p&gt;此模式下创建容器是不会为容器配置任何网络参数的，如：容器网卡、IP、通信路由等，全部需要自己去配置。&lt;/p&gt;
&lt;pre class="highlight docker"&gt;&lt;code&gt;[root@docker01 ~]# docker run  -it --network none busybox:latest  /bin/sh 
/ # ip a
1: lo: &amp;lt;LOOPBACK,UP,LOWER_UP&amp;gt; mtu 65536 qdisc noqueue 
    link/loopback 00:00:00:00:00:00 brd 00:00:00:00:00:00
    inet 127.0.0.1/8 scope host lo
       valid_lft forever preferred_lft forever
&lt;/code&gt;&lt;/pre&gt;&lt;h3 id="与其他容器共享网络配置"&gt;&lt;strong&gt;与其他容器共享网络配置&lt;/strong&gt;&lt;/h3&gt;
&lt;p&gt;此模式和 host 模式很类似，只是此模式创建容器共享的是其他容器的 IP 和端口而不是物理机，此模式容器自身是不会配置网络和端口，创建此模式容器进去后，你会发现里边的 IP 是你所指定的那个容器 IP 并且端口也是共享的，而且其它还是互相隔离的，如进程等。&lt;/p&gt;
&lt;pre class="highlight docker"&gt;&lt;code&gt;[root@docker01 ~]# docker run  -it --network container:mywordpress_db_1  busybox:latest  /bin/sh 
/ # ip a
1: lo: &amp;lt;LOOPBACK,UP,LOWER_UP&amp;gt; mtu 65536 qdisc noqueue 
    link/loopback 00:00:00:00:00:00 brd 00:00:00:00:00:00
    inet 127.0.0.1/8 scope host lo
       valid_lft forever preferred_lft forever
105: eth0@if106: &amp;lt;BROADCAST,MULTICAST,UP,LOWER_UP,M-DOWN&amp;gt; mtu 1500 qdisc noqueue 
    link/ether 02:42:ac:12:00:03 brd ff:ff:ff:ff:ff:ff
    inet 172.18.0.3/16 brd 172.18.255.255 scope global eth0
       valid_lft forever preferred_lft forever
&lt;/code&gt;&lt;/pre&gt;&lt;h3 id="使用宿主机网络"&gt;&lt;strong&gt;使用宿主机网络&lt;/strong&gt;&lt;/h3&gt;
&lt;p&gt;此模式创建的容器没有自己独立的网络命名空间，是和物理机共享一个 Network Namespace，并且共享物理机的所有端口与 IP，并且这个模式认为是不安全的。&lt;/p&gt;
&lt;pre class="highlight docker"&gt;&lt;code&gt;[root@docker01 ~]# docker run  -it --network host  busybox:latest  /bin/sh
&lt;/code&gt;&lt;/pre&gt;&lt;h3 id="查看网络列表"&gt;&lt;strong&gt;查看网络列表&lt;/strong&gt;&lt;/h3&gt;&lt;pre class="highlight docker"&gt;&lt;code&gt;[root@docker01 ~]# docker network list 
NETWORK ID          NAME                  DRIVER              SCOPE
b15e8a720d3b        bridge                bridge              local
345d65b4c2a0        host                  host                local
bc5e2a32bb55        mywordpress_default   bridge              local
ebf76eea91bb        none                  null                local
&lt;/code&gt;&lt;/pre&gt;&lt;h3 id="用 PIPEWORK 为 docker 容器配置独立 IP"&gt;&lt;strong&gt;用 PIPEWORK 为 docker 容器配置独立 IP&lt;/strong&gt;&lt;/h3&gt;
&lt;p&gt;1、安装 pipework&lt;/p&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;wget https://github.com/jpetazzo/pipework/archive/master.zip
unzip master.zip 
&lt;span class="nb"&gt;cp &lt;/span&gt;pipework-master/pipework  /usr/local/bin/
&lt;span class="nb"&gt;chmod&lt;/span&gt; +x /usr/local/bin/pipework
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;2、配置桥接网卡&lt;/p&gt;

&lt;p&gt;安装桥接工具&lt;/p&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;yum &lt;span class="nb"&gt;install &lt;/span&gt;bridge-utils.x86_64 &lt;span class="nt"&gt;-y&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;修改网卡配置，实现桥接&lt;/p&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="c"&gt;# 修改eth0配置，让br0实现桥接&lt;/span&gt;
&lt;span class="o"&gt;[&lt;/span&gt;root@docker01 ~]# &lt;span class="nb"&gt;cat&lt;/span&gt; /etc/sysconfig/network-scripts/ifcfg-eth0 
&lt;span class="nv"&gt;TYPE&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;Ethernet
&lt;span class="nv"&gt;BOOTPROTO&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;static
&lt;span class="nv"&gt;NAME&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;eth0
&lt;span class="nv"&gt;DEVICE&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;eth0
&lt;span class="nv"&gt;ONBOOT&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="nb"&gt;yes
&lt;/span&gt;&lt;span class="nv"&gt;BRIDGE&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;br0
&lt;span class="o"&gt;[&lt;/span&gt;root@docker01 ~]# &lt;span class="nb"&gt;cat&lt;/span&gt; /etc/sysconfig/network-scripts/ifcfg-br0 
&lt;span class="nv"&gt;TYPE&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;Bridge
&lt;span class="nv"&gt;BOOTPROTO&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;static
&lt;span class="nv"&gt;NAME&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;br0
&lt;span class="nv"&gt;DEVICE&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;br0
&lt;span class="nv"&gt;ONBOOT&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="nb"&gt;yes
&lt;/span&gt;&lt;span class="nv"&gt;IPADDR&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;10.0.0.100
&lt;span class="nv"&gt;NETMASK&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;255.255.255.0
&lt;span class="nv"&gt;GATEWAY&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;10.0.0.254
&lt;span class="nv"&gt;DNS1&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;223.5.5.5
&lt;span class="c"&gt;# 重启网络&lt;/span&gt;
&lt;span class="o"&gt;[&lt;/span&gt;root@docker01 ~]# /etc/init.d/network restart
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;3、运行一个容器镜像测试：&lt;/p&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;pipework br0 &lt;span class="si"&gt;$(&lt;/span&gt;docker run &lt;span class="nt"&gt;-d&lt;/span&gt; &lt;span class="nt"&gt;-it&lt;/span&gt; &lt;span class="nt"&gt;-p&lt;/span&gt; 6880:80 &lt;span class="nt"&gt;--name&lt;/span&gt;  httpd_pw httpd&lt;span class="si"&gt;)&lt;/span&gt; 10.0.0.220/24@10.0.0.254
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;在其他主机上测试端口及连通性&lt;/p&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="o"&gt;[&lt;/span&gt;root@docker01 ~]# curl 10.0.0.220
&amp;lt;html&amp;gt;&amp;lt;body&amp;gt;&amp;lt;h1&amp;gt;It works!&amp;lt;/h1&amp;gt;&amp;lt;/body&amp;gt;&amp;lt;/html&amp;gt;
&lt;span class="o"&gt;[&lt;/span&gt;root@docker01 ~]# ping 10.0.0.220 &lt;span class="nt"&gt;-c&lt;/span&gt; 1
PING 10.0.0.220 &lt;span class="o"&gt;(&lt;/span&gt;10.0.0.220&lt;span class="o"&gt;)&lt;/span&gt; 56&lt;span class="o"&gt;(&lt;/span&gt;84&lt;span class="o"&gt;)&lt;/span&gt; bytes of data.
64 bytes from 10.0.0.220: &lt;span class="nv"&gt;icmp_seq&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;1 &lt;span class="nv"&gt;ttl&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;64 &lt;span class="nb"&gt;time&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;0.043 ms
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;4、再运行一个容器，设置网路类型为 none：&lt;/p&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;pipework br0 &lt;span class="si"&gt;$(&lt;/span&gt;docker run &lt;span class="nt"&gt;-d&lt;/span&gt; &lt;span class="nt"&gt;-it&lt;/span&gt; &lt;span class="nt"&gt;--net&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;none &lt;span class="nt"&gt;--name&lt;/span&gt; &lt;span class="nb"&gt;test &lt;/span&gt;httpd:2.4&lt;span class="si"&gt;)&lt;/span&gt; 10.0.0.221/24@10.0.0.254
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;进行访问测试&lt;/p&gt;
&lt;pre class="highlight html"&gt;&lt;code&gt;[root@docker01 ~]# curl 10.0.0.221
&lt;span class="nt"&gt;&amp;lt;html&amp;gt;&amp;lt;body&amp;gt;&amp;lt;h1&amp;gt;&lt;/span&gt;It works!&lt;span class="nt"&gt;&amp;lt;/h1&amp;gt;&amp;lt;/body&amp;gt;&amp;lt;/html&amp;gt;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;5、重启容器后需要再次指定：&lt;/p&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;pipework br0 testduliip  172.16.146.113/24@172.16.146.1
pipework br0 testduliip01  172.16.146.112/24@172.16.146.1
&lt;/code&gt;&lt;/pre&gt;&lt;h3 id="Docker 跨主机通信之 macvlan"&gt;&lt;strong&gt;Docker 跨主机通信之 macvlan&lt;/strong&gt;&lt;/h3&gt;
&lt;p&gt;创建网络&lt;/p&gt;
&lt;pre class="highlight docker"&gt;&lt;code&gt;[root@docker01 ~]# docker network  create --driver macvlan  --subnet 10.1.0.0/24 --gateway 10.1.0.254 -o parent=eth0  macvlan_1
33a1f41dcc074f91b5bd45e7dfedabfb2b8ec82db16542f05213839a119b62ca
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;设置网卡为混杂模式&lt;/p&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;ip &lt;span class="nb"&gt;link set &lt;/span&gt;eth0 promisc on
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;创建使用 macvlan 网络容器&lt;/p&gt;
&lt;pre class="highlight docker"&gt;&lt;code&gt;[root@docker02 ~]# docker run  -it --network macvlan_1  --ip=10.1.0.222 busybox /b
&lt;/code&gt;&lt;/pre&gt;&lt;h2 id="docker 企业级镜像仓库 harbor"&gt;docker 企业级镜像仓库 harbor&lt;/h2&gt;
&lt;p&gt;容器管理&lt;/p&gt;
&lt;pre class="highlight docker"&gt;&lt;code&gt;[root@docker01 harbor]# pwd
/opt/harbor
[root@docker01 harbor]# docker-compose stop
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;1、安装 docker、docker-compose、下载 harbor&lt;/p&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="nb"&gt;cd&lt;/span&gt; /opt &lt;span class="o"&gt;&amp;amp;&amp;amp;&lt;/span&gt; https://storage.googleapis.com/harbor-releases/harbor-offline-installer-v1.3.0.tgz
&lt;span class="nb"&gt;tar &lt;/span&gt;xf harbor-offline-installer-v1.3.0.tgz
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;2、修改主机及 web 界面密码&lt;/p&gt;
&lt;pre class="highlight viml"&gt;&lt;code&gt;&lt;span class="p"&gt;[&lt;/span&gt;root@docker01 harbor&lt;span class="p"&gt;]&lt;/span&gt;# &lt;span class="k"&gt;vim&lt;/span&gt; harbor&lt;span class="p"&gt;.&lt;/span&gt;cfg 
    ···
    &lt;span class="nb"&gt;hostname&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="m"&gt;10&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="m"&gt;0&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="m"&gt;0&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="m"&gt;100&lt;/span&gt;
    harbor_admin_password &lt;span class="p"&gt;=&lt;/span&gt; Harbor12345
    ···
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;3、执行安装脚本&lt;/p&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="o"&gt;[&lt;/span&gt;root@docker01 harbor]# ./install.sh
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;浏览器访问 &lt;a href="http://10.0.0.11" rel="nofollow" target="_blank"&gt;http://10.0.0.11&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;img src="/uploads/photo/wangbaochen/1114fd15-2ade-40ef-8737-3d3e91626a30.png!large" title="" alt=""&gt;&lt;/p&gt;

&lt;p&gt;添加一个项目&lt;/p&gt;

&lt;p&gt;&lt;img src="/uploads/photo/wangbaochen/275a6a52-681f-4941-aa99-10b44d35f77c.png!large" title="" alt=""&gt;&lt;/p&gt;

&lt;p&gt;4、镜像推送到仓库的指定项目&lt;/p&gt;
&lt;pre class="highlight docker"&gt;&lt;code&gt;[root@docker02 ~]# docker  tag centos:6.8  10.0.0.100/clsn/centos6.8:1.0
[root@docker02 ~]#  
[root@docker02 ~]# docker images 
REPOSITORY                  TAG                 IMAGE ID            CREATED             SIZE
busybox                     latest              5b0d59026729        8 days ago          1.15MB
10.0.0.100/clsn/centos6.8   1.0                 6704d778b3ba        2 months ago        195MB
centos                      6.8                 6704d778b3ba        2 months ago        195MB
[root@docker02 ~]# docker login 10.0.0.100
Username: admin
Password: 
Login Succeeded
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;5、推送镜像&lt;/p&gt;
&lt;pre class="highlight docker"&gt;&lt;code&gt;[root@docker02 ~]# docker push 10.0.0.100/clsn/centos6.8 
The push refers to repository [10.0.0.100/clsn/centos6.8]
e00c9229b481: Pushing  13.53MB/194.5MB
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;6、在 web 界面里查看&lt;/p&gt;

&lt;p&gt;&lt;img src="/uploads/photo/wangbaochen/75354f34-e429-4a50-8533-4ecb8ade17b8.png!large" title="" alt=""&gt;&lt;/p&gt;
&lt;h3 id="使用容器的建议"&gt;&lt;strong&gt;使用容器的建议&lt;/strong&gt;&lt;/h3&gt;
&lt;p&gt;1. 不要以拆分方式进行应用程序发布&lt;/p&gt;

&lt;p&gt;2. 不要创建大型镜像&lt;/p&gt;

&lt;p&gt;3. 不要在单个容器中运行多个进程&lt;/p&gt;

&lt;p&gt;4. 不要再镜像内保存凭证，不要依赖 IP 地址&lt;/p&gt;

&lt;p&gt;5. 以非 root 用户运行进程&lt;/p&gt;

&lt;p&gt;6. 不要使用 “最新” 标签&lt;/p&gt;

&lt;p&gt;7. 不要利用运行中的容器创建镜像&lt;/p&gt;

&lt;p&gt;8. 不要使用单层镜像&lt;/p&gt;

&lt;p&gt;9. 不要将数据存放在容器内&lt;/p&gt;
&lt;h3 id="关于 Docker 容器的监控"&gt;&lt;strong&gt;关于 Docker 容器的监控&lt;/strong&gt;&lt;/h3&gt;
&lt;p&gt;&lt;strong&gt;容器的基本信息&lt;/strong&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;包括容器的数量、ID、名称、镜像、启动命令、端口等信息&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;strong&gt;容器的运行状态&lt;/strong&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;统计各状态的容器的数量，包括运行中、暂停、停止及异常退出&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;strong&gt;容器的用量信息&lt;/strong&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;统计容器的 CPU 使用率、内存使用量、块设备 I/O 使用量、网络使用情况等资源的使用情况&lt;/li&gt;
&lt;/ul&gt;</description>
      <author>wangbaochen</author>
      <pubDate>Wed, 14 Apr 2021 13:42:16 +0800</pubDate>
      <link>https://xxb.lttc.cn/topics/227</link>
      <guid>https://xxb.lttc.cn/topics/227</guid>
    </item>
    <item>
      <title>集团 ERP 桌面工具 - 短信平台显示不全的解决方案</title>
      <description>&lt;p&gt;安装完集团 ERP 后默认桌面工具中的&lt;code&gt;短信平台&lt;/code&gt;会因为尺寸原因显示不全，如图：&lt;/p&gt;

&lt;p&gt;&lt;img src="/uploads/photo/xinggang/833087c5-89b1-4a92-a096-61835cab9147.png!large" title="" alt=""&gt;&lt;/p&gt;
&lt;h3 id="解决方案"&gt;解决方案&lt;/h3&gt;
&lt;p&gt;可以通过&lt;code&gt;桌面工具设置&lt;/code&gt;窗口的&lt;code&gt;高度&lt;/code&gt;和&lt;code&gt;栏数&lt;/code&gt;来调整显示尺寸。&lt;/p&gt;

&lt;p&gt;1、点击 ERP 主窗口右下角&lt;code&gt;桌面工具设置&lt;/code&gt;图标，打开&lt;code&gt;桌面工具设置&lt;/code&gt;小窗口。&lt;/p&gt;

&lt;p&gt;2、将高度设置为&lt;code&gt;600&lt;/code&gt;，将栏数设置为&lt;code&gt;2&lt;/code&gt;，保存后关闭设置窗口。&lt;/p&gt;

&lt;p&gt;&lt;img src="/uploads/photo/xinggang/5242e75b-3c5e-477e-befa-4fba4d80097d.png!large" title="" alt=""&gt;&lt;/p&gt;

&lt;p&gt;3、最终调整后效果如图。&lt;/p&gt;

&lt;p&gt;&lt;img src="/uploads/photo/xinggang/1bebcdde-364d-4774-a1da-378b8a314ac7.png!large" title="" alt=""&gt;&lt;/p&gt;</description>
      <author>xinggang</author>
      <pubDate>Mon, 12 Apr 2021 16:47:40 +0800</pubDate>
      <link>https://xxb.lttc.cn/topics/226</link>
      <guid>https://xxb.lttc.cn/topics/226</guid>
    </item>
    <item>
      <title>【DEV18.1】导出功能升级</title>
      <description>&lt;h2 id="由于用户导出需求的日渐提高，添加ExportData(ExportOptionsEx)重载，开放导出设置选项，满足用户的不同需求"&gt;由于用户导出需求的日渐提高，添加 ExportData(ExportOptionsEx) 重载，开放导出设置选项，满足用户的不同需求&lt;/h2&gt;&lt;h3 id="ExportOptionsEx类位于WnPlatform中，以下为属性："&gt;ExportOptionsEx 类位于 WnPlatform 中，以下为属性：&lt;/h3&gt;&lt;h4 id="1. SheetName"&gt;1. SheetName&lt;/h4&gt;
&lt;ul&gt;
&lt;li&gt;&lt;p&gt;名称：工作表名称&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;类型：string&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;描述：设置导出文档在 Excel 中的工作表名称&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;默认值：string.Empty&lt;/p&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;h4 id="2. ShowPageTitle"&gt;2. ShowPageTitle&lt;/h4&gt;
&lt;ul&gt;
&lt;li&gt;&lt;p&gt;名称：显示每页标题&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;类型：DefaultBoolean&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;描述：设置是否为导出文档的每个打印预览页显示标题&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;默认值：DefaultBoolean.Default&lt;/p&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;h4 id="3. LayoutMode"&gt;3. LayoutMode&lt;/h4&gt;
&lt;ul&gt;
&lt;li&gt;&lt;p&gt;名称：文档模式&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;类型：LayoutMode&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;描述：设置数据是以常规模式还是以本机 Excel 表格的形式导出&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;默认值：LayoutMode.Standard&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;来源：Devexpress.Printing.v18.1.Core&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;展示：&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Standard - 默认导出模式&lt;br&gt;
&lt;img src="/uploads/photo/admin/7f3607a2-efa0-4465-982e-280a5fe024a5.png!large" title="" alt=""&gt;
&lt;/li&gt;
&lt;li&gt;Table - 启用导出到本地 Excel 表格&lt;br&gt;
&lt;img src="/uploads/photo/admin/17b14d59-70a1-4c46-813d-51054be3b6cd.png!large" title="" alt=""&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;h4 id="4. AllowBandHeaderCellMerge"&gt;4. AllowBandHeaderCellMerge&lt;/h4&gt;
&lt;ul&gt;
&lt;li&gt;&lt;p&gt;名称：允许合并表头&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;类型：DefaultBoolean&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;描述：设置导出文档中的带头是否启用单元格合并&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;默认值：DefaultBoolean.Default，DefaultBoolean.Default 值相当于 DefaultBoolean.True&lt;/p&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;h4 id="5. AllowCellMerge"&gt;5. AllowCellMerge&lt;/h4&gt;
&lt;ul&gt;
&lt;li&gt;&lt;p&gt;名称：允许单元格合并&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;类型：DefaultBoolean&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;描述：设置是否在导出的文档中启用单元格合并&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;默认值：DefaultBoolean.Default&lt;/p&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;h4 id="6. AllowFixedColumns"&gt;6. AllowFixedColumns&lt;/h4&gt;
&lt;ul&gt;
&lt;li&gt;&lt;p&gt;名称：允许固定列&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;类型：DefaultBoolean&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;描述：设置在网格控件中启用的左固定列是否在导出的文档中固定&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;默认值：DefaultBoolean.Default&lt;/p&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;h4 id="7. AllowFixedColumnHeaderPanel"&gt;7. AllowFixedColumnHeaderPanel&lt;/h4&gt;
&lt;ul&gt;
&lt;li&gt;&lt;p&gt;名称：允许固定表头行&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;类型：DefaultBoolean&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;描述：设置表头是否固定在导出文档的顶部，而不是垂直滚动&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;默认值：DefaultBoolean.Default&lt;/p&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;h4 id="8. ShowColumnHeaders"&gt;8. ShowColumnHeaders&lt;/h4&gt;
&lt;ul&gt;
&lt;li&gt;&lt;p&gt;名称：是否显示表头&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;类型：DefaultBoolean&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;描述：设置列头在导出的文档中是否可见&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;默认值：DefaultBoolean.Default&lt;/p&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;h4 id="9. AllowGrouping"&gt;9. AllowGrouping&lt;/h4&gt;
&lt;ul&gt;
&lt;li&gt;&lt;p&gt;名称：是否允许分组&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;类型：DefaultBoolean&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;描述：设置从源导出到输出文档的数据是否分组&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;默认值：DefaultBoolean.Default&lt;/p&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;h4 id="10. AllowSortingAndFiltering"&gt;10. AllowSortingAndFiltering&lt;/h4&gt;
&lt;ul&gt;
&lt;li&gt;&lt;p&gt;名称：是否允许排序和过滤&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;类型：DefaultBoolean&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;描述：设置是否在导出的文档中启用列的排序和过滤功能&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;默认值：DefaultBoolean.Default&lt;/p&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;h4 id="11. ShowGroupSummaries"&gt;11. ShowGroupSummaries&lt;/h4&gt;
&lt;ul&gt;
&lt;li&gt;&lt;p&gt;名称：是否显示分组摘要&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;类型：DefaultBoolean&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;描述：设置是否在导出的文档中启用分组摘要&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;默认值：DefaultBoolean.Default，DefaultBoolean.Default 值相当于 DefaultBoolean.True&lt;/p&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;h4 id="12. AllowLookupValues"&gt;12. AllowLookupValues&lt;/h4&gt;
&lt;ul&gt;
&lt;li&gt;&lt;p&gt;名称：是否导出下拉列表&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;类型：DefaultBoolean&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;描述：设置是否导出组合框和查找列的查找值&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;默认值：DefaultBoolean.Default&lt;/p&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;h4 id="13. AllowConditionFormatting"&gt;13. AllowConditionFormatting&lt;/h4&gt;
&lt;ul&gt;
&lt;li&gt;&lt;p&gt;名称：是否导出条件规则&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;类型：DefaultBoolean&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;描述：设置是否在导出的文档中保留应用于列的条件格式化规则&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;默认值：DefaultBoolean.Default，DefaultBoolean.Default 值相当于 DefaultBoolean.True&lt;/p&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;h4 id="14.TextExportMode"&gt;14.TextExportMode&lt;/h4&gt;
&lt;ul&gt;
&lt;li&gt;&lt;p&gt;名称：文本导出模式&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;类型：TextExportMode&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;描述：指定是否在导出的 XLS（或 XLSX）文档中的单元格中使用绑定数据集的数据字段格式。&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;默认值：TextExportMode.Value：将所有数据字段导出到 XLS（或 XLSX）文件中，使用与原始文件中相同的格式。TextExportMode.Text：将所有的数据字段以字符串的形式导出到 XLS（或 XLSX）文件，并将相应的非 HTML 格式嵌入到这些字符串中。HTML 标签被忽略。&lt;/p&gt;&lt;/li&gt;
&lt;/ul&gt;</description>
      <author>wangbaochen</author>
      <pubDate>Thu, 08 Apr 2021 15:24:05 +0800</pubDate>
      <link>https://xxb.lttc.cn/topics/225</link>
      <guid>https://xxb.lttc.cn/topics/225</guid>
    </item>
    <item>
      <title>【转】解决 winform 窗体控件多，加载慢、卡顿的问题</title>
      <description>&lt;pre class="highlight csharp"&gt;&lt;code&gt;&lt;span class="k"&gt;protected&lt;/span&gt; &lt;span class="k"&gt;override&lt;/span&gt; &lt;span class="n"&gt;CreateParams&lt;/span&gt; &lt;span class="n"&gt;CreateParams&lt;/span&gt;
&lt;span class="p"&gt;{&lt;/span&gt;
     &lt;span class="k"&gt;get&lt;/span&gt;
     &lt;span class="p"&gt;{&lt;/span&gt;
         &lt;span class="n"&gt;CreateParams&lt;/span&gt; &lt;span class="n"&gt;cp&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="k"&gt;base&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;CreateParams&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
         &lt;span class="n"&gt;cp&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;ExStyle&lt;/span&gt; &lt;span class="p"&gt;|=&lt;/span&gt; &lt;span class="m"&gt;0x02000000&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt; &lt;span class="c1"&gt;// 用双缓冲绘制窗口的所有子控件&lt;/span&gt;
         &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="n"&gt;cp&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
     &lt;span class="p"&gt;}&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;将此方法放在需要优化的窗体代码中即可。如下图：
&lt;img src="/uploads/photo/wangbaochen/033f06ce-e0ed-4fed-973e-14748ee411e0.png!large" title="" alt=""&gt;&lt;/p&gt;

&lt;p&gt;原&lt;a href="https://blog.csdn.net/weixin_42288432/article/details/103207077" rel="nofollow" target="_blank"&gt;https://blog.csdn.net/weixin_42288432/article/details/103207077&lt;/a&gt;&lt;br&gt;
&lt;a href="/tianjiangang" class="user-mention" title="@tianjiangang"&gt;&lt;i&gt;@&lt;/i&gt;tianjiangang&lt;/a&gt; 田建刚 提供&lt;/p&gt;</description>
      <author>wangbaochen</author>
      <pubDate>Tue, 06 Apr 2021 17:02:21 +0800</pubDate>
      <link>https://xxb.lttc.cn/topics/223</link>
      <guid>https://xxb.lttc.cn/topics/223</guid>
    </item>
    <item>
      <title>DEV18.1 升级问题汇总以及解决方案</title>
      <description>&lt;h2 id="此贴用来收集DEV18.1的升级问题，以及解决方案"&gt;此贴用来收集 DEV18.1 的升级问题，以及解决方案&lt;/h2&gt;</description>
      <author>wangbaochen</author>
      <pubDate>Tue, 06 Apr 2021 09:17:07 +0800</pubDate>
      <link>https://xxb.lttc.cn/topics/222</link>
      <guid>https://xxb.lttc.cn/topics/222</guid>
    </item>
    <item>
      <title>关于【鲁泰集团 ERP】DEV 控件汉化的意见收集贴</title>
      <description>&lt;p&gt;现 DEV 控件官方汉化率为&lt;code&gt;64%&lt;/code&gt;，并且部分汉化不符合阅读习惯，现收集汉化的意见，格式如下：&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;问题原因：无汉化/汉化错误&lt;br&gt;
&lt;/li&gt;
&lt;li&gt;汉化位置：文字/截图&lt;br&gt;
&lt;/li&gt;
&lt;li&gt;汉化建议：建议汉化后的文字&lt;/li&gt;
&lt;/ul&gt;</description>
      <author>admin</author>
      <pubDate>Fri, 02 Apr 2021 17:36:55 +0800</pubDate>
      <link>https://xxb.lttc.cn/topics/221</link>
      <guid>https://xxb.lttc.cn/topics/221</guid>
    </item>
    <item>
      <title>集团 ERP 报错：应用层错误！反序列化时发生错误，原因是：Error GZIP header,first magic byte doesn't match</title>
      <description>&lt;h2 id="问题描述"&gt;问题描述&lt;/h2&gt;
&lt;p&gt;应用层错误！&lt;/p&gt;

&lt;p&gt;反序列化时发生错误，错误原因是：&lt;code&gt;Error GZIP header,first magic byte doesn't match&lt;/code&gt;&lt;/p&gt;
&lt;h2 id="问题截图"&gt;问题截图&lt;/h2&gt;
&lt;p&gt;&lt;img src="/uploads/photo/xinggang/d7c410dd-df43-4289-a451-4c384f3793d4.png!large" title="" alt=""&gt;&lt;/p&gt;
&lt;h2 id="解决方案"&gt;解决方案&lt;/h2&gt;
&lt;blockquote&gt;
&lt;p&gt;待完善...&lt;/p&gt;
&lt;/blockquote&gt;</description>
      <author>xinggang</author>
      <pubDate>Thu, 01 Apr 2021 12:04:54 +0800</pubDate>
      <link>https://xxb.lttc.cn/topics/220</link>
      <guid>https://xxb.lttc.cn/topics/220</guid>
    </item>
    <item>
      <title>关于 flash 过期不显示的解决方案之一</title>
      <description>&lt;p&gt;flash player 将在 2020 年 12 月不再支持&lt;/p&gt;

&lt;p&gt;&lt;img src="/uploads/photo/attachment/2101/thread/4_59_33b6bcd3b0b769d.jpg!large" title="" alt="QQ图片20210113165649"&gt;&lt;/p&gt;

&lt;p&gt;程序不影响使用可以删除两个.swf 文件，删除后将会显示静态图。&lt;br&gt;
也可使用永久解决方案，删除 flash&lt;br&gt;
&lt;a href="https://pan.lttc.cn/s/2LpSRPSzmEtfYQA" rel="nofollow" target="_blank"&gt;https://pan.lttc.cn/s/2LpSRPSzmEtfYQA&lt;/a&gt;&lt;br&gt;
删除之后的隐患为使用 flash 的网页不在显示内容，例如下图，不显示上传按钮&lt;br&gt;
&lt;img src="/uploads/photo/attachment/2101/thread/4_59_445dd676d647414.png!large" title="" alt="Snipaste_2021-01-13_16-59-55"&gt;&lt;/p&gt;

&lt;p&gt;可根据自身情况，合理使用。&lt;/p&gt;</description>
      <author>wangbaochen</author>
      <pubDate>Thu, 14 Jan 2021 01:16:16 +0800</pubDate>
      <link>https://xxb.lttc.cn/topics/218</link>
      <guid>https://xxb.lttc.cn/topics/218</guid>
    </item>
  </channel>
</rss>
